author | Gurzau Raul <rgurzau@mozilla.com> |
Thu, 29 Mar 2018 00:55:16 +0300 | |
changeset 410517 | 6aa3b57955fed5e137d0306478e1a4b424a6d392 |
parent 410516 | c03413e3a8233d7ea0afff73717e1cdeae39fa49 (current diff) |
parent 410414 | b47ea5c26a80ccc400743e5d9ad57268dcee1636 (diff) |
child 410518 | e51ae123ece9088fbcd1831027a355c5069e427c |
child 410574 | c5ab4533710cc2a151495340c712ba66c7acc1ec |
push id | 101504 |
push user | rgurzau@mozilla.com |
push date | Wed, 28 Mar 2018 22:01:17 +0000 |
treeherder | mozilla-inbound@e51ae123ece9 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge |
milestone | 61.0a1 |
first release with | nightly linux32
6aa3b57955fe
/
61.0a1
/
20180328220110
/
files
nightly linux64
6aa3b57955fe
/
61.0a1
/
20180328220110
/
files
nightly win32
6aa3b57955fe
/
61.0a1
/
20180328220110
/
files
nightly win64
6aa3b57955fe
/
61.0a1
/
20180328220110
/
files
nightly mac
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
releases | nightly linux32
61.0a1
/
20180328220110
/
pushlog to previous
nightly linux64
61.0a1
/
20180328220110
/
pushlog to previous
nightly win32
61.0a1
/
20180328220110
/
pushlog to previous
nightly win64
61.0a1
/
20180328220110
/
pushlog to previous
|
--- a/browser/base/content/test/static/browser_all_files_referenced.js +++ b/browser/base/content/test/static/browser_all_files_referenced.js @@ -46,17 +46,16 @@ var whitelist = [ // browser/extensions/pdfjs/content/PdfStreamConverter.jsm {file: "chrome://pdf.js/locale/chrome.properties"}, {file: "chrome://pdf.js/locale/viewer.properties"}, // security/manager/pki/resources/content/device_manager.js {file: "chrome://pippki/content/load_device.xul"}, // Add-on compat - {file: "chrome://global/content/XPCNativeWrapper.js"}, {file: "chrome://global/locale/brand.dtd"}, // The l10n build system can't package string files only for some platforms. // See bug 1339424 for why this is hard to fix. {file: "chrome://global/locale/fallbackMenubar.properties", platforms: ["linux", "win"]}, {file: "chrome://global/locale/printPageSetup.dtd", platforms: ["macosx"]}, {file: "chrome://global/locale/printPreviewProgress.dtd",
--- a/devtools/client/debugger/new/README.mozilla +++ b/devtools/client/debugger/new/README.mozilla @@ -1,13 +1,13 @@ This is the debugger.html project output. See https://github.com/devtools-html/debugger.html -Version 28.0 +Version 29.0 -Comparison: https://github.com/devtools-html/debugger.html/compare/release-28...release-27 +Comparison: https://github.com/devtools-html/debugger.html/compare/release-28...release-29 Packages: - babel-plugin-transform-es2015-modules-commonjs @6.26.0 - babel-preset-react @6.24.1 - react @16.2.0 - react-dom @16.2.0 - webpack @3.11.0
--- a/devtools/client/debugger/new/debugger.css +++ b/devtools/client/debugger/new/debugger.css @@ -1213,16 +1213,17 @@ html[dir="rtl"] .tree-node img.arrow { flex: 1; overflow-x: auto; overflow-y: auto; } .sources-list { flex: 1; display: flex; + overflow: hidden; } .sources-list .tree:focus { outline: none; } .sources-list .managed-tree { flex: 1; @@ -3977,16 +3978,20 @@ html .welcomebox .toggle-button-end.coll } .result-list.big li { padding: 10px; flex-direction: row; border-bottom: 1px solid var(--theme-splitter-color); } +.result-list.small li { + justify-content: space-between; +} + .result-list li:hover { background: var(--theme-tab-toolbar-background); } .result-list li.selected { background: var(--accordion-header-background); }
--- a/devtools/client/debugger/new/debugger.js +++ b/devtools/client/debugger/new/debugger.js @@ -5400,25 +5400,27 @@ var _extends = Object.assign || function /* eslint complexity: ["error", 30]*/ /** * Pause reducer * @module reducers/pause */ exports.getPauseReason = getPauseReason; +exports.getPauseCommand = getPauseCommand; exports.isStepping = isStepping; exports.isPaused = isPaused; exports.getPreviousPauseFrameLocation = getPreviousPauseFrameLocation; exports.isEvaluatingExpression = isEvaluatingExpression; exports.getPopupObjectProperties = getPopupObjectProperties; exports.getIsWaitingOnBreak = getIsWaitingOnBreak; exports.getShouldPauseOnExceptions = getShouldPauseOnExceptions; exports.getShouldIgnoreCaughtExceptions = getShouldIgnoreCaughtExceptions; exports.getCanRewind = getCanRewind; +exports.getExtra = getExtra; exports.getFrames = getFrames; exports.getGeneratedFrameScope = getGeneratedFrameScope; exports.getOriginalFrameScope = getOriginalFrameScope; exports.getFrameScopes = getFrameScopes; exports.getFrameScope = getFrameScope; exports.getSelectedScope = getSelectedScope; exports.getSelectedScopeMappings = getSelectedScopeMappings; exports.getSelectedFrameId = getSelectedFrameId; @@ -5430,31 +5432,32 @@ var _reselect = __webpack_require__(993) var _devtoolsSourceMap = __webpack_require__(1360); var _prefs = __webpack_require__(226); var _sources = __webpack_require__(1369); const createPauseState = exports.createPauseState = () => ({ + extra: {}, why: null, isWaitingOnBreak: false, frames: undefined, selectedFrameId: undefined, frameScopes: { generated: {}, original: {}, mappings: {} }, loadedObjects: {}, shouldPauseOnExceptions: _prefs.prefs.pauseOnExceptions, shouldIgnoreCaughtExceptions: _prefs.prefs.ignoreCaughtExceptions, canRewind: false, debuggeeUrl: "", - command: "", + command: null, previousLocation: null }); const emptyPauseState = { pause: null, frames: null, frameScopes: { generated: {}, @@ -5490,26 +5493,27 @@ function update(state = createPauseState case "MAP_FRAMES": { return _extends({}, state, { frames: action.frames }); } case "ADD_SCOPES": { - const { frame, status, value } = action; + const { frame, extra, status, value } = action; const selectedFrameId = frame.id; const generated = _extends({}, state.frameScopes.generated, { [selectedFrameId]: { pending: status !== "done", scope: value } }); return _extends({}, state, { + extra: extra, frameScopes: _extends({}, state.frameScopes, { generated }) }); } case "TRAVEL_TO": return _extends({}, state, action.data.paused); @@ -5573,27 +5577,27 @@ function update(state = createPauseState shouldIgnoreCaughtExceptions }); case "COMMAND": { return action.status === "start" ? _extends({}, state, emptyPauseState, { command: action.command, previousLocation: buildPreviousLocation(state, action) - }) : _extends({}, state, { command: "" }); + }) : _extends({}, state, { command: null }); } case "RESUME": // We clear why on resume because we need it to decide if // we shoul re-evaluate watch expressions. return _extends({}, state, { why: null }); case "EVALUATE_EXPRESSION": return _extends({}, state, { - command: action.status === "start" ? "expression" : "" + command: action.status === "start" ? "expression" : null }); case "NAVIGATE": return _extends({}, state, emptyPauseState, { debuggeeUrl: action.url }); } return state; } @@ -5630,18 +5634,22 @@ function buildPreviousLocation(state, ac const getPauseState = state => state.pause; const getAllPopupObjectProperties = exports.getAllPopupObjectProperties = (0, _reselect.createSelector)(getPauseState, pauseWrapper => pauseWrapper.loadedObjects); function getPauseReason(state) { return state.pause.why; } +function getPauseCommand(state) { + return state.pause && state.pause.command; +} + function isStepping(state) { - return ["stepIn", "stepOver", "stepOut"].includes(state.pause.command); + return ["stepIn", "stepOver", "stepOut"].includes(getPauseCommand(state)); } function isPaused(state) { return !!getFrames(state); } function getPreviousPauseFrameLocation(state) { return state.pause.previousLocation; @@ -5666,16 +5674,20 @@ function getShouldPauseOnExceptions(stat function getShouldIgnoreCaughtExceptions(state) { return state.pause.shouldIgnoreCaughtExceptions; } function getCanRewind(state) { return state.pause.canRewind; } +function getExtra(state) { + return state.pause.extra; +} + function getFrames(state) { return state.pause.frames; } function getGeneratedFrameScope(state, frameId) { if (!frameId) { return null; } @@ -6383,57 +6395,61 @@ Object.defineProperty(exports, "__esModu }); exports.setSourceMetaData = setSourceMetaData; exports.setSymbols = setSymbols; exports.setOutOfScopeLocations = setOutOfScopeLocations; exports.setPausePoints = setPausePoints; var _selectors = __webpack_require__(3590); +var _pause = __webpack_require__(1639); + var _setInScopeLines = __webpack_require__(1781); var _parser = __webpack_require__(1365); var _promise = __webpack_require__(1653); -/* 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/>. */ - function setSourceMetaData(sourceId) { return async ({ dispatch, getState }) => { const source = (0, _selectors.getSource)(getState(), sourceId); if (!source || !source.text || source.isWasm) { return; } const framework = await (0, _parser.getFramework)(source.id); dispatch({ type: "SET_SOURCE_METADATA", sourceId: source.id, sourceMetaData: { framework } }); }; -} +} /* 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/>. */ function setSymbols(sourceId) { return async ({ dispatch, getState }) => { const source = (0, _selectors.getSource)(getState(), sourceId); if (!source || !source.text || source.isWasm || (0, _selectors.hasSymbols)(getState(), source)) { return; } await dispatch({ type: "SET_SYMBOLS", source: source.toJS(), [_promise.PROMISE]: (0, _parser.getSymbols)(source.id) }); + if ((0, _selectors.isPaused)(getState())) { + await dispatch((0, _pause.mapFrames)()); + } + await dispatch(setPausePoints(sourceId)); await dispatch(setSourceMetaData(sourceId)); }; } function setOutOfScopeLocations() { return async ({ dispatch, getState }) => { const location = (0, _selectors.getSelectedLocation)(getState()); @@ -7707,64 +7723,35 @@ module.exports = { /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.containsPosition = containsPosition; -exports.findClosestScope = findClosestScope; exports.getASTLocation = getASTLocation; exports.findScopeByName = findScopeByName; var _parser = __webpack_require__(1365); -function containsPosition(a, b) { - const startsBefore = a.start.line < b.line || a.start.line === b.line && a.start.column <= b.column; - const endsAfter = a.end.line > b.line || a.end.line === b.line && a.end.column >= b.column; - - return startsBefore && endsAfter; -} /* 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/>. */ - -function findClosestScope(functions, location) { - return functions.reduce((found, currNode) => { - if (currNode.name === "anonymous" || !containsPosition(currNode.location, { - line: location.line, - column: location.column || 0 - })) { - return found; - } - - if (!found) { - return currNode; - } - - if (found.location.start.line > currNode.location.start.line) { - return found; - } - if (found.location.start.line === currNode.location.start.line && found.location.start.column > currNode.location.start.column) { - return found; - } - - return currNode; - }, null); -} +var _ast = __webpack_require__(1638); + +/* 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/>. */ function getASTLocation(source, symbols, location) { if (source.isWasm || !symbols || symbols.loading) { return { name: undefined, offset: location }; } const functions = [...symbols.functions]; - const scope = findClosestScope(functions, location); + const scope = (0, _ast.findClosestFunction)(functions, location); if (scope) { // we only record the line, but at some point we may // also do column offsets const line = location.line - scope.location.start.line; return { name: scope.name, offset: { line, column: undefined } }; @@ -21925,17 +21912,17 @@ var _reactRedux = __webpack_require__(35 var _devtoolsContextmenu = __webpack_require__(1413); var _devtoolsSourceMap = __webpack_require__(1360); var _clipboard = __webpack_require__(1388); var _function = __webpack_require__(1597); -var _astBreakpointLocation = __webpack_require__(1416); +var _ast = __webpack_require__(1638); var _editor = __webpack_require__(1358); var _source = __webpack_require__(1356); var _selectors = __webpack_require__(3590); var _actions = __webpack_require__(1354); @@ -22133,18 +22120,18 @@ const { exports.default = (0, _reactRedux.connect)(state => { const selectedSource = (0, _selectors.getSelectedSource)(state); return { selectedLocation: (0, _selectors.getSelectedLocation)(state), selectedSource, hasPrettyPrint: !!(0, _selectors.getPrettySource)(state, selectedSource.get("id")), contextMenu: (0, _selectors.getContextMenu)(state), - getFunctionText: line => (0, _function.findFunctionText)(line, selectedSource.toJS(), (0, _selectors.getSymbols)(state, selectedSource.toJS())), - getFunctionLocation: line => (0, _astBreakpointLocation.findClosestScope)((0, _selectors.getSymbols)(state, selectedSource.toJS()).functions, { + getFunctionText: line => (0, _function.findFunctionText)(line, selectedSource.toJS(), (0, _selectors.getSymbols)(state, selectedSource)), + getFunctionLocation: line => (0, _ast.findClosestFunction)((0, _selectors.getSymbols)(state, selectedSource).functions, { line, column: Infinity }) }; }, { addExpression, evaluateInConsole, flashLineRange, @@ -22162,26 +22149,29 @@ exports.default = (0, _reactRedux.connec "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.findFunctionText = findFunctionText; -var _astBreakpointLocation = __webpack_require__(1416); +var _ast = __webpack_require__(1638); var _indentation = __webpack_require__(1438); /* 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/>. */ function findFunctionText(line, source, symbols) { - const func = (0, _astBreakpointLocation.findClosestScope)(symbols.functions, { line, column: Infinity }); + const func = (0, _ast.findClosestFunction)(symbols.functions, { + line, + column: Infinity + }); if (!func) { return null; } const { location: { start, end } } = func; const lines = source.text.split("\n"); const firstLine = lines[start.line - 1].slice(start.column); const lastLine = lines[end.line - 1].slice(0, end.column); @@ -22466,33 +22456,35 @@ var _CommandBar2 = _interopRequireDefaul var _UtilsBar = __webpack_require__(1609); var _UtilsBar2 = _interopRequireDefault(_UtilsBar); var _BreakpointsDropdown = __webpack_require__(1790); var _BreakpointsDropdown2 = _interopRequireDefault(_BreakpointsDropdown); +var _FrameworkComponent = __webpack_require__(3623); + +var _FrameworkComponent2 = _interopRequireDefault(_FrameworkComponent); + var _ChromeScopes = __webpack_require__(1610); var _ChromeScopes2 = _interopRequireDefault(_ChromeScopes); var _Scopes2 = __webpack_require__(1611); var _Scopes3 = _interopRequireDefault(_Scopes2); __webpack_require__(1342); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } -/* 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/>. */ - -const Scopes = _prefs.features.chromeScopes ? _ChromeScopes2.default : _Scopes3.default; +const Scopes = _prefs.features.chromeScopes ? _ChromeScopes2.default : _Scopes3.default; /* 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/>. */ function debugBtn(onClick, type, className, tooltip) { return _react2.default.createElement( "button", { onClick: onClick, className: `${type} ${className}`, key: type, @@ -22553,16 +22545,30 @@ class SecondaryPanes extends _react.Comp component: _react2.default.createElement(Scopes, null), opened: _prefs.prefs.scopesVisible, onToggle: opened => { _prefs.prefs.scopesVisible = opened; } }; } + getComponentItem() { + const { extra: { react } } = this.props; + + return { + header: react.displayName, + className: "component-pane", + component: _react2.default.createElement(_FrameworkComponent2.default, null), + opened: _prefs.prefs.componentVisible, + onToggle: opened => { + _prefs.prefs.componentVisible = opened; + } + }; + } + getWatchItem() { return { header: L10N.getStr("watchExpressions.header"), className: "watch-expressions-pane", buttons: this.watchExpressionHeaderButtons(), component: _react2.default.createElement(_Expressions2.default, null), opened: _prefs.prefs.expressionsVisible, onToggle: opened => { @@ -22620,32 +22626,37 @@ class SecondaryPanes extends _react.Comp shouldIgnoreCaughtExceptions, isWaitingOnBreak } = this.props; return (0, _BreakpointsDropdown2.default)(breakOnNext, pauseOnExceptions, shouldPauseOnExceptions, shouldIgnoreCaughtExceptions, isWaitingOnBreak); } getStartItems() { - const { workers } = this.props; + const { extra, workers } = this.props; const items = []; if (this.props.horizontal) { if (_prefs.features.workers && workers.size > 0) { items.push(this.getWorkersItem()); } items.push(this.getWatchItem()); } items.push(this.getBreakpointsItem()); if (this.props.hasFrames) { items.push(this.getCallStackItem()); + if (this.props.horizontal) { + if (extra && extra.react) { + items.push(this.getComponentItem()); + } + items.push(this.getScopeItem()); } } if (_prefs.features.eventListeners) { items.push({ header: L10N.getStr("eventListenersHeader"), className: "event-listeners-pane", @@ -22656,30 +22667,34 @@ class SecondaryPanes extends _react.Comp return items.filter(item => item); } renderHorizontalLayout() { return _react2.default.createElement(_Accordion2.default, { items: this.getItems() }); } getEndItems() { - const { workers } = this.props; + const { extra, workers } = this.props; let items = []; if (this.props.horizontal) { return []; } if (_prefs.features.workers && workers.size > 0) { items.push(this.getWorkersItem()); } items.push(this.getWatchItem()); + if (extra && extra.react) { + items.push(this.getComponentItem()); + } + if (this.props.hasFrames) { items = [...items, this.getScopeItem()]; } return items; } getItems() { @@ -22723,16 +22738,17 @@ class SecondaryPanes extends _react.Comp } } SecondaryPanes.contextTypes = { shortcuts: _propTypes2.default.object }; exports.default = (0, _reactRedux.connect)(state => ({ + extra: (0, _selectors.getExtra)(state), hasFrames: !!(0, _selectors.getTopFrame)(state), breakpoints: (0, _selectors.getBreakpoints)(state), breakpointsDisabled: (0, _selectors.getBreakpointsDisabled)(state), breakpointsLoading: (0, _selectors.getBreakpointsLoading)(state), isWaitingOnBreak: (0, _selectors.getIsWaitingOnBreak)(state), shouldPauseOnExceptions: (0, _selectors.getShouldPauseOnExceptions)(state), shouldIgnoreCaughtExceptions: (0, _selectors.getShouldIgnoreCaughtExceptions)(state), workers: (0, _selectors.getWorkers)(state) @@ -26370,16 +26386,18 @@ function astCommand(stepType) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.findBestMatchExpression = findBestMatchExpression; exports.findEmptyLines = findEmptyLines; +exports.containsPosition = containsPosition; +exports.findClosestFunction = findClosestFunction; var _lodash = __webpack_require__(2); function findBestMatchExpression(symbols, tokenPos) { const { memberExpressions, identifiers } = symbols; const { line, column } = tokenPos; return identifiers.concat(memberExpressions).reduce((found, expression) => { const overlaps = expression.location.start.line == line && expression.location.start.column <= column && expression.location.end.column >= column && !expression.computed; @@ -26405,16 +26423,47 @@ function findEmptyLines(selectedSource, if (!selectedSource.text) { return []; } const lineCount = selectedSource.text.split("\n").length; const sourceLines = (0, _lodash.range)(1, lineCount + 1); return (0, _lodash.without)(sourceLines, ...breakpointLines); } +function containsPosition(a, b) { + const startsBefore = a.start.line < b.line || a.start.line === b.line && a.start.column <= b.column; + const endsAfter = a.end.line > b.line || a.end.line === b.line && a.end.column >= b.column; + + return startsBefore && endsAfter; +} + +function findClosestFunction(functions, location) { + return functions.reduce((found, currNode) => { + if (currNode.name === "anonymous" || !containsPosition(currNode.location, { + line: location.line, + column: location.column || 0 + })) { + return found; + } + + if (!found) { + return currNode; + } + + if (found.location.start.line > currNode.location.start.line) { + return found; + } + if (found.location.start.line === currNode.location.start.line && found.location.start.column > currNode.location.start.column) { + return found; + } + + return currNode; + }, null); +} + /***/ }), /***/ 1639: /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -27174,17 +27223,20 @@ class QuickOpenModal extends _react.Comp }; this.searchSymbols = query => { const { symbols: { functions, variables } } = this.props; let results = functions; if (this.isVariableQuery()) { results = variables; - } + } else { + results = results.filter(result => result.title !== "anonymous"); + } + if (query === "@" || query === "#") { return this.setState({ results }); } this.setState({ results: filter(results, query.slice(1)) }); }; this.searchShortcuts = query => { @@ -27590,36 +27642,43 @@ Object.defineProperty(exports, "__esModu value: true }); exports.fetchScopes = fetchScopes; var _selectors = __webpack_require__(3590); var _mapScopes = __webpack_require__(1634); +var _preview = __webpack_require__(1786); + var _promise = __webpack_require__(1653); +/* 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/>. */ + function fetchScopes() { return async function ({ dispatch, getState, client, sourceMaps }) { const frame = (0, _selectors.getSelectedFrame)(getState()); if (!frame || (0, _selectors.getGeneratedFrameScope)(getState(), frame.id)) { return; } + const extra = await dispatch((0, _preview.getExtra)("this;", frame.this, frame)); + const scopes = dispatch({ type: "ADD_SCOPES", frame, + extra, [_promise.PROMISE]: client.getFrameScopes(frame) }); await dispatch((0, _mapScopes.mapScopes)(scopes, frame)); }; -} /* 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/>. */ +} /***/ }), /***/ 1657: /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -30163,16 +30222,17 @@ function createLocation({ Object.defineProperty(exports, "__esModule", { value: true }); var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; /* 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/>. */ +exports.getExtra = getExtra; exports.updatePreview = updatePreview; exports.setPreview = setPreview; exports.clearPreview = clearPreview; var _ast = __webpack_require__(1638); var _editor = __webpack_require__(1358); @@ -30237,16 +30297,24 @@ function isInvalidTarget(target) { const invalidToken = tokenText === "" || tokenText.match(/[(){}\|&%,.;=<>\+-/\*\s]/); // exclude codemirror elements that are not tokens const invalidTarget = target.parentElement && !target.parentElement.closest(".CodeMirror-line") || cursorPos.top == 0; return invalidTarget || invalidToken || invaildType; } +function getExtra(expression, result, selectedFrame) { + return async ({ dispatch, getState, client, sourceMaps }) => { + const extra = await getExtraProps(expression, result, expr => client.evaluateInFrame(selectedFrame.id, expr)); + + return extra; + }; +} + function updatePreview(target, editor) { return ({ dispatch, getState, client, sourceMaps }) => { const tokenPos = (0, _editor.getTokenLocation)(editor.codeMirror, target); const cursorPos = target.getBoundingClientRect(); const preview = (0, _selectors.getPreview)(getState()); if ((0, _selectors.getCanRewind)(getState())) { return; @@ -30317,17 +30385,17 @@ function setPreview(expression, location } const { result } = await client.evaluateInFrame(selectedFrame.id, expression); if (result === undefined) { return; } - const extra = await getExtraProps(expression, result, expr => client.evaluateInFrame(selectedFrame.id, expr)); + const extra = await dispatch(getExtra(expression, result, selectedFrame)); return { expression, result, location, tokenPos, cursorPos, extra @@ -30745,37 +30813,71 @@ function isDebugLine(selectedFrame, sele return selectedFrame.location.sourceId == selectedLocation.sourceId && selectedFrame.location.line == selectedLocation.line; } function isDocumentReady(selectedSource, selectedLocation) { return selectedLocation && (0, _source.isLoaded)(selectedSource) && (0, _sourceDocuments.hasDocument)(selectedLocation.sourceId); } -class HighlightLine extends _react.PureComponent { +class HighlightLine extends _react.Component { + constructor(...args) { + var _temp; + + return _temp = super(...args), this.isStepping = false, this.previousEditorLine = null, _temp; + } + + shouldComponentUpdate(nextProps) { + const { selectedLocation, selectedSource } = nextProps; + return this.shouldSetHighlightLine(selectedLocation, selectedSource); + } + + shouldSetHighlightLine(selectedLocation, selectedSource) { + const { sourceId, line } = selectedLocation; + const editorLine = (0, _editor.toEditorLine)(sourceId, line); + + if (!isDocumentReady(selectedSource, selectedLocation)) { + return false; + } + + if (this.isStepping && editorLine === this.previousEditorLine) { + return false; + } + + return true; + } + componentDidUpdate(prevProps) { - const { selectedLocation, selectedFrame, selectedSource } = this.props; + const { + pauseCommand, + selectedLocation, + selectedFrame, + selectedSource + } = this.props; + if (pauseCommand) { + this.isStepping = true; + } this.clearHighlightLine(prevProps.selectedLocation, prevProps.selectedSource); - this.setHighlightLine(selectedLocation, selectedFrame, selectedSource); } setHighlightLine(selectedLocation, selectedFrame, selectedSource) { - if (!isDocumentReady(selectedSource, selectedLocation)) { - return; - } - const { sourceId, line } = selectedLocation; + if (!this.shouldSetHighlightLine(selectedLocation, selectedSource)) { + return; + } + this.isStepping = false; + const editorLine = (0, _editor.toEditorLine)(sourceId, line); + this.previousEditorLine = editorLine; if (!line || isDebugLine(selectedFrame, selectedLocation)) { return; } - const editorLine = (0, _editor.toEditorLine)(sourceId, line); const doc = (0, _sourceDocuments.getDocument)(sourceId); doc.addLineClass(editorLine, "line", "highlight-line"); } clearHighlightLine(selectedLocation, selectedSource) { if (!isDocumentReady(selectedSource, selectedLocation)) { return; } @@ -30788,16 +30890,17 @@ class HighlightLine extends _react.PureC render() { return null; } } exports.HighlightLine = HighlightLine; exports.default = (0, _reactRedux.connect)(state => ({ + pauseCommand: (0, _selectors.getPauseCommand)(state), selectedFrame: (0, _selectors.getVisibleSelectedFrame)(state), selectedLocation: (0, _selectors.getSelectedLocation)(state), selectedSource: (0, _selectors.getSelectedSource)(state) }))(HighlightLine); /***/ }), /***/ 1797: @@ -31576,52 +31679,76 @@ function jumpToMappedSelectedLocation() Object.defineProperty(exports, "__esModule", { value: true }); var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; exports.updateFrameLocation = updateFrameLocation; +exports.mapDisplayNames = mapDisplayNames; exports.mapFrames = mapFrames; var _selectors = __webpack_require__(3590); +var _ast = __webpack_require__(1638); + function updateFrameLocation(frame, sourceMaps) { return sourceMaps.getOriginalLocation(frame.location).then(loc => _extends({}, frame, { location: loc, generatedLocation: frame.generatedLocation || frame.location })); } function updateFrameLocations(frames, sourceMaps) { if (!frames || frames.length == 0) { return Promise.resolve(frames); } return Promise.all(frames.map(frame => updateFrameLocation(frame, sourceMaps))); } -/** - * Map call stack frame locations to original locations. +function mapDisplayNames(frames, getState) { + return frames.map(frame => { + const source = (0, _selectors.getSource)(getState(), frame.location.sourceId); + const symbols = (0, _selectors.getSymbols)(getState(), source); + + if (!symbols || !symbols.functions) { + return frame; + } + + const originalFunction = (0, _ast.findClosestFunction)(symbols.functions, frame.location); + + if (!originalFunction) { + return frame; + } + + const originalDisplayName = originalFunction.name; + return _extends({}, frame, { originalDisplayName }); + }); +} + +/** + * Map call stack frame locations and display names to originals. * e.g. * 1. When the debuggee pauses * 2. When a source is pretty printed - * + * 3. When symbols are loaded * @memberof actions/pause * @static */ function mapFrames() { return async function ({ dispatch, getState, sourceMaps }) { const frames = (0, _selectors.getFrames)(getState()); if (!frames) { return; } - const mappedFrames = await updateFrameLocations(frames, sourceMaps); + let mappedFrames = await updateFrameLocations(frames, sourceMaps); + mappedFrames = mapDisplayNames(mappedFrames, getState); dispatch({ type: "MAP_FRAMES", frames: mappedFrames }); }; } @@ -33921,16 +34048,17 @@ const pref = Services.pref; if (isDevelopment()) { pref("devtools.debugger.alphabetize-outline", false); pref("devtools.debugger.auto-pretty-print", true); pref("devtools.source-map.client-service.enabled", true); pref("devtools.debugger.pause-on-exceptions", false); pref("devtools.debugger.ignore-caught-exceptions", false); pref("devtools.debugger.call-stack-visible", true); pref("devtools.debugger.scopes-visible", true); + pref("devtools.debugger.component-visible", true); pref("devtools.debugger.workers-visible", true); pref("devtools.debugger.expressions-visible", true); pref("devtools.debugger.breakpoints-visible", true); pref("devtools.debugger.start-panel-collapsed", false); pref("devtools.debugger.end-panel-collapsed", false); pref("devtools.debugger.tabs", "[]"); pref("devtools.debugger.tabsBlackBoxed", "[]"); pref("devtools.debugger.ui.framework-grouping-on", true); @@ -33963,16 +34091,17 @@ if (isDevelopment()) { const prefs = new PrefsHelper("devtools", { alphabetizeOutline: ["Bool", "debugger.alphabetize-outline"], autoPrettyPrint: ["Bool", "debugger.auto-pretty-print"], clientSourceMapsEnabled: ["Bool", "source-map.client-service.enabled"], pauseOnExceptions: ["Bool", "debugger.pause-on-exceptions"], ignoreCaughtExceptions: ["Bool", "debugger.ignore-caught-exceptions"], callStackVisible: ["Bool", "debugger.call-stack-visible"], scopesVisible: ["Bool", "debugger.scopes-visible"], + componentVisible: ["Bool", "debugger.component-visible"], workersVisible: ["Bool", "debugger.workers-visible"], breakpointsVisible: ["Bool", "debugger.breakpoints-visible"], expressionsVisible: ["Bool", "debugger.expressions-visible"], startPanelCollapsed: ["Bool", "debugger.start-panel-collapsed"], endPanelCollapsed: ["Bool", "debugger.end-panel-collapsed"], frameworkGroupingOn: ["Bool", "debugger.ui.framework-grouping-on"], tabs: ["Json", "debugger.tabs", []], tabsBlackBoxed: ["Json", "debugger.tabsBlackBoxed", []], @@ -37216,34 +37345,43 @@ function annotateBabelAsyncFrames(frames const isBabelFrame = frameIndex => babelFrameIndexes.includes(frameIndex); return frames.map((frame, frameIndex) => isBabelFrame(frameIndex) ? _extends({}, frame, { library: "Babel" }) : frame); } // Receives an array of frames and looks for babel async // call stack groups. function getBabelFrameIndexes(frames) { - const startIndexes = getFrameIndices(frames, (displayName, url) => url.match(/regenerator-runtime/i) && displayName === "tryCatch"); - - const endIndexes = getFrameIndices(frames, (displayName, url) => displayName === "_asyncToGenerator/<" || url.match(/_microtask/i) && displayName === "flush"); + const startIndexes = frames.reduce((accumulator, frame, index) => { + if ((0, _getFrameUrl.getFrameUrl)(frame).match(/regenerator-runtime/i) && frame.displayName === "tryCatch") { + return [...accumulator, index]; + } + return accumulator; + }, []); + + const endIndexes = frames.reduce((accumulator, frame, index) => { + if ((0, _getFrameUrl.getFrameUrl)(frame).match(/_microtask/i) && frame.displayName === "flush") { + return [...accumulator, index]; + } + if (frame.displayName === "_asyncToGenerator/<") { + return [...accumulator, index + 1]; + } + return accumulator; + }, []); if (startIndexes.length != endIndexes.length || startIndexes.length === 0) { return frames; } // Receives an array of start and end index tuples and returns // an array of async call stack index ranges. // e.g. [[1,3], [5,7]] => [[1,2,3], [5,6,7]] return (0, _lodash.flatMap)((0, _lodash.zip)(startIndexes, endIndexes), ([startIndex, endIndex]) => (0, _lodash.range)(startIndex, endIndex + 1)); } -function getFrameIndices(frames, predicate) { - return frames.reduce((accumulator, frame, index) => predicate(frame.displayName, (0, _getFrameUrl.getFrameUrl)(frame)) ? [...accumulator, index] : accumulator, []); -} - /***/ }), /***/ 3609: /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -37397,17 +37535,18 @@ const displayNameMap = { }; function mapDisplayNames(frame, library) { const { displayName } = frame; return displayNameMap[library] && displayNameMap[library][displayName] || displayName; } function formatDisplayName(frame, { shouldMapDisplayName = true } = {}) { - let { displayName, library } = frame; + let { displayName, originalDisplayName, library } = frame; + displayName = originalDisplayName || displayName; if (library && shouldMapDisplayName) { displayName = mapDisplayNames(frame, library); } displayName = simplifyDisplayName(displayName); return (0, _utils.endTruncateStr)(displayName, 25); } @@ -38301,16 +38440,121 @@ function formatPausePoints(text, nodes) lines[line - 1] = insertStrtAt(lines[line - 1], column, `/*${types} ${num}*/`); }); return lines.join("\n"); } /***/ }), +/***/ 3623: +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _react = __webpack_require__(0); + +var _react2 = _interopRequireDefault(_react); + +var _redux = __webpack_require__(3593); + +var _reactRedux = __webpack_require__(3592); + +var _actions = __webpack_require__(1354); + +var _actions2 = _interopRequireDefault(_actions); + +var _firefox = __webpack_require__(1500); + +var _selectors = __webpack_require__(3590); + +var _devtoolsReps = __webpack_require__(1408); + +var _preview = __webpack_require__(1807); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +/* 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/>. */ + +const { createNode, getChildren } = _devtoolsReps.ObjectInspectorUtils.node; +const { loadItemProperties } = _devtoolsReps.ObjectInspectorUtils.loadProperties; + +class FrameworkComponent extends _react.PureComponent { + async componentWillMount() { + const expression = "this;"; + const { selectedFrame, setPopupObjectProperties } = this.props; + const value = selectedFrame.this; + + const root = createNode(null, expression, expression, { value }); + const properties = await loadItemProperties(root, _firefox.createObjectClient); + if (properties) { + setPopupObjectProperties(value, properties); + } + } + + renderReactComponent() { + const { selectedFrame, popupObjectProperties } = this.props; + const expression = "this;"; + const value = selectedFrame.this; + const root = { + name: expression, + path: expression, + contents: { value } + }; + + const loadedRootProperties = popupObjectProperties[value.actor]; + + let roots = getChildren({ + item: root, + loadedProperties: new Map([[root.path, loadedRootProperties]]) + }); + + roots = roots.filter(r => ["state", "props"].includes(r.name)); + + return _react2.default.createElement( + "div", + { className: "pane framework-component" }, + _react2.default.createElement(_devtoolsReps.ObjectInspector, { + roots: roots, + autoExpandAll: false, + autoExpandDepth: 0, + disableWrap: true, + disabledFocus: true, + dimTopLevelWindow: true, + createObjectClient: grip => (0, _firefox.createObjectClient)(grip) + }) + ); + } + + render() { + const { selectedFrame } = this.props; + if ((0, _preview.isReactComponent)(selectedFrame.this)) { + return this.renderReactComponent(); + } + + return null; + } +} + +exports.default = (0, _reactRedux.connect)(state => { + return { + selectedFrame: (0, _selectors.getSelectedFrame)(state), + popupObjectProperties: (0, _selectors.getAllPopupObjectProperties)(state) + }; +}, dispatch => (0, _redux.bindActionCreators)(_actions2.default, dispatch))(FrameworkComponent); + +/***/ }), + /***/ 363: /***/ (function(module, exports) { module.exports = "<!-- 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/. --><svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:svg=\"http://www.w3.org/2000/svg\"><path fill=\"black\" id=\"svg_1\" fill-rule=\"evenodd\" d=\"m4.55195,12.97461l7.4,-5l-7.4,-5l0,10zm-0.925,0l0,-10c0,-0.785 0.8,-1.264 1.415,-0.848l7.4,5c0.58,0.392 0.58,1.304 0,1.696l-7.4,5c-0.615,0.416 -1.415,-0.063 -1.415,-0.848z\"></path></svg>" /***/ }), /***/ 364:
--- a/devtools/client/debugger/new/parser-worker.js +++ b/devtools/client/debugger/new/parser-worker.js @@ -992,20 +992,17 @@ let ASTs = new Map(); function _parse(code, opts) { return babylon.parse(code, _extends({}, opts, { tokens: true })); } const sourceOptions = { generated: { - tokens: true, - plugins: [ - "objectRestSpread" - ] + tokens: true }, original: { sourceType: "unambiguous", tokens: true, plugins: ["jsx", "flow", "doExpressions", "objectRestSpread", "classProperties", "exportDefaultFrom", "exportNamespaceFrom", "asyncGenerators", "functionBind", "functionSent", "dynamicImport"] } }; @@ -40560,9 +40557,9 @@ function listCacheHas(key) { } module.exports = listCacheHas; /***/ }) /******/ }); -}); +}); \ No newline at end of file
--- a/devtools/client/preferences/debugger.js +++ b/devtools/client/preferences/debugger.js @@ -24,16 +24,17 @@ pref("devtools.debugger.ui.panes-workers pref("devtools.debugger.ui.panes-instruments-width", 300); pref("devtools.debugger.ui.panes-visible-on-startup", false); pref("devtools.debugger.ui.variables-sorting-enabled", true); pref("devtools.debugger.ui.variables-only-enum-visible", false); pref("devtools.debugger.ui.variables-searchbox-visible", false); pref("devtools.debugger.ui.framework-grouping-on", true); pref("devtools.debugger.call-stack-visible", true); pref("devtools.debugger.scopes-visible", true); +pref("devtools.debugger.component-visible", true); pref("devtools.debugger.workers-visible", true); pref("devtools.debugger.breakpoints-visible", true); pref("devtools.debugger.expressions-visible", true); pref("devtools.debugger.start-panel-collapsed", false); pref("devtools.debugger.end-panel-collapsed", false); pref("devtools.debugger.tabs", "[]"); pref("devtools.debugger.tabsBlackBoxed", "[]"); pref("devtools.debugger.pending-selected-location", "{}");
--- a/dom/base/StructuredCloneHolder.cpp +++ b/dom/base/StructuredCloneHolder.cpp @@ -118,32 +118,44 @@ StructuredCloneCallbacksFreeTransfer(uin { StructuredCloneHolderBase* holder = static_cast<StructuredCloneHolderBase*>(aClosure); MOZ_ASSERT(holder); return holder->CustomFreeTransferHandler(aTag, aOwnership, aContent, aExtraData); } +bool +StructuredCloneCallbacksCanTransfer(JSContext* aCx, + JS::Handle<JSObject*> aObject, + void* aClosure) +{ + StructuredCloneHolderBase* holder = + static_cast<StructuredCloneHolderBase*>(aClosure); + MOZ_ASSERT(holder); + return holder->CustomCanTransferHandler(aCx, aObject); +} + void StructuredCloneCallbacksError(JSContext* aCx, uint32_t aErrorId) { NS_WARNING("Failed to clone data."); } } // anonymous namespace const JSStructuredCloneCallbacks StructuredCloneHolder::sCallbacks = { StructuredCloneCallbacksRead, StructuredCloneCallbacksWrite, StructuredCloneCallbacksError, StructuredCloneCallbacksReadTransfer, StructuredCloneCallbacksWriteTransfer, - StructuredCloneCallbacksFreeTransfer + StructuredCloneCallbacksFreeTransfer, + StructuredCloneCallbacksCanTransfer, }; // StructuredCloneHolderBase class StructuredCloneHolderBase::StructuredCloneHolderBase(StructuredCloneScope aScope) : mStructuredCloneScope(aScope) #ifdef DEBUG , mClearCalled(false) @@ -235,16 +247,23 @@ void StructuredCloneHolderBase::CustomFreeTransferHandler(uint32_t aTag, JS::TransferableOwnership aOwnership, void* aContent, uint64_t aExtraData) { MOZ_CRASH("Nothing to free."); } +bool +StructuredCloneHolderBase::CustomCanTransferHandler(JSContext* aCx, + JS::Handle<JSObject*> aObj) +{ + return false; +} + // StructuredCloneHolder class StructuredCloneHolder::StructuredCloneHolder(CloningSupport aSupportsCloning, TransferringSupport aSupportsTransferring, StructuredCloneScope aScope) : StructuredCloneHolderBase(aScope) , mSupportsCloning(aSupportsCloning == CloningSupported) , mSupportsTransferring(aSupportsTransferring == TransferringSupported) @@ -1216,32 +1235,40 @@ StructuredCloneHolder::CustomWriteTransf { MessagePort* port = nullptr; nsresult rv = UNWRAP_OBJECT(MessagePort, &obj, port); if (NS_SUCCEEDED(rv)) { // We use aExtraData to store the index of this new port identifier. *aExtraData = mPortIdentifiers.Length(); MessagePortIdentifier* identifier = mPortIdentifiers.AppendElement(); + if (!port->CanBeCloned()) { + return false; + } + port->CloneAndDisentangle(*identifier); *aTag = SCTAG_DOM_MAP_MESSAGEPORT; *aOwnership = JS::SCTAG_TMO_CUSTOM; *aContent = nullptr; return true; } if (mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread || mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread) { OffscreenCanvas* canvas = nullptr; rv = UNWRAP_OBJECT(OffscreenCanvas, &obj, canvas); if (NS_SUCCEEDED(rv)) { MOZ_ASSERT(canvas); + if (canvas->IsNeutered()) { + return false; + } + *aExtraData = 0; *aTag = SCTAG_DOM_CANVAS; *aOwnership = JS::SCTAG_TMO_CUSTOM; *aContent = canvas->ToCloneData(); MOZ_ASSERT(*aContent); canvas->SetNeutered(); return true; @@ -1310,16 +1337,52 @@ StructuredCloneHolder::CustomFreeTransfe ImageBitmapCloneData* data = static_cast<ImageBitmapCloneData*>(aContent); delete data; return; } } bool +StructuredCloneHolder::CustomCanTransferHandler(JSContext* aCx, + JS::Handle<JSObject*> aObj) +{ + if (!mSupportsTransferring) { + return false; + } + + JS::Rooted<JSObject*> obj(aCx, aObj); + + { + MessagePort* port = nullptr; + nsresult rv = UNWRAP_OBJECT(MessagePort, &obj, port); + if (NS_SUCCEEDED(rv)) { + return true; + } + + if (mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread || + mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread) { + OffscreenCanvas* canvas = nullptr; + rv = UNWRAP_OBJECT(OffscreenCanvas, &obj, canvas); + if (NS_SUCCEEDED(rv)) { + return true; + } + + ImageBitmap* bitmap = nullptr; + rv = UNWRAP_OBJECT(ImageBitmap, &obj, bitmap); + if (NS_SUCCEEDED(rv)) { + return true; + } + } + } + + return false; +} + +bool StructuredCloneHolder::TakeTransferredPortsAsSequence(Sequence<OwningNonNull<mozilla::dom::MessagePort>>& aPorts) { nsTArray<RefPtr<MessagePort>> ports = TakeTransferredPorts(); aPorts.Clear(); for (uint32_t i = 0, len = ports.Length(); i < len; ++i) { if (!aPorts.AppendElement(ports[i].forget(), fallible)) { return false;
--- a/dom/base/StructuredCloneHolder.h +++ b/dom/base/StructuredCloneHolder.h @@ -83,16 +83,20 @@ public: uint64_t* aExtraData); virtual void CustomFreeTransferHandler(uint32_t aTag, JS::TransferableOwnership aOwnership, void* aContent, uint64_t aExtraData); + virtual bool + CustomCanTransferHandler(JSContext* aCx, + JS::Handle<JSObject*> aObj); + // These methods are what you should use to read/write data. // Execute the serialization of aValue using the Structured Clone Algorithm. // The data can read back using Read(). bool Write(JSContext* aCx, JS::Handle<JS::Value> aValue); // Like Write() but it supports the transferring of objects and handling @@ -278,16 +282,19 @@ public: void** aContent, uint64_t* aExtraData) override; virtual void CustomFreeTransferHandler(uint32_t aTag, JS::TransferableOwnership aOwnership, void* aContent, uint64_t aExtraData) override; + virtual bool CustomCanTransferHandler(JSContext* aCx, + JS::Handle<JSObject*> aObj) override; + // These 2 static methods are useful to read/write fully serializable objects. // They can be used by custom StructuredCloneHolderBase classes to // serialize objects such as ImageData, CryptoKey, RTCCertificate, etc. static JSObject* ReadFullySerializableObjects(JSContext* aCx, JSStructuredCloneReader* aReader, uint32_t aTag);
--- a/dom/base/nsINode.h +++ b/dom/base/nsINode.h @@ -608,19 +608,21 @@ public: */ nsIDocument* GetUncomposedDoc() const { return IsInUncomposedDoc() ? OwnerDoc() : nullptr; } /** - * This method returns the owner doc if the node is in the - * composed document (as defined in the Shadow DOM spec), otherwise - * it returns null. + * This method returns the owner document if the node is connected to it + * (as defined in the DOM spec), otherwise it returns null. + * In other words, returns non-null even in the case the node is in + * Shadow DOM, if there is a possibly shadow boundary crossing path from + * the node to its owner document. */ nsIDocument* GetComposedDoc() const { return IsInShadowTree() ? GetComposedDocInternal() : GetUncomposedDoc(); } /**
--- a/dom/indexedDB/IDBObjectStore.cpp +++ b/dom/indexedDB/IDBObjectStore.cpp @@ -695,17 +695,18 @@ nsresult GetAddInfoCallback(JSContext* aCx, void* aClosure) { static const JSStructuredCloneCallbacks kStructuredCloneCallbacks = { nullptr /* read */, StructuredCloneWriteCallback /* write */, nullptr /* reportError */, nullptr /* readTransfer */, nullptr /* writeTransfer */, - nullptr /* freeTransfer */ + nullptr /* freeTransfer */, + nullptr /* canTransfer */ }; MOZ_ASSERT(aCx); auto* data = static_cast<GetAddInfoClosure*>(aClosure); MOZ_ASSERT(data); data->mCloneWriteInfo.mOffsetToKeyProp = 0; @@ -1318,16 +1319,17 @@ IDBObjectStore::DeserializeValue(JSConte JSAutoRequest ar(aCx); static const JSStructuredCloneCallbacks callbacks = { CommonStructuredCloneReadCallback, nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr }; // FIXME: Consider to use StructuredCloneHolder here and in other // deserializing methods. if (!JS_ReadStructuredClone(aCx, aCloneReadInfo.mData, JS_STRUCTURED_CLONE_VERSION, JS::StructuredCloneScope::SameProcessSameThread, aValue, &callbacks, &aCloneReadInfo)) { @@ -1491,16 +1493,17 @@ private: DeserializeIndexValue(JSContext* aCx, JS::MutableHandle<JS::Value> aValue) { static const JSStructuredCloneCallbacks callbacks = { CommonStructuredCloneReadCallback, nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr }; if (!JS_ReadStructuredClone(aCx, mCloneReadInfo.mData, JS_STRUCTURED_CLONE_VERSION, JS::StructuredCloneScope::SameProcessSameThread, aValue, &callbacks, &mCloneReadInfo)) { return NS_ERROR_DOM_DATA_CLONE_ERR; @@ -1604,16 +1607,17 @@ private: DeserializeUpgradeValue(JSContext* aCx, JS::MutableHandle<JS::Value> aValue) { static const JSStructuredCloneCallbacks callbacks = { CommonStructuredCloneReadCallback, nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr }; if (!JS_ReadStructuredClone(aCx, mCloneReadInfo.mData, JS_STRUCTURED_CLONE_VERSION, JS::StructuredCloneScope::SameProcessSameThread, aValue, &callbacks, &mCloneReadInfo)) { return NS_ERROR_DOM_DATA_CLONE_ERR;
--- a/dom/messagechannel/MessagePort.cpp +++ b/dom/messagechannel/MessagePort.cpp @@ -199,16 +199,17 @@ NS_INTERFACE_MAP_END_INHERITING(DOMEvent NS_IMPL_ADDREF_INHERITED(MessagePort, DOMEventTargetHelper) NS_IMPL_RELEASE_INHERITED(MessagePort, DOMEventTargetHelper) MessagePort::MessagePort(nsIGlobalObject* aGlobal) : DOMEventTargetHelper(aGlobal) , mInnerID(0) , mMessageQueueEnabled(false) , mIsKeptAlive(false) + , mHasBeenTransferredOrClosed(false) { MOZ_ASSERT(aGlobal); mIdentifier = new MessagePortIdentifier(); mIdentifier->neutered() = true; mIdentifier->sequenceId() = 0; } @@ -496,16 +497,17 @@ MessagePort::Dispatch() } MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(mPostMessageRunnable)); } void MessagePort::Close() { + mHasBeenTransferredOrClosed = true; CloseInternal(true /* aSoftly */); } void MessagePort::CloseForced() { CloseInternal(false /* aSoftly */); } @@ -720,16 +722,19 @@ MessagePort::Disentangle() UpdateMustKeepAlive(); } void MessagePort::CloneAndDisentangle(MessagePortIdentifier& aIdentifier) { MOZ_ASSERT(mIdentifier); + MOZ_ASSERT(!mHasBeenTransferredOrClosed); + + mHasBeenTransferredOrClosed = true; // We can clone a port that has already been transfered. In this case, on the // otherside will have a neutered port. Here we set neutered to true so that // we are safe in case a early return. aIdentifier.neutered() = true; if (mState > eStateEntangled) { return;
--- a/dom/messagechannel/MessagePort.h +++ b/dom/messagechannel/MessagePort.h @@ -69,16 +69,21 @@ public: void SetOnmessage(EventHandlerNonNull* aCallback); IMPL_EVENT_HANDLER(messageerror) // Non WebIDL methods void UnshippedEntangle(MessagePort* aEntangledPort); + bool CanBeCloned() const + { + return !mHasBeenTransferredOrClosed; + } + void CloneAndDisentangle(MessagePortIdentifier& aIdentifier); void CloseForced(); // These methods are useful for MessagePortChild void Entangled(nsTArray<ClonedMessageData>& aMessages); void MessagesReceived(nsTArray<ClonedMessageData>& aMessages); @@ -177,14 +182,20 @@ private: uint64_t mInnerID; State mState; bool mMessageQueueEnabled; bool mIsKeptAlive; + + // mHasBeenTransferredOrClosed is used to know if this port has been manually + // closed or transferred via postMessage. Note that if the entangled port is + // closed, this port is closed as well (see mState) but, just because close() + // has not been called directly, by spec, this port can still be transferred. + bool mHasBeenTransferredOrClosed; }; } // namespace dom } // namespace mozilla #endif // mozilla_dom_MessagePort_h
--- a/gfx/2d/DrawCommands.h +++ b/gfx/2d/DrawCommands.h @@ -235,16 +235,23 @@ public: CLONE_INTO(DrawFilterCommand)(mFilter, mSourceRect, mDestPoint, mOptions); } virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const override { RefPtr<FilterNode> filter = mFilter; if (mFilter->GetBackendType() == FilterBackend::FILTER_BACKEND_CAPTURE) { filter = static_cast<FilterNodeCapture*>(filter.get())->Validate(aDT); + + // This can happen if the FilterNodeCapture is unable to create a + // backing FilterNode on the target backend. Normally this would be + // handled by the painting code, but here there's not much we can do. + if (!filter) { + return; + } } aDT->DrawFilter(filter, mSourceRect, mDestPoint, mOptions); } void Log(TreeLog& aStream) const override { aStream << "[DrawFilter surf=" << mFilter; aStream << " src=" << mSourceRect;
--- a/gfx/2d/FilterNodeD2D1.cpp +++ b/gfx/2d/FilterNodeD2D1.cpp @@ -197,32 +197,36 @@ uint32_t ConvertValue(FilterType aType, aValue = D2DTurbulenceNoise(aValue); } break; case FilterType::COMPOSITE: if (aAttribute == ATT_COMPOSITE_OPERATOR) { aValue = D2DFilterCompositionMode(aValue); } break; + default: + break; } return aValue; } void ConvertValue(FilterType aType, uint32_t aAttribute, IntSize &aValue) { switch (aType) { case FilterType::MORPHOLOGY: if (aAttribute == ATT_MORPHOLOGY_RADII) { aValue.width *= 2; aValue.width += 1; aValue.height *= 2; aValue.height += 1; } break; + default: + break; } } UINT32 GetD2D1InputForInput(FilterType aType, uint32_t aIndex) { return aIndex; } @@ -436,32 +440,36 @@ GetD2D1PropForAttribute(FilterType aType CONVERT_PROP(DISTANT_SPECULAR_KERNEL_UNIT_LENGTH, DISTANTSPECULAR_PROP_KERNEL_UNIT_LENGTH); } break; case FilterType::CROP: switch (aIndex) { CONVERT_PROP(CROP_RECT, CROP_PROP_RECT); } break; + default: + break; } return UINT32_MAX; } bool GetD2D1PropsForIntSize(FilterType aType, uint32_t aIndex, UINT32 *aPropWidth, UINT32 *aPropHeight) { switch (aType) { case FilterType::MORPHOLOGY: if (aIndex == ATT_MORPHOLOGY_RADII) { *aPropWidth = D2D1_MORPHOLOGY_PROP_WIDTH; *aPropHeight = D2D1_MORPHOLOGY_PROP_HEIGHT; return true; } break; + default: + break; } return false; } static inline REFCLSID GetCLDIDForFilterType(FilterType aType) { switch (aType) { case FilterType::COLOR_MATRIX: @@ -509,16 +517,18 @@ static inline REFCLSID GetCLDIDForFilter case FilterType::DISTANT_SPECULAR: return CLSID_D2D1DistantSpecular; case FilterType::CROP: return CLSID_D2D1Crop; case FilterType::PREMULTIPLY: return CLSID_D2D1Premultiply; case FilterType::UNPREMULTIPLY: return CLSID_D2D1UnPremultiply; + default: + break; } return GUID_NULL; } static bool IsTransferFilterType(FilterType aType) { switch (aType) {
--- a/gfx/layers/ImageContainer.cpp +++ b/gfx/layers/ImageContainer.cpp @@ -860,17 +860,17 @@ SourceSurfaceImage::GetTextureClient(Kno } if (textureClient) { textureClient->SyncWithObject(aForwarder->GetSyncObject()); entry.OrInsert([&textureClient](){ return textureClient; }); return textureClient; } // Remove the speculatively added entry. - mTextureClients.Remove(aForwarder->GetSerial()); + entry.OrRemove(); return nullptr; } ImageContainer::ProducerID ImageContainer::AllocateProducerID() { // Callable on all threads. static Atomic<ImageContainer::ProducerID> sProducerID(0u);
--- a/gfx/layers/apz/src/APZCTreeManager.cpp +++ b/gfx/layers/apz/src/APZCTreeManager.cpp @@ -862,17 +862,18 @@ APZCTreeManager::PrepareNodeForLayer(con // that is supposed to scroll together is split into multiple layers because of // e.g. non-scrolling content interleaved in z-index order. ScrollableLayerGuid guid(aLayersId, aMetrics); auto insertResult = aState.mApzcMap.insert(std::make_pair(guid, static_cast<AsyncPanZoomController*>(nullptr))); if (!insertResult.second) { apzc = insertResult.first->second; PrintAPZCInfo(aLayer, apzc); } - APZCTM_LOG("Found APZC %p for layer %p with identifiers %" PRId64 " %" PRId64 "\n", apzc, aLayer.GetLayer(), guid.mLayersId, guid.mScrollId); + APZCTM_LOG("Found APZC %p for layer %p with identifiers %" PRIx64 " %" PRId64 "\n", + apzc, aLayer.GetLayer(), uint64_t(guid.mLayersId), guid.mScrollId); // If we haven't encountered a layer already with the same metrics, then we need to // do the full reuse-or-make-an-APZC algorithm, which is contained inside the block // below. if (apzc == nullptr) { apzc = aLayer.GetApzc(); // If the content represented by the scrollable layer has changed (which may @@ -933,17 +934,18 @@ APZCTreeManager::PrepareNodeForLayer(con // be in the tree. These pointers will get reset properly as we continue // building the tree. Also remove it from the set of nodes that are going // to be destroyed, because it's going to remain active. aState.mNodesToDestroy.RemoveElement(node); node->SetPrevSibling(nullptr); node->SetLastChild(nullptr); } - APZCTM_LOG("Using APZC %p for layer %p with identifiers %" PRId64 " %" PRId64 "\n", apzc, aLayer.GetLayer(), aLayersId, aMetrics.GetScrollId()); + APZCTM_LOG("Using APZC %p for layer %p with identifiers %" PRIx64 " %" PRId64 "\n", + apzc, aLayer.GetLayer(), uint64_t(aLayersId), aMetrics.GetScrollId()); apzc->NotifyLayersUpdated(aLayer.Metadata(), aState.mIsFirstPaint, aLayersId == aState.mOriginatingLayersId); // Since this is the first time we are encountering an APZC with this guid, // the node holding it must be the primary holder. It may be newly-created // or not, depending on whether it went through the newApzc branch above. MOZ_ASSERT(node->IsPrimaryHolder() && node->GetApzc() && node->GetApzc()->Matches(guid)); @@ -1082,17 +1084,17 @@ WillHandleInput(const PanGestureOrScroll } void APZCTreeManager::FlushApzRepaints(LayersId aLayersId) { // Previously, paints were throttled and therefore this method was used to // ensure any pending paints were flushed. Now, paints are flushed // immediately, so it is safe to simply send a notification now. - APZCTM_LOG("Flushing repaints for layers id 0x%" PRIx64 "\n", aLayersId); + APZCTM_LOG("Flushing repaints for layers id 0x%" PRIx64 "\n", uint64_t(aLayersId)); RefPtr<GeckoContentController> controller = GetContentController(aLayersId); MOZ_ASSERT(controller); controller->DispatchToRepaintThread( NewRunnableMethod("layers::GeckoContentController::NotifyFlushComplete", controller, &GeckoContentController::NotifyFlushComplete)); }
--- a/gfx/layers/apz/util/APZCCallbackHelper.cpp +++ b/gfx/layers/apz/util/APZCCallbackHelper.cpp @@ -670,25 +670,19 @@ PrepareForSetTargetAPZCNotification(nsIW dpElement, &(guid.mPresShellId), &(guid.mScrollId)); aTargets->AppendElement(guid); if (!guidIsValid || nsLayoutUtils::HasDisplayPort(dpElement)) { return false; } if (!scrollAncestor) { - MOZ_ASSERT(false); // If you hit this, please file a bug with STR. - - // Attempt some sort of graceful handling based on a theory as to why we - // reach this point... - // If we get here, the document element is non-null, valid, but doesn't have - // a displayport. It's possible that the init code in ChromeProcessController - // failed for some reason, or the document element got swapped out at some - // later time. In this case let's try to set a displayport on the document - // element again and bail out on this operation. + // This can happen if the document element gets swapped out after ChromeProcessController + // runs InitializeRootDisplayport. In this case let's try to set a displayport again and + // bail out on this operation. APZCCH_LOG("Widget %p's document element %p didn't have a displayport\n", aWidget, dpElement.get()); APZCCallbackHelper::InitializeRootDisplayport(aRootFrame->PresShell()); return false; } APZCCH_LOG("%p didn't have a displayport, so setting one...\n", dpElement.get()); bool activated = nsLayoutUtils::CalculateAndSetDisplayPortMargins(
--- a/gfx/layers/wr/WebRenderLayerManager.cpp +++ b/gfx/layers/wr/WebRenderLayerManager.cpp @@ -103,17 +103,16 @@ WebRenderLayerManager::DoDestroy(bool aI if (IsDestroyed()) { return; } LayerManager::Destroy(); if (WrBridge()) { // Just clear ImageKeys, they are deleted during WebRenderAPI destruction. - mImageKeysToDeleteLater.Clear(); mImageKeysToDelete.Clear(); // CompositorAnimations are cleared by WebRenderBridgeParent. mDiscardedCompositorAnimationsIds.Clear(); WrBridge()->Destroy(aIsSync); } // Clear this before calling RemoveUnusedAndResetWebRenderUserData(), // otherwise that function might destroy some WebRenderAnimationData instances @@ -293,17 +292,16 @@ WebRenderLayerManager::EndTransactionWit mLatestTransactionId = mTransactionIdAllocator->GetTransactionId(/*aThrottle*/ true); TimeStamp transactionStart = mTransactionIdAllocator->GetTransactionStart(); for (const auto& key : mImageKeysToDelete) { resourceUpdates.DeleteImage(key); } mImageKeysToDelete.Clear(); - mImageKeysToDelete.SwapElements(mImageKeysToDeleteLater); WrBridge()->RemoveExpiredFontKeys(resourceUpdates); // Skip the synchronization for buffer since we also skip the painting during // device-reset status. if (!gfxPlatform::GetPlatform()->DidRenderingDeviceReset()) { if (WrBridge()->GetSyncObject() && WrBridge()->GetSyncObject()->IsSyncObjectValid()) { @@ -401,30 +399,26 @@ WebRenderLayerManager::MakeSnapshotIfReq dt->FillRect(dst, pattern); mTarget = nullptr; } void WebRenderLayerManager::AddImageKeyForDiscard(wr::ImageKey key) { - mImageKeysToDeleteLater.AppendElement(key); + mImageKeysToDelete.AppendElement(key); } void WebRenderLayerManager::DiscardImages() { wr::IpcResourceUpdateQueue resources(WrBridge()); - for (const auto& key : mImageKeysToDeleteLater) { - resources.DeleteImage(key); - } for (const auto& key : mImageKeysToDelete) { resources.DeleteImage(key); } - mImageKeysToDeleteLater.Clear(); mImageKeysToDelete.Clear(); WrBridge()->UpdateResources(resources); } void WebRenderLayerManager::AddActiveCompositorAnimationId(uint64_t aId) { // In layers-free mode we track the active compositor animation ids on the @@ -457,17 +451,16 @@ WebRenderLayerManager::DiscardCompositor } void WebRenderLayerManager::DiscardLocalImages() { // Removes images but doesn't tell the parent side about them // This is useful in empty / failed transactions where we created // image keys but didn't tell the parent about them yet. - mImageKeysToDeleteLater.Clear(); mImageKeysToDelete.Clear(); } void WebRenderLayerManager::SetLayerObserverEpoch(uint64_t aLayerObserverEpoch) { if (WrBridge()->IPCOpen()) { WrBridge()->SendSetLayerObserverEpoch(aLayerObserverEpoch);
--- a/gfx/layers/wr/WebRenderLayerManager.h +++ b/gfx/layers/wr/WebRenderLayerManager.h @@ -170,21 +170,16 @@ private: * Take a snapshot of the parent context, and copy * it into mTarget. */ void MakeSnapshotIfRequired(LayoutDeviceIntSize aSize); private: nsIWidget* MOZ_NON_OWNING_REF mWidget; nsTArray<wr::ImageKey> mImageKeysToDelete; - // TODO - This is needed because we have some code that creates image keys - // and enqueues them for deletion right away which is bad not only because - // of poor texture cache usage, but also because images end up deleted before - // they are used. This should hopfully be temporary. - nsTArray<wr::ImageKey> mImageKeysToDeleteLater; // Set of compositor animation ids for which there are active animations (as // of the last transaction) on the compositor side. std::unordered_set<uint64_t> mActiveCompositorAnimationIds; // Compositor animation ids for animations that are done now and that we want // the compositor to discard information for. nsTArray<uint64_t> mDiscardedCompositorAnimationsIds;
--- a/intl/hyphenation/glue/hnjalloc.h +++ b/intl/hyphenation/glue/hnjalloc.h @@ -26,26 +26,32 @@ #include <stdio.h> /* ensure stdio.h is loaded before our macros */ #undef FILE #define FILE hnjFile #define fopen(path,mode) hnjFopen(path,mode) #define fclose(file) hnjFclose(file) #define fgets(buf,count,file) hnjFgets(buf,count,file) +#define feof(file) hnjFeof(file) +#define fgetc(file) hnjFgetc(file) typedef struct hnjFile_ hnjFile; #ifdef __cplusplus extern "C" { #endif hnjFile* hnjFopen(const char* aURISpec, const char* aMode); int hnjFclose(hnjFile* f); char* hnjFgets(char* s, int n, hnjFile* f); +int hnjFeof(hnjFile* f); + +int hnjFgetc(hnjFile* f); + #ifdef __cplusplus } #endif
--- a/intl/hyphenation/glue/hnjstdio.cpp +++ b/intl/hyphenation/glue/hnjstdio.cpp @@ -17,16 +17,17 @@ #define BUFSIZE 1024 struct hnjFile_ { nsCOMPtr<nsIInputStream> mStream; char mBuffer[BUFSIZE]; uint32_t mCurPos; uint32_t mLimit; + bool mEOF; }; // replacement for fopen() // (not a full substitute: only supports read access) hnjFile* hnjFopen(const char* aURISpec, const char* aMode) { // this override only needs to support "r" @@ -53,16 +54,17 @@ hnjFopen(const char* aURISpec, const cha if (NS_FAILED(rv)) { return nullptr; } hnjFile *f = new hnjFile; f->mStream = instream; f->mCurPos = 0; f->mLimit = 0; + f->mEOF = false; return f; } // replacement for fclose() int hnjFclose(hnjFile* f) { @@ -74,46 +76,64 @@ hnjFclose(hnjFile* f) result = EOF; } f->mStream = nullptr; delete f; return result; } +// replacement for fgetc() +int +hnjFgetc(hnjFile* f) +{ + if (f->mCurPos >= f->mLimit) { + f->mCurPos = 0; + + nsresult rv = f->mStream->Read(f->mBuffer, BUFSIZE, &f->mLimit); + if (NS_FAILED(rv)) { + f->mLimit = 0; + } + + if (f->mLimit == 0) { + f->mEOF = true; + return EOF; + } + } + + return f->mBuffer[f->mCurPos++]; +} + // replacement for fgets() // (not a full reimplementation, but sufficient for libhyphen's needs) char* hnjFgets(char* s, int n, hnjFile* f) { NS_ASSERTION(s && f, "bad argument to hnjFgets"); int i = 0; while (i < n - 1) { - if (f->mCurPos < f->mLimit) { - char c = f->mBuffer[f->mCurPos++]; - s[i++] = c; - if (c == '\n' || c == '\r') { - break; - } - continue; + int c = hnjFgetc(f); + + if (c == EOF) { + break; } - f->mCurPos = 0; + s[i++] = c; - nsresult rv = f->mStream->Read(f->mBuffer, BUFSIZE, &f->mLimit); - if (NS_FAILED(rv)) { - f->mLimit = 0; - return nullptr; - } - - if (f->mLimit == 0) { + if (c == '\n' || c == '\r') { break; } } if (i == 0) { return nullptr; // end of file } s[i] = '\0'; // null-terminate the returned string return s; } + +int +hnjFeof(hnjFile* f) +{ + return f->mEOF ? EOF : 0; +}
--- a/intl/hyphenation/hyphen/hyphen.c +++ b/intl/hyphenation/hyphen/hyphen.c @@ -433,21 +433,35 @@ for (k = 0; k < 2; k++) { dict[k]->utf8 = (strcmp(dict[k]->cset, "UTF-8") == 0); } else { strncpy(dict[k]->cset, dict[0]->cset, sizeof(dict[k]->cset)-1); dict[k]->cset[sizeof(dict[k]->cset)-1] = '\0'; dict[k]->utf8 = dict[0]->utf8; } if (k == 0 || nextlevel) { - while (fgets (buf, sizeof(buf), f) != NULL) { + while (fgets(buf, sizeof(buf), f) != NULL) { + + /* discard lines that don't fit in buffer */ + if (!feof(f) && strchr(buf, '\n') == NULL) { + int c; + while ((c = fgetc(f)) != '\n' && c != EOF); + /* issue warning if not a comment */ + if (buf[0] != '%') { + fprintf(stderr, "Warning: skipping too long pattern (more than %lu chars)\n", sizeof(buf)); + } + continue; + } + if (strncmp(buf, "NEXTLEVEL", 9) == 0) { - nextlevel = 1; - break; - } else if (buf[0] != '%') hnj_hyphen_load_line(buf, dict[k], hashtab); + nextlevel = 1; + break; + } else if (buf[0] != '%') { + hnj_hyphen_load_line(buf, dict[k], hashtab); + } } } else if (k == 1) { /* default first level: hyphen and ASCII apostrophe */ if (!dict[0]->utf8) hnj_hyphen_load_line("NOHYPHEN ',-\n", dict[k], hashtab); else hnj_hyphen_load_line("NOHYPHEN ',\xe2\x80\x93,\xe2\x80\x99,-\n", dict[k], hashtab); strncpy(buf, "1-1\n", MAX_CHARS-1); /* buf rewritten by hnj_hyphen_load here */ buf[MAX_CHARS-1] = '\0'; hnj_hyphen_load_line(buf, dict[k], hashtab); /* remove hyphen */
--- a/js/public/StructuredClone.h +++ b/js/public/StructuredClone.h @@ -290,23 +290,32 @@ typedef bool (*TransferStructuredCloneOp /** * Called when freeing an unknown transferable object. Note that it * should never trigger a garbage collection (and will assert in a * debug build if it does.) */ typedef void (*FreeTransferStructuredCloneOp)(uint32_t tag, JS::TransferableOwnership ownership, void* content, uint64_t extraData, void* closure); +/** + * Called when the transferring objects are checked. If this function returns false, the + * serialization ends throwing a DataCloneError exception. + */ +typedef bool (*CanTransferStructuredCloneOp)(JSContext* cx, + JS::Handle<JSObject*> obj, + void* closure); + struct JSStructuredCloneCallbacks { ReadStructuredCloneOp read; WriteStructuredCloneOp write; StructuredCloneErrorOp reportError; ReadTransferStructuredCloneOp readTransfer; TransferStructuredCloneOp writeTransfer; FreeTransferStructuredCloneOp freeTransfer; + CanTransferStructuredCloneOp canTransfer; }; enum OwnTransferablePolicy { OwnsTransferablesIfAny, IgnoreTransferablesIfAny, NoTransferables };
--- a/js/public/TrackedOptimizationInfo.h +++ b/js/public/TrackedOptimizationInfo.h @@ -33,26 +33,24 @@ namespace JS { _(SetProp_TypedObject) \ _(SetProp_DefiniteSlot) \ _(SetProp_Unboxed) \ _(SetProp_InlineAccess) \ _(SetProp_InlineCache) \ \ _(GetElem_TypedObject) \ _(GetElem_Dense) \ - _(GetElem_TypedStatic) \ _(GetElem_TypedArray) \ _(GetElem_String) \ _(GetElem_Arguments) \ _(GetElem_ArgumentsInlinedConstant) \ _(GetElem_ArgumentsInlinedSwitch) \ _(GetElem_InlineCache) \ \ _(SetElem_TypedObject) \ - _(SetElem_TypedStatic) \ _(SetElem_TypedArray) \ _(SetElem_Dense) \ _(SetElem_Arguments) \ _(SetElem_InlineCache) \ \ _(BinaryArith_Concat) \ _(BinaryArith_SpecializedTypes) \ _(BinaryArith_SpecializedOnBaselineTypes) \ @@ -118,18 +116,16 @@ namespace JS { _(AccessNotTypedObject) \ _(AccessNotTypedArray) \ _(AccessNotString) \ _(OperandNotString) \ _(OperandNotNumber) \ _(OperandNotStringOrNumber) \ _(OperandNotSimpleArith) \ _(OperandNotEasilyCoercibleToString) \ - _(StaticTypedArrayUint32) \ - _(StaticTypedArrayCantComputeMask) \ _(OutOfBounds) \ _(GetElemStringNotCached) \ _(NonNativeReceiver) \ _(IndexType) \ _(SetElemNonDenseNonTANotCached) \ _(NoSimdJitSupport) \ _(SimdTypeNotOptimized) \ _(UnknownSimdProperty) \
--- a/js/src/builtin/Module.js +++ b/js/src/builtin/Module.js @@ -183,19 +183,23 @@ function IsResolvedBinding(resolution) } // 15.2.1.18 GetModuleNamespace(module) function GetModuleNamespace(module) { // Step 1 assert(IsModule(module), "GetModuleNamespace called with non-module"); + // Until issue https://github.com/tc39/ecma262/issues/1155 is resolved, + // violate the spec here and throw if called on an errored module. + if (module.status === MODULE_STATUS_EVALUATED_ERROR) + throw GetModuleEvaluationError(module); + // Steps 2-3 - assert(module.status !== MODULE_STATUS_UNINSTANTIATED && - module.status !== MODULE_STATUS_EVALUATED_ERROR, + assert(module.status !== MODULE_STATUS_UNINSTANTIATED, "Bad module state in GetModuleNamespace"); // Step 4 let namespace = module.namespace; // Step 3 if (typeof namespace === "undefined") { let exportedNames = callFunction(module.getExportedNames, module);
--- a/js/src/builtin/Object.cpp +++ b/js/src/builtin/Object.cpp @@ -1750,42 +1750,16 @@ obj_getOwnPropertySymbols(JSContext* cx, if (!obj) return false; return GetOwnPropertyKeys(cx, obj, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS | JSITER_SYMBOLSONLY, args.rval()); } -/* ES6 draft rev 32 (2015 Feb 2) 19.1.2.4: Object.defineProperty(O, P, Attributes) */ -bool -js::obj_defineProperty(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - - // Steps 1-3. - RootedObject obj(cx); - if (!GetFirstArgumentAsObject(cx, args, "Object.defineProperty", &obj)) - return false; - RootedId id(cx); - if (!ToPropertyKey(cx, args.get(1), &id)) - return false; - - // Steps 4-5. - Rooted<PropertyDescriptor> desc(cx); - if (!ToPropertyDescriptor(cx, args.get(2), true, &desc)) - return false; - - // Steps 6-8. - if (!DefineProperty(cx, obj, id, desc)) - return false; - args.rval().setObject(*obj); - return true; -} - /* ES5 15.2.3.7: Object.defineProperties(O, Properties) */ static bool obj_defineProperties(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); /* Steps 1 and 7. */ RootedObject obj(cx);
--- a/js/src/builtin/Object.h +++ b/js/src/builtin/Object.h @@ -17,50 +17,39 @@ class Value; } // namespace JS namespace js { // Object constructor native. Exposed only so the JIT can know its address. MOZ_MUST_USE bool obj_construct(JSContext* cx, unsigned argc, JS::Value* vp); -MOZ_MUST_USE bool -obj_propertyIsEnumerable(JSContext* cx, unsigned argc, Value* vp); - PlainObject* ObjectCreateImpl(JSContext* cx, HandleObject proto, NewObjectKind newKind = GenericObject, HandleObjectGroup group = nullptr); PlainObject* ObjectCreateWithTemplate(JSContext* cx, HandlePlainObject templateObj); // Object methods exposed so they can be installed in the self-hosting global. MOZ_MUST_USE bool -obj_create(JSContext* cx, unsigned argc, JS::Value* vp); +obj_propertyIsEnumerable(JSContext* cx, unsigned argc, Value* vp); MOZ_MUST_USE bool -obj_defineProperty(JSContext* cx, unsigned argc, JS::Value* vp); +obj_create(JSContext* cx, unsigned argc, JS::Value* vp); MOZ_MUST_USE bool obj_getOwnPropertyNames(JSContext* cx, unsigned argc, JS::Value* vp); MOZ_MUST_USE bool -obj_getPrototypeOf(JSContext* cx, unsigned argc, JS::Value* vp); - - -MOZ_MUST_USE bool -obj_isExtensible(JSContext* cx, unsigned argc, JS::Value* vp); - -MOZ_MUST_USE bool obj_toString(JSContext* cx, unsigned argc, JS::Value* vp); JSString* ObjectClassToString(JSContext* cx, HandleObject obj); -// Exposed so SelfHosting.cpp can use it in the OwnPropertyKeys intrinsic MOZ_MUST_USE bool GetOwnPropertyKeys(JSContext* cx, HandleObject obj, unsigned flags, JS::MutableHandleValue rval); // Exposed for SelfHosting.cpp MOZ_MUST_USE bool GetOwnPropertyDescriptorToArray(JSContext* cx, unsigned argc, JS::Value* vp); /*
--- a/js/src/builtin/Object.js +++ b/js/src/builtin/Object.js @@ -3,17 +3,17 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ // ES stage 4 proposal function ObjectGetOwnPropertyDescriptors(O) { // Step 1. var obj = ToObject(O); // Step 2. - var keys = OwnPropertyKeys(obj); + var keys = std_Reflect_ownKeys(obj); // Step 3. var descriptors = {}; // Step 4. for (var index = 0, len = keys.length; index < len; index++) { var key = keys[index];
--- a/js/src/builtin/Reflect.cpp +++ b/js/src/builtin/Reflect.cpp @@ -82,18 +82,18 @@ js::Reflect_isExtensible(JSContext* cx, if (!IsExtensible(cx, target, &extensible)) return false; args.rval().setBoolean(extensible); return true; } // ES2018 draft rev c164be80f7ea91de5526b33d54e5c9321ed03d3f // 26.1.10 Reflect.ownKeys ( target ) -static bool -Reflect_ownKeys(JSContext* cx, unsigned argc, Value* vp) +bool +js::Reflect_ownKeys(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); // Step 1. RootedObject target(cx, NonNullObjectArg(cx, "`target`", "Reflect.ownKeys", args.get(0))); if (!target) return false;
--- a/js/src/builtin/Reflect.h +++ b/js/src/builtin/Reflect.h @@ -19,11 +19,14 @@ InitReflect(JSContext* cx, js::HandleObj namespace js { extern MOZ_MUST_USE bool Reflect_getPrototypeOf(JSContext* cx, unsigned argc, Value* vp); extern MOZ_MUST_USE bool Reflect_isExtensible(JSContext* cx, unsigned argc, Value* vp); +extern MOZ_MUST_USE bool +Reflect_ownKeys(JSContext* cx, unsigned argc, Value* vp); + } #endif /* builtin_Reflect_h */
--- a/js/src/builtin/Utilities.js +++ b/js/src/builtin/Utilities.js @@ -214,77 +214,88 @@ function GetInternalError(msg) { return e; } assert(false, "the catch block should've returned from this function."); } // To be used when a function is required but calling it shouldn't do anything. function NullFunction() {} -// Object Rest/Spread Properties proposal -// Abstract operation: CopyDataProperties (target, source, excluded) -function CopyDataProperties(target, source, excluded) { +// ES2019 draft rev 4c2df13f4194057f09b920ee88712e5a70b1a556 +// 7.3.23 CopyDataProperties (target, source, excludedItems) +function CopyDataProperties(target, source, excludedItems) { // Step 1. assert(IsObject(target), "target is an object"); // Step 2. - assert(IsObject(excluded), "excluded is an object"); + assert(IsObject(excludedItems), "excludedItems is an object"); - // Steps 3, 6. + // Steps 3 and 7. if (source === undefined || source === null) return; - // Step 4.a. - source = ToObject(source); - - // Step 4.b. - var keys = OwnPropertyKeys(source); + // Step 4. + var from = ToObject(source); // Step 5. + var keys = CopyDataPropertiesOrGetOwnKeys(target, from, excludedItems); + + // Return if we copied all properties in native code. + if (keys === null) + return; + + // Step 6. for (var index = 0; index < keys.length; index++) { var key = keys[index]; // We abbreviate this by calling propertyIsEnumerable which is faster // and returns false for not defined properties. - if (!hasOwn(key, excluded) && callFunction(std_Object_propertyIsEnumerable, source, key)) - _DefineDataProperty(target, key, source[key]); + if (!hasOwn(key, excludedItems) && + callFunction(std_Object_propertyIsEnumerable, from, key)) + { + _DefineDataProperty(target, key, from[key]); + } } - // Step 6 (Return). + // Step 7 (Return). } -// Object Rest/Spread Properties proposal -// Abstract operation: CopyDataProperties (target, source, excluded) +// ES2019 draft rev 4c2df13f4194057f09b920ee88712e5a70b1a556 +// 7.3.23 CopyDataProperties (target, source, excludedItems) function CopyDataPropertiesUnfiltered(target, source) { // Step 1. assert(IsObject(target), "target is an object"); // Step 2 (Not applicable). - // Steps 3, 6. + // Steps 3 and 7. if (source === undefined || source === null) return; - // Step 4.a. - source = ToObject(source); - - // Step 4.b. - var keys = OwnPropertyKeys(source); + // Step 4. + var from = ToObject(source); // Step 5. + var keys = CopyDataPropertiesOrGetOwnKeys(target, from, null); + + // Return if we copied all properties in native code. + if (keys === null) + return; + + // Step 6. for (var index = 0; index < keys.length; index++) { var key = keys[index]; // We abbreviate this by calling propertyIsEnumerable which is faster // and returns false for not defined properties. - if (callFunction(std_Object_propertyIsEnumerable, source, key)) - _DefineDataProperty(target, key, source[key]); + if (callFunction(std_Object_propertyIsEnumerable, from, key)) + _DefineDataProperty(target, key, from[key]); } - // Step 6 (Return). + // Step 7 (Return). } /*************************************** Testing functions ***************************************/ function outer() { return function inner() { return "foo"; }; }
--- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -5484,30 +5484,17 @@ BytecodeEmitter::setOrEmitSetFunName(Par FunctionPrefixKind prefixKind) { MOZ_ASSERT(maybeFun->isDirectRHSAnonFunction()); if (maybeFun->isKind(ParseNodeKind::Function)) { // Function doesn't have 'name' property at this point. // Set function's name at compile time. JSFunction* fun = maybeFun->pn_funbox->function(); - - // Single node can be emitted multiple times if it appears in - // array destructuring default. If function already has a name, - // just return. - if (fun->hasInferredName()) { -#ifdef DEBUG - RootedFunction rootedFun(cx, fun); - JSAtom* funName = NameToFunctionName(cx, name, prefixKind); - if (!funName) - return false; - MOZ_ASSERT(funName == rootedFun->inferredName()); -#endif - return true; - } + MOZ_ASSERT(!fun->hasInferredName()); fun->setInferredName(name); return true; } MOZ_ASSERT(maybeFun->isKind(ParseNodeKind::Class)); uint32_t nameIndex;
--- a/js/src/frontend/FoldConstants.cpp +++ b/js/src/frontend/FoldConstants.cpp @@ -1285,19 +1285,21 @@ FoldElement(JSContext* cx, ParseNode** n name = atom->asPropertyName(); } } else if (key->isKind(ParseNodeKind::Number)) { double number = key->pn_dval; if (number != ToUint32(number)) { // Optimization 2: We have something like expr[3.14]. The number // isn't an array index, so it converts to a string ("3.14"), // enabling optimization 3 below. - JSAtom* atom = ToAtom<NoGC>(cx, DoubleValue(number)); - if (!atom) + JSAtom* atom = NumberToAtom(cx, number); + if (!atom) { + cx->recoverFromOutOfMemory(); return false; + } name = atom->asPropertyName(); } } // If we don't have a name, we can't optimize to getprop. if (!name) return true;
--- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -9304,24 +9304,16 @@ GeneralParser<ParseHandler, CharT>::arra reportMissingClosing(JSMSG_BRACKET_AFTER_LIST, JSMSG_BRACKET_OPENED, begin)); } handler.setEndPosition(literal, pos().end); return literal; } -static JSAtom* -DoubleToAtom(JSContext* cx, double value) -{ - // This is safe because doubles can not be moved. - Value tmp = DoubleValue(value); - return ToAtom<CanGC>(cx, HandleValue::fromMarkedLocation(&tmp)); -} - template <class ParseHandler, typename CharT> typename ParseHandler::Node GeneralParser<ParseHandler, CharT>::propertyName(YieldHandling yieldHandling, const Maybe<DeclarationKind>& maybeDecl, Node propList, PropertyType* propType, MutableHandleAtom propAtom) { @@ -9369,17 +9361,17 @@ GeneralParser<ParseHandler, CharT>::prop if (!tokenStream.getToken(<ok)) return null(); } propAtom.set(nullptr); Node propName; switch (ltok) { case TokenKind::Number: - propAtom.set(DoubleToAtom(context, anyChars.currentToken().number())); + propAtom.set(NumberToAtom(context, anyChars.currentToken().number())); if (!propAtom.get()) return null(); propName = newNumber(anyChars.currentToken()); if (!propName) return null(); break; case TokenKind::String: { @@ -9433,27 +9425,27 @@ GeneralParser<ParseHandler, CharT>::prop } if (tt == TokenKind::String) { tokenStream.consumeKnownToken(TokenKind::String); propAtom.set(anyChars.currentToken().atom()); uint32_t index; if (propAtom->isIndex(&index)) { - propAtom.set(DoubleToAtom(context, index)); + propAtom.set(NumberToAtom(context, index)); if (!propAtom.get()) return null(); return handler.newNumber(index, NoDecimal, pos()); } return stringLiteral(); } if (tt == TokenKind::Number) { tokenStream.consumeKnownToken(TokenKind::Number); - propAtom.set(DoubleToAtom(context, anyChars.currentToken().number())); + propAtom.set(NumberToAtom(context, anyChars.currentToken().number())); if (!propAtom.get()) return null(); return newNumber(anyChars.currentToken()); } if (tt == TokenKind::Lb) { tokenStream.consumeKnownToken(TokenKind::Lb); return computedPropertyName(yieldHandling, maybeDecl, propList);
--- a/js/src/fuzz-tests/moz.build +++ b/js/src/fuzz-tests/moz.build @@ -4,16 +4,17 @@ # 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/. GeckoProgram('fuzz-tests', linkage=None) UNIFIED_SOURCES += [ 'testExample.cpp', 'tests.cpp', + 'testStructuredCloneReader.cpp', ] if CONFIG['JS_BUILD_BINAST']: UNIFIED_SOURCES += [ 'testBinASTReader.cpp', ] DEFINES['EXPORT_JS_API'] = True
new file mode 100644 --- /dev/null +++ b/js/src/fuzz-tests/testStructuredCloneReader.cpp @@ -0,0 +1,86 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + */ +/* 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/. */ + + +#include "mozilla/ScopeExit.h" + +#include "jsapi.h" + +#include "fuzz-tests/tests.h" +#include "vm/Interpreter.h" + +#include "vm/JSContext-inl.h" + +using namespace js; + +// These are defined and pre-initialized by the harness (in tests.cpp). +extern JS::PersistentRootedObject gGlobal; +extern JSContext* gCx; + +static int +testStructuredCloneReaderInit(int *argc, char ***argv) { + return 0; +} + +static int +testStructuredCloneReaderFuzz(const uint8_t* buf, size_t size) { + auto gcGuard = mozilla::MakeScopeExit([&] { + JS::PrepareForFullGC(gCx); + JS::GCForReason(gCx, GC_NORMAL, JS::gcreason::API); + }); + + if (!size) return 0; + + // Make sure to pad the buffer to a multiple of kSegmentAlignment + const size_t kSegmentAlignment = 8; + size_t buf_size = JS_ROUNDUP(size, kSegmentAlignment); + + auto clonebuf = MakeUnique<JSStructuredCloneData>(0, 0, buf_size); + if (!clonebuf || !clonebuf->Init(buf_size, buf_size)) { + ReportOutOfMemory(gCx); + return 0; + } + + // Initialize with zeros, including padding, then copy buffer + memset(clonebuf->Start(), '\0', buf_size); + js_memcpy(clonebuf->Start(), buf, size); + + JS::StructuredCloneScope scope = JS::StructuredCloneScope::DifferentProcess; + + RootedValue deserialized(gCx); + if (!JS_ReadStructuredClone(gCx, *clonebuf, + JS_STRUCTURED_CLONE_VERSION, + scope, + &deserialized, nullptr, nullptr)) + { + return 0; + } + + /* If we succeeded in deserializing, we should try to reserialize the data. + This has two main advantages: + + 1) It tests parts of the serializer as well. + 2) The deserialized data is actually used, making it more likely to detect + further memory-related problems. + + Tests show that this also doesn't cause a serious performance penalty. + */ + mozilla::Maybe<JSAutoStructuredCloneBuffer> clonebufOut; + JS::CloneDataPolicy policy; + + clonebufOut.emplace(scope, nullptr, nullptr); + if (!clonebufOut->write(gCx, deserialized, UndefinedHandleValue, policy)) + return 0; + + return 0; +} + +MOZ_FUZZING_INTERFACE_RAW( + testStructuredCloneReaderInit, + testStructuredCloneReaderFuzz, + StructuredCloneReader +);
new file mode 100644 --- /dev/null +++ b/js/src/jit-test/tests/auto-regress/bug1448582-1.js @@ -0,0 +1,21 @@ +// Overview: +// - The outer function is an IIFE which gets marked as a singleton. +// - The |o[index]| inner function is then also marked as a singleton. +// - The |o[index]| inner function has a dynamic name from a computed property name. +// - The |self| inner function uses |Function.prototype.caller| to reinvoke the outer function. +// +// When we reinvoke outer, we end up cloning a previously reused, i.e. non-cloned, +// function which triggered an assertion in js::SetFunctionNameIfNoOwnName(). + +(function(index) { + var o = { + [index]: function() {} + }; + + // Reinvoke the IIFE through |Function.prototype.caller|. + if (index === 0) { + (function self() { + self.caller(1); + })(); + } +})(0);
new file mode 100644 --- /dev/null +++ b/js/src/jit-test/tests/auto-regress/bug1448582-2.js @@ -0,0 +1,25 @@ +// Overview: +// - The outer function is an IIFE which gets marked as a singleton. +// - The |o[index]| inner function is then also marked as a singleton. +// - The |o[index]| inner function has a dynamic name from a computed property name. +// - The |self| inner function uses |Function.prototype.caller| to reinvoke the outer function. +// +// When we reinvoke outer, we end up cloning a previously reused, i.e. non-cloned, +// function which triggered an assertion in js::SetFunctionNameIfNoOwnName(). + +(function(index) { + var o = { + [index]: function() {} + }; + + // Accessing |.name| sets the resolved-name flag, which triggered yet + // another assertion when compared to bug1448582-1.js + assertEq(o[index].name, String(index)); + + // Reinvoke the IIFE through |Function.prototype.caller|. + if (index === 0) { + (function self() { + self.caller(1); + })(); + } +})(0);
new file mode 100644 --- /dev/null +++ b/js/src/jit-test/tests/auto-regress/bug1448582-3.js @@ -0,0 +1,22 @@ +// Overview: +// - The outer function is an IIFE which gets marked as a singleton. +// - The |fn| inner function is then also marked as a singleton. +// - The |self| inner function uses |Function.prototype.caller| to reinvoke the outer function. +// +// When we reinvoke outer, we end up cloning a previously reused, i.e. non-cloned, +// function. + +(function(index) { + var fn = function() {}; + + // Accessing |.name| sets the resolved-name flag, which should not be + // copied over to the function clone. + assertEq(fn.name, "fn"); + + // Reinvoke the IIFE through |Function.prototype.caller|. + if (index === 0) { + (function self() { + self.caller(1); + })(); + } +})(0);
new file mode 100644 --- /dev/null +++ b/js/src/jit-test/tests/auto-regress/bug1448582-4.js @@ -0,0 +1,22 @@ +// Overview: +// - The outer function is an IIFE which gets marked as a singleton. +// - The |fn| inner function is then also marked as a singleton. +// - The |self| inner function uses |Function.prototype.caller| to reinvoke the outer function. +// +// When we reinvoke outer, we end up cloning a previously reused, i.e. non-cloned, +// function. + +(function(index) { + var fn = function(a) {}; + + // Accessing |.length| sets the resolved-length flag, which should not be + // copied over to the function clone. + assertEq(fn.length, 1); + + // Reinvoke the IIFE through |Function.prototype.caller|. + if (index === 0) { + (function self() { + self.caller(1); + })(); + } +})(0);
new file mode 100644 --- /dev/null +++ b/js/src/jit-test/tests/auto-regress/bug1448582-5.js @@ -0,0 +1,133 @@ +// Repeat 1448582-{1,3,4}.js for classes. + +(function(index) { + // Does not assert. + var c = class { constructor(){} }; + + if (index === 0) { + (function self() { + self.caller(1); + })(); + } +})(0); + +(function(index) { + var c = class { constructor(){} }; + + // Accessing |.name| sets the resolved-name flag, which should not be + // copied over to the function clone. + assertEq(c.name, "c"); + + if (index === 0) { + (function self() { + self.caller(1); + })(); + } +})(0); + +(function(index) { + var c = class { constructor(a){} }; + + // Accessing |.length| sets the resolved-length flag, which should not be + // copied over to the function clone. + assertEq(c.length, 1); + + if (index === 0) { + (function self() { + self.caller(1); + })(); + } +})(0); + + +// Repeat 1448582-{3,4}.js for generator functions. + +(function(index) { + function* f() {} + + // Accessing |.name| sets the resolved-name flag, which should not be + // copied over to the function clone. + assertEq(f.name, "f"); + + if (index === 0) { + (function self() { + self.caller(1); + })(); + } +})(0); + +(function(index) { + function* f(a) {} + + // Accessing |.length| sets the resolved-length flag, which should not be + // copied over to the function clone. + assertEq(f.length, 1); + + if (index === 0) { + (function self() { + self.caller(1); + })(); + } +})(0); + + +// Repeat 1448582-{3,4}.js for async functions. + +(function(index) { + async function f() {} + + // Accessing |.name| sets the resolved-name flag, which should not be + // copied over to the function clone. + assertEq(f.name, "f"); + + if (index === 0) { + (function self() { + self.caller(1); + })(); + } +})(0); + +(function(index) { + async function f(a) {} + + // Accessing |.length| sets the resolved-length flag, which should not be + // copied over to the function clone. + assertEq(f.length, 1); + + if (index === 0) { + (function self() { + self.caller(1); + })(); + } +})(0); + + +// Repeat 1448582-{3,4}.js for async generator functions. + +(function(index) { + async function* f() {} + + // Accessing |.name| sets the resolved-name flag, which should not be + // copied over to the function clone. + assertEq(f.name, "f"); + + if (index === 0) { + (function self() { + self.caller(1); + })(); + } +})(0); + +(function(index) { + async function* f(a) {} + + // Accessing |.length| sets the resolved-length flag, which should not be + // copied over to the function clone. + assertEq(f.length, 1); + + if (index === 0) { + (function self() { + self.caller(1); + })(); + } +})(0);
new file mode 100644 --- /dev/null +++ b/js/src/jit-test/tests/auto-regress/bug1448582-6.js @@ -0,0 +1,27 @@ +// Overview: +// - The outer function is an IIFE which gets marked as a singleton. +// - The |o[index]| inner function is then also marked as a singleton. +// - The |o[index]| inner function has a dynamic name from a computed property name. +// - The |self| inner function uses |Function.prototype.caller| to reinvoke the outer function. + +(function(index) { + var o = { + [index]: class { + constructor() {} + + // Prevent adding an inferred name at index = 1 by creating a + // static method named "name". + static [(index === 0 ? "not-name" : "name")]() {} + } + } + + // At index = 0 the class will get the inferred name "0". + // At index = 1 the class should have no inferred name. + assertEq(displayName(o[index]), index === 0 ? "0" : ""); + + if (index === 0) { + (function self() { + self.caller(1); + })(); + } +})(0);
--- a/js/src/jit-test/tests/basic/object-assign-unboxed.js +++ b/js/src/jit-test/tests/basic/object-assign-unboxed.js @@ -5,17 +5,18 @@ function Unboxed() { this.b = true; } function tryCreateUnboxedObject() { var obj; for (var i = 0; i < 1000; ++i) { obj = new Unboxed(); } - + if (unboxedObjectsEnabled()) + assertEq(isUnboxedObject(obj), true); return obj; } function basic() { var unboxed = tryCreateUnboxedObject(); var target = {}; Object.assign(target, unboxed);
new file mode 100644 --- /dev/null +++ b/js/src/jit-test/tests/basic/object-rest-unboxed.js @@ -0,0 +1,44 @@ +load(libdir + "asserts.js"); + +function Unboxed() { + this.a = 0; + this.b = true; +} + +function tryCreateUnboxedObject() { + var obj; + for (var i = 0; i < 1000; ++i) { + obj = new Unboxed(); + } + if (unboxedObjectsEnabled()) + assertEq(isUnboxedObject(obj), true); + return obj; +} + +function basic() { + var unboxed = tryCreateUnboxedObject(); + + var {...target} = unboxed; + assertDeepEq(target, {a: 0, b: true}); + + var {a, c, ...target} = unboxed; + assertDeepEq(a, 0); + assertDeepEq(c, undefined); + assertDeepEq(target, {b: true}); +} + +function expando() { + var unboxed = tryCreateUnboxedObject(); + unboxed.c = 3.5; + + var {...target} = unboxed; + assertDeepEq(target, {a: 0, b: true, c: 3.5}); + + var {a, d, ...target} = unboxed; + assertDeepEq(a, 0); + assertDeepEq(d, undefined); + assertDeepEq(target, {b: true, c: 3.5}); +} + +basic(); +expando();
new file mode 100644 --- /dev/null +++ b/js/src/jit-test/tests/basic/object-rest.js @@ -0,0 +1,90 @@ +function test() { + var from, to; + + // From values. + from = {x: 1, y: 2}; + ({...to} = from); + assertEq(to.y, 2); + + var z; + from = {x: 1, y: 2}; + ({x: z, ...to} = from); + assertEq(z, 1); + assertEq(to.y, 2); + + // From getter. + var c = 7; + from = {x: 1, get y() { return ++c; }}; + ({...to} = from); + assertEq(c, 8); + assertEq(to.y, 8); + + from = {x: 1, get y() { return ++c; }}; + ({y: z, ...to} = from); + assertEq(c, 9); + assertEq(z, 9); + assertEq(to.y, undefined); + + // Array with dense elements. + from = [1, 2, 3]; + ({...to} = from); + assertEq(to[2], 3); + assertEq("length" in to, false); + + from = [1, 2, 3]; + ({2: z, ...to} = from); + assertEq(z, 3); + assertEq(to[2], undefined); + assertEq(to[0], 1); + assertEq("length" in to, false); + + // Object with sparse elements and symbols. + from = {x: 1, 1234567: 2, 1234560: 3, [Symbol.iterator]: 5, z: 3}; + ({...to} = from); + assertEq(to[1234567], 2); + assertEq(Object.keys(to).toString(), "1234560,1234567,x,z"); + assertEq(to[Symbol.iterator], 5); + + from = {x: 1, 1234567: 2, 1234560: 3, [Symbol.iterator]: 5, z: 3}; + ({[Symbol.iterator]: z, ...to} = from); + assertEq(to[1234567], 2); + assertEq(Object.keys(to).toString(), "1234560,1234567,x,z"); + assertEq(to[Symbol.iterator], undefined); + assertEq(z, 5); + + // Typed array. + from = new Int32Array([1, 2, 3]); + ({...to} = from); + assertEq(to[1], 2); + + from = new Int32Array([1, 2, 3]); + ({1: z, ...to} = from); + assertEq(z, 2); + assertEq(to[1], undefined); + assertEq(to[2], 3); + + // Primitive string. + from = "foo"; + ({...to} = from); + assertEq(to[0], "f"); + + from = "foo"; + ({0: z, ...to} = from); + assertEq(z, "f"); + assertEq(to[0], undefined); + assertEq(to[1], "o"); + + // String object. + from = new String("bar"); + ({...to} = from); + assertEq(to[2], "r"); + + from = new String("bar"); + ({1: z, ...to} = from); + assertEq(z, "a"); + assertEq(to[1], undefined); + assertEq(to[2], "r"); +} +test(); +test(); +test();
new file mode 100644 --- /dev/null +++ b/js/src/jit-test/tests/basic/object-spread-unboxed.js @@ -0,0 +1,40 @@ +load(libdir + "asserts.js"); + +function Unboxed() { + this.a = 0; + this.b = true; +} + +function tryCreateUnboxedObject() { + var obj; + for (var i = 0; i < 1000; ++i) { + obj = new Unboxed(); + } + if (unboxedObjectsEnabled()) + assertEq(isUnboxedObject(obj), true); + return obj; +} + +function basic() { + var unboxed = tryCreateUnboxedObject(); + + var target = {...unboxed}; + assertDeepEq(target, {a: 0, b: true}); + + target = {a: 1, c: 3, ...unboxed}; + assertDeepEq(target, {a: 0, c: 3, b: true}); +} + +function expando() { + var unboxed = tryCreateUnboxedObject(); + unboxed.c = 3.5; + + var target = {...unboxed}; + assertDeepEq(target, {a: 0, b: true, c: 3.5}); + + target = {a: 1, d: 3, ...unboxed}; + assertDeepEq(target, {a: 0, d: 3, b: true, c: 3.5}); +} + +basic(); +expando();
new file mode 100644 --- /dev/null +++ b/js/src/jit-test/tests/basic/object-spread.js @@ -0,0 +1,49 @@ +function test() { + var from, to; + + // From values. + from = {x: 1, y: 2}; + to = {...from}; + assertEq(to.y, 2); + to = {...from, ...from}; + assertEq(to.y, 2); + + // From getter. + var c = 7; + from = {x: 1, get y() { return ++c; }}; + to = {...from}; + assertEq(to.y, 8); + to = {...from, ...from}; + assertEq(to.y, 10); + + // Array with dense elements. + from = [1, 2, 3]; + to = {...from}; + assertEq(to[2], 3); + assertEq("length" in to, false); + + // Object with sparse elements and symbols. + from = {x: 1, 1234567: 2, 1234560: 3, [Symbol.iterator]: 5, z: 3}; + to = {...from}; + assertEq(to[1234567], 2); + assertEq(Object.keys(to).toString(), "1234560,1234567,x,z"); + assertEq(to[Symbol.iterator], 5); + + // Typed array. + from = new Int32Array([1, 2, 3]); + to = {...from}; + assertEq(to[1], 2); + + // Primitive string. + from = "foo"; + to = {...from}; + assertEq(to[0], "f"); + + // String object. + from = new String("bar"); + to = {...from}; + assertEq(to[2], "r"); +} +test(); +test(); +test();
new file mode 100644 --- /dev/null +++ b/js/src/jit-test/tests/modules/bug1449153.js @@ -0,0 +1,35 @@ +// Test performing GetModuleNamespace on an errored module. + +class MyError {} + +function assertThrowsMyError(f) +{ + let caught = false; + try { + f(); + } catch (e) { + caught = true; + assertEq(e.constructor, MyError); + } + assertEq(caught, true); +} + +let moduleRepo = {}; +setModuleResolveHook(function(module, specifier) { + return moduleRepo[specifier]; +}); + +moduleRepo["a"] = parseModule(` + throw new MyError(); +`); + +let c = moduleRepo["c"] = parseModule(` + import "a"; +`); +c.declarationInstantiation(); +assertThrowsMyError(() => c.evaluation()); + +let b = moduleRepo['b'] = parseModule(` + import * as ns0 from 'a' +`); +assertThrowsMyError(() => b.declarationInstantiation());
--- a/js/src/jit-test/tests/wasm/regress/baseline-getglobal-scratch.js +++ b/js/src/jit-test/tests/wasm/regress/baseline-getglobal-scratch.js @@ -27,9 +27,32 @@ new WebAssembly.Module(wasmTextToBinary( get_global $g get_global $g get_global $g get_global $g get_global $g unreachable ) ) -`)) +`)); + +new WebAssembly.Module(wasmTextToBinary(` +(module + (global $g (mut i32) (i32.const 42)) + (func (param $i i32) + get_global $g + get_global $g + get_global $g + get_global $g + get_global $g + get_global $g + get_global $g + get_global $g + get_global $g + get_global $g + get_global $g + get_global $g + get_local $i + set_global $g + unreachable + ) +) +`));
--- a/js/src/jit/AliasAnalysisShared.cpp +++ b/js/src/jit/AliasAnalysisShared.cpp @@ -118,18 +118,16 @@ GetObject(const MDefinition* ins) case MDefinition::Opcode::LoadElementHole: case MDefinition::Opcode::TypedArrayElements: case MDefinition::Opcode::TypedObjectElements: case MDefinition::Opcode::CopyLexicalEnvironmentObject: case MDefinition::Opcode::IsPackedArray: object = ins->getOperand(0); break; case MDefinition::Opcode::GetPropertyCache: - case MDefinition::Opcode::LoadTypedArrayElementStatic: - case MDefinition::Opcode::StoreTypedArrayElementStatic: case MDefinition::Opcode::GetDOMProperty: case MDefinition::Opcode::GetDOMMember: case MDefinition::Opcode::Call: case MDefinition::Opcode::Compare: case MDefinition::Opcode::GetArgumentsObjectArg: case MDefinition::Opcode::SetArgumentsObjectArg: case MDefinition::Opcode::GetFrameArgument: case MDefinition::Opcode::SetFrameArgument:
--- a/js/src/jit/BaselineCacheIRCompiler.cpp +++ b/js/src/jit/BaselineCacheIRCompiler.cpp @@ -1375,19 +1375,21 @@ BaselineCacheIRCompiler::emitStoreDenseE FailurePath* failure; if (!addFailurePath(&failure)) return false; // Load obj->elements in scratch. masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratch); - // Bounds check. + // Bounds check. Unfortunately we don't have more registers available on + // x86, so use InvalidReg and emit slightly slower code on x86. + Register spectreTemp = InvalidReg; Address initLength(scratch, ObjectElements::offsetOfInitializedLength()); - masm.branch32(Assembler::BelowOrEqual, initLength, index, failure->label()); + masm.spectreBoundsCheck32(index, initLength, spectreTemp, failure->label()); // Hole check. BaseObjectElementIndex element(scratch, index); masm.branchTestMagic(Assembler::Equal, element, failure->label()); // Perform a single test to see if we either need to convert double // elements, clone the copy on write elements in the object or fail // due to a frozen element. @@ -1466,28 +1468,40 @@ BaselineCacheIRCompiler::emitStoreDenseE Address elementsFlags(scratch, ObjectElements::offsetOfFlags()); // Check for copy-on-write or frozen elements. masm.branchTest32(Assembler::NonZero, elementsFlags, Imm32(ObjectElements::COPY_ON_WRITE | ObjectElements::FROZEN), failure->label()); + // We don't have enough registers on x86 so use InvalidReg. This will emit + // slightly less efficient code on x86. + Register spectreTemp = InvalidReg; + if (handleAdd) { - // Fail if index > initLength. - masm.branch32(Assembler::Below, initLength, index, failure->label()); + // Bounds check. + Label capacityOk, outOfBounds; + masm.spectreBoundsCheck32(index, initLength, spectreTemp, &outOfBounds); + masm.jump(&capacityOk); + + // If we're out-of-bounds, only handle the index == initLength case. + masm.bind(&outOfBounds); + masm.branch32(Assembler::NotEqual, initLength, index, failure->label()); // If index < capacity, we can add a dense element inline. If not we // need to allocate more elements. - Label capacityOk; + Label allocElement; Address capacity(scratch, ObjectElements::offsetOfCapacity()); - masm.branch32(Assembler::Above, capacity, index, &capacityOk); + masm.spectreBoundsCheck32(index, capacity, spectreTemp, &allocElement); + masm.jump(&capacityOk); // Check for non-writable array length. We only have to do this if // index >= capacity. + masm.bind(&allocElement); masm.branchTest32(Assembler::NonZero, elementsFlags, Imm32(ObjectElements::NONWRITABLE_ARRAY_LENGTH), failure->label()); LiveRegisterSet save(GeneralRegisterSet::Volatile(), liveVolatileFloatRegs()); save.takeUnchecked(scratch); masm.PushRegsInMask(save); @@ -1505,17 +1519,17 @@ BaselineCacheIRCompiler::emitStoreDenseE masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratch); masm.bind(&capacityOk); // We increment initLength after the callTypeUpdateIC call, to ensure // the type update code doesn't read uninitialized memory. } else { // Fail if index >= initLength. - masm.branch32(Assembler::BelowOrEqual, initLength, index, failure->label()); + masm.spectreBoundsCheck32(index, initLength, spectreTemp, failure->label()); } // Check if we have to convert a double element. Label noConversion; masm.branchTest32(Assembler::Zero, elementsFlags, Imm32(ObjectElements::CONVERT_DOUBLE_ELEMENTS), &noConversion); @@ -1590,39 +1604,41 @@ BaselineCacheIRCompiler::emitArrayPush() AutoScratchRegister scratchLength(allocator, masm); FailurePath* failure; if (!addFailurePath(&failure)) return false; // Load obj->elements in scratch. masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratch); - masm.load32(Address(scratch, ObjectElements::offsetOfLength()), scratchLength); - - BaseObjectElementIndex element(scratch, scratchLength); - Address initLength(scratch, ObjectElements::offsetOfInitializedLength()); + + Address elementsInitLength(scratch, ObjectElements::offsetOfInitializedLength()); + Address elementsLength(scratch, ObjectElements::offsetOfLength()); Address elementsFlags(scratch, ObjectElements::offsetOfFlags()); // Check for copy-on-write or frozen elements. masm.branchTest32(Assembler::NonZero, elementsFlags, Imm32(ObjectElements::COPY_ON_WRITE | ObjectElements::FROZEN), failure->label()); // Fail if length != initLength. - masm.branch32(Assembler::NotEqual, initLength, scratchLength, failure->label()); + masm.load32(elementsInitLength, scratchLength); + masm.branch32(Assembler::NotEqual, elementsLength, scratchLength, failure->label()); // If scratchLength < capacity, we can add a dense element inline. If not we // need to allocate more elements. - Label capacityOk; + Label capacityOk, allocElement; Address capacity(scratch, ObjectElements::offsetOfCapacity()); - masm.branch32(Assembler::Above, capacity, scratchLength, &capacityOk); + masm.spectreBoundsCheck32(scratchLength, capacity, InvalidReg, &allocElement); + masm.jump(&capacityOk); // Check for non-writable array length. We only have to do this if // index >= capacity. + masm.bind(&allocElement); masm.branchTest32(Assembler::NonZero, elementsFlags, Imm32(ObjectElements::NONWRITABLE_ARRAY_LENGTH), failure->label()); LiveRegisterSet save(GeneralRegisterSet::Volatile(), liveVolatileFloatRegs()); save.takeUnchecked(scratch); masm.PushRegsInMask(save); @@ -1667,22 +1683,22 @@ BaselineCacheIRCompiler::emitArrayPush() saveRegs.add(val); if (!callTypeUpdateIC(obj, val, scratch, saveRegs)) return false; // Reload obj->elements as callTypeUpdateIC used the scratch register. masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratch); // Increment initLength and length. - Address length(scratch, ObjectElements::offsetOfLength()); - masm.add32(Imm32(1), initLength); - masm.load32(length, scratchLength); - masm.add32(Imm32(1), length); + masm.add32(Imm32(1), elementsInitLength); + masm.load32(elementsLength, scratchLength); + masm.add32(Imm32(1), elementsLength); // Store the value. + BaseObjectElementIndex element(scratch, scratchLength); masm.storeValue(val, element); emitPostBarrierElement(obj, val, scratch, scratchLength); // Return value is new length. masm.add32(Imm32(1), scratchLength); masm.tagValue(JSVAL_TYPE_INT32, scratchLength, val); return true; @@ -1703,17 +1719,21 @@ BaselineCacheIRCompiler::emitStoreTypedE FailurePath* failure; if (!addFailurePath(&failure)) return false; // Bounds check. Label done; LoadTypedThingLength(masm, layout, obj, scratch1); - masm.branch32(Assembler::BelowOrEqual, scratch1, index, handleOOB ? &done : failure->label()); + + // Unfortunately we don't have more registers available on x86, so use + // InvalidReg and emit slightly slower code on x86. + Register spectreTemp = InvalidReg; + masm.spectreBoundsCheck32(index, scratch1, spectreTemp, handleOOB ? &done : failure->label()); // Load the elements vector. LoadTypedThingData(masm, layout, obj, scratch1); BaseIndex dest(scratch1, index, ScaleFromElemWidth(Scalar::byteSize(type))); // Use ICStubReg as second scratch register. TODO: consider doing the RHS // type check/conversion as a separate IR instruction so we can simplify
--- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -6853,26 +6853,34 @@ CodeGenerator::visitArrowNewTarget(LArro void CodeGenerator::visitArrayLength(LArrayLength* lir) { Address length(ToRegister(lir->elements()), ObjectElements::offsetOfLength()); masm.load32(length, ToRegister(lir->output())); } +static void +SetLengthFromIndex(MacroAssembler& masm, const LAllocation* index, const Address& length) +{ + if (index->isConstant()) { + masm.store32(Imm32(ToInt32(index) + 1), length); + } else { + Register newLength = ToRegister(index); + masm.add32(Imm32(1), newLength); + masm.store32(newLength, length); + masm.sub32(Imm32(1), newLength); + } +} + void CodeGenerator::visitSetArrayLength(LSetArrayLength* lir) { Address length(ToRegister(lir->elements()), ObjectElements::offsetOfLength()); - RegisterOrInt32Constant newLength = ToRegisterOrInt32Constant(lir->index()); - - masm.inc32(&newLength); - masm.store32(newLength, length); - // Restore register value if it is used/captured after. - masm.dec32(&newLength); + SetLengthFromIndex(masm, lir->index(), length); } template <class OrderedHashTable> static void RangeFront(MacroAssembler&, Register, Register, Register); template <> void @@ -8893,22 +8901,17 @@ CodeGenerator::visitInitializedLength(LI Address initLength(ToRegister(lir->elements()), ObjectElements::offsetOfInitializedLength()); masm.load32(initLength, ToRegister(lir->output())); } void CodeGenerator::visitSetInitializedLength(LSetInitializedLength* lir) { Address initLength(ToRegister(lir->elements()), ObjectElements::offsetOfInitializedLength()); - RegisterOrInt32Constant index = ToRegisterOrInt32Constant(lir->index()); - - masm.inc32(&index); - masm.store32(index, initLength); - // Restore register value if it is used/captured after. - masm.dec32(&index); + SetLengthFromIndex(masm, lir->index(), initLength); } void CodeGenerator::visitNotO(LNotO* lir) { MOZ_ASSERT(lir->mir()->operandMightEmulateUndefined(), "This should be constant-folded if the object can't emulate undefined."); @@ -9218,28 +9221,28 @@ CodeGenerator::emitStoreElementHoleT(T* static_assert(std::is_same<T, LStoreElementHoleT>::value || std::is_same<T, LFallibleStoreElementT>::value, "emitStoreElementHoleT called with unexpected argument type"); OutOfLineStoreElementHole* ool = new(alloc()) OutOfLineStoreElementHole(lir, current->mir()->strict()); addOutOfLineCode(ool, lir->mir()); Register elements = ToRegister(lir->elements()); - const LAllocation* index = lir->index(); - RegisterOrInt32Constant key = ToRegisterOrInt32Constant(index); + Register index = ToRegister(lir->index()); + Register spectreTemp = ToTempRegisterOrInvalid(lir->spectreTemp()); Address initLength(elements, ObjectElements::offsetOfInitializedLength()); - masm.branch32(Assembler::BelowOrEqual, initLength, key, ool->entry()); + masm.spectreBoundsCheck32(index, initLength, spectreTemp, ool->entry()); if (lir->mir()->needsBarrier()) - emitPreBarrier(elements, index, 0); + emitPreBarrier(elements, lir->index(), 0); masm.bind(ool->rejoinStore()); emitStoreElementTyped(lir->value(), lir->mir()->value()->type(), lir->mir()->elementType(), - elements, index, 0); + elements, lir->index(), 0); masm.bind(ool->rejoin()); } void CodeGenerator::visitStoreElementHoleT(LStoreElementHoleT* lir) { emitStoreElementHoleT(lir); @@ -9251,31 +9254,28 @@ CodeGenerator::emitStoreElementHoleV(T* static_assert(std::is_same<T, LStoreElementHoleV>::value || std::is_same<T, LFallibleStoreElementV>::value, "emitStoreElementHoleV called with unexpected parameter type"); OutOfLineStoreElementHole* ool = new(alloc()) OutOfLineStoreElementHole(lir, current->mir()->strict()); addOutOfLineCode(ool, lir->mir()); Register elements = ToRegister(lir->elements()); - const LAllocation* index = lir->index(); + Register index = ToRegister(lir->index()); const ValueOperand value = ToValue(lir, T::Value); - RegisterOrInt32Constant key = ToRegisterOrInt32Constant(index); + Register spectreTemp = ToTempRegisterOrInvalid(lir->spectreTemp()); Address initLength(elements, ObjectElements::offsetOfInitializedLength()); - masm.branch32(Assembler::BelowOrEqual, initLength, key, ool->entry()); + masm.spectreBoundsCheck32(index, initLength, spectreTemp, ool->entry()); if (lir->mir()->needsBarrier()) - emitPreBarrier(elements, index, 0); + emitPreBarrier(elements, lir->index(), 0); masm.bind(ool->rejoinStore()); - if (index->isConstant()) - masm.storeValue(value, Address(elements, ToInt32(index) * sizeof(js::Value))); - else - masm.storeValue(value, BaseIndex(elements, ToRegister(index), TimesEight)); + masm.storeValue(value, BaseIndex(elements, index, TimesEight)); masm.bind(ool->rejoin()); } void CodeGenerator::visitStoreElementHoleV(LStoreElementHoleV* lir) { emitStoreElementHoleV(lir); @@ -9357,84 +9357,91 @@ static const VMFunction SetDenseElementI void CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool) { Register object, elements; LInstruction* ins = ool->ins(); const LAllocation* index; MIRType valueType; ConstantOrRegister value; + Register spectreTemp; if (ins->isStoreElementHoleV()) { LStoreElementHoleV* store = ins->toStoreElementHoleV(); object = ToRegister(store->object()); elements = ToRegister(store->elements()); index = store->index(); valueType = store->mir()->value()->type(); value = TypedOrValueRegister(ToValue(store, LStoreElementHoleV::Value)); + spectreTemp = ToTempRegisterOrInvalid(store->spectreTemp()); } else if (ins->isFallibleStoreElementV()) { LFallibleStoreElementV* store = ins->toFallibleStoreElementV(); object = ToRegister(store->object()); elements = ToRegister(store->elements()); index = store->index(); valueType = store->mir()->value()->type(); value = TypedOrValueRegister(ToValue(store, LFallibleStoreElementV::Value)); + spectreTemp = ToTempRegisterOrInvalid(store->spectreTemp()); } else if (ins->isStoreElementHoleT()) { LStoreElementHoleT* store = ins->toStoreElementHoleT(); object = ToRegister(store->object()); elements = ToRegister(store->elements()); index = store->index(); valueType = store->mir()->value()->type(); if (store->value()->isConstant()) value = ConstantOrRegister(store->value()->toConstant()->toJSValue()); else value = TypedOrValueRegister(valueType, ToAnyRegister(store->value())); + spectreTemp = ToTempRegisterOrInvalid(store->spectreTemp()); } else { // ins->isFallibleStoreElementT() LFallibleStoreElementT* store = ins->toFallibleStoreElementT(); object = ToRegister(store->object()); elements = ToRegister(store->elements()); index = store->index(); valueType = store->mir()->value()->type(); if (store->value()->isConstant()) value = ConstantOrRegister(store->value()->toConstant()->toJSValue()); else value = TypedOrValueRegister(valueType, ToAnyRegister(store->value())); - } - - RegisterOrInt32Constant key = ToRegisterOrInt32Constant(index); + spectreTemp = ToTempRegisterOrInvalid(store->spectreTemp()); + } + + Register indexReg = ToRegister(index); // If index == initializedLength, try to bump the initialized length inline. // If index > initializedLength, call a stub. Note that this relies on the // condition flags sticking from the incoming branch. + // Also note: this branch does not need Spectre mitigations, doing that for + // the capacity check below is sufficient. Label callStub; #if defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) // Had to reimplement for MIPS because there are no flags. Address initLength(elements, ObjectElements::offsetOfInitializedLength()); - masm.branch32(Assembler::NotEqual, initLength, key, &callStub); + masm.branch32(Assembler::NotEqual, initLength, indexReg, &callStub); #else masm.j(Assembler::NotEqual, &callStub); #endif // Check array capacity. - masm.branch32(Assembler::BelowOrEqual, Address(elements, ObjectElements::offsetOfCapacity()), - key, &callStub); + masm.spectreBoundsCheck32(indexReg, Address(elements, ObjectElements::offsetOfCapacity()), + spectreTemp, &callStub); // Update initialized length. The capacity guard above ensures this won't overflow, // due to MAX_DENSE_ELEMENTS_COUNT. - masm.inc32(&key); - masm.store32(key, Address(elements, ObjectElements::offsetOfInitializedLength())); + masm.add32(Imm32(1), indexReg); + masm.store32(indexReg, Address(elements, ObjectElements::offsetOfInitializedLength())); // Update length if length < initializedLength. Label dontUpdate; masm.branch32(Assembler::AboveOrEqual, Address(elements, ObjectElements::offsetOfLength()), - key, &dontUpdate); - masm.store32(key, Address(elements, ObjectElements::offsetOfLength())); + indexReg, &dontUpdate); + masm.store32(indexReg, Address(elements, ObjectElements::offsetOfLength())); masm.bind(&dontUpdate); - masm.dec32(&key); + masm.sub32(Imm32(1), indexReg); if ((ins->isStoreElementHoleT() || ins->isFallibleStoreElementT()) && valueType != MIRType::Double) { // The inline path for StoreElementHoleT and FallibleStoreElementT does not always store // the type tag, so we do the store on the OOL path. We use MIRType::None for the element // type so that storeElementTyped will always store the type tag. if (ins->isStoreElementHoleT()) { @@ -9555,23 +9562,23 @@ CodeGenerator::emitArrayPopShift(LInstru } else { MOZ_ASSERT(mir->mode() == MArrayPopShift::Shift); ool = oolCallVM(ArrayShiftDenseInfo, lir, ArgList(obj), StoreValueTo(out)); } // VM call if a write barrier is necessary. masm.branchTestNeedsIncrementalBarrier(Assembler::NonZero, ool->entry()); - // Load elements and length, and VM call if length != initializedLength. - RegisterOrInt32Constant key = RegisterOrInt32Constant(lengthTemp); + // Load elements and initializedLength, and VM call if + // length != initializedLength. masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), elementsTemp); - masm.load32(Address(elementsTemp, ObjectElements::offsetOfLength()), lengthTemp); - - Address initLength(elementsTemp, ObjectElements::offsetOfInitializedLength()); - masm.branch32(Assembler::NotEqual, initLength, key, ool->entry()); + masm.load32(Address(elementsTemp, ObjectElements::offsetOfInitializedLength()), lengthTemp); + + Address lengthAddr(elementsTemp, ObjectElements::offsetOfLength()); + masm.branch32(Assembler::NotEqual, lengthAddr, lengthTemp, ool->entry()); // Test for length != 0. On zero length either take a VM call or generate // an undefined value, depending on whether the call is known to produce // undefined. Label done; if (mir->maybeUndefined()) { Label notEmpty; masm.branchTest32(Assembler::NonZero, lengthTemp, lengthTemp, ¬Empty); @@ -9585,17 +9592,17 @@ CodeGenerator::emitArrayPopShift(LInstru masm.moveValue(UndefinedValue(), out.valueReg()); masm.jump(&done); masm.bind(¬Empty); } else { masm.branchTest32(Assembler::Zero, lengthTemp, lengthTemp, ool->entry()); } - masm.dec32(&key); + masm.sub32(Imm32(1), lengthTemp); if (mir->mode() == MArrayPopShift::Pop) { BaseIndex addr(elementsTemp, lengthTemp, TimesEight); masm.loadElementTypedOrValue(addr, out, mir->needsHoleCheck(), ool->entry()); } else { MOZ_ASSERT(mir->mode() == MArrayPopShift::Shift); Address addr(elementsTemp, 0); masm.loadElementTypedOrValue(addr, out, mir->needsHoleCheck(), ool->entry()); @@ -9653,68 +9660,69 @@ CodeGenerator::visitArrayPopShiftT(LArra } typedef bool (*ArrayPushDenseFn)(JSContext*, HandleArrayObject, HandleValue, uint32_t*); static const VMFunction ArrayPushDenseInfo = FunctionInfo<ArrayPushDenseFn>(jit::ArrayPushDense, "ArrayPushDense"); void CodeGenerator::emitArrayPush(LInstruction* lir, Register obj, - const ConstantOrRegister& value, Register elementsTemp, Register length) + const ConstantOrRegister& value, Register elementsTemp, Register length, + Register spectreTemp) { OutOfLineCode* ool = oolCallVM(ArrayPushDenseInfo, lir, ArgList(obj, value), StoreRegisterTo(length)); - RegisterOrInt32Constant key = RegisterOrInt32Constant(length); - // Load elements and length. masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), elementsTemp); masm.load32(Address(elementsTemp, ObjectElements::offsetOfLength()), length); // Guard length == initializedLength. Address initLength(elementsTemp, ObjectElements::offsetOfInitializedLength()); - masm.branch32(Assembler::NotEqual, initLength, key, ool->entry()); + masm.branch32(Assembler::NotEqual, initLength, length, ool->entry()); // Guard length < capacity. Address capacity(elementsTemp, ObjectElements::offsetOfCapacity()); - masm.branch32(Assembler::BelowOrEqual, capacity, key, ool->entry()); + masm.spectreBoundsCheck32(length, capacity, spectreTemp, ool->entry()); // Do the store. masm.storeConstantOrRegister(value, BaseIndex(elementsTemp, length, TimesEight)); - masm.inc32(&key); + masm.add32(Imm32(1), length); // Update length and initialized length. masm.store32(length, Address(elementsTemp, ObjectElements::offsetOfLength())); masm.store32(length, Address(elementsTemp, ObjectElements::offsetOfInitializedLength())); masm.bind(ool->rejoin()); } void CodeGenerator::visitArrayPushV(LArrayPushV* lir) { Register obj = ToRegister(lir->object()); Register elementsTemp = ToRegister(lir->temp()); Register length = ToRegister(lir->output()); ConstantOrRegister value = TypedOrValueRegister(ToValue(lir, LArrayPushV::Value)); - emitArrayPush(lir, obj, value, elementsTemp, length); + Register spectreTemp = ToTempRegisterOrInvalid(lir->spectreTemp()); + emitArrayPush(lir, obj, value, elementsTemp, length, spectreTemp); } void CodeGenerator::visitArrayPushT(LArrayPushT* lir) { Register obj = ToRegister(lir->object()); Register elementsTemp = ToRegister(lir->temp()); Register length = ToRegister(lir->output()); ConstantOrRegister value; if (lir->value()->isConstant()) value = ConstantOrRegister(lir->value()->toConstant()->toJSValue()); else value = TypedOrValueRegister(lir->mir()->value()->type(), ToAnyRegister(lir->value())); - emitArrayPush(lir, obj, value, elementsTemp, length); + Register spectreTemp = ToTempRegisterOrInvalid(lir->spectreTemp()); + emitArrayPush(lir, obj, value, elementsTemp, length, spectreTemp); } typedef JSObject* (*ArraySliceDenseFn)(JSContext*, HandleObject, int32_t, int32_t, HandleObject); static const VMFunction ArraySliceDenseInfo = FunctionInfo<ArraySliceDenseFn>(array_slice_dense, "array_slice_dense"); void CodeGenerator::visitArraySlice(LArraySlice* lir) @@ -11815,52 +11823,30 @@ void CodeGenerator::visitStoreTypedArrayElementHole(LStoreTypedArrayElementHole* lir) { Register elements = ToRegister(lir->elements()); const LAllocation* value = lir->value(); Scalar::Type arrayType = lir->mir()->arrayType(); int width = Scalar::byteSize(arrayType); - const LAllocation* index = lir->index(); + Register index = ToRegister(lir->index()); const LAllocation* length = lir->length(); - - bool guardLength = true; - if (index->isConstant() && length->isConstant()) { - uint32_t idx = ToInt32(index); - uint32_t len = ToInt32(length); - if (idx >= len) - return; - guardLength = false; - } + Register spectreTemp = ToTempRegisterOrInvalid(lir->spectreTemp()); + Label skip; - if (index->isConstant()) { - uint32_t idx = ToInt32(index); - if (guardLength) { - if (length->isRegister()) - masm.branch32(Assembler::BelowOrEqual, ToRegister(length), Imm32(idx), &skip); - else - masm.branch32(Assembler::BelowOrEqual, ToAddress(length), Imm32(idx), &skip); - } - Address dest(elements, idx * width); - StoreToTypedArray(masm, arrayType, value, dest); - } else { - Register idxReg = ToRegister(index); - MOZ_ASSERT(guardLength); - if (length->isConstant()) - masm.branch32(Assembler::AboveOrEqual, idxReg, Imm32(ToInt32(length)), &skip); - else if (length->isRegister()) - masm.branch32(Assembler::BelowOrEqual, ToRegister(length), idxReg, &skip); - else - masm.branch32(Assembler::BelowOrEqual, ToAddress(length), idxReg, &skip); - BaseIndex dest(elements, ToRegister(index), ScaleFromElemWidth(width)); - StoreToTypedArray(masm, arrayType, value, dest); - } - if (guardLength) - masm.bind(&skip); + if (length->isRegister()) + masm.spectreBoundsCheck32(index, ToRegister(length), spectreTemp, &skip); + else + masm.spectreBoundsCheck32(index, ToAddress(length), spectreTemp, &skip); + + BaseIndex dest(elements, index, ScaleFromElemWidth(width)); + StoreToTypedArray(masm, arrayType, value, dest); + + masm.bind(&skip); } void CodeGenerator::visitAtomicIsLockFree(LAtomicIsLockFree* lir) { Register value = ToRegister(lir->value()); Register output = ToRegister(lir->output());
--- a/js/src/jit/CodeGenerator.h +++ b/js/src/jit/CodeGenerator.h @@ -158,17 +158,18 @@ class CodeGenerator final : public CodeG template<typename T> void emitLoadElementT(LLoadElementT* lir, const T& source); template <typename T> void emitStoreElementHoleT(T* lir); template <typename T> void emitStoreElementHoleV(T* lir); void emitArrayPopShift(LInstruction* lir, const MArrayPopShift* mir, Register obj, Register elementsTemp, Register lengthTemp, TypedOrValueRegister out); void emitArrayPush(LInstruction* lir, Register obj, - const ConstantOrRegister& value, Register elementsTemp, Register length); + const ConstantOrRegister& value, Register elementsTemp, Register length, + Register spectreTemp); void emitRest(LInstruction* lir, Register array, Register numActuals, Register temp0, Register temp1, unsigned numFormals, JSObject* templateObject, bool saveAndRestore, Register resultreg); void emitInstanceOf(LInstruction* ins, JSObject* prototypeObject); enum CallableOrConstructor { Callable,
--- a/js/src/jit/IonAnalysis.cpp +++ b/js/src/jit/IonAnalysis.cpp @@ -1676,17 +1676,17 @@ TypeAnalyzer::adjustPhiInputs(MPhi* phi) bool TypeAnalyzer::adjustInputs(MDefinition* def) { // Definitions such as MPhi have no type policy. if (!def->isInstruction()) return true; MInstruction* ins = def->toInstruction(); - TypePolicy* policy = ins->typePolicy(); + const TypePolicy* policy = ins->typePolicy(); if (policy && !policy->adjustInputs(alloc(), ins)) return false; return true; } void TypeAnalyzer::replaceRedundantPhi(MPhi* phi) {
--- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -7796,21 +7796,16 @@ IonBuilder::jsop_getelem() if (emitted) return Ok(); trackOptimizationAttempt(TrackedStrategy::GetElem_Dense); MOZ_TRY(getElemTryDense(&emitted, obj, index)); if (emitted) return Ok(); - trackOptimizationAttempt(TrackedStrategy::GetElem_TypedStatic); - MOZ_TRY(getElemTryTypedStatic(&emitted, obj, index)); - if (emitted) - return Ok(); - trackOptimizationAttempt(TrackedStrategy::GetElem_TypedArray); MOZ_TRY(getElemTryTypedArray(&emitted, obj, index)); if (emitted) return Ok(); trackOptimizationAttempt(TrackedStrategy::GetElem_String); MOZ_TRY(getElemTryString(&emitted, obj, index)); if (emitted) @@ -8224,112 +8219,16 @@ IonBuilder::getElemTryDense(bool* emitte MOZ_TRY(jsop_getelem_dense(obj, index)); trackOptimizationSuccess(); *emitted = true; return Ok(); } -AbortReasonOr<JSObject*> -IonBuilder::getStaticTypedArrayObject(MDefinition* obj, MDefinition* index) -{ - Scalar::Type arrayType; - if (!ElementAccessIsTypedArray(constraints(), obj, index, &arrayType)) { - trackOptimizationOutcome(TrackedOutcome::AccessNotTypedArray); - return nullptr; - } - - if (!LIRGenerator::allowStaticTypedArrayAccesses()) { - trackOptimizationOutcome(TrackedOutcome::Disabled); - return nullptr; - } - - bool hasExtraIndexedProperty; - MOZ_TRY_VAR(hasExtraIndexedProperty, ElementAccessHasExtraIndexedProperty(this, obj)); - if (hasExtraIndexedProperty) { - trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps); - return nullptr; - } - - if (!obj->resultTypeSet()) { - trackOptimizationOutcome(TrackedOutcome::NoTypeInfo); - return nullptr; - } - - JSObject* tarrObj = obj->resultTypeSet()->maybeSingleton(); - if (!tarrObj) { - trackOptimizationOutcome(TrackedOutcome::NotSingleton); - return nullptr; - } - - TypeSet::ObjectKey* tarrKey = TypeSet::ObjectKey::get(tarrObj); - if (tarrKey->unknownProperties()) { - trackOptimizationOutcome(TrackedOutcome::UnknownProperties); - return nullptr; - } - - return tarrObj; -} - -AbortReasonOr<Ok> -IonBuilder::getElemTryTypedStatic(bool* emitted, MDefinition* obj, MDefinition* index) -{ - MOZ_ASSERT(*emitted == false); - - JSObject* tarrObj; - MOZ_TRY_VAR(tarrObj, getStaticTypedArrayObject(obj, index)); - if (!tarrObj) - return Ok(); - - // LoadTypedArrayElementStatic currently treats uint32 arrays as int32. - Scalar::Type viewType = tarrObj->as<TypedArrayObject>().type(); - if (viewType == Scalar::Uint32) { - trackOptimizationOutcome(TrackedOutcome::StaticTypedArrayUint32); - return Ok(); - } - - MDefinition* ptr = convertShiftToMaskForStaticTypedArray(index, viewType); - if (!ptr) - return Ok(); - - // Emit LoadTypedArrayElementStatic. - - if (tarrObj->is<TypedArrayObject>()) { - TypeSet::ObjectKey* tarrKey = TypeSet::ObjectKey::get(tarrObj); - tarrKey->watchStateChangeForTypedArrayData(constraints()); - } - - obj->setImplicitlyUsedUnchecked(); - index->setImplicitlyUsedUnchecked(); - - MLoadTypedArrayElementStatic* load = MLoadTypedArrayElementStatic::New(alloc(), tarrObj, ptr); - current->add(load); - current->push(load); - - // The load is infallible if an undefined result will be coerced to the - // appropriate numeric type if the read is out of bounds. The truncation - // analysis picks up some of these cases, but is incomplete with respect - // to others. For now, sniff the bytecode for simple patterns following - // the load which guarantee a truncation or numeric conversion. - if (viewType == Scalar::Float32 || viewType == Scalar::Float64) { - jsbytecode* next = pc + JSOP_GETELEM_LENGTH; - if (*next == JSOP_POS) - load->setInfallible(); - } else { - jsbytecode* next = pc + JSOP_GETELEM_LENGTH; - if (*next == JSOP_ZERO && *(next + JSOP_ZERO_LENGTH) == JSOP_BITOR) - load->setInfallible(); - } - - trackOptimizationSuccess(); - *emitted = true; - return Ok(); -} - AbortReasonOr<Ok> IonBuilder::getElemTryTypedArray(bool* emitted, MDefinition* obj, MDefinition* index) { MOZ_ASSERT(*emitted == false); Scalar::Type arrayType; if (!ElementAccessIsTypedArray(constraints(), obj, index, &arrayType)) { trackOptimizationOutcome(TrackedOutcome::AccessNotTypedArray); @@ -8782,60 +8681,16 @@ IonBuilder::addTypedArrayLengthAndData(M if (checking == DoBoundsCheck) *index = addBoundsCheck(*index, *length); *elements = MTypedArrayElements::New(alloc(), obj); current->add(*elements); } } -MDefinition* -IonBuilder::convertShiftToMaskForStaticTypedArray(MDefinition* id, - Scalar::Type viewType) -{ - trackOptimizationOutcome(TrackedOutcome::StaticTypedArrayCantComputeMask); - - // No shifting is necessary if the typed array has single byte elements. - if (TypedArrayShift(viewType) == 0) - return id; - - // If the index is an already shifted constant, undo the shift to get the - // absolute offset being accessed. - if (MConstant* idConst = id->maybeConstantValue()) { - if (idConst->type() == MIRType::Int32) { - int32_t index = idConst->toInt32(); - MConstant* offset = MConstant::New(alloc(), Int32Value(index << TypedArrayShift(viewType))); - current->add(offset); - return offset; - } - } - - if (!id->isRsh() || id->isEffectful()) - return nullptr; - - MConstant* shiftAmount = id->toRsh()->rhs()->maybeConstantValue(); - if (!shiftAmount || shiftAmount->type() != MIRType::Int32) - return nullptr; - if (uint32_t(shiftAmount->toInt32()) != TypedArrayShift(viewType)) - return nullptr; - - // Instead of shifting, mask off the low bits of the index so that - // a non-scaled access on the typed array can be performed. - MConstant* mask = MConstant::New(alloc(), Int32Value(~((1 << shiftAmount->toInt32()) - 1))); - MBitAnd* ptr = MBitAnd::New(alloc(), id->getOperand(0), mask); - - ptr->infer(nullptr, nullptr); - MOZ_ASSERT(!ptr->isEffectful()); - - current->add(mask); - current->add(ptr); - - return ptr; -} - AbortReasonOr<Ok> IonBuilder::jsop_getelem_typed(MDefinition* obj, MDefinition* index, Scalar::Type arrayType) { TemporaryTypeSet* types = bytecodeTypes(pc); bool maybeUndefined = types->hasType(TypeSet::UndefinedType()); @@ -8928,21 +8783,16 @@ IonBuilder::jsop_setelem() if (shouldAbortOnPreliminaryGroups(object)) { MInstruction* ins = MCallSetElement::New(alloc(), object, index, value, IsStrictSetPC(pc)); current->add(ins); current->push(value); return resumeAfter(ins); } if (!forceInlineCaches()) { - trackOptimizationAttempt(TrackedStrategy::SetElem_TypedStatic); - MOZ_TRY(setElemTryTypedStatic(&emitted, object, index, value)); - if (emitted) - return Ok(); - trackOptimizationAttempt(TrackedStrategy::SetElem_TypedArray); MOZ_TRY(setElemTryTypedArray(&emitted, object, index, value)); if (emitted) return Ok(); trackOptimizationAttempt(TrackedStrategy::SetElem_Dense); SetElemICInspector icInspect(inspector->setElemICInspector(pc)); bool writeHole = icInspect.sawOOBDenseWrite(); @@ -9070,64 +8920,16 @@ IonBuilder::setElemTryScalarElemOfTypedO LinearSum indexAsByteOffset(alloc()); if (!checkTypedObjectIndexInBounds(elemSize, index, objPrediction, &indexAsByteOffset)) return Ok(); return setPropTryScalarTypedObjectValue(emitted, obj, indexAsByteOffset, elemType, value); } AbortReasonOr<Ok> -IonBuilder::setElemTryTypedStatic(bool* emitted, MDefinition* object, - MDefinition* index, MDefinition* value) -{ - MOZ_ASSERT(*emitted == false); - - JSObject* tarrObj; - MOZ_TRY_VAR(tarrObj, getStaticTypedArrayObject(object, index)); - if (!tarrObj) - return Ok(); - - SharedMem<void*> viewData = tarrObj->as<TypedArrayObject>().viewDataEither(); - if (tarrObj->zone()->group()->nursery().isInside(viewData)) - return Ok(); - - Scalar::Type viewType = tarrObj->as<TypedArrayObject>().type(); - MDefinition* ptr = convertShiftToMaskForStaticTypedArray(index, viewType); - if (!ptr) - return Ok(); - - // Emit StoreTypedArrayElementStatic. - - if (tarrObj->is<TypedArrayObject>()) { - TypeSet::ObjectKey* tarrKey = TypeSet::ObjectKey::get(tarrObj); - tarrKey->watchStateChangeForTypedArrayData(constraints()); - } - - object->setImplicitlyUsedUnchecked(); - index->setImplicitlyUsedUnchecked(); - - // Clamp value to [0, 255] for Uint8ClampedArray. - MDefinition* toWrite = value; - if (viewType == Scalar::Uint8Clamped) { - toWrite = MClampToUint8::New(alloc(), value); - current->add(toWrite->toInstruction()); - } - - MInstruction* store = MStoreTypedArrayElementStatic::New(alloc(), tarrObj, ptr, toWrite); - current->add(store); - current->push(value); - - MOZ_TRY(resumeAfter(store)); - - trackOptimizationSuccess(); - *emitted = true; - return Ok(); -} - -AbortReasonOr<Ok> IonBuilder::setElemTryTypedArray(bool* emitted, MDefinition* object, MDefinition* index, MDefinition* value) { MOZ_ASSERT(*emitted == false); Scalar::Type arrayType; if (!ElementAccessIsTypedArray(constraints(), object, index, &arrayType)) { trackOptimizationOutcome(TrackedOutcome::AccessNotTypedArray);
--- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -204,19 +204,16 @@ class IonBuilder MInstruction* addShapeGuard(MDefinition* obj, Shape* const shape, BailoutKind bailoutKind); MInstruction* addGroupGuard(MDefinition* obj, ObjectGroup* group, BailoutKind bailoutKind); MInstruction* addUnboxedExpandoGuard(MDefinition* obj, bool hasExpando, BailoutKind bailoutKind); MInstruction* addSharedTypedArrayGuard(MDefinition* obj); MInstruction* addGuardReceiverPolymorphic(MDefinition* obj, const BaselineInspector::ReceiverVector& receivers); - MDefinition* convertShiftToMaskForStaticTypedArray(MDefinition* id, - Scalar::Type viewType); - bool invalidatedIdempotentCache(); bool hasStaticEnvironmentObject(JSObject** pcall); AbortReasonOr<Ok> loadSlot(MDefinition* obj, size_t slot, size_t nfixed, MIRType rvalType, BarrierKind barrier, TemporaryTypeSet* types); AbortReasonOr<Ok> loadSlot(MDefinition* obj, Shape* shape, MIRType rvalType, BarrierKind barrier, TemporaryTypeSet* types); AbortReasonOr<Ok> storeSlot(MDefinition* obj, size_t slot, size_t nfixed, MDefinition* value, @@ -386,25 +383,22 @@ class IonBuilder MDefinition* derivedTypeObj); AbortReasonOr<Ok> pushScalarLoadFromTypedObject(MDefinition* obj, const LinearSum& byteoffset, ScalarTypeDescr::Type type); AbortReasonOr<Ok> pushReferenceLoadFromTypedObject(MDefinition* typedObj, const LinearSum& byteOffset, ReferenceTypeDescr::Type type, PropertyName* name); - AbortReasonOr<JSObject*> getStaticTypedArrayObject(MDefinition* obj, MDefinition* index); // jsop_setelem() helpers. AbortReasonOr<Ok> setElemTryTypedArray(bool* emitted, MDefinition* object, MDefinition* index, MDefinition* value); AbortReasonOr<Ok> setElemTryTypedObject(bool* emitted, MDefinition* obj, MDefinition* index, MDefinition* value); - AbortReasonOr<Ok> setElemTryTypedStatic(bool* emitted, MDefinition* object, - MDefinition* index, MDefinition* value); AbortReasonOr<Ok> initOrSetElemTryDense(bool* emitted, MDefinition* object, MDefinition* index, MDefinition* value, bool writeHole); AbortReasonOr<Ok> setElemTryArguments(bool* emitted, MDefinition* object); AbortReasonOr<Ok> initOrSetElemTryCache(bool* emitted, MDefinition* object, MDefinition* index, MDefinition* value); AbortReasonOr<Ok> setElemTryReferenceElemOfTypedObject(bool* emitted, MDefinition* obj, @@ -420,17 +414,16 @@ class IonBuilder TypedObjectPrediction elemTypeReprs, uint32_t elemSize); AbortReasonOr<Ok> initializeArrayElement(MDefinition* obj, size_t index, MDefinition* value, bool addResumePointAndIncrementInitializedLength); // jsop_getelem() helpers. AbortReasonOr<Ok> getElemTryDense(bool* emitted, MDefinition* obj, MDefinition* index); AbortReasonOr<Ok> getElemTryGetProp(bool* emitted, MDefinition* obj, MDefinition* index); - AbortReasonOr<Ok> getElemTryTypedStatic(bool* emitted, MDefinition* obj, MDefinition* index); AbortReasonOr<Ok> getElemTryTypedArray(bool* emitted, MDefinition* obj, MDefinition* index); AbortReasonOr<Ok> getElemTryTypedObject(bool* emitted, MDefinition* obj, MDefinition* index); AbortReasonOr<Ok> getElemTryString(bool* emitted, MDefinition* obj, MDefinition* index); AbortReasonOr<Ok> getElemTryArguments(bool* emitted, MDefinition* obj, MDefinition* index); AbortReasonOr<Ok> getElemTryArgumentsInlinedConstant(bool* emitted, MDefinition* obj, MDefinition* index); AbortReasonOr<Ok> getElemTryArgumentsInlinedIndex(bool* emitted, MDefinition* obj, MDefinition* index);
--- a/js/src/jit/IonCacheIRCompiler.cpp +++ b/js/src/jit/IonCacheIRCompiler.cpp @@ -1896,125 +1896,134 @@ EmitStoreDenseElement(MacroAssembler& ma bool IonCacheIRCompiler::emitStoreDenseElement() { Register obj = allocator.useRegister(masm, reader.objOperandId()); Register index = allocator.useRegister(masm, reader.int32OperandId()); ConstantOrRegister val = allocator.useConstantOrRegister(masm, reader.valOperandId()); - AutoScratchRegister scratch(allocator, masm); + AutoScratchRegister scratch1(allocator, masm); + AutoScratchRegister scratch2(allocator, masm); FailurePath* failure; if (!addFailurePath(&failure)) return false; EmitCheckPropertyTypes(masm, typeCheckInfo_, obj, val, *liveRegs_, failure->label()); // Load obj->elements in scratch. - masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratch); + masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratch1); // Bounds check. - Address initLength(scratch, ObjectElements::offsetOfInitializedLength()); - masm.branch32(Assembler::BelowOrEqual, initLength, index, failure->label()); + Address initLength(scratch1, ObjectElements::offsetOfInitializedLength()); + masm.spectreBoundsCheck32(index, initLength, scratch2, failure->label()); // Hole check. - BaseObjectElementIndex element(scratch, index); + BaseObjectElementIndex element(scratch1, index); masm.branchTestMagic(Assembler::Equal, element, failure->label()); EmitPreBarrier(masm, element, MIRType::Value); - EmitStoreDenseElement(masm, val, scratch, element); + EmitStoreDenseElement(masm, val, scratch1, element); if (needsPostBarrier()) - emitPostBarrierElement(obj, val, scratch, index); + emitPostBarrierElement(obj, val, scratch1, index); return true; } bool IonCacheIRCompiler::emitStoreDenseElementHole() { Register obj = allocator.useRegister(masm, reader.objOperandId()); Register index = allocator.useRegister(masm, reader.int32OperandId()); ConstantOrRegister val = allocator.useConstantOrRegister(masm, reader.valOperandId()); // handleAdd boolean is only relevant for Baseline. Ion ICs can always // handle adds as we don't have to set any flags on the fallback stub to // track this. reader.readBool(); - AutoScratchRegister scratch(allocator, masm); + AutoScratchRegister scratch1(allocator, masm); + AutoScratchRegister scratch2(allocator, masm); FailurePath* failure; if (!addFailurePath(&failure)) return false; EmitCheckPropertyTypes(masm, typeCheckInfo_, obj, val, *liveRegs_, failure->label()); - // Load obj->elements in scratch. - masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratch); - - Address initLength(scratch, ObjectElements::offsetOfInitializedLength()); - BaseObjectElementIndex element(scratch, index); - - Label inBounds, doStore; - masm.branch32(Assembler::Above, initLength, index, &inBounds); + // Load obj->elements in scratch1. + masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratch1); + + Address initLength(scratch1, ObjectElements::offsetOfInitializedLength()); + BaseObjectElementIndex element(scratch1, index); + + Label inBounds, outOfBounds; + Register spectreTemp = scratch2; + masm.spectreBoundsCheck32(index, initLength, spectreTemp, &outOfBounds); + masm.jump(&inBounds); + + masm.bind(&outOfBounds); masm.branch32(Assembler::NotEqual, initLength, index, failure->label()); // If index < capacity, we can add a dense element inline. If not we // need to allocate more elements. - Label capacityOk; - Address capacity(scratch, ObjectElements::offsetOfCapacity()); - masm.branch32(Assembler::Above, capacity, index, &capacityOk); + Label capacityOk, allocElement; + Address capacity(scratch1, ObjectElements::offsetOfCapacity()); + masm.spectreBoundsCheck32(index, capacity, spectreTemp, &allocElement); + masm.jump(&capacityOk); // Check for non-writable array length. We only have to do this if // index >= capacity. - Address elementsFlags(scratch, ObjectElements::offsetOfFlags()); + masm.bind(&allocElement); + Address elementsFlags(scratch1, ObjectElements::offsetOfFlags()); masm.branchTest32(Assembler::NonZero, elementsFlags, Imm32(ObjectElements::NONWRITABLE_ARRAY_LENGTH), failure->label()); LiveRegisterSet save(GeneralRegisterSet::Volatile(), liveVolatileFloatRegs()); - save.takeUnchecked(scratch); + save.takeUnchecked(scratch1); masm.PushRegsInMask(save); - masm.setupUnalignedABICall(scratch); - masm.loadJSContext(scratch); - masm.passABIArg(scratch); + masm.setupUnalignedABICall(scratch1); + masm.loadJSContext(scratch1); + masm.passABIArg(scratch1); masm.passABIArg(obj); masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, NativeObject::addDenseElementDontReportOOM)); - masm.mov(ReturnReg, scratch); + masm.mov(ReturnReg, scratch1); masm.PopRegsInMask(save); - masm.branchIfFalseBool(scratch, failure->label()); + masm.branchIfFalseBool(scratch1, failure->label()); // Load the reallocated elements pointer. - masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratch); + masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratch1); masm.bind(&capacityOk); // Increment initLength. masm.add32(Imm32(1), initLength); // If length is now <= index, increment length too. Label skipIncrementLength; - Address length(scratch, ObjectElements::offsetOfLength()); + Address length(scratch1, ObjectElements::offsetOfLength()); masm.branch32(Assembler::Above, length, index, &skipIncrementLength); masm.add32(Imm32(1), length); masm.bind(&skipIncrementLength); // Skip EmitPreBarrier as the memory is uninitialized. + Label doStore; masm.jump(&doStore); masm.bind(&inBounds); EmitPreBarrier(masm, element, MIRType::Value); masm.bind(&doStore); - EmitStoreDenseElement(masm, val, scratch, element); + EmitStoreDenseElement(masm, val, scratch1, element); if (needsPostBarrier()) - emitPostBarrierElement(obj, val, scratch, index); + emitPostBarrierElement(obj, val, scratch1, index); return true; } bool IonCacheIRCompiler::emitArrayPush() { MOZ_ASSERT_UNREACHABLE("emitArrayPush not supported for IonCaches."); return false; @@ -2027,29 +2036,26 @@ IonCacheIRCompiler::emitStoreTypedElemen Register index = allocator.useRegister(masm, reader.int32OperandId()); ConstantOrRegister val = allocator.useConstantOrRegister(masm, reader.valOperandId()); TypedThingLayout layout = reader.typedThingLayout(); Scalar::Type arrayType = reader.scalarType(); bool handleOOB = reader.readBool(); AutoScratchRegister scratch1(allocator, masm); - - Maybe<AutoScratchRegister> scratch2; - if (arrayType != Scalar::Float32 && arrayType != Scalar::Float64) - scratch2.emplace(allocator, masm); + AutoScratchRegister scratch2(allocator, masm); FailurePath* failure; if (!addFailurePath(&failure)) return false; // Bounds check. Label done; LoadTypedThingLength(masm, layout, obj, scratch1); - masm.branch32(Assembler::BelowOrEqual, scratch1, index, handleOOB ? &done : failure->label()); + masm.spectreBoundsCheck32(index, scratch1, scratch2, handleOOB ? &done : failure->label()); // Load the elements vector. LoadTypedThingData(masm, layout, obj, scratch1); BaseIndex dest(scratch1, index, ScaleFromElemWidth(Scalar::byteSize(arrayType))); FloatRegister maybeTempDouble = ic_->asSetPropertyIC()->maybeTempDouble(); FloatRegister maybeTempFloat32 = ic_->asSetPropertyIC()->maybeTempFloat32(); @@ -2061,17 +2067,17 @@ IonCacheIRCompiler::emitStoreTypedElemen if (!masm.convertConstantOrRegisterToFloat(cx_, val, tempFloat, failure->label())) return false; masm.storeToTypedFloatArray(arrayType, tempFloat, dest); } else if (arrayType == Scalar::Float64) { if (!masm.convertConstantOrRegisterToDouble(cx_, val, maybeTempDouble, failure->label())) return false; masm.storeToTypedFloatArray(arrayType, maybeTempDouble, dest); } else { - Register valueToStore = scratch2.ref(); + Register valueToStore = scratch2; if (arrayType == Scalar::Uint8Clamped) { if (!masm.clampConstantOrRegisterToUint8(cx_, val, maybeTempDouble, valueToStore, failure->label())) { return false; } } else { if (!masm.truncateConstantOrRegisterToInt32(cx_, val, maybeTempDouble, valueToStore,
--- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -3414,62 +3414,81 @@ LIRGenerator::visitStoreElement(MStoreEl if (ins->fallible()) assignSnapshot(lir, Bailout_Hole); add(lir, ins); break; } } } +static bool +BoundsCheckNeedsSpectreTemp() +{ + // On x86, spectreBoundsCheck32 can emit better code if it has a scratch + // register and index masking is enabled. +#ifdef JS_CODEGEN_X86 + return JitOptions.spectreIndexMasking; +#else + return false; +#endif +} + void LIRGenerator::visitStoreElementHole(MStoreElementHole* ins) { MOZ_ASSERT(ins->elements()->type() == MIRType::Elements); MOZ_ASSERT(ins->index()->type() == MIRType::Int32); const LUse object = useRegister(ins->object()); const LUse elements = useRegister(ins->elements()); - const LAllocation index = useRegisterOrConstant(ins->index()); + const LAllocation index = useRegister(ins->index()); + + LDefinition spectreTemp = BoundsCheckNeedsSpectreTemp() ? temp() : LDefinition::BogusTemp(); LInstruction* lir; switch (ins->value()->type()) { case MIRType::Value: - lir = new(alloc()) LStoreElementHoleV(object, elements, index, useBox(ins->value())); + lir = new(alloc()) LStoreElementHoleV(object, elements, index, useBox(ins->value()), + spectreTemp); break; default: { const LAllocation value = useRegisterOrNonDoubleConstant(ins->value()); - lir = new(alloc()) LStoreElementHoleT(object, elements, index, value); + lir = new(alloc()) LStoreElementHoleT(object, elements, index, value, spectreTemp); break; } } add(lir, ins); assignSafepoint(lir, ins); } void LIRGenerator::visitFallibleStoreElement(MFallibleStoreElement* ins) { MOZ_ASSERT(ins->elements()->type() == MIRType::Elements); MOZ_ASSERT(ins->index()->type() == MIRType::Int32); const LUse object = useRegister(ins->object()); const LUse elements = useRegister(ins->elements()); - const LAllocation index = useRegisterOrConstant(ins->index()); + const LAllocation index = useRegister(ins->index()); + + LDefinition spectreTemp = BoundsCheckNeedsSpectreTemp() ? temp() : LDefinition::BogusTemp(); LInstruction* lir; switch (ins->value()->type()) { case MIRType::Value: - lir = new(alloc()) LFallibleStoreElementV(object, elements, index, useBox(ins->value())); + lir = new(alloc()) LFallibleStoreElementV(object, elements, index, useBox(ins->value()), + spectreTemp); break; default: const LAllocation value = useRegisterOrNonDoubleConstant(ins->value()); - lir = new(alloc()) LFallibleStoreElementT(object, elements, index, value); + lir = new(alloc()) LFallibleStoreElementT(object, elements, index, value, + spectreTemp); break; } add(lir, ins); assignSafepoint(lir, ins); } @@ -3558,29 +3577,32 @@ LIRGenerator::visitArrayPopShift(MArrayP void LIRGenerator::visitArrayPush(MArrayPush* ins) { MOZ_ASSERT(ins->type() == MIRType::Int32); LUse object = useRegister(ins->object()); + LDefinition spectreTemp = BoundsCheckNeedsSpectreTemp() ? temp() : LDefinition::BogusTemp(); + switch (ins->value()->type()) { case MIRType::Value: { - LArrayPushV* lir = new(alloc()) LArrayPushV(object, useBox(ins->value()), temp()); + LArrayPushV* lir = new(alloc()) LArrayPushV(object, useBox(ins->value()), temp(), + spectreTemp); define(lir, ins); assignSafepoint(lir, ins); break; } default: { const LAllocation value = useRegisterOrNonDoubleConstant(ins->value()); - LArrayPushT* lir = new(alloc()) LArrayPushT(object, value, temp()); + LArrayPushT* lir = new(alloc()) LArrayPushT(object, value, temp(), spectreTemp); define(lir, ins); assignSafepoint(lir, ins); break; } } } void @@ -3726,28 +3748,16 @@ LIRGenerator::visitLoadTypedArrayElement temp()); if (ins->fallible()) assignSnapshot(lir, Bailout_Overflow); defineBox(lir, ins); assignSafepoint(lir, ins); } void -LIRGenerator::visitLoadTypedArrayElementStatic(MLoadTypedArrayElementStatic* ins) -{ - LLoadTypedArrayElementStatic* lir = - new(alloc()) LLoadTypedArrayElementStatic(useRegisterAtStart(ins->ptr())); - - // In case of out of bounds, may bail out, or may jump to ool code. - if (ins->fallible()) - assignSnapshot(lir, Bailout_BoundsCheck); - define(lir, ins); -} - -void LIRGenerator::visitStoreUnboxedScalar(MStoreUnboxedScalar* ins) { MOZ_ASSERT(IsValidElementsType(ins->elements(), ins->offsetAdjustment())); MOZ_ASSERT(ins->index()->type() == MIRType::Int32); if (ins->isSimdWrite()) { MOZ_ASSERT_IF(ins->writeType() == Scalar::Float32x4, ins->value()->type() == MIRType::Float32x4); MOZ_ASSERT_IF(ins->writeType() == Scalar::Int8x16, ins->value()->type() == MIRType::Int8x16); @@ -3796,26 +3806,29 @@ LIRGenerator::visitStoreTypedArrayElemen if (ins->isFloatWrite()) { MOZ_ASSERT_IF(ins->arrayType() == Scalar::Float32, ins->value()->type() == MIRType::Float32); MOZ_ASSERT_IF(ins->arrayType() == Scalar::Float64, ins->value()->type() == MIRType::Double); } else { MOZ_ASSERT(ins->value()->type() == MIRType::Int32); } LUse elements = useRegister(ins->elements()); - LAllocation length = useAnyOrConstant(ins->length()); - LAllocation index = useRegisterOrConstant(ins->index()); + LAllocation length = useAny(ins->length()); + LAllocation index = useRegister(ins->index()); + // For byte arrays, the value has to be in a byte register on x86. LAllocation value; - - // For byte arrays, the value has to be in a byte register on x86. if (ins->isByteWrite()) value = useByteOpRegisterOrNonDoubleConstant(ins->value()); else value = useRegisterOrNonDoubleConstant(ins->value()); - add(new(alloc()) LStoreTypedArrayElementHole(elements, length, index, value), ins); + + LDefinition spectreTemp = BoundsCheckNeedsSpectreTemp() ? temp() : LDefinition::BogusTemp(); + auto* lir = + new(alloc()) LStoreTypedArrayElementHole(elements, length, index, value, spectreTemp); + add(lir, ins); } void LIRGenerator::visitLoadFixedSlot(MLoadFixedSlot* ins) { MDefinition* obj = ins->object(); MOZ_ASSERT(obj->type() == MIRType::Object);
--- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -5766,66 +5766,25 @@ InlinePropertyTable::appendRoots(MRootLi { for (const Entry* entry : entries_) { if (!entry->appendRoots(roots)) return false; } return true; } -SharedMem<void*> -MLoadTypedArrayElementStatic::base() const -{ - return someTypedArray_->as<TypedArrayObject>().viewDataEither(); -} - -size_t -MLoadTypedArrayElementStatic::length() const -{ - return someTypedArray_->as<TypedArrayObject>().byteLength(); -} - -bool -MLoadTypedArrayElementStatic::congruentTo(const MDefinition* ins) const -{ - if (!ins->isLoadTypedArrayElementStatic()) - return false; - const MLoadTypedArrayElementStatic* other = ins->toLoadTypedArrayElementStatic(); - if (offset() != other->offset()) - return false; - if (needsBoundsCheck() != other->needsBoundsCheck()) - return false; - if (accessType() != other->accessType()) - return false; - if (base() != other->base()) - return false; - return congruentIfOperandsEqual(other); -} - -SharedMem<void*> -MStoreTypedArrayElementStatic::base() const -{ - return someTypedArray_->as<TypedArrayObject>().viewDataEither(); -} - bool MGetPropertyCache::allowDoubleResult() const { if (!resultTypeSet()) return true; return resultTypeSet()->hasType(TypeSet::DoubleType()); } -size_t -MStoreTypedArrayElementStatic::length() const -{ - return someTypedArray_->as<TypedArrayObject>().byteLength(); -} - MDefinition::AliasType MGetPropertyPolymorphic::mightAlias(const MDefinition* store) const { // Allow hoisting this instruction if the store does not write to a // slot read by this instruction. if (!store->isStoreFixedSlot() && !store->isStoreSlot()) return AliasType::MayAlias;
--- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -1213,29 +1213,29 @@ class MInstruction // MIR instructions containing GC pointers should override this to append // these pointers to the root list. virtual bool appendRoots(MRootList& roots) const { return true; } // Instructions needing to hook into type analysis should return a // TypePolicy. - virtual TypePolicy* typePolicy() = 0; + virtual const TypePolicy* typePolicy() = 0; virtual MIRType typePolicySpecialization() = 0; }; // Note: GenerateOpcodeFiles.py generates MOpcodes.h based on the // INSTRUCTION_HEADER* macros. #define INSTRUCTION_HEADER_WITHOUT_TYPEPOLICY(opcode) \ static const Opcode classOpcode = Opcode::opcode; \ using MThisOpcode = M##opcode; #define INSTRUCTION_HEADER(opcode) \ INSTRUCTION_HEADER_WITHOUT_TYPEPOLICY(opcode) \ - virtual TypePolicy* typePolicy() override; \ + virtual const TypePolicy* typePolicy() override; \ virtual MIRType typePolicySpecialization() override; #define ALLOW_CLONE(typename) \ bool canClone() const override { \ return true; \ } \ MInstruction* clone(TempAllocator& alloc, \ const MDefinitionVector& inputs) const override { \ @@ -7933,17 +7933,17 @@ class MPhi final return &inputs_[index]; } const MUse* getUseFor(size_t index) const override { return &inputs_[index]; } public: INSTRUCTION_HEADER_WITHOUT_TYPEPOLICY(Phi) - virtual TypePolicy* typePolicy(); + virtual const TypePolicy* typePolicy(); virtual MIRType typePolicySpecialization(); MPhi(TempAllocator& alloc, MIRType resultType) : MDefinition(classOpcode), inputs_(alloc), truncateKind_(NoTruncate), hasBackedgeType_(false), triedToSpecialize_(false), @@ -10581,83 +10581,16 @@ class MLoadTypedArrayElementHole AliasSet getAliasSet() const override { return AliasSet::Load(AliasSet::UnboxedElement); } bool canProduceFloat32() const override { return arrayType_ == Scalar::Float32; } ALLOW_CLONE(MLoadTypedArrayElementHole) }; -// Load a value fallibly or infallibly from a statically known typed array. -class MLoadTypedArrayElementStatic - : public MUnaryInstruction, - public ConvertToInt32Policy<0>::Data -{ - MLoadTypedArrayElementStatic(JSObject* someTypedArray, MDefinition* ptr, - int32_t offset = 0, bool needsBoundsCheck = true) - : MUnaryInstruction(classOpcode, ptr), someTypedArray_(someTypedArray), offset_(offset), - needsBoundsCheck_(needsBoundsCheck), fallible_(true) - { - int type = accessType(); - if (type == Scalar::Float32) - setResultType(MIRType::Float32); - else if (type == Scalar::Float64) - setResultType(MIRType::Double); - else - setResultType(MIRType::Int32); - } - - CompilerObject someTypedArray_; - - // An offset to be encoded in the load instruction - taking advantage of the - // addressing modes. This is only non-zero when the access is proven to be - // within bounds. - int32_t offset_; - bool needsBoundsCheck_; - bool fallible_; - - public: - INSTRUCTION_HEADER(LoadTypedArrayElementStatic) - TRIVIAL_NEW_WRAPPERS - - Scalar::Type accessType() const { - return someTypedArray_->as<TypedArrayObject>().type(); - } - SharedMem<void*> base() const; - size_t length() const; - - MDefinition* ptr() const { return getOperand(0); } - int32_t offset() const { return offset_; } - void setOffset(int32_t offset) { offset_ = offset; } - bool congruentTo(const MDefinition* ins) const override; - AliasSet getAliasSet() const override { - return AliasSet::Load(AliasSet::UnboxedElement); - } - - bool needsBoundsCheck() const { return needsBoundsCheck_; } - void setNeedsBoundsCheck(bool v) { needsBoundsCheck_ = v; } - - bool fallible() const { - return fallible_; - } - - void setInfallible() { - fallible_ = false; - } - - void computeRange(TempAllocator& alloc) override; - bool needTruncation(TruncateKind kind) override; - bool canProduceFloat32() const override { return accessType() == Scalar::Float32; } - void collectRangeInfoPreTrunc() override; - - bool appendRoots(MRootList& roots) const override { - return roots.append(someTypedArray_); - } -}; - // Base class for MIR ops that write unboxed scalar values. class StoreUnboxedScalarBase { Scalar::Type writeType_; protected: explicit StoreUnboxedScalarBase(Scalar::Type writeType) : writeType_(writeType) @@ -10807,70 +10740,16 @@ class MStoreTypedArrayElementHole bool canConsumeFloat32(MUse* use) const override { return use == getUseFor(3) && arrayType() == Scalar::Float32; } ALLOW_CLONE(MStoreTypedArrayElementHole) }; -// Store a value infallibly to a statically known typed array. -class MStoreTypedArrayElementStatic : - public MBinaryInstruction, - public StoreUnboxedScalarBase, - public StoreTypedArrayElementStaticPolicy::Data -{ - MStoreTypedArrayElementStatic(JSObject* someTypedArray, MDefinition* ptr, MDefinition* v, - int32_t offset = 0, bool needsBoundsCheck = true) - : MBinaryInstruction(classOpcode, ptr, v), - StoreUnboxedScalarBase(someTypedArray->as<TypedArrayObject>().type()), - someTypedArray_(someTypedArray), - offset_(offset), needsBoundsCheck_(needsBoundsCheck) - {} - - CompilerObject someTypedArray_; - - // An offset to be encoded in the store instruction - taking advantage of the - // addressing modes. This is only non-zero when the access is proven to be - // within bounds. - int32_t offset_; - bool needsBoundsCheck_; - - public: - INSTRUCTION_HEADER(StoreTypedArrayElementStatic) - TRIVIAL_NEW_WRAPPERS - - Scalar::Type accessType() const { - return writeType(); - } - - SharedMem<void*> base() const; - size_t length() const; - - MDefinition* ptr() const { return getOperand(0); } - MDefinition* value() const { return getOperand(1); } - bool needsBoundsCheck() const { return needsBoundsCheck_; } - void setNeedsBoundsCheck(bool v) { needsBoundsCheck_ = v; } - int32_t offset() const { return offset_; } - void setOffset(int32_t offset) { offset_ = offset; } - AliasSet getAliasSet() const override { - return AliasSet::Store(AliasSet::UnboxedElement); - } - TruncateKind operandTruncateKind(size_t index) const override; - - bool canConsumeFloat32(MUse* use) const override { - return use == getUseFor(1) && accessType() == Scalar::Float32; - } - void collectRangeInfoPreTrunc() override; - - bool appendRoots(MRootList& roots) const override { - return roots.append(someTypedArray_); - } -}; - // Compute an "effective address", i.e., a compound computation of the form: // base + index * scale + displacement class MEffectiveAddress : public MBinaryInstruction, public NoTypePolicy::Data { MEffectiveAddress(MDefinition* base, MDefinition* index, Scale scale, int32_t displacement) : MBinaryInstruction(classOpcode, base, index),
--- a/js/src/jit/MacroAssembler-inl.h +++ b/js/src/jit/MacroAssembler-inl.h @@ -327,62 +327,19 @@ MacroAssembler::moveValue(const Constant // Arithmetic functions void MacroAssembler::addPtr(ImmPtr imm, Register dest) { addPtr(ImmWord(uintptr_t(imm.value)), dest); } -void -MacroAssembler::inc32(RegisterOrInt32Constant* key) -{ - if (key->isRegister()) - add32(Imm32(1), key->reg()); - else - key->bumpConstant(1); -} - -void -MacroAssembler::dec32(RegisterOrInt32Constant* key) -{ - if (key->isRegister()) - add32(Imm32(-1), key->reg()); - else - key->bumpConstant(-1); -} - // =============================================================== // Branch functions -void -MacroAssembler::branch32(Condition cond, Register length, const RegisterOrInt32Constant& key, - Label* label) -{ - branch32Impl(cond, length, key, label); -} - -void -MacroAssembler::branch32(Condition cond, const Address& length, const RegisterOrInt32Constant& key, - Label* label) -{ - branch32Impl(cond, length, key, label); -} - -template <typename T> -void -MacroAssembler::branch32Impl(Condition cond, const T& length, const RegisterOrInt32Constant& key, - Label* label) -{ - if (key.isRegister()) - branch32(cond, length, key.reg(), label); - else - branch32(cond, length, Imm32(key.constant()), label); -} - template <class L> void MacroAssembler::branchIfFalseBool(Register reg, L label) { // Note that C++ bool is only 1 byte, so ignore the higher-order bits. branchTest32(Assembler::Zero, reg, Imm32(0xFF), label); }
--- a/js/src/jit/MacroAssembler.h +++ b/js/src/jit/MacroAssembler.h @@ -815,21 +815,18 @@ class MacroAssembler : public MacroAssem // // On x86_shared, srcDest must be eax and edx will be clobbered. // On ARM, the chip must have hardware division instructions. inline void remainder32(Register rhs, Register srcDest, bool isUnsigned) PER_SHARED_ARCH; inline void divFloat32(FloatRegister src, FloatRegister dest) PER_SHARED_ARCH; inline void divDouble(FloatRegister src, FloatRegister dest) PER_SHARED_ARCH; - inline void inc32(RegisterOrInt32Constant* key); inline void inc64(AbsoluteAddress dest) PER_ARCH; - inline void dec32(RegisterOrInt32Constant* key); - inline void neg32(Register reg) PER_SHARED_ARCH; inline void neg64(Register64 reg) DEFINED_ON(x86, x64, arm, mips32, mips64); inline void negateFloat(FloatRegister reg) PER_SHARED_ARCH; inline void negateDouble(FloatRegister reg) PER_SHARED_ARCH; inline void absFloat32(FloatRegister src, FloatRegister dest) PER_SHARED_ARCH; @@ -933,23 +930,19 @@ class MacroAssembler : public MacroAssem // =============================================================== // Branch functions template <class L> inline void branch32(Condition cond, Register lhs, Register rhs, L label) PER_SHARED_ARCH; template <class L> inline void branch32(Condition cond, Register lhs, Imm32 rhs, L label) PER_SHARED_ARCH; - inline void branch32(Condition cond, Register length, const RegisterOrInt32Constant& key, - Label* label); inline void branch32(Condition cond, const Address& lhs, Register rhs, Label* label) PER_SHARED_ARCH; inline void branch32(Condition cond, const Address& lhs, Imm32 rhs, Label* label) PER_SHARED_ARCH; - inline void branch32(Condition cond, const Address& length, const RegisterOrInt32Constant& key, - Label* label); inline void branch32(Condition cond, const AbsoluteAddress& lhs, Register rhs, Label* label) DEFINED_ON(arm, arm64, mips_shared, x86, x64); inline void branch32(Condition cond, const AbsoluteAddress& lhs, Imm32 rhs, Label* label) DEFINED_ON(arm, arm64, mips_shared, x86, x64); inline void branch32(Condition cond, const BaseIndex& lhs, Register rhs, Label* label) DEFINED_ON(x86_shared); @@ -1273,21 +1266,16 @@ class MacroAssembler : public MacroAssem inline void branchTestStringTruthy(bool truthy, const ValueOperand& value, Label* label) DEFINED_ON(arm, arm64, mips32, mips64, x86_shared); // Create an unconditional branch to the address given as argument. inline void branchToComputedAddress(const BaseIndex& address) PER_ARCH; private: - // Implementation for branch* methods. - template <typename T> - inline void branch32Impl(Condition cond, const T& length, const RegisterOrInt32Constant& key, - Label* label); - template <typename T, typename S, typename L> inline void branchPtrImpl(Condition cond, const T& lhs, const S& rhs, L label) DEFINED_ON(x86_shared); void branchPtrInNurseryChunkImpl(Condition cond, Register ptr, Label* label) DEFINED_ON(x86); template <typename T> void branchValueIsNurseryCellImpl(Condition cond, const T& value, Register temp, Label* label) @@ -1357,22 +1345,30 @@ class MacroAssembler : public MacroAssem DEFINED_ON(arm, arm64, mips_shared, x86, x64); // Zeroes dest if the condition is true. inline void spectreZeroRegister(Condition cond, Register scratch, Register dest) DEFINED_ON(arm, arm64, mips_shared, x86_shared); // Performs a bounds check and zeroes the index register if out-of-bounds // (to mitigate Spectre). - inline void spectreBoundsCheck32(Register index, Register length, Register scratch, + private: + + inline void spectreBoundsCheck32(Register index, const Operand& length, Register maybeScratch, Label* failure) - DEFINED_ON(arm, arm64, mips_shared, x86_shared); - inline void spectreBoundsCheck32(Register index, const Address& length, Register scratch, + DEFINED_ON(x86); + + public: + + inline void spectreBoundsCheck32(Register index, Register length, Register maybeScratch, Label* failure) - DEFINED_ON(arm, arm64, mips_shared, x86_shared); + DEFINED_ON(arm, arm64, mips_shared, x86, x64); + inline void spectreBoundsCheck32(Register index, const Address& length, Register maybeScratch, + Label* failure) + DEFINED_ON(arm, arm64, mips_shared, x86, x64); // ======================================================================== // Canonicalization primitives. inline void canonicalizeDouble(FloatRegister reg); inline void canonicalizeDoubleIfDeterministic(FloatRegister reg); inline void canonicalizeFloat(FloatRegister reg); inline void canonicalizeFloatIfDeterministic(FloatRegister reg); @@ -2116,24 +2112,16 @@ class MacroAssembler : public MacroAssem mov(JSReturnReg, dest.valueReg()); #else #error "Bad architecture" #endif } inline void storeCallResultValue(TypedOrValueRegister dest); - using MacroAssemblerSpecific::store32; - void store32(const RegisterOrInt32Constant& key, const Address& dest) { - if (key.isRegister()) - store32(key.reg(), dest); - else - store32(Imm32(key.constant()), dest); - } - template <typename T> void guardedCallPreBarrier(const T& address, MIRType type) { Label done; branchTestNeedsIncrementalBarrier(Assembler::Zero, &done); if (type == MIRType::Value) branchTestGCThing(Assembler::NotEqual, address, &done);
--- a/js/src/jit/RangeAnalysis.cpp +++ b/js/src/jit/RangeAnalysis.cpp @@ -1797,26 +1797,16 @@ void MLoadUnboxedScalar::computeRange(TempAllocator& alloc) { // We have an Int32 type and if this is a UInt32 load it may produce a value // outside of our range, but we have a bailout to handle those cases. setRange(GetTypedArrayRange(alloc, readType())); } void -MLoadTypedArrayElementStatic::computeRange(TempAllocator& alloc) -{ - // We don't currently use MLoadTypedArrayElementStatic for uint32, so we - // don't have to worry about it returning a value outside our type. - MOZ_ASSERT(someTypedArray_->as<TypedArrayObject>().type() != Scalar::Uint32); - - setRange(GetTypedArrayRange(alloc, someTypedArray_->as<TypedArrayObject>().type())); -} - -void MArrayLength::computeRange(TempAllocator& alloc) { // Array lengths can go up to UINT32_MAX, but we only create MArrayLength // nodes when the value is known to be int32 (see the // OBJECT_FLAG_LENGTH_OVERFLOW flag). setRange(Range::NewUInt32Range(alloc, 0, INT32_MAX)); } @@ -2668,28 +2658,16 @@ MToDouble::truncate() setResultType(MIRType::Int32); if (truncateKind() >= IndirectTruncate) { if (range()) range()->wrapAroundToInt32(); } } bool -MLoadTypedArrayElementStatic::needTruncation(TruncateKind kind) -{ - // IndirectTruncate not possible, since it returns 'undefined' - // upon out of bounds read. Doing arithmetic on 'undefined' gives wrong - // results. So only set infallible if explicitly truncated. - if (kind == Truncate) - setInfallible(); - - return false; -} - -bool MLimitedTruncate::needTruncation(TruncateKind kind) { setTruncateKind(kind); setResultType(MIRType::Int32); if (kind >= IndirectTruncate && range()) range()->wrapAroundToInt32(); return false; } @@ -2800,23 +2778,16 @@ MStoreUnboxedScalar::operandTruncateKind MDefinition::TruncateKind MStoreTypedArrayElementHole::operandTruncateKind(size_t index) const { // An integer store truncates the stored value. return index == 3 && isIntegerWrite() ? Truncate : NoTruncate; } MDefinition::TruncateKind -MStoreTypedArrayElementStatic::operandTruncateKind(size_t index) const -{ - // An integer store truncates the stored value. - return index == 1 && isIntegerWrite() ? Truncate : NoTruncate; -} - -MDefinition::TruncateKind MDiv::operandTruncateKind(size_t index) const { return Min(truncateKind(), TruncateAfterBailouts); } MDefinition::TruncateKind MMod::operandTruncateKind(size_t index) const { @@ -3264,46 +3235,16 @@ MLoadElementHole::collectRangeInfoPreTru Range indexRange(index()); if (indexRange.isFiniteNonNegative()) { needsNegativeIntCheck_ = false; setNotGuard(); } } void -MLoadTypedArrayElementStatic::collectRangeInfoPreTrunc() -{ - Range range(ptr()); - - if (range.hasInt32LowerBound() && range.hasInt32UpperBound()) { - int64_t offset = this->offset(); - int64_t lower = range.lower() + offset; - int64_t upper = range.upper() + offset; - int64_t length = this->length(); - if (lower >= 0 && upper < length) - setNeedsBoundsCheck(false); - } -} - -void -MStoreTypedArrayElementStatic::collectRangeInfoPreTrunc() -{ - Range range(ptr()); - - if (range.hasInt32LowerBound() && range.hasInt32UpperBound()) { - int64_t offset = this->offset(); - int64_t lower = range.lower() + offset; - int64_t upper = range.upper() + offset; - int64_t length = this->length(); - if (lower >= 0 && upper < length) - setNeedsBoundsCheck(false); - } -} - -void MClz::collectRangeInfoPreTrunc() { Range inputRange(input()); if (!inputRange.canBeZero()) operandIsNeverZero_ = true; } void
--- a/js/src/jit/RegisterSets.h +++ b/js/src/jit/RegisterSets.h @@ -277,51 +277,16 @@ class ConstantOrRegister return dataValue(); } const TypedOrValueRegister& reg() const { return dataReg(); } }; -struct RegisterOrInt32Constant { - bool isRegister_; - union { - Register reg_; - int32_t constant_; - }; - - explicit RegisterOrInt32Constant(Register reg) - : isRegister_(true), reg_(reg) - { } - - explicit RegisterOrInt32Constant(int32_t index) - : isRegister_(false), constant_(index) - { } - - inline void bumpConstant(int diff) { - MOZ_ASSERT(!isRegister_); - constant_ += diff; - } - inline Register reg() const { - MOZ_ASSERT(isRegister_); - return reg_; - } - inline int32_t constant() const { - MOZ_ASSERT(!isRegister_); - return constant_; - } - inline bool isRegister() const { - return isRegister_; - } - inline bool isConstant() const { - return !isRegister_; - } -}; - template <typename T> class TypedRegisterSet { public: typedef T RegType; typedef typename T::SetType SetType; private:
--- a/js/src/jit/TypePolicy.cpp +++ b/js/src/jit/TypePolicy.cpp @@ -61,17 +61,17 @@ BoxInputsPolicy::staticAdjustInputs(Temp if (in->type() == MIRType::Value) continue; ins->replaceOperand(i, BoxAt(alloc, ins, in)); } return true; } bool -ArithPolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins) +ArithPolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins) const { MIRType specialization = ins->typePolicySpecialization(); if (specialization == MIRType::None) return BoxInputsPolicy::staticAdjustInputs(alloc, ins); MOZ_ASSERT(ins->type() == MIRType::Double || ins->type() == MIRType::Int32 || ins->type() == MIRType::Float32); for (size_t i = 0, e = ins->numOperands(); i < e; i++) { @@ -116,17 +116,17 @@ AllDoublePolicy::staticAdjustInputs(Temp if (!replace->typePolicy()->adjustInputs(alloc, replace)) return false; } return true; } bool -ComparePolicy::adjustInputs(TempAllocator& alloc, MInstruction* def) +ComparePolicy::adjustInputs(TempAllocator& alloc, MInstruction* def) const { MOZ_ASSERT(def->isCompare()); MCompare* compare = def->toCompare(); // Convert Float32 operands to doubles for (size_t i = 0; i < 2; i++) { MDefinition* in = def->getOperand(i); if (in->type() == MIRType::Float32) { @@ -264,17 +264,17 @@ ComparePolicy::adjustInputs(TempAllocato if (!replace->typePolicy()->adjustInputs(alloc, replace)) return false; } return true; } bool -SameValuePolicy::adjustInputs(TempAllocator& alloc, MInstruction* def) +SameValuePolicy::adjustInputs(TempAllocator& alloc, MInstruction* def) const { MOZ_ASSERT(def->isSameValue()); MSameValue* sameValue = def->toSameValue(); MIRType lhsType = sameValue->lhs()->type(); MIRType rhsType = sameValue->rhs()->type(); // If both operands are numbers, convert them to doubles. if (IsNumberType(lhsType) && IsNumberType(rhsType)) @@ -295,17 +295,17 @@ SameValuePolicy::adjustInputs(TempAlloca return true; } // Otherwise box both operands. return BoxInputsPolicy::staticAdjustInputs(alloc, def); } bool -TypeBarrierPolicy::adjustInputs(TempAllocator& alloc, MInstruction* def) +TypeBarrierPolicy::adjustInputs(TempAllocator& alloc, MInstruction* def) const { MTypeBarrier* ins = def->toTypeBarrier(); MIRType inputType = ins->getOperand(0)->type(); MIRType outputType = ins->type(); // Input and output type are already in accordance. if (inputType == outputType) return true; @@ -350,17 +350,17 @@ TypeBarrierPolicy::adjustInputs(TempAllo // types. The unexpected types would have changed Range Analysis // predictions. As such, we need to prevent destructive optimizations. ins->block()->flagOperandsOfPrunedBranches(replace); return true; } bool -TestPolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins) +TestPolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins) const { MDefinition* op = ins->getOperand(0); switch (op->type()) { case MIRType::Value: case MIRType::Null: case MIRType::Undefined: case MIRType::Boolean: case MIRType::Int32: @@ -381,17 +381,17 @@ TestPolicy::adjustInputs(TempAllocator& default: ins->replaceOperand(0, BoxAt(alloc, ins, op)); break; } return true; } bool -BitwisePolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins) +BitwisePolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins) const { MIRType specialization = ins->typePolicySpecialization(); if (specialization == MIRType::None) return BoxInputsPolicy::staticAdjustInputs(alloc, ins); MOZ_ASSERT(ins->type() == specialization); MOZ_ASSERT(specialization == MIRType::Int32 || specialization == MIRType::Double); @@ -408,17 +408,17 @@ BitwisePolicy::adjustInputs(TempAllocato if (!replace->typePolicy()->adjustInputs(alloc, replace)) return false; } return true; } bool -PowPolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins) +PowPolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins) const { MIRType specialization = ins->typePolicySpecialization(); MOZ_ASSERT(specialization == MIRType::Int32 || specialization == MIRType::Double || specialization == MIRType::None); // Inputs will be boxed if either is non-numeric. if (specialization == MIRType::None) @@ -581,51 +581,51 @@ Float32Policy<Op>::staticAdjustInputs(Te } template bool Float32Policy<0>::staticAdjustInputs(TempAllocator& alloc, MInstruction* def); template bool Float32Policy<1>::staticAdjustInputs(TempAllocator& alloc, MInstruction* def); template bool Float32Policy<2>::staticAdjustInputs(TempAllocator& alloc, MInstruction* def); template <unsigned Op> bool -FloatingPointPolicy<Op>::adjustInputs(TempAllocator& alloc, MInstruction* def) +FloatingPointPolicy<Op>::adjustInputs(TempAllocator& alloc, MInstruction* def) const { MIRType policyType = def->typePolicySpecialization(); if (policyType == MIRType::Double) return DoublePolicy<Op>::staticAdjustInputs(alloc, def); return Float32Policy<Op>::staticAdjustInputs(alloc, def); } -template bool FloatingPointPolicy<0>::adjustInputs(TempAllocator& alloc, MInstruction* def); +template bool FloatingPointPolicy<0>::adjustInputs(TempAllocator& alloc, MInstruction* def) const; template <unsigned Op> bool NoFloatPolicy<Op>::staticAdjustInputs(TempAllocator& alloc, MInstruction* def) { EnsureOperandNotFloat32(alloc, def, Op); return true; } template bool NoFloatPolicy<0>::staticAdjustInputs(TempAllocator& alloc, MInstruction* def); template bool NoFloatPolicy<1>::staticAdjustInputs(TempAllocator& alloc, MInstruction* def); template bool NoFloatPolicy<2>::staticAdjustInputs(TempAllocator& alloc, MInstruction* def); template bool NoFloatPolicy<3>::staticAdjustInputs(TempAllocator& alloc, MInstruction* def); template <unsigned FirstOp> bool -NoFloatPolicyAfter<FirstOp>::adjustInputs(TempAllocator& alloc, MInstruction* def) +NoFloatPolicyAfter<FirstOp>::adjustInputs(TempAllocator& alloc, MInstruction* def) const { for (size_t op = FirstOp, e = def->numOperands(); op < e; op++) EnsureOperandNotFloat32(alloc, def, op); return true; } -template bool NoFloatPolicyAfter<0>::adjustInputs(TempAllocator& alloc, MInstruction* def); -template bool NoFloatPolicyAfter<1>::adjustInputs(TempAllocator& alloc, MInstruction* def); -template bool NoFloatPolicyAfter<2>::adjustInputs(TempAllocator& alloc, MInstruction* def); +template bool NoFloatPolicyAfter<0>::adjustInputs(TempAllocator& alloc, MInstruction* def) const; +template bool NoFloatPolicyAfter<1>::adjustInputs(TempAllocator& alloc, MInstruction* def) const; +template bool NoFloatPolicyAfter<2>::adjustInputs(TempAllocator& alloc, MInstruction* def) const; template <unsigned Op> bool SimdScalarPolicy<Op>::staticAdjustInputs(TempAllocator& alloc, MInstruction* ins) { MOZ_ASSERT(IsSimdType(ins->type())); MIRType laneType = SimdTypeToLaneType(ins->type()); @@ -851,36 +851,36 @@ SimdSameAsReturnedTypePolicy<Op>::static } template bool SimdSameAsReturnedTypePolicy<0>::staticAdjustInputs(TempAllocator& alloc, MInstruction* ins); template bool SimdSameAsReturnedTypePolicy<1>::staticAdjustInputs(TempAllocator& alloc, MInstruction* ins); bool -SimdAllPolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins) +SimdAllPolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins) const { for (unsigned i = 0, e = ins->numOperands(); i < e; i++) MOZ_ASSERT(ins->getOperand(i)->type() == ins->typePolicySpecialization()); return true; } template <unsigned Op> bool -SimdPolicy<Op>::adjustInputs(TempAllocator& alloc, MInstruction* ins) +SimdPolicy<Op>::adjustInputs(TempAllocator& alloc, MInstruction* ins) const { MOZ_ASSERT(ins->typePolicySpecialization() == ins->getOperand(Op)->type()); return true; } template bool -SimdPolicy<0>::adjustInputs(TempAllocator& alloc, MInstruction* ins); +SimdPolicy<0>::adjustInputs(TempAllocator& alloc, MInstruction* ins) const; bool -SimdShufflePolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins) +SimdShufflePolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins) const { MSimdGeneralShuffle* s = ins->toSimdGeneralShuffle(); for (unsigned i = 0; i < s->numVectors(); i++) MOZ_ASSERT(ins->getOperand(i)->type() == ins->typePolicySpecialization()); // Next inputs are the lanes, which need to be int32 for (unsigned i = 0; i < s->numLanes(); i++) { @@ -894,30 +894,30 @@ SimdShufflePolicy::adjustInputs(TempAllo if (!replace->typePolicy()->adjustInputs(alloc, replace)) return false; } return true; } bool -SimdSelectPolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins) +SimdSelectPolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins) const { // First input is the mask, which has to be a boolean. MOZ_ASSERT(IsBooleanSimdType(ins->getOperand(0)->type())); // Next inputs are the two vectors of a particular type. for (unsigned i = 1; i < 3; i++) MOZ_ASSERT(ins->getOperand(i)->type() == ins->typePolicySpecialization()); return true; } bool -CallPolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins) +CallPolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins) const { MCall* call = ins->toCall(); MDefinition* func = call->getFunction(); if (func->type() != MIRType::Object) { MInstruction* unbox = MUnbox::New(alloc, func, MIRType::Object, MUnbox::Fallible); call->block()->insertBefore(call, unbox); call->replaceFunction(unbox); @@ -931,34 +931,34 @@ CallPolicy::adjustInputs(TempAllocator& return false; EnsureOperandNotFloat32(alloc, call, MCall::IndexOfStackArg(i)); } return true; } bool -CallSetElementPolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins) +CallSetElementPolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins) const { // The first operand should be an object. if (!SingleObjectPolicy::staticAdjustInputs(alloc, ins)) return false; // Box the index and value operands. for (size_t i = 1, e = ins->numOperands(); i < e; i++) { MDefinition* in = ins->getOperand(i); if (in->type() == MIRType::Value) continue; ins->replaceOperand(i, BoxAt(alloc, ins, in)); } return true; } bool -InstanceOfPolicy::adjustInputs(TempAllocator& alloc, MInstruction* def) +InstanceOfPolicy::adjustInputs(TempAllocator& alloc, MInstruction* def) const { // Box first operand if it isn't object if (def->getOperand(0)->type() != MIRType::Object) if (!BoxPolicy<0>::staticAdjustInputs(alloc, def)) return false; return true; } @@ -1049,50 +1049,41 @@ StoreUnboxedScalarPolicy::adjustValueInp if (value != curValue) ins->replaceOperand(valueOperand, value); return true; } bool -StoreUnboxedScalarPolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins) +StoreUnboxedScalarPolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins) const { if (!SingleObjectPolicy::staticAdjustInputs(alloc, ins)) return false; MStoreUnboxedScalar* store = ins->toStoreUnboxedScalar(); MOZ_ASSERT(IsValidElementsType(store->elements(), store->offsetAdjustment())); MOZ_ASSERT(store->index()->type() == MIRType::Int32); return adjustValueInput(alloc, store, store->writeType(), store->value(), 2); } bool -StoreTypedArrayHolePolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins) +StoreTypedArrayHolePolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins) const { MStoreTypedArrayElementHole* store = ins->toStoreTypedArrayElementHole(); MOZ_ASSERT(store->elements()->type() == MIRType::Elements); MOZ_ASSERT(store->index()->type() == MIRType::Int32); MOZ_ASSERT(store->length()->type() == MIRType::Int32); return StoreUnboxedScalarPolicy::adjustValueInput(alloc, ins, store->arrayType(), store->value(), 3); } bool -StoreTypedArrayElementStaticPolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins) -{ - MStoreTypedArrayElementStatic* store = ins->toStoreTypedArrayElementStatic(); - - return ConvertToInt32Policy<0>::staticAdjustInputs(alloc, ins) && - StoreUnboxedScalarPolicy::adjustValueInput(alloc, ins, store->accessType(), store->value(), 1); -} - -bool -StoreUnboxedObjectOrNullPolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins) +StoreUnboxedObjectOrNullPolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins) const { if (!ObjectPolicy<0>::staticAdjustInputs(alloc, ins)) return false; if (!ObjectPolicy<3>::staticAdjustInputs(alloc, ins)) return false; // Change the value input to a ToObjectOrNull instruction if it might be @@ -1123,17 +1114,17 @@ StoreUnboxedObjectOrNullPolicy::adjustIn MInstruction* barrier = MPostWriteBarrier::New(alloc, store->typedObj(), replace); store->block()->insertBefore(store, barrier); return true; } bool -StoreUnboxedStringPolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins) +StoreUnboxedStringPolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins) const { if (!ObjectPolicy<0>::staticAdjustInputs(alloc, ins)) return false; // Change the value input to a ToString instruction if it might be // a non-null primitive. if (!ConvertToStringPolicy<2>::staticAdjustInputs(alloc, ins)) return false; @@ -1150,17 +1141,17 @@ StoreUnboxedStringPolicy::adjustInputs(T MDefinition* value = store->value(); MOZ_ASSERT(value->type() == MIRType::String); MInstruction* barrier = MPostWriteBarrier::New(alloc, store->typedObj(), value); store->block()->insertBefore(store, barrier); return true; } bool -ClampPolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins) +ClampPolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins) const { MDefinition* in = ins->toClampToUint8()->input(); switch (in->type()) { case MIRType::Int32: case MIRType::Double: case MIRType::Value: break; @@ -1168,17 +1159,17 @@ ClampPolicy::adjustInputs(TempAllocator& ins->replaceOperand(0, BoxAt(alloc, ins, in)); break; } return true; } bool -FilterTypeSetPolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins) +FilterTypeSetPolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins) const { MOZ_ASSERT(ins->numOperands() == 1); MIRType inputType = ins->getOperand(0)->type(); MIRType outputType = ins->type(); // Special case when output is a Float32, but input isn't. if (outputType == MIRType::Float32 && inputType != MIRType::Float32) { // Create a MToFloat32 to add between the MFilterTypeSet and @@ -1261,17 +1252,16 @@ FilterTypeSetPolicy::adjustInputs(TempAl _(ComparePolicy) \ _(FilterTypeSetPolicy) \ _(InstanceOfPolicy) \ _(PowPolicy) \ _(SameValuePolicy) \ _(SimdAllPolicy) \ _(SimdSelectPolicy) \ _(SimdShufflePolicy) \ - _(StoreTypedArrayElementStaticPolicy) \ _(StoreTypedArrayHolePolicy) \ _(StoreUnboxedScalarPolicy) \ _(StoreUnboxedObjectOrNullPolicy) \ _(StoreUnboxedStringPolicy) \ _(TestPolicy) \ _(AllDoublePolicy) \ _(ToDoublePolicy) \ _(ToInt32Policy) \ @@ -1347,20 +1337,20 @@ namespace jit { // Define for all used TypePolicy specialization, the definition for // |TypePolicy::Data::thisTypePolicy|. This function returns one constant // instance of the TypePolicy which is shared among all MIR Instructions of the // same type. // // This Macro use __VA_ARGS__ to account for commas of template parameters. #define DEFINE_TYPE_POLICY_SINGLETON_INSTANCES_(...) \ - TypePolicy * \ + const TypePolicy* \ __VA_ARGS__::Data::thisTypePolicy() \ { \ - static __VA_ARGS__ singletonType; \ + static constexpr __VA_ARGS__ singletonType; \ return &singletonType; \ } TYPE_POLICY_LIST(DEFINE_TYPE_POLICY_SINGLETON_INSTANCES_) TEMPLATE_TYPE_POLICY_LIST(template<> DEFINE_TYPE_POLICY_SINGLETON_INSTANCES_) #undef DEFINE_TYPE_POLICY_SINGLETON_INSTANCES_ } // namespace jit @@ -1383,17 +1373,17 @@ thisTypeSpecialization() } // namespace // For each MIR Instruction, this macro define the |typePolicy| method which is // using the |thisTypePolicy| method. The |thisTypePolicy| method is either a // member of the MIR Instruction, such as with MGetElementCache, a member // inherited from the TypePolicy::Data structure, or a member inherited from // NoTypePolicy if the MIR instruction has no type policy. #define DEFINE_MIR_TYPEPOLICY_MEMBERS_(op) \ - TypePolicy * \ + const TypePolicy* \ js::jit::M##op::typePolicy() \ { \ return M##op::thisTypePolicy(); \ } \ \ MIRType \ js::jit::M##op::typePolicySpecialization() \ { \
--- a/js/src/jit/TypePolicy.h +++ b/js/src/jit/TypePolicy.h @@ -27,17 +27,17 @@ class TypePolicy { public: // Analyze the inputs of the instruction and perform one of the following // actions for each input: // * Nothing; the input already type-checks. // * If untyped, optionally ask the input to try and specialize its value. // * Replace the operand with a conversion instruction. // * Insert an unconditional deoptimization (no conversion possible). - virtual MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* def) = 0; + virtual MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* def) const = 0; }; struct TypeSpecializationData { protected: // Specifies three levels of specialization: // - < Value. This input is expected and required. // - == None. This op should not be specialized. @@ -51,377 +51,377 @@ struct TypeSpecializationData MIRType specialization() const { return specialization_; } }; #define EMPTY_DATA_ \ struct Data \ { \ - static TypePolicy* thisTypePolicy(); \ + static const TypePolicy* thisTypePolicy(); \ } #define INHERIT_DATA_(DATA_TYPE) \ struct Data : public DATA_TYPE \ { \ - static TypePolicy* thisTypePolicy(); \ + static const TypePolicy* thisTypePolicy(); \ } #define SPECIALIZATION_DATA_ INHERIT_DATA_(TypeSpecializationData) class NoTypePolicy { public: struct Data { - static TypePolicy* thisTypePolicy() { + static const TypePolicy* thisTypePolicy() { return nullptr; } }; }; class BoxInputsPolicy final : public TypePolicy { public: SPECIALIZATION_DATA_; static MOZ_MUST_USE bool staticAdjustInputs(TempAllocator& alloc, MInstruction* def); - virtual MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* def) override { + MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* def) const override { return staticAdjustInputs(alloc, def); } }; class ArithPolicy final : public TypePolicy { public: SPECIALIZATION_DATA_; - virtual MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* def) override; + MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* def) const override; }; class AllDoublePolicy final : public TypePolicy { public: EMPTY_DATA_; static MOZ_MUST_USE bool staticAdjustInputs(TempAllocator& alloc, MInstruction* def); - MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* def) override { + MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* def) const override { return staticAdjustInputs(alloc, def); } }; class BitwisePolicy final : public TypePolicy { public: SPECIALIZATION_DATA_; - virtual MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* def) override; + MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* def) const override; }; class ComparePolicy final : public TypePolicy { public: EMPTY_DATA_; - virtual MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* def) override; + MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* def) const override; }; class SameValuePolicy final : public TypePolicy { public: EMPTY_DATA_; - virtual MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* def) override; + MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* def) const override; }; // Policy for MTest instructions. class TestPolicy final : public TypePolicy { public: EMPTY_DATA_; - virtual MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* ins) override; + MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* ins) const override; }; class TypeBarrierPolicy final : public TypePolicy { public: EMPTY_DATA_; - virtual MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* ins) override; + MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* ins) const override; }; class CallPolicy final : public TypePolicy { public: EMPTY_DATA_; - virtual MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* def) override; + MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* def) const override; }; // Policy for MPow. First operand Double; second Double or Int32. class PowPolicy final : public TypePolicy { public: SPECIALIZATION_DATA_; - virtual MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* ins) override; + MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* ins) const override; }; // Expect a string for operand Op. If the input is a Value, it is unboxed. template <unsigned Op> class StringPolicy final : public TypePolicy { public: EMPTY_DATA_; static MOZ_MUST_USE bool staticAdjustInputs(TempAllocator& alloc, MInstruction* def); - virtual MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* def) override { + MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* def) const override { return staticAdjustInputs(alloc, def); } }; // Expect a string for operand Op. Else a ToString instruction is inserted. template <unsigned Op> class ConvertToStringPolicy final : public TypePolicy { public: EMPTY_DATA_; static MOZ_MUST_USE bool staticAdjustInputs(TempAllocator& alloc, MInstruction* def); - virtual MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* def) override { + MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* def) const override { return staticAdjustInputs(alloc, def); } }; // Expect an Boolean for operand Op. If the input is a Value, it is unboxed. template <unsigned Op> class BooleanPolicy final : private TypePolicy { public: EMPTY_DATA_; static MOZ_MUST_USE bool staticAdjustInputs(TempAllocator& alloc, MInstruction* def); - virtual MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* def) override { + MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* def) const override { return staticAdjustInputs(alloc, def); } }; // Expects either an Int32 or a boxed Int32 for operand Op; may unbox if needed. template <unsigned Op> class UnboxedInt32Policy final : private TypePolicy { public: EMPTY_DATA_; static MOZ_MUST_USE bool staticAdjustInputs(TempAllocator& alloc, MInstruction* def); - virtual MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* def) override { + MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* def) const override { return staticAdjustInputs(alloc, def); } }; // Expect an Int for operand Op. Else a ToInt32 instruction is inserted. template <unsigned Op> class ConvertToInt32Policy final : public TypePolicy { public: EMPTY_DATA_; static MOZ_MUST_USE bool staticAdjustInputs(TempAllocator& alloc, MInstruction* def); - virtual MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* def) override { + MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* def) const override { return staticAdjustInputs(alloc, def); } }; // Expect an Int for operand Op. Else a TruncateToInt32 instruction is inserted. template <unsigned Op> class TruncateToInt32Policy final : public TypePolicy { public: EMPTY_DATA_; static MOZ_MUST_USE bool staticAdjustInputs(TempAllocator& alloc, MInstruction* def); - virtual MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* def) override { + MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* def) const override { return staticAdjustInputs(alloc, def); } }; // Expect a double for operand Op. If the input is a Value, it is unboxed. template <unsigned Op> class DoublePolicy final : public TypePolicy { public: EMPTY_DATA_; static MOZ_MUST_USE bool staticAdjustInputs(TempAllocator& alloc, MInstruction* def); - virtual MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* def) override { + MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* def) const override { return staticAdjustInputs(alloc, def); } }; // Expect a float32 for operand Op. If the input is a Value, it is unboxed. template <unsigned Op> class Float32Policy final : public TypePolicy { public: EMPTY_DATA_; static MOZ_MUST_USE bool staticAdjustInputs(TempAllocator& alloc, MInstruction* def); - virtual MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* def) override { + MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* def) const override { return staticAdjustInputs(alloc, def); } }; // Expect a float32 OR a double for operand Op, but will prioritize Float32 // if the result type is set as such. If the input is a Value, it is unboxed. template <unsigned Op> class FloatingPointPolicy final : public TypePolicy { public: SPECIALIZATION_DATA_; - virtual MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* def) override; + MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* def) const override; }; template <unsigned Op> class NoFloatPolicy final : public TypePolicy { public: EMPTY_DATA_; static MOZ_MUST_USE bool staticAdjustInputs(TempAllocator& alloc, MInstruction* def); - virtual MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* def) override { + MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* def) const override { return staticAdjustInputs(alloc, def); } }; // Policy for guarding variadic instructions such as object / array state // instructions. template <unsigned FirstOp> class NoFloatPolicyAfter final : public TypePolicy { public: EMPTY_DATA_; - virtual MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* ins) override; + MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* ins) const override; }; // Box objects or strings as an input to a ToDouble instruction. class ToDoublePolicy final : public TypePolicy { public: EMPTY_DATA_; static MOZ_MUST_USE bool staticAdjustInputs(TempAllocator& alloc, MInstruction* def); - virtual MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* def) override { + MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* def) const override { return staticAdjustInputs(alloc, def); } }; // Box objects, strings and undefined as input to a ToInt32 instruction. class ToInt32Policy final : public TypePolicy { public: EMPTY_DATA_; static MOZ_MUST_USE bool staticAdjustInputs(TempAllocator& alloc, MInstruction* def); - virtual MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* def) override { + MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* def) const override { return staticAdjustInputs(alloc, def); } }; // Box objects as input to a ToString instruction. class ToStringPolicy final : public TypePolicy { public: EMPTY_DATA_; static MOZ_MUST_USE bool staticAdjustInputs(TempAllocator& alloc, MInstruction* def); - virtual MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* def) override { + MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* def) const override { return staticAdjustInputs(alloc, def); } }; template <unsigned Op> class ObjectPolicy final : public TypePolicy { public: EMPTY_DATA_; static MOZ_MUST_USE bool staticAdjustInputs(TempAllocator& alloc, MInstruction* ins); - virtual MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* ins) override { + MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* ins) const override { return staticAdjustInputs(alloc, ins); } }; // Single-object input. If the input is a Value, it is unboxed. If it is // a primitive, we use ValueToNonNullObject. typedef ObjectPolicy<0> SingleObjectPolicy; // Convert an operand to have a type identical to the scalar type of the // returned type of the instruction. template <unsigned Op> class SimdScalarPolicy final : public TypePolicy { public: EMPTY_DATA_; static MOZ_MUST_USE bool staticAdjustInputs(TempAllocator& alloc, MInstruction* def); - virtual MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* def) override { + MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* def) const override { return staticAdjustInputs(alloc, def); } }; class SimdAllPolicy final : public TypePolicy { public: SPECIALIZATION_DATA_; - virtual MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* ins) override; + MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* ins) const override; }; template <unsigned Op> class SimdPolicy final : public TypePolicy { public: SPECIALIZATION_DATA_; - virtual MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* ins) override; + MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* ins) const override; }; class SimdSelectPolicy final : public TypePolicy { public: SPECIALIZATION_DATA_; - virtual MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* ins) override; + MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* ins) const override; }; class SimdShufflePolicy final : public TypePolicy { public: SPECIALIZATION_DATA_; - virtual MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* ins) override; + MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* ins) const override; }; // SIMD value-type policy, use the returned type of the instruction to determine // how to unbox its operand. template <unsigned Op> class SimdSameAsReturnedTypePolicy final : public TypePolicy { public: EMPTY_DATA_; static MOZ_MUST_USE bool staticAdjustInputs(TempAllocator& alloc, MInstruction* ins); - virtual MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* ins) override { + MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* ins) const override { return staticAdjustInputs(alloc, ins); } }; template <unsigned Op> class BoxPolicy final : public TypePolicy { public: EMPTY_DATA_; static MOZ_MUST_USE bool staticAdjustInputs(TempAllocator& alloc, MInstruction* ins); - virtual MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* ins) override { + MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* ins) const override { return staticAdjustInputs(alloc, ins); } }; // Boxes everything except inputs of type Type. template <unsigned Op, MIRType Type> class BoxExceptPolicy final : public TypePolicy { public: EMPTY_DATA_; static MOZ_MUST_USE bool staticAdjustInputs(TempAllocator& alloc, MInstruction* ins); - MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* ins) override { + MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* ins) const override { return staticAdjustInputs(alloc, ins); } }; // Box if not a typical property id (string, symbol, int32). template <unsigned Op> class CacheIdPolicy final : public TypePolicy { public: EMPTY_DATA_; static MOZ_MUST_USE bool staticAdjustInputs(TempAllocator& alloc, MInstruction* ins); - MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* ins) override { + MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* ins) const override { return staticAdjustInputs(alloc, ins); } }; // Combine multiple policies. template <class... Policies> class MixPolicy final : public TypePolicy { @@ -437,96 +437,87 @@ class MixPolicy final : public TypePolic MixPolicy::staticAdjustInputsHelper<Rest...>(alloc, ins); } public: EMPTY_DATA_; static MOZ_MUST_USE bool staticAdjustInputs(TempAllocator& alloc, MInstruction* ins) { return MixPolicy::staticAdjustInputsHelper<Policies...>(alloc, ins); } - virtual MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* ins) override { + MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* ins) const override { return staticAdjustInputs(alloc, ins); } }; class CallSetElementPolicy final : public TypePolicy { public: EMPTY_DATA_; - virtual MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* def) override; + MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* def) const override; }; // First operand will be boxed to a Value (except for an object) // Second operand (if specified) will forcefully be unboxed to an object class InstanceOfPolicy final : public TypePolicy { public: EMPTY_DATA_; - virtual MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* def) override; + MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* def) const override; }; class StoreTypedArrayHolePolicy; -class StoreTypedArrayElementStaticPolicy; class StoreUnboxedScalarPolicy : public TypePolicy { private: static MOZ_MUST_USE bool adjustValueInput(TempAllocator& alloc, MInstruction* ins, Scalar::Type arrayType, MDefinition* value, int valueOperand); friend class StoreTypedArrayHolePolicy; - friend class StoreTypedArrayElementStaticPolicy; public: EMPTY_DATA_; - virtual MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* ins) override; + MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* ins) const override; }; class StoreTypedArrayHolePolicy final : public StoreUnboxedScalarPolicy { public: EMPTY_DATA_; - virtual MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* ins) override; -}; - -class StoreTypedArrayElementStaticPolicy final : public StoreUnboxedScalarPolicy -{ - public: - EMPTY_DATA_; - virtual MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* ins) override; + MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* ins) const override; }; class StoreUnboxedObjectOrNullPolicy final : public TypePolicy { public: EMPTY_DATA_; - virtual MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* def) override; + MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* def) const override; }; class StoreUnboxedStringPolicy final : public TypePolicy { public: EMPTY_DATA_; - virtual MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* def) override; + MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* def) const override; }; // Accepts integers and doubles. Everything else is boxed. class ClampPolicy final : public TypePolicy { public: EMPTY_DATA_; - virtual MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* ins) override; + MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* ins) const override; }; class FilterTypeSetPolicy final : public TypePolicy { public: EMPTY_DATA_; - virtual MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* ins) override; + MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* ins) const override; }; #undef SPECIALIZATION_DATA_ #undef INHERIT_DATA_ #undef EMPTY_DATA_ } // namespace jit } // namespace js
--- a/js/src/jit/arm/CodeGenerator-arm.cpp +++ b/js/src/jit/arm/CodeGenerator-arm.cpp @@ -1762,28 +1762,16 @@ CodeGeneratorARM::generateInvalidateEpil masm.jump(thunk); // We should never reach this point in JIT code -- the invalidation thunk // should pop the invalidated JS frame and return directly to its caller. masm.assumeUnreachable("Should have returned directly to its caller instead of here."); } void -CodeGenerator::visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic* ins) -{ - MOZ_CRASH("NYI"); -} - -void -CodeGenerator::visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic* ins) -{ - MOZ_CRASH("NYI"); -} - -void CodeGenerator::visitCompareExchangeTypedArrayElement(LCompareExchangeTypedArrayElement* lir) { Register elements = ToRegister(lir->elements()); AnyRegister output = ToAnyRegister(lir->output()); Register temp = lir->temp()->isBogusTemp() ? InvalidReg : ToRegister(lir->temp()); Register oldval = ToRegister(lir->oldval()); Register newval = ToRegister(lir->newval());
--- a/js/src/jit/arm/Lowering-arm.cpp +++ b/js/src/jit/arm/Lowering-arm.cpp @@ -780,22 +780,16 @@ LIRGeneratorARM::lowerTruncateFToInt32(M { MDefinition* opd = ins->input(); MOZ_ASSERT(opd->type() == MIRType::Float32); define(new(alloc()) LTruncateFToInt32(useRegister(opd), LDefinition::BogusTemp()), ins); } void -LIRGenerator::visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic* ins) -{ - MOZ_CRASH("NYI"); -} - -void LIRGenerator::visitAtomicExchangeTypedArrayElement(MAtomicExchangeTypedArrayElement* ins) { MOZ_ASSERT(HasLDSTREXBHD()); MOZ_ASSERT(ins->arrayType() <= Scalar::Uint32); MOZ_ASSERT(ins->elements()->type() == MIRType::Elements); MOZ_ASSERT(ins->index()->type() == MIRType::Int32);
--- a/js/src/jit/arm/MacroAssembler-arm-inl.h +++ b/js/src/jit/arm/MacroAssembler-arm-inl.h @@ -2189,47 +2189,41 @@ MacroAssembler::spectreMovePtr(Condition void MacroAssembler::spectreZeroRegister(Condition cond, Register, Register dest) { ma_mov(Imm32(0), dest, cond); } void -MacroAssembler::spectreBoundsCheck32(Register index, Register length, Register scratch, +MacroAssembler::spectreBoundsCheck32(Register index, Register length, Register maybeScratch, Label* failure) { MOZ_ASSERT(index != length); - MOZ_ASSERT(length != scratch); - MOZ_ASSERT(index != scratch); - - if (JitOptions.spectreIndexMasking) - move32(Imm32(0), scratch); + MOZ_ASSERT(length != maybeScratch); + MOZ_ASSERT(index != maybeScratch); branch32(Assembler::BelowOrEqual, length, index, failure); if (JitOptions.spectreIndexMasking) - ma_mov(scratch, index, LeaveCC, Assembler::BelowOrEqual); + ma_mov(Imm32(0), index, Assembler::BelowOrEqual); } void -MacroAssembler::spectreBoundsCheck32(Register index, const Address& length, Register scratch, +MacroAssembler::spectreBoundsCheck32(Register index, const Address& length, Register maybeScratch, Label* failure) { MOZ_ASSERT(index != length.base); - MOZ_ASSERT(length.base != scratch); - MOZ_ASSERT(index != scratch); - - if (JitOptions.spectreIndexMasking) - move32(Imm32(0), scratch); + MOZ_ASSERT(length.base != maybeScratch); + MOZ_ASSERT(index != maybeScratch); branch32(Assembler::BelowOrEqual, length, index, failure); if (JitOptions.spectreIndexMasking) - ma_mov(scratch, index, LeaveCC, Assembler::BelowOrEqual); + ma_mov(Imm32(0), index, Assembler::BelowOrEqual); } // ======================================================================== // Memory access primitives. void MacroAssembler::storeUncanonicalizedDouble(FloatRegister src, const Address& addr) { ScratchRegisterScope scratch(*this);
--- a/js/src/jit/arm64/CodeGenerator-arm64.cpp +++ b/js/src/jit/arm64/CodeGenerator-arm64.cpp @@ -564,28 +564,16 @@ getBase(U* mir) { switch (mir->base()) { case U::Heap: return HeapReg; } return InvalidReg; } void -CodeGenerator::visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic* ins) -{ - MOZ_CRASH("CodeGenerator::visitLoadTypedArrayElementStatic"); -} - -void -CodeGenerator::visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic* ins) -{ - MOZ_CRASH("CodeGenerator::visitStoreTypedArrayElementStatic"); -} - -void CodeGenerator::visitAsmJSLoadHeap(LAsmJSLoadHeap* ins) { MOZ_CRASH("visitAsmJSLoadHeap"); } void CodeGenerator::visitAsmJSStoreHeap(LAsmJSStoreHeap* ins) {
--- a/js/src/jit/arm64/Lowering-arm64.cpp +++ b/js/src/jit/arm64/Lowering-arm64.cpp @@ -276,22 +276,16 @@ LIRGeneratorARM64::lowerTruncateDToInt32 void LIRGeneratorARM64::lowerTruncateFToInt32(MTruncateToInt32* ins) { MOZ_CRASH("lowerTruncateFToInt32"); } void -LIRGenerator::visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic* ins) -{ - MOZ_CRASH("NYI"); -} - -void LIRGenerator::visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop* ins) { MOZ_CRASH("NYI"); } void LIRGenerator::visitCompareExchangeTypedArrayElement(MCompareExchangeTypedArrayElement* ins) {
--- a/js/src/jit/arm64/MacroAssembler-arm64-inl.h +++ b/js/src/jit/arm64/MacroAssembler-arm64-inl.h @@ -1870,36 +1870,36 @@ MacroAssembler::spectreMovePtr(Condition void MacroAssembler::spectreZeroRegister(Condition cond, Register, Register dest) { Csel(ARMRegister(dest, 64), ARMRegister(dest, 64), vixl::xzr, Assembler::InvertCondition(cond)); } void -MacroAssembler::spectreBoundsCheck32(Register index, Register length, Register scratch, +MacroAssembler::spectreBoundsCheck32(Register index, Register length, Register maybeScratch, Label* failure) { MOZ_ASSERT(index != length); - MOZ_ASSERT(length != scratch); - MOZ_ASSERT(index != scratch); + MOZ_ASSERT(length != maybeScratch); + MOZ_ASSERT(index != maybeScratch); branch32(Assembler::BelowOrEqual, length, index, failure); if (JitOptions.spectreIndexMasking) Csel(ARMRegister(index, 32), ARMRegister(index, 32), vixl::wzr, Assembler::Above); } void -MacroAssembler::spectreBoundsCheck32(Register index, const Address& length, Register scratch, +MacroAssembler::spectreBoundsCheck32(Register index, const Address& length, Register maybeScratch, Label* failure) { MOZ_ASSERT(index != length.base); - MOZ_ASSERT(length.base != scratch); - MOZ_ASSERT(index != scratch); + MOZ_ASSERT(length.base != maybeScratch); + MOZ_ASSERT(index != maybeScratch); branch32(Assembler::BelowOrEqual, length, index, failure); if (JitOptions.spectreIndexMasking) Csel(ARMRegister(index, 32), ARMRegister(index, 32), vixl::wzr, Assembler::Above); } // ========================================================================
--- a/js/src/jit/mips-shared/CodeGenerator-mips-shared.cpp +++ b/js/src/jit/mips-shared/CodeGenerator-mips-shared.cpp @@ -1795,28 +1795,16 @@ CodeGeneratorMIPSShared::generateInvalid masm.jump(thunk); // We should never reach this point in JIT code -- the invalidation thunk // should pop the invalidated JS frame and return directly to its caller. masm.assumeUnreachable("Should have returned directly to its caller instead of here."); } -void -CodeGenerator::visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic* ins) -{ - MOZ_CRASH("NYI"); -} - -void -CodeGenerator::visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic* ins) -{ - MOZ_CRASH("NYI"); -} - class js::jit::OutOfLineTableSwitch : public OutOfLineCodeBase<CodeGeneratorMIPSShared> { MTableSwitch* mir_; CodeLabel jumpLabel_; void accept(CodeGeneratorMIPSShared* codegen) { codegen->visitOutOfLineTableSwitch(this); }
--- a/js/src/jit/mips-shared/Lowering-mips-shared.cpp +++ b/js/src/jit/mips-shared/Lowering-mips-shared.cpp @@ -553,22 +553,16 @@ LIRGenerator::visitSubstr(MSubstr* ins) temp(), temp(), tempByteOpRegister()); define(lir, ins); assignSafepoint(lir, ins); } void -LIRGenerator::visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic* ins) -{ - MOZ_CRASH("NYI"); -} - -void LIRGenerator::visitCompareExchangeTypedArrayElement(MCompareExchangeTypedArrayElement* ins) { MOZ_ASSERT(ins->arrayType() != Scalar::Float32); MOZ_ASSERT(ins->arrayType() != Scalar::Float64); MOZ_ASSERT(ins->elements()->type() == MIRType::Elements); MOZ_ASSERT(ins->index()->type() == MIRType::Int32);
--- a/js/src/jit/mips-shared/MacroAssembler-mips-shared-inl.h +++ b/js/src/jit/mips-shared/MacroAssembler-mips-shared-inl.h @@ -1012,25 +1012,25 @@ MacroAssembler::test32LoadPtr(Condition void MacroAssembler::test32MovePtr(Condition cond, const Address& addr, Imm32 mask, Register src, Register dest) { MOZ_CRASH(); } void -MacroAssembler::spectreBoundsCheck32(Register index, Register length, Register scratch, +MacroAssembler::spectreBoundsCheck32(Register index, Register length, Register maybeScratch, Label* failure) { MOZ_RELEASE_ASSERT(!JitOptions.spectreIndexMasking); branch32(Assembler::BelowOrEqual, length, index, failure); } void -MacroAssembler::spectreBoundsCheck32(Register index, const Address& length, Register scratch, +MacroAssembler::spectreBoundsCheck32(Register index, const Address& length, Register maybeScratch, Label* failure) { MOZ_RELEASE_ASSERT(!JitOptions.spectreIndexMasking); branch32(Assembler::BelowOrEqual, length, index, failure); } void MacroAssembler::spectreMovePtr(Condition cond, Register src, Register dest)
--- a/js/src/jit/shared/CodeGenerator-shared-inl.h +++ b/js/src/jit/shared/CodeGenerator-shared-inl.h @@ -179,24 +179,16 @@ ToAnyRegister(const LAllocation* a) } static inline AnyRegister ToAnyRegister(const LDefinition* def) { return ToAnyRegister(def->output()); } -static inline RegisterOrInt32Constant -ToRegisterOrInt32Constant(const LAllocation* a) -{ - if (a->isConstant()) - return RegisterOrInt32Constant(ToInt32(a)); - return RegisterOrInt32Constant(ToRegister(a)); -} - static inline ValueOperand ToOutValue(LInstruction* ins) { #if defined(JS_NUNBOX32) return ValueOperand(ToRegister(ins->getDef(TYPE_INDEX)), ToRegister(ins->getDef(PAYLOAD_INDEX))); #elif defined(JS_PUNBOX64) return ValueOperand(ToRegister(ins->getDef(0)));
--- a/js/src/jit/shared/LIR-shared.h +++ b/js/src/jit/shared/LIR-shared.h @@ -6435,61 +6435,68 @@ class LStoreElementT : public LInstructi return getOperand(1); } const LAllocation* value() { return getOperand(2); } }; // Like LStoreElementV, but supports indexes >= initialized length. -class LStoreElementHoleV : public LInstructionHelper<0, 3 + BOX_PIECES, 0> +class LStoreElementHoleV : public LInstructionHelper<0, 3 + BOX_PIECES, 1> { public: LIR_HEADER(StoreElementHoleV) LStoreElementHoleV(const LAllocation& object, const LAllocation& elements, - const LAllocation& index, const LBoxAllocation& value) + const LAllocation& index, const LBoxAllocation& value, + const LDefinition& spectreTemp) : LInstructionHelper(classOpcode) { setOperand(0, object); setOperand(1, elements); setOperand(2, index); setBoxOperand(Value, value); + setTemp(0, spectreTemp); } static const size_t Value = 3; const MStoreElementHole* mir() const { return mir_->toStoreElementHole(); } const LAllocation* object() { return getOperand(0); } const LAllocation* elements() { return getOperand(1); } const LAllocation* index() { return getOperand(2); } + const LDefinition* spectreTemp() { + return getTemp(0); + } }; // Like LStoreElementT, but supports indexes >= initialized length. -class LStoreElementHoleT : public LInstructionHelper<0, 4, 0> +class LStoreElementHoleT : public LInstructionHelper<0, 4, 1> { public: LIR_HEADER(StoreElementHoleT) LStoreElementHoleT(const LAllocation& object, const LAllocation& elements, - const LAllocation& index, const LAllocation& value) + const LAllocation& index, const LAllocation& value, + const LDefinition& spectreTemp) : LInstructionHelper(classOpcode) { setOperand(0, object); setOperand(1, elements); setOperand(2, index); setOperand(3, value); + setTemp(0, spectreTemp); } const MStoreElementHole* mir() const { return mir_->toStoreElementHole(); } const LAllocation* object() { return getOperand(0); } @@ -6497,64 +6504,74 @@ class LStoreElementHoleT : public LInstr return getOperand(1); } const LAllocation* index() { return getOperand(2); } const LAllocation* value() { return getOperand(3); } + const LDefinition* spectreTemp() { + return getTemp(0); + } }; // Like LStoreElementV, but can just ignore assignment (for eg. frozen objects) -class LFallibleStoreElementV : public LInstructionHelper<0, 3 + BOX_PIECES, 0> +class LFallibleStoreElementV : public LInstructionHelper<0, 3 + BOX_PIECES, 1> { public: LIR_HEADER(FallibleStoreElementV) LFallibleStoreElementV(const LAllocation& object, const LAllocation& elements, - const LAllocation& index, const LBoxAllocation& value) + const LAllocation& index, const LBoxAllocation& value, + const LDefinition& spectreTemp) : LInstructionHelper(classOpcode) { setOperand(0, object); setOperand(1, elements); setOperand(2, index); setBoxOperand(Value, value); + setTemp(0, spectreTemp); } static const size_t Value = 3; const MFallibleStoreElement* mir() const { return mir_->toFallibleStoreElement(); } const LAllocation* object() { return getOperand(0); } const LAllocation* elements() { return getOperand(1); } const LAllocation* index() { return getOperand(2); } + const LDefinition* spectreTemp() { + return getTemp(0); + } }; // Like LStoreElementT, but can just ignore assignment (for eg. frozen objects) -class LFallibleStoreElementT : public LInstructionHelper<0, 4, 0> +class LFallibleStoreElementT : public LInstructionHelper<0, 4, 1> { public: LIR_HEADER(FallibleStoreElementT) LFallibleStoreElementT(const LAllocation& object, const LAllocation& elements, - const LAllocation& index, const LAllocation& value) + const LAllocation& index, const LAllocation& value, + const LDefinition& spectreTemp) : LInstructionHelper(classOpcode) { setOperand(0, object); setOperand(1, elements); setOperand(2, index); setOperand(3, value); + setTemp(0, spectreTemp); } const MFallibleStoreElement* mir() const { return mir_->toFallibleStoreElement(); } const LAllocation* object() { return getOperand(0); } @@ -6562,16 +6579,19 @@ class LFallibleStoreElementT : public LI return getOperand(1); } const LAllocation* index() { return getOperand(2); } const LAllocation* value() { return getOperand(3); } + const LDefinition* spectreTemp() { + return getTemp(0); + } }; class LStoreUnboxedPointer : public LInstructionHelper<0, 3, 0> { public: LIR_HEADER(StoreUnboxedPointer) LStoreUnboxedPointer(LAllocation elements, LAllocation index, LAllocation value) @@ -6676,67 +6696,77 @@ class LArrayPopShiftT : public LInstruct const LDefinition* temp0() { return getTemp(0); } const LDefinition* temp1() { return getTemp(1); } }; -class LArrayPushV : public LInstructionHelper<1, 1 + BOX_PIECES, 1> +class LArrayPushV : public LInstructionHelper<1, 1 + BOX_PIECES, 2> { public: LIR_HEADER(ArrayPushV) - LArrayPushV(const LAllocation& object, const LBoxAllocation& value, const LDefinition& temp) + LArrayPushV(const LAllocation& object, const LBoxAllocation& value, const LDefinition& temp, + const LDefinition& spectreTemp) : LInstructionHelper(classOpcode) { setOperand(0, object); setBoxOperand(Value, value); setTemp(0, temp); + setTemp(1, spectreTemp); } static const size_t Value = 1; const MArrayPush* mir() const { return mir_->toArrayPush(); } const LAllocation* object() { return getOperand(0); } const LDefinition* temp() { return getTemp(0); } -}; - -class LArrayPushT : public LInstructionHelper<1, 2, 1> + const LDefinition* spectreTemp() { + return getTemp(1); + } +}; + +class LArrayPushT : public LInstructionHelper<1, 2, 2> { public: LIR_HEADER(ArrayPushT) - LArrayPushT(const LAllocation& object, const LAllocation& value, const LDefinition& temp) + LArrayPushT(const LAllocation& object, const LAllocation& value, const LDefinition& temp, + const LDefinition& spectreTemp) : LInstructionHelper(classOpcode) { setOperand(0, object); setOperand(1, value); setTemp(0, temp); + setTemp(1, spectreTemp); } const MArrayPush* mir() const { return mir_->toArrayPush(); } const LAllocation* object() { return getOperand(0); } const LAllocation* value() { return getOperand(1); } const LDefinition* temp() { return getTemp(0); } + const LDefinition* spectreTemp() { + return getTemp(1); + } }; class LArraySlice : public LCallInstructionHelper<1, 3, 2> { public: LIR_HEADER(ArraySlice) LArraySlice(const LAllocation& obj, const LAllocation& begin, const LAllocation& end, @@ -6849,33 +6879,16 @@ class LLoadTypedArrayElementHole : publi const LAllocation* index() { return getOperand(1); } const LDefinition* temp() { return getTemp(0); } }; -class LLoadTypedArrayElementStatic : public LInstructionHelper<1, 1, 0> -{ - public: - LIR_HEADER(LoadTypedArrayElementStatic); - explicit LLoadTypedArrayElementStatic(const LAllocation& ptr) - : LInstructionHelper(classOpcode) - { - setOperand(0, ptr); - } - MLoadTypedArrayElementStatic* mir() const { - return mir_->toLoadTypedArrayElementStatic(); - } - const LAllocation* ptr() { - return getOperand(0); - } -}; - class LStoreUnboxedScalar : public LInstructionHelper<0, 3, 0> { public: LIR_HEADER(StoreUnboxedScalar) LStoreUnboxedScalar(const LAllocation& elements, const LAllocation& index, const LAllocation& value) : LInstructionHelper(classOpcode) @@ -6894,29 +6907,31 @@ class LStoreUnboxedScalar : public LInst const LAllocation* index() { return getOperand(1); } const LAllocation* value() { return getOperand(2); } }; -class LStoreTypedArrayElementHole : public LInstructionHelper<0, 4, 0> +class LStoreTypedArrayElementHole : public LInstructionHelper<0, 4, 1> { public: LIR_HEADER(StoreTypedArrayElementHole) LStoreTypedArrayElementHole(const LAllocation& elements, const LAllocation& length, - const LAllocation& index, const LAllocation& value) + const LAllocation& index, const LAllocation& value, + const LDefinition& spectreTemp) : LInstructionHelper(classOpcode) { setOperand(0, elements); setOperand(1, length); setOperand(2, index); setOperand(3, value); + setTemp(0, spectreTemp); } const MStoreTypedArrayElementHole* mir() const { return mir_->toStoreTypedArrayElementHole(); } const LAllocation* elements() { return getOperand(0); } @@ -6924,36 +6939,18 @@ class LStoreTypedArrayElementHole : publ return getOperand(1); } const LAllocation* index() { return getOperand(2); } const LAllocation* value() { return getOperand(3); } -}; - -class LStoreTypedArrayElementStatic : public LInstructionHelper<0, 2, 0> -{ - public: - LIR_HEADER(StoreTypedArrayElementStatic); - LStoreTypedArrayElementStatic(const LAllocation& ptr, const LAllocation& value) - : LInstructionHelper(classOpcode) - { - setOperand(0, ptr); - setOperand(1, value); - } - MStoreTypedArrayElementStatic* mir() const { - return mir_->toStoreTypedArrayElementStatic(); - } - const LAllocation* ptr() { - return getOperand(0); - } - const LAllocation* value() { - return getOperand(1); + const LDefinition* spectreTemp() { + return getTemp(0); } }; class LAtomicIsLockFree : public LInstructionHelper<1, 1, 0> { public: LIR_HEADER(AtomicIsLockFree)
--- a/js/src/jit/shared/Lowering-shared.h +++ b/js/src/jit/shared/Lowering-shared.h @@ -285,19 +285,14 @@ class LIRGeneratorShared define(new(alloc()) LFloat32(f), mir); } public: // Whether to generate typed reads for element accesses with hole checks. static bool allowTypedElementHoleCheck() { return false; } - - // Whether to generate typed array accesses on statically known objects. - static bool allowStaticTypedArrayAccesses() { - return false; - } }; } // namespace jit } // namespace js #endif /* jit_shared_Lowering_shared_h */
--- a/js/src/jit/x64/CodeGenerator-x64.cpp +++ b/js/src/jit/x64/CodeGenerator-x64.cpp @@ -378,28 +378,16 @@ CodeGenerator::visitWasmUint32ToDouble(L void CodeGenerator::visitWasmUint32ToFloat32(LWasmUint32ToFloat32* lir) { masm.convertUInt32ToFloat32(ToRegister(lir->input()), ToFloatRegister(lir->output())); } void -CodeGenerator::visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic* ins) -{ - MOZ_CRASH("NYI"); -} - -void -CodeGenerator::visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic* ins) -{ - MOZ_CRASH("NYI"); -} - -void CodeGeneratorX64::wasmStore(const wasm::MemoryAccessDesc& access, const LAllocation* value, Operand dstAddr) { if (value->isConstant()) { MOZ_ASSERT(!access.isSimd()); masm.memoryBarrierBefore(access.sync());
--- a/js/src/jit/x64/Lowering-x64.cpp +++ b/js/src/jit/x64/Lowering-x64.cpp @@ -412,22 +412,16 @@ LIRGenerator::visitSubstr(MSubstr* ins) temp(), temp(), tempByteOpRegister()); define(lir, ins); assignSafepoint(lir, ins); } void -LIRGenerator::visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic* ins) -{ - MOZ_CRASH("NYI"); -} - -void LIRGenerator::visitRandom(MRandom* ins) { LRandom *lir = new(alloc()) LRandom(temp(), temp(), temp()); defineFixed(lir, ins, LFloatReg(ReturnDoubleReg)); }
--- a/js/src/jit/x64/MacroAssembler-x64-inl.h +++ b/js/src/jit/x64/MacroAssembler-x64-inl.h @@ -847,16 +847,60 @@ MacroAssembler::test32MovePtr(Condition } void MacroAssembler::spectreMovePtr(Condition cond, Register src, Register dest) { cmovCCq(cond, Operand(src), dest); } +void +MacroAssembler::spectreBoundsCheck32(Register index, Register length, Register maybeScratch, + Label* failure) +{ + MOZ_ASSERT(index != length); + MOZ_ASSERT(length != maybeScratch); + MOZ_ASSERT(index != maybeScratch); + + ScratchRegisterScope scratch(*this); + MOZ_ASSERT(index != scratch); + MOZ_ASSERT(length != scratch); + + if (JitOptions.spectreIndexMasking) + move32(Imm32(0), scratch); + + cmp32(index, length); + j(Assembler::AboveOrEqual, failure); + + if (JitOptions.spectreIndexMasking) + cmovCCl(Assembler::AboveOrEqual, scratch, index); +} + +void +MacroAssembler::spectreBoundsCheck32(Register index, const Address& length, Register maybeScratch, + Label* failure) +{ + MOZ_ASSERT(index != length.base); + MOZ_ASSERT(length.base != maybeScratch); + MOZ_ASSERT(index != maybeScratch); + + ScratchRegisterScope scratch(*this); + MOZ_ASSERT(index != scratch); + MOZ_ASSERT(length.base != scratch); + + if (JitOptions.spectreIndexMasking) + move32(Imm32(0), scratch); + + cmp32(index, Operand(length)); + j(Assembler::AboveOrEqual, failure); + + if (JitOptions.spectreIndexMasking) + cmovCCl(Assembler::AboveOrEqual, scratch, index); +} + // ======================================================================== // Truncate floating point. void MacroAssembler::truncateFloat32ToUInt64(Address src, Address dest, Register temp, FloatRegister floatTemp) { Label done;
--- a/js/src/jit/x86-shared/MacroAssembler-x86-shared-inl.h +++ b/js/src/jit/x86-shared/MacroAssembler-x86-shared-inl.h @@ -1113,52 +1113,16 @@ MacroAssembler::cmp32Move32(Condition co void MacroAssembler::spectreZeroRegister(Condition cond, Register scratch, Register dest) { // Note: use movl instead of move32/xorl to ensure flags are not clobbered. movl(Imm32(0), scratch); spectreMovePtr(cond, scratch, dest); } -void -MacroAssembler::spectreBoundsCheck32(Register index, Register length, Register scratch, - Label* failure) -{ - MOZ_ASSERT(index != length); - MOZ_ASSERT(length != scratch); - MOZ_ASSERT(index != scratch); - - if (JitOptions.spectreIndexMasking) - move32(Imm32(0), scratch); - - cmp32(index, length); - j(Assembler::AboveOrEqual, failure); - - if (JitOptions.spectreIndexMasking) - cmovCCl(Assembler::AboveOrEqual, scratch, index); -} - -void -MacroAssembler::spectreBoundsCheck32(Register index, const Address& length, Register scratch, - Label* failure) -{ - MOZ_ASSERT(index != length.base); - MOZ_ASSERT(length.base != scratch); - MOZ_ASSERT(index != scratch); - - if (JitOptions.spectreIndexMasking) - move32(Imm32(0), scratch); - - cmp32(index, Operand(length)); - j(Assembler::AboveOrEqual, failure); - - if (JitOptions.spectreIndexMasking) - cmovCCl(Assembler::AboveOrEqual, scratch, index); -} - // ======================================================================== // Canonicalization primitives. void MacroAssembler::canonicalizeFloat32x4(FloatRegister reg, FloatRegister scratch) { ScratchSimd128Scope scratch2(*this); MOZ_ASSERT(scratch.asSimd128() != scratch2.asSimd128());
--- a/js/src/jit/x86/CodeGenerator-x86.cpp +++ b/js/src/jit/x86/CodeGenerator-x86.cpp @@ -254,65 +254,16 @@ CodeGenerator::visitWasmUint32ToFloat32( if (input != temp) masm.mov(input, temp); // Beware: convertUInt32ToFloat32 clobbers input. masm.convertUInt32ToFloat32(temp, output); } -void -CodeGenerator::visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic* ins) -{ - const MLoadTypedArrayElementStatic* mir = ins->mir(); - Scalar::Type accessType = mir->accessType(); - MOZ_ASSERT_IF(accessType == Scalar::Float32, mir->type() == MIRType::Float32); - - Register ptr = ToRegister(ins->ptr()); - AnyRegister out = ToAnyRegister(ins->output()); - OutOfLineLoadTypedArrayOutOfBounds* ool = nullptr; - uint32_t offset = mir->offset(); - - if (mir->needsBoundsCheck()) { - MOZ_ASSERT(offset == 0); - if (!mir->fallible()) { - ool = new(alloc()) OutOfLineLoadTypedArrayOutOfBounds(out, accessType); - addOutOfLineCode(ool, ins->mir()); - } - - masm.cmpPtr(ptr, ImmWord(mir->length())); - if (ool) - masm.j(Assembler::AboveOrEqual, ool->entry()); - else - bailoutIf(Assembler::AboveOrEqual, ins->snapshot()); - } - - Operand srcAddr(ptr, int32_t(mir->base().asValue()) + int32_t(offset)); - switch (accessType) { - case Scalar::Int8: masm.movsblWithPatch(srcAddr, out.gpr()); break; - case Scalar::Uint8Clamped: - case Scalar::Uint8: masm.movzblWithPatch(srcAddr, out.gpr()); break; - case Scalar::Int16: masm.movswlWithPatch(srcAddr, out.gpr()); break; - case Scalar::Uint16: masm.movzwlWithPatch(srcAddr, out.gpr()); break; - case Scalar::Int32: - case Scalar::Uint32: masm.movlWithPatch(srcAddr, out.gpr()); break; - case Scalar::Float32: masm.vmovssWithPatch(srcAddr, out.fpu()); break; - case Scalar::Float64: masm.vmovsdWithPatch(srcAddr, out.fpu()); break; - default: MOZ_CRASH("Unexpected type"); - } - - if (accessType == Scalar::Float64) - masm.canonicalizeDouble(out.fpu()); - if (accessType == Scalar::Float32) - masm.canonicalizeFloat(out.fpu()); - - if (ool) - masm.bind(ool->rejoin()); -} - template <typename T> void CodeGeneratorX86::emitWasmLoad(T* ins) { const MWasmLoad* mir = ins->mir(); uint32_t offset = mir->access().offset(); MOZ_ASSERT(offset < wasm::OffsetGuardLimit); @@ -416,65 +367,16 @@ CodeGenerator::visitAsmJSLoadHeap(LAsmJS masm.wasmLoad(mir->access(), srcAddr, out); if (ool) masm.bind(ool->rejoin()); } void -CodeGenerator::visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic* ins) -{ - MStoreTypedArrayElementStatic* mir = ins->mir(); - Scalar::Type accessType = mir->accessType(); - Register ptr = ToRegister(ins->ptr()); - const LAllocation* value = ins->value(); - - canonicalizeIfDeterministic(accessType, value); - - uint32_t offset = mir->offset(); - MOZ_ASSERT_IF(mir->needsBoundsCheck(), offset == 0); - - Label rejoin; - if (mir->needsBoundsCheck()) { - MOZ_ASSERT(offset == 0); - masm.cmpPtr(ptr, ImmWord(mir->length())); - masm.j(Assembler::AboveOrEqual, &rejoin); - } - - Operand dstAddr(ptr, int32_t(mir->base().asValue()) + int32_t(offset)); - switch (accessType) { - case Scalar::Int8: - case Scalar::Uint8Clamped: - case Scalar::Uint8: - masm.movbWithPatch(ToRegister(value), dstAddr); - break; - case Scalar::Int16: - case Scalar::Uint16: - masm.movwWithPatch(ToRegister(value), dstAddr); - break; - case Scalar::Int32: - case Scalar::Uint32: - masm.movlWithPatch(ToRegister(value), dstAddr); - break; - case Scalar::Float32: - masm.vmovssWithPatch(ToFloatRegister(value), dstAddr); - break; - case Scalar::Float64: - masm.vmovsdWithPatch(ToFloatRegister(value), dstAddr); - break; - default: - MOZ_CRASH("unexpected type"); - } - - if (rejoin.used()) - masm.bind(&rejoin); -} - -void CodeGenerator::visitAsmJSStoreHeap(LAsmJSStoreHeap* ins) { const MAsmJSStoreHeap* mir = ins->mir(); MOZ_ASSERT(mir->offset() == 0); const LAllocation* ptr = ins->ptr(); const LAllocation* value = ins->value(); const LAllocation* boundsCheckLimit = ins->boundsCheckLimit();
--- a/js/src/jit/x86/Lowering-x86.cpp +++ b/js/src/jit/x86/Lowering-x86.cpp @@ -465,40 +465,16 @@ LIRGenerator::visitAsmJSStoreHeap(MAsmJS case Scalar::Uint8Clamped: case Scalar::MaxTypedArrayViewType: MOZ_CRASH("unexpected array type"); } add(lir, ins); } void -LIRGenerator::visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic* ins) -{ - // The code generated for StoreTypedArrayElementStatic is identical to that - // for AsmJSStoreHeap, and the same concerns apply. - LStoreTypedArrayElementStatic* lir; - switch (ins->accessType()) { - case Scalar::Int8: case Scalar::Uint8: - case Scalar::Uint8Clamped: - lir = new(alloc()) LStoreTypedArrayElementStatic(useRegister(ins->ptr()), - useFixed(ins->value(), eax)); - break; - case Scalar::Int16: case Scalar::Uint16: - case Scalar::Int32: case Scalar::Uint32: - case Scalar::Float32: case Scalar::Float64: - lir = new(alloc()) LStoreTypedArrayElementStatic(useRegisterAtStart(ins->ptr()), - useRegisterAtStart(ins->value())); - break; - default: MOZ_CRASH("unexpected array type"); - } - - add(lir, ins); -} - -void LIRGenerator::visitWasmCompareExchangeHeap(MWasmCompareExchangeHeap* ins) { MDefinition* base = ins->base(); MOZ_ASSERT(base->type() == MIRType::Int32); MDefinition* memoryBase = ins->memoryBase(); MOZ_ASSERT(memoryBase->type() == MIRType::Pointer);
--- a/js/src/jit/x86/Lowering-x86.h +++ b/js/src/jit/x86/Lowering-x86.h @@ -55,19 +55,16 @@ class LIRGeneratorX86 : public LIRGenera void lowerUModI64(MMod* mod); void lowerPhi(MPhi* phi); public: static bool allowTypedElementHoleCheck() { return true; } - static bool allowStaticTypedArrayAccesses() { - return true; - } }; typedef LIRGeneratorX86 LIRGeneratorSpecific; } // namespace jit } // namespace js #endif /* jit_x86_Lowering_x86_h */
--- a/js/src/jit/x86/MacroAssembler-x86-inl.h +++ b/js/src/jit/x86/MacroAssembler-x86-inl.h @@ -1036,16 +1036,74 @@ MacroAssembler::test32MovePtr(Condition } void MacroAssembler::spectreMovePtr(Condition cond, Register src, Register dest) { cmovCCl(cond, Operand(src), dest); } +void +MacroAssembler::spectreBoundsCheck32(Register index, const Operand& length, Register maybeScratch, + Label* failure) +{ + Label failurePopValue; + bool pushedValue = false; + if (JitOptions.spectreIndexMasking) { + if (maybeScratch == InvalidReg) { + push(Imm32(0)); + pushedValue = true; + } else { + move32(Imm32(0), maybeScratch); + } + } + + cmp32(index, length); + j(Assembler::AboveOrEqual, pushedValue ? &failurePopValue : failure); + + if (JitOptions.spectreIndexMasking) { + if (maybeScratch == InvalidReg) { + Label done; + cmovCCl(Assembler::AboveOrEqual, Operand(StackPointer, 0), index); + lea(Operand(StackPointer, sizeof(void*)), StackPointer); + jump(&done); + + bind(&failurePopValue); + lea(Operand(StackPointer, sizeof(void*)), StackPointer); + jump(failure); + + bind(&done); + } else { + cmovCCl(Assembler::AboveOrEqual, maybeScratch, index); + } + } +} + +void +MacroAssembler::spectreBoundsCheck32(Register index, Register length, Register maybeScratch, + Label* failure) +{ + MOZ_ASSERT(index != length); + MOZ_ASSERT(length != maybeScratch); + MOZ_ASSERT(index != maybeScratch); + + spectreBoundsCheck32(index, Operand(length), maybeScratch, failure); +} + +void +MacroAssembler::spectreBoundsCheck32(Register index, const Address& length, Register maybeScratch, + Label* failure) +{ + MOZ_ASSERT(index != length.base); + MOZ_ASSERT(length.base != maybeScratch); + MOZ_ASSERT(index != maybeScratch); + + spectreBoundsCheck32(index, Operand(length), maybeScratch, failure); +} + // ======================================================================== // Truncate floating point. void MacroAssembler::truncateFloat32ToUInt64(Address src, Address dest, Register temp, FloatRegister floatTemp) { Label done;
--- a/js/src/moz.build +++ b/js/src/moz.build @@ -44,25 +44,41 @@ if CONFIG['JS_BUNDLED_EDITLINE']: DIRS += ['editline'] if not CONFIG['JS_DISABLE_SHELL']: DIRS += ['shell'] TEST_DIRS += ['jsapi-tests', 'tests', 'gdb'] if CONFIG['FUZZING_INTERFACES']: - if CONFIG['LIBFUZZER']: - # Add trace-pc coverage for libfuzzer - CFLAGS += ['-fsanitize-coverage=trace-pc-guard'] - CXXFLAGS += ['-fsanitize-coverage=trace-pc-guard'] - TEST_DIRS += [ 'fuzz-tests', ] +if CONFIG['FUZZING_INTERFACES'] and CONFIG['LIBFUZZER']: + # In addition to regular coverage provided by trace-pc-guard, + # LibFuzzer can use trace-cmp to instrument various compare instructions. + # Only use this feature on source files that do a lot of constant + # comparisons that would otherwise be hard to guess by LibFuzzer, + # as it comes with a larger overhead (requires -use_value_profile=1). + libfuzzer_flags = ['-fsanitize-coverage=trace-pc-guard'] + libfuzzer_flags_cmp = ['-fsanitize-coverage=trace-pc-guard', '-fsanitize-coverage=trace-cmp'] + + # Any files that are targeted by LibFuzzer should be added here so they can + # be built with the necessary instrumentation flags, rather than just building + # the whole JS engine with instrumentation, to reduce the amount of noise. + SOURCES += [ + 'vm/StructuredClone.cpp', + ] + SOURCES['vm/StructuredClone.cpp'].flags += libfuzzer_flags_cmp +else: + UNIFIED_SOURCES += [ + 'vm/StructuredClone.cpp', + ] + CONFIGURE_SUBST_FILES += [ 'devtools/rootAnalysis/Makefile', ] CONFIGURE_DEFINE_FILES += [ 'js-confdefs.h', ] if not CONFIG['JS_STANDALONE']: @@ -369,17 +385,16 @@ UNIFIED_SOURCES += [ 'vm/Scope.cpp', 'vm/SelfHosting.cpp', 'vm/Shape.cpp', 'vm/SharedArrayObject.cpp', 'vm/SharedImmutableStringsCache.cpp', 'vm/Stack.cpp', 'vm/Stopwatch.cpp', 'vm/StringType.cpp', - 'vm/StructuredClone.cpp', 'vm/SymbolType.cpp', 'vm/TaggedProto.cpp', 'vm/Time.cpp', 'vm/TypedArrayObject.cpp', 'vm/TypeInference.cpp', 'vm/UbiNode.cpp', 'vm/UbiNodeCensus.cpp', 'vm/UbiNodeShortestPaths.cpp', @@ -692,19 +707,19 @@ if CONFIG['JS_BUILD_BINAST']: # These parts of BinAST should eventually move to release. SOURCES += [ 'frontend/BinSource.cpp', 'frontend/BinToken.cpp' ] # Instrument BinAST files for fuzzing as we have a fuzzing target for BinAST. if CONFIG['FUZZING_INTERFACES'] and CONFIG['LIBFUZZER']: - SOURCES['frontend/BinSource.cpp'].flags += ['-fsanitize-coverage=trace-pc-guard'] - SOURCES['frontend/BinToken.cpp'].flags += ['-fsanitize-coverage=trace-pc-guard'] - SOURCES['frontend/BinTokenReaderTester.cpp'].flags += ['-fsanitize-coverage=trace-pc-guard'] + SOURCES['frontend/BinSource.cpp'].flags += libfuzzer_flags_cmp + SOURCES['frontend/BinToken.cpp'].flags += libfuzzer_flags_cmp + SOURCES['frontend/BinTokenReaderTester.cpp'].flags += libfuzzer_flags_cmp # Wasm code should use WASM_HUGE_MEMORY instead of JS_CODEGEN_X64 # so that it is easy to use the huge-mapping optimization for other # 64-bit platforms in the future. if CONFIG['JS_CODEGEN_X64'] or CONFIG['JS_CODEGEN_ARM64']: DEFINES['WASM_HUGE_MEMORY'] = True
--- a/js/src/vm/JSFunction-inl.h +++ b/js/src/vm/JSFunction-inl.h @@ -16,26 +16,16 @@ namespace js { inline const char* GetFunctionNameBytes(JSContext* cx, JSFunction* fun, JSAutoByteString* bytes) { if (JSAtom* name = fun->explicitName()) return bytes->encodeLatin1(cx, name); return js_anonymous_str; } -static inline JSObject* -SkipEnvironmentObjects(JSObject* env) -{ - if (!env) - return nullptr; - while (env->is<EnvironmentObject>()) - env = &env->as<EnvironmentObject>().enclosingEnvironment(); - return env; -} - inline bool CanReuseFunctionForClone(JSContext* cx, HandleFunction fun) { if (!fun->isSingleton()) return false; if (fun->isInterpretedLazy()) { LazyScript* lazy = fun->lazyScript(); if (lazy->hasBeenCloned()) @@ -63,17 +53,16 @@ CloneFunctionObjectIfNotSingleton(JSCont * with its type in existence. * * For functions inner to run once lambda, it may be possible that * the lambda runs multiple times and we repeatedly clone it. In these * cases, fall through to CloneFunctionObject, which will deep clone * the function's script. */ if (CanReuseFunctionForClone(cx, fun)) { - RootedObject obj(cx, SkipEnvironmentObjects(parent)); ObjectOpResult succeeded; if (proto && !SetPrototype(cx, fun, proto, succeeded)) return nullptr; MOZ_ASSERT(!proto || succeeded); fun->setEnvironment(parent); return fun; }
--- a/js/src/vm/JSFunction.cpp +++ b/js/src/vm/JSFunction.cpp @@ -2028,16 +2028,26 @@ js::NewScriptedFunction(JSContext* cx, u RootedObject enclosingEnv(cx, enclosingEnvArg); if (!enclosingEnv) enclosingEnv = &cx->global()->lexicalEnvironment(); return NewFunctionWithProto(cx, nullptr, nargs, flags, enclosingEnv, atom, proto, allocKind, newKind); } #ifdef DEBUG +static JSObject* +SkipEnvironmentObjects(JSObject* env) +{ + if (!env) + return nullptr; + while (env->is<EnvironmentObject>()) + env = &env->as<EnvironmentObject>().enclosingEnvironment(); + return env; +} + static bool NewFunctionEnvironmentIsWellFormed(JSContext* cx, HandleObject env) { // Assert that the terminating environment is null, global, or a debug // scope proxy. All other cases of polluting global scope behavior are // handled by EnvironmentObjects (viz. non-syntactic DynamicWithObject and // NonSyntacticVariablesObject). RootedObject terminatingEnv(cx, SkipEnvironmentObjects(env)); @@ -2132,17 +2142,27 @@ NewFunctionClone(JSContext* cx, HandleFu } JSObject* cloneobj = NewObjectWithClassProto(cx, &JSFunction::class_, cloneProto, allocKind, newKind); if (!cloneobj) return nullptr; RootedFunction clone(cx, &cloneobj->as<JSFunction>()); - uint16_t flags = fun->flags() & ~JSFunction::EXTENDED; + // JSFunction::HAS_INFERRED_NAME can be set at compile-time and at + // runtime. In the latter case we should actually clear the flag before + // cloning the function, but since we can't differentiate between both + // cases here, we'll end up with a momentarily incorrect function name. + // This will be fixed up in SetFunctionNameIfNoOwnName(), which should + // happen through JSOP_SETFUNNAME directly after JSOP_LAMBDA. + constexpr uint16_t NonCloneableFlags = JSFunction::EXTENDED | + JSFunction::RESOLVED_LENGTH | + JSFunction::RESOLVED_NAME; + + uint16_t flags = fun->flags() & ~NonCloneableFlags; if (allocKind == AllocKind::FUNCTION_EXTENDED) flags |= JSFunction::EXTENDED; clone->setArgCount(fun->nargs()); clone->setFlags(flags); JSAtom* atom = fun->displayAtom(); if (atom) @@ -2306,98 +2326,109 @@ SymbolToFunctionName(JSContext* cx, JS:: if (desc) { // Step 4.c. if (!sb.append('[') || !sb.append(desc) || !sb.append(']')) return nullptr; } return sb.finishAtom(); } +static JSAtom* +NameToFunctionName(JSContext* cx, HandleValue name, FunctionPrefixKind prefixKind) +{ + MOZ_ASSERT(name.isString() || name.isNumber()); + + if (prefixKind == FunctionPrefixKind::None) + return ToAtom<CanGC>(cx, name); + + JSString* nameStr = ToString(cx, name); + if (!nameStr) + return nullptr; + + StringBuffer sb(cx); + if (prefixKind == FunctionPrefixKind::Get) { + if (!sb.append("get ")) + return nullptr; + } else { + if (!sb.append("set ")) + return nullptr; + } + if (!sb.append(nameStr)) + return nullptr; + return sb.finishAtom(); +} + /* * Return an atom for use as the name of a builtin method with the given * property id. * * Function names are always strings. If id is the well-known @@iterator * symbol, this returns "[Symbol.iterator]". If a prefix is supplied the final * name is |prefix + " " + name|. * * Implements steps 3-5 of 9.2.11 SetFunctionName in ES2016. */ JSAtom* js::IdToFunctionName(JSContext* cx, HandleId id, FunctionPrefixKind prefixKind /* = FunctionPrefixKind::None */) { + MOZ_ASSERT(JSID_IS_STRING(id) || JSID_IS_SYMBOL(id) || JSID_IS_INT(id)); + // No prefix fastpath. if (JSID_IS_ATOM(id) && prefixKind == FunctionPrefixKind::None) return JSID_TO_ATOM(id); // Step 3 (implicit). // Step 4. if (JSID_IS_SYMBOL(id)) return SymbolToFunctionName(cx, JSID_TO_SYMBOL(id), prefixKind); + // Step 5. RootedValue idv(cx, IdToValue(id)); - RootedAtom name(cx, ToAtom<CanGC>(cx, idv)); - if (!name) - return nullptr; - - // Step 5. - return NameToFunctionName(cx, name, prefixKind); -} - -JSAtom* -js::NameToFunctionName(JSContext* cx, HandleAtom name, - FunctionPrefixKind prefixKind /* = FunctionPrefixKind::None */) -{ - if (prefixKind == FunctionPrefixKind::None) - return name; - - StringBuffer sb(cx); - if (prefixKind == FunctionPrefixKind::Get) { - if (!sb.append("get ")) - return nullptr; - } else { - if (!sb.append("set ")) - return nullptr; - } - if (!sb.append(name)) - return nullptr; - return sb.finishAtom(); + return NameToFunctionName(cx, idv, prefixKind); } bool js::SetFunctionNameIfNoOwnName(JSContext* cx, HandleFunction fun, HandleValue name, FunctionPrefixKind prefixKind) { MOZ_ASSERT(name.isString() || name.isSymbol() || name.isNumber()); + // An inferred name may already be set if this function is a clone of a + // singleton function. Clear the inferred name in all cases, even if we + // end up not adding a new inferred name if |fun| is a class constructor. + if (fun->hasInferredName()) { + MOZ_ASSERT(fun->isSingleton()); + fun->clearInferredName(); + } + if (fun->isClassConstructor()) { // A class may have static 'name' method or accessor. if (fun->contains(cx, cx->names().name)) return true; } else { // Anonymous function shouldn't have own 'name' property at this point. MOZ_ASSERT(!fun->containsPure(cx->names().name)); } - JSAtom* funNameAtom; - if (name.isSymbol()) { - funNameAtom = SymbolToFunctionName(cx, name.toSymbol(), prefixKind); - } else { - RootedAtom nameAtom(cx, ToAtom<CanGC>(cx, name)); - if (!nameAtom) - return false; - funNameAtom = NameToFunctionName(cx, nameAtom, prefixKind); - } - if (!funNameAtom) + JSAtom* funName = name.isSymbol() + ? SymbolToFunctionName(cx, name.toSymbol(), prefixKind) + : NameToFunctionName(cx, name, prefixKind); + if (!funName) return false; + // RESOLVED_NAME shouldn't yet be set, at least as long as we don't + // support the "static public fields" or "decorators" proposal. + // These two proposals allow to access class constructors before + // JSOP_SETFUNNAME is executed, which means user code may have set the + // RESOLVED_NAME flag when we reach this point. MOZ_ASSERT(!fun->hasResolvedName()); - fun->setInferredName(funNameAtom); + + fun->setInferredName(funName); return true; } JSFunction* js::DefineFunction(JSContext* cx, HandleObject obj, HandleId id, Native native, unsigned nargs, unsigned flags, AllocKind allocKind /* = AllocKind::FUNCTION */) {
--- a/js/src/vm/JSFunction.h +++ b/js/src/vm/JSFunction.h @@ -56,28 +56,30 @@ class JSFunction : public js::NativeObje enum Flags { INTERPRETED = 0x0001, /* function has a JSScript and environment. */ CONSTRUCTOR = 0x0002, /* function that can be called as a constructor */ EXTENDED = 0x0004, /* structure is FunctionExtended */ BOUND_FUN = 0x0008, /* function was created with Function.prototype.bind. */ WASM_OPTIMIZED = 0x0010, /* asm.js/wasm function that has a jit entry */ HAS_GUESSED_ATOM = 0x0020, /* function had no explicit name, but a - name was guessed for it anyway */ + name was guessed for it anyway. See + atom_ for more info about this flag. */ HAS_BOUND_FUNCTION_NAME_PREFIX = 0x0020, /* bound functions reuse the HAS_GUESSED_ATOM flag to track if atom_ already contains the "bound " function name prefix */ LAMBDA = 0x0040, /* function comes from a FunctionExpression, ArrowFunction, or Function() call (not a FunctionDeclaration or nonstandard function-statement) */ SELF_HOSTED = 0x0080, /* function is self-hosted builtin and must not be decompilable nor constructible. */ HAS_INFERRED_NAME = 0x0100, /* function had no explicit name, but a name was set by SetFunctionName at compile time or - SetFunctionNameIfNoOwnName at runtime. */ + SetFunctionNameIfNoOwnName at runtime. See + atom_ for more info about this flag. */ INTERPRETED_LAZY = 0x0200, /* function is interpreted but doesn't have a script yet */ RESOLVED_LENGTH = 0x0400, /* f.length has been resolved (see fun_resolve). */ RESOLVED_NAME = 0x0800, /* f.name has been resolved (see fun_resolve). */ FUNCTION_KIND_SHIFT = 13, FUNCTION_KIND_MASK = 0x7 << FUNCTION_KIND_SHIFT, ASMJS_KIND = AsmJS << FUNCTION_KIND_SHIFT, @@ -136,17 +138,52 @@ class JSFunction : public js::NativeObje JSObject* env_; /* environment for new activations */ union { JSScript* script_; /* interpreted bytecode descriptor or null; use the accessor! */ js::LazyScript* lazy_; /* lazily compiled script, or nullptr */ } s; } scripted; } u; - js::GCPtrAtom atom_; /* name for diagnostics and decompiling */ + + // The |atom_| field can have different meanings depending on the function + // type and flags. It is used for diagnostics, decompiling, and + // + // 1. If the function is not a bound function: + // a. If HAS_GUESSED_ATOM is not set, to store the initial value of the + // "name" property of functions. But also see RESOLVED_NAME. + // b. If HAS_GUESSED_ATOM is set, |atom_| is only used for diagnostics, + // but must not be used for the "name" property. + // c. If HAS_INFERRED_NAME is set, the function wasn't given an explicit + // name in the source text, e.g. |function fn(){}|, but instead it + // was inferred based on how the function was defined in the source + // text. The exact name inference rules are defined in the ECMAScript + // specification. + // Name inference can happen at compile-time, for example in + // |var fn = function(){}|, or it can happen at runtime, for example + // in |var o = {[Symbol.iterator]: function(){}}|. When it happens at + // compile-time, the HAS_INFERRED_NAME is set directly in the + // bytecode emitter, when it happens at runtime, the flag is set when + // evaluating the JSOP_SETFUNNAME bytecode. + // d. HAS_GUESSED_ATOM and HAS_INFERRED_NAME cannot both be set. + // e. |atom_| can be null if neither an explicit, nor inferred, nor a + // guessed name was set. + // f. HAS_INFERRED_NAME can be set for cloned singleton function, even + // though the clone shouldn't receive an inferred name. See the + // comments in NewFunctionClone() and SetFunctionNameIfNoOwnName() + // for details. + // + // 2. If the function is a bound function: + // a. To store the initial value of the "name" property. + // b. If HAS_BOUND_FUNCTION_NAME_PREFIX is not set, |atom_| doesn't + // contain the "bound " prefix which is prepended to the "name" + // property of bound functions per ECMAScript. + // c. Bound functions can never have an inferred or guessed name. + // d. |atom_| is never null for bound functions. + js::GCPtrAtom atom_; public: /* Call objects must be created for each invocation of this function. */ bool needsCallObject() const { MOZ_ASSERT(!isInterpretedLazy()); if (isNative()) return false; @@ -799,20 +836,16 @@ NewScriptedFunction(JSContext* cx, unsig HandleAtom atom, HandleObject proto = nullptr, gc::AllocKind allocKind = gc::AllocKind::FUNCTION, NewObjectKind newKind = GenericObject, HandleObject enclosingEnv = nullptr); extern JSAtom* IdToFunctionName(JSContext* cx, HandleId id, FunctionPrefixKind prefixKind = FunctionPrefixKind::None); -extern JSAtom* -NameToFunctionName(JSContext* cx, HandleAtom name, - FunctionPrefixKind prefixKind = FunctionPrefixKind::None); - extern bool SetFunctionNameIfNoOwnName(JSContext* cx, HandleFunction fun, HandleValue name, FunctionPrefixKind prefixKind); extern JSFunction* DefineFunction(JSContext* cx, HandleObject obj, HandleId id, JSNative native, unsigned nargs, unsigned flags, gc::AllocKind allocKind = gc::AllocKind::FUNCTION);
--- a/js/src/vm/JSScript.cpp +++ b/js/src/vm/JSScript.cpp @@ -2377,50 +2377,62 @@ js::SharedScriptData::new_(JSContext* cx * We expect bytecode is always non-empty. */ MOZ_DIAGNOSTIC_ASSERT(codeLength > 0); entry->refCount_ = 0; entry->natoms_ = natoms; entry->codeLength_ = codeLength; entry->noteLength_ = srcnotesLength; - /* * Call constructors to initialize the storage that will be accessed as a * GCPtrAtom array via atoms(). */ static_assert(offsetof(SharedScriptData, data_) % sizeof(GCPtrAtom) == 0, "atoms must have GCPtrAtom alignment"); GCPtrAtom* atoms = entry->atoms(); for (unsigned i = 0; i < natoms; ++i) new (&atoms[i]) GCPtrAtom(); // Sanity check the dataLength() computation MOZ_ASSERT(entry->dataLength() == dataLength); return entry; } +inline +js::ScriptBytecodeHasher::Lookup::Lookup(SharedScriptData* data) + : scriptData(data), + hash(mozilla::HashBytes(scriptData->data(), scriptData->dataLength())) +{ + scriptData->incRefCount(); +} + +inline +js::ScriptBytecodeHasher::Lookup::~Lookup() +{ + scriptData->decRefCount(); +} + bool JSScript::createScriptData(JSContext* cx, uint32_t codeLength, uint32_t srcnotesLength, uint32_t natoms) { MOZ_ASSERT(!scriptData()); SharedScriptData* ssd = SharedScriptData::new_(cx, codeLength, srcnotesLength, natoms); if (!ssd) return false; setScriptData(ssd); return true; } void JSScript::freeScriptData() { - MOZ_ASSERT(scriptData_->refCount() == 1); scriptData_->decRefCount(); scriptData_ = nullptr; } void JSScript::setScriptData(js::SharedScriptData* data) { MOZ_ASSERT(!scriptData_); @@ -2436,19 +2448,23 @@ JSScript::setScriptData(js::SharedScript */ bool JSScript::shareScriptData(JSContext* cx) { SharedScriptData* ssd = scriptData(); MOZ_ASSERT(ssd); MOZ_ASSERT(ssd->refCount() == 1); + // Calculate the hash before taking the lock. Because the data is reference + // counted, it also will be freed after releasing the lock if necessary. + ScriptBytecodeHasher::Lookup lookup(ssd); + AutoLockScriptData lock(cx->runtime()); - ScriptDataTable::AddPtr p = cx->scriptDataTable(lock).lookupForAdd(*ssd); + ScriptDataTable::AddPtr p = cx->scriptDataTable(lock).lookupForAdd(lookup); if (p) { MOZ_ASSERT(ssd != *p); freeScriptData(); setScriptData(*p); } else { if (!cx->scriptDataTable(lock).add(p, ssd)) { freeScriptData(); ReportOutOfMemory(cx);
--- a/js/src/vm/JSScript.h +++ b/js/src/vm/JSScript.h @@ -851,29 +851,39 @@ class SharedScriptData private: SharedScriptData() = delete; SharedScriptData(const SharedScriptData&) = delete; SharedScriptData& operator=(const SharedScriptData&) = delete; }; struct ScriptBytecodeHasher { - typedef SharedScriptData Lookup; + class Lookup { + friend struct ScriptBytecodeHasher; + + SharedScriptData* scriptData; + HashNumber hash; + + public: + explicit Lookup(SharedScriptData* data); + ~Lookup(); + }; static HashNumber hash(const Lookup& l) { - return mozilla::HashBytes(l.data(), l.dataLength()); + return l.hash; } static bool match(SharedScriptData* entry, const Lookup& lookup) { - if (entry->natoms() != lookup.natoms()) - return false; - if (entry->codeLength() != lookup.codeLength()) + const SharedScriptData* data = lookup.scriptData; + if (entry->natoms() != data->natoms()) return false; - if (entry->numNotes() != lookup.numNotes()) + if (entry->codeLength() != data->codeLength()) return false; - return mozilla::PodEqual<uint8_t>(entry->data(), lookup.data(), lookup.dataLength()); + if (entry->numNotes() != data->numNotes()) + return false; + return mozilla::PodEqual<uint8_t>(entry->data(), data->data(), data->dataLength()); } }; class AutoLockScriptData; using ScriptDataTable = HashSet<SharedScriptData*, ScriptBytecodeHasher, SystemAllocPolicy>;
--- a/js/src/vm/NativeObject.cpp +++ b/js/src/vm/NativeObject.cpp @@ -4,27 +4,30 @@ * 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/. */ #include "vm/NativeObject-inl.h" #include "mozilla/ArrayUtils.h" #include "mozilla/Casting.h" #include "mozilla/CheckedInt.h" +#include "mozilla/DebugOnly.h" #include "gc/Marking.h" #include "js/Value.h" #include "vm/Debugger.h" #include "vm/TypedArrayObject.h" +#include "vm/UnboxedObject.h" #include "gc/Nursery-inl.h" #include "vm/ArrayObject-inl.h" #include "vm/EnvironmentObject-inl.h" #include "vm/JSObject-inl.h" #include "vm/Shape-inl.h" +#include "vm/UnboxedObject-inl.h" using namespace js; using JS::AutoCheckCannotGC; using mozilla::ArrayLength; using mozilla::CheckedInt; using mozilla::DebugOnly; using mozilla::PodCopy; @@ -1493,18 +1496,18 @@ AddOrChangeProperty(JSContext* cx, Handl MOZ_ASSERT(!desc.setter()); return CallAddPropertyHookDense(cx, obj, index, desc.value()); } } return CallAddPropertyHook(cx, obj, id, desc.value()); } -// Version of AddOrChangeProperty optimized for adding a plain data property. -// This function doesn't handle integer ids as we may have to store them in +// Versions of AddOrChangeProperty optimized for adding a plain data property. +// These function doesn't handle integer ids as we may have to store them in // dense elements. static MOZ_ALWAYS_INLINE bool AddDataProperty(JSContext* cx, HandleNativeObject obj, HandleId id, HandleValue v) { MOZ_ASSERT(!JSID_IS_INT(id)); if (!ReshapeForShadowedProp(cx, obj, id)) return false; @@ -1513,16 +1516,34 @@ AddDataProperty(JSContext* cx, HandleNat if (!shape) return false; UpdateShapeTypeAndValueForWritableDataProp(cx, obj, shape, id, v); return CallAddPropertyHook(cx, obj, id, v); } +static MOZ_ALWAYS_INLINE bool +AddDataPropertyNonDelegate(JSContext* cx, HandlePlainObject obj, HandleId id, HandleValue v) +{ + MOZ_ASSERT(!JSID_IS_INT(id)); + MOZ_ASSERT(!obj->isDelegate()); + + // If we know this is a new property we can call addProperty instead of + // the slower putProperty. + Shape* shape = NativeObject::addEnumerableDataProperty(cx, obj, id); + if (!shape) + return false; + + UpdateShapeTypeAndValueForWritableDataProp(cx, obj, shape, id, v); + + MOZ_ASSERT(!obj->getClass()->getAddProperty()); + return true; +} + static bool IsConfigurable(unsigned attrs) { return (attrs & JSPROP_PERMANENT) == 0; } static bool IsEnumerable(unsigned attrs) { return (attrs & JSPROP_ENUMERATE) != 0; } static bool IsWritable(unsigned attrs) { return (attrs & JSPROP_READONLY) == 0; } static bool IsAccessorDescriptor(unsigned attrs) { return (attrs & (JSPROP_GETTER | JSPROP_SETTER)) != 0; } @@ -2890,8 +2911,148 @@ js::NativeDeleteProperty(JSContext* cx, obj->setDenseElementHole(cx, JSID_TO_INT(id)); } else { if (!NativeObject::removeProperty(cx, obj, id)) return false; } return SuppressDeletedProperty(cx, obj, id); } + +bool +js::CopyDataPropertiesNative(JSContext* cx, HandlePlainObject target, HandleNativeObject from, + HandlePlainObject excludedItems, bool* optimized) +{ + MOZ_ASSERT(!target->isDelegate(), + "CopyDataPropertiesNative should only be called during object literal construction" + "which precludes that |target| is the prototype of any other object"); + + *optimized = false; + + // Don't use the fast path if |from| may have extra indexed or lazy + // properties. + if (from->getDenseInitializedLength() > 0 || + from->isIndexed() || + from->is<TypedArrayObject>() || + from->getClass()->getNewEnumerate() || + from->getClass()->getEnumerate()) + { + return true; + } + + // Collect all enumerable data properties. + using ShapeVector = GCVector<Shape*, 8>; + Rooted<ShapeVector> shapes(cx, ShapeVector(cx)); + + RootedShape fromShape(cx, from->lastProperty()); + for (Shape::Range<NoGC> r(fromShape); !r.empty(); r.popFront()) { + Shape* shape = &r.front(); + jsid id = shape->propid(); + MOZ_ASSERT(!JSID_IS_INT(id)); + + if (!shape->enumerable()) + continue; + if (excludedItems && excludedItems->contains(cx, id)) + continue; + + // Don't use the fast path if |from| contains non-data properties. + // + // This enables two optimizations: + // 1. We don't need to handle the case when accessors modify |from|. + // 2. String and symbol properties can be added in one go. + if (!shape->isDataProperty()) + return true; + + if (!shapes.append(shape)) + return false; + } + + *optimized = true; + + // If |target| contains no own properties, we can directly call + // addProperty instead of the slower putProperty. + const bool targetHadNoOwnProperties = target->lastProperty()->isEmptyShape(); + + RootedId key(cx); + RootedValue value(cx); + for (size_t i = shapes.length(); i > 0; i--) { + Shape* shape = shapes[i - 1]; + MOZ_ASSERT(shape->isDataProperty()); + MOZ_ASSERT(shape->enumerable()); + + key = shape->propid(); + MOZ_ASSERT(!JSID_IS_INT(key)); + + MOZ_ASSERT(from->isNative()); + MOZ_ASSERT(from->lastProperty() == fromShape); + + value = from->getSlot(shape->slot()); + if (targetHadNoOwnProperties) { + MOZ_ASSERT(!target->contains(cx, key), + "didn't expect to find an existing property"); + + if (!AddDataPropertyNonDelegate(cx, target, key, value)) + return false; + } else { + if (!NativeDefineDataProperty(cx, target, key, value, JSPROP_ENUMERATE)) + return false; + } + } + + return true; +} + +bool +js::CopyDataPropertiesNative(JSContext* cx, HandlePlainObject target, + Handle<UnboxedPlainObject*> from, HandlePlainObject excludedItems, + bool* optimized) +{ + MOZ_ASSERT(!target->isDelegate(), + "CopyDataPropertiesNative should only be called during object literal construction" + "which precludes that |target| is the prototype of any other object"); + + *optimized = false; + + // Don't use the fast path for unboxed objects with expandos. + if (from->maybeExpando()) + return true; + + *optimized = true; + + // If |target| contains no own properties, we can directly call + // addProperty instead of the slower putProperty. + const bool targetHadNoOwnProperties = target->lastProperty()->isEmptyShape(); + +#ifdef DEBUG + RootedObjectGroup fromGroup(cx, from->group()); +#endif + + RootedId key(cx); + RootedValue value(cx); + const UnboxedLayout& layout = from->layout(); + for (size_t i = 0; i < layout.properties().length(); i++) { + const UnboxedLayout::Property& property = layout.properties()[i]; + key = NameToId(property.name); + MOZ_ASSERT(!JSID_IS_INT(key)); + + if (excludedItems && excludedItems->contains(cx, key)) + continue; + + // Ensure the object stays unboxed. + MOZ_ASSERT(from->group() == fromGroup); + + // All unboxed properties are enumerable. + value = from->getValue(property); + + if (targetHadNoOwnProperties) { + MOZ_ASSERT(!target->contains(cx, key), + "didn't expect to find an existing property"); + + if (!AddDataPropertyNonDelegate(cx, target, key, value)) + return false; + } else { + if (!NativeDefineDataProperty(cx, target, key, value, JSPROP_ENUMERATE)) + return false; + } + } + + return true; +}
--- a/js/src/vm/NativeObject.h +++ b/js/src/vm/NativeObject.h @@ -24,16 +24,17 @@ #include "vm/ShapedObject.h" #include "vm/StringType.h" #include "vm/TypeInference.h" namespace js { class Shape; class TenuringTracer; +class UnboxedPlainObject; /* * To really poison a set of values, using 'magic' or 'undefined' isn't good * enough since often these will just be ignored by buggy code (see bug 629974) * in debug builds and crash in release builds. Instead, we use a safe-for-crash * pointer. */ static MOZ_ALWAYS_INLINE void @@ -1602,16 +1603,26 @@ MaybeNativeObject(JSObject* obj) } // Defined in NativeObject-inl.h. bool IsPackedArray(JSObject* obj); extern void AddPropertyTypesAfterProtoChange(JSContext* cx, NativeObject* obj, ObjectGroup* oldGroup); +// Specializations of 7.3.23 CopyDataProperties(...) for NativeObjects. +extern bool +CopyDataPropertiesNative(JSContext* cx, HandlePlainObject target, HandleNativeObject from, + HandlePlainObject excludedItems, bool* optimized); + +extern bool +CopyDataPropertiesNative(JSContext* cx, HandlePlainObject target, + Handle<UnboxedPlainObject*> from, HandlePlainObject excludedItems, + bool* optimized); + } // namespace js /*** Inline functions declared in JSObject.h that use the native declarations above **************/ inline bool js::HasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp) {
--- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -257,27 +257,16 @@ intrinsic_SubstringKernel(JSContext* cx, JSString* substr = SubstringKernel(cx, str, begin, length); if (!substr) return false; args.rval().setString(substr); return true; } -static bool -intrinsic_OwnPropertyKeys(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - MOZ_ASSERT(args.length() == 1); - MOZ_ASSERT(args[0].isObject()); - RootedObject obj(cx, &args[0].toObject()); - return GetOwnPropertyKeys(cx, obj, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, - args.rval()); -} - static void ThrowErrorWithType(JSContext* cx, JSExnType type, const CallArgs& args) { uint32_t errorNumber = args[0].toInt32(); #ifdef DEBUG const JSErrorFormatString* efs = GetErrorMessage(nullptr, errorNumber); MOZ_ASSERT(efs->argCount == args.length() - 1); @@ -2154,16 +2143,69 @@ intrinsic_PromiseResolve(JSContext* cx, JSObject* promise = js::PromiseResolve(cx, constructor, args[1]); if (!promise) return false; args.rval().setObject(*promise); return true; } +static bool +intrinsic_CopyDataPropertiesOrGetOwnKeys(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 3); + MOZ_ASSERT(args[0].isObject()); + MOZ_ASSERT(args[1].isObject()); + MOZ_ASSERT(args[2].isObjectOrNull()); + + RootedObject target(cx, &args[0].toObject()); + RootedObject from(cx, &args[1].toObject()); + RootedObject excludedItems(cx, args[2].toObjectOrNull()); + + if (from->isNative() && + target->is<PlainObject>() && + (!excludedItems || excludedItems->is<PlainObject>())) + { + bool optimized; + if (!CopyDataPropertiesNative(cx, target.as<PlainObject>(), from.as<NativeObject>(), + (excludedItems ? excludedItems.as<PlainObject>() : nullptr), + &optimized)) + { + return false; + } + + if (optimized) { + args.rval().setNull(); + return true; + } + } + + if (from->is<UnboxedPlainObject>() && + target->is<PlainObject>() && + (!excludedItems || excludedItems->is<PlainObject>())) + { + bool optimized; + if (!CopyDataPropertiesNative(cx, target.as<PlainObject>(), from.as<UnboxedPlainObject>(), + (excludedItems ? excludedItems.as<PlainObject>() : nullptr), + &optimized)) + { + return false; + } + + if (optimized) { + args.rval().setNull(); + return true; + } + } + + return GetOwnPropertyKeys(cx, from, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, + args.rval()); +} + // The self-hosting global isn't initialized with the normal set of builtins. // Instead, individual C++-implemented functions that're required by // self-hosted code are defined as global functions. Accessing these // functions via a content compartment's builtins would be unsafe, because // content script might have changed the builtins' prototypes' members. // Installing the whole set of builtins in the self-hosting compartment, OTOH, // would be wasteful: it increases memory usage and initialization time for // self-hosting compartment. @@ -2200,16 +2242,17 @@ static const JSFunctionSpec intrinsic_fu JS_INLINABLE_FN("std_Object_create", obj_create, 2, 0, ObjectCreate), JS_FN("std_Object_propertyIsEnumerable", obj_propertyIsEnumerable, 1,0), JS_FN("std_Object_getOwnPropertyNames", obj_getOwnPropertyNames, 1,0), JS_FN("std_Object_toString", obj_toString, 0,0), JS_INLINABLE_FN("std_Reflect_getPrototypeOf", Reflect_getPrototypeOf, 1,0, ReflectGetPrototypeOf), JS_FN("std_Reflect_isExtensible", Reflect_isExtensible, 1,0), + JS_FN("std_Reflect_ownKeys", Reflect_ownKeys, 1,0), JS_FN("std_Set_has", SetObject::has, 1,0), JS_FN("std_Set_iterator", SetObject::values, 0,0), JS_INLINABLE_FN("std_String_fromCharCode", str_fromCharCode, 1,0, StringFromCharCode), JS_INLINABLE_FN("std_String_fromCodePoint", str_fromCodePoint, 1,0, StringFromCodePoint), JS_INLINABLE_FN("std_String_charCodeAt", str_charCodeAt, 1,0, StringCharCodeAt), JS_FN("std_String_includes", str_includes, 1,0), @@ -2267,28 +2310,28 @@ static const JSFunctionSpec intrinsic_fu JS_FN("ThrowRangeError", intrinsic_ThrowRangeError, 4,0), JS_FN("ThrowTypeError", intrinsic_ThrowTypeError, 4,0), JS_FN("ThrowSyntaxError", intrinsic_ThrowSyntaxError, 4,0), JS_FN("ThrowInternalError", intrinsic_ThrowInternalError, 4,0), JS_FN("GetErrorMessage", intrinsic_GetErrorMessage, 1,0), JS_FN("CreateModuleSyntaxError", intrinsic_CreateModuleSyntaxError, 4,0), JS_FN("AssertionFailed", intrinsic_AssertionFailed, 1,0), JS_FN("DumpMessage", intrinsic_DumpMessage, 1,0), - JS_FN("OwnPropertyKeys", intrinsic_OwnPropertyKeys, 1,0), JS_FN("MakeDefaultConstructor", intrinsic_MakeDefaultConstructor, 2,0), JS_FN("_ConstructorForTypedArray", intrinsic_ConstructorForTypedArray, 1,0), JS_FN("_NameForTypedArray", intrinsic_NameForTypedArray, 1,0), JS_FN("DecompileArg", intrinsic_DecompileArg, 2,0), JS_INLINABLE_FN("_FinishBoundFunctionInit", intrinsic_FinishBoundFunctionInit, 3,0, IntrinsicFinishBoundFunctionInit), JS_FN("RuntimeDefaultLocale", intrinsic_RuntimeDefaultLocale, 0,0), JS_FN("IsRuntimeDefaultLocale", intrinsic_IsRuntimeDefaultLocale, 1,0), JS_FN("AddContentTelemetry", intrinsic_AddContentTelemetry, 2,0), JS_FN("_DefineDataProperty", intrinsic_DefineDataProperty, 4,0), JS_FN("_DefineProperty", intrinsic_DefineProperty, 6,0), + JS_FN("CopyDataPropertiesOrGetOwnKeys", intrinsic_CopyDataPropertiesOrGetOwnKeys, 3,0), JS_INLINABLE_FN("_IsConstructing", intrinsic_IsConstructing, 0,0, IntrinsicIsConstructing), JS_INLINABLE_FN("SubstringKernel", intrinsic_SubstringKernel, 3,0, IntrinsicSubstringKernel), JS_INLINABLE_FN("ObjectHasPrototype", intrinsic_ObjectHasPrototype, 2,0, IntrinsicObjectHasPrototype), JS_INLINABLE_FN("UnsafeSetReservedSlot", intrinsic_UnsafeSetReservedSlot, 3,0,
--- a/js/src/vm/StructuredClone.cpp +++ b/js/src/vm/StructuredClone.cpp @@ -1097,31 +1097,50 @@ JSStructuredCloneWriter::parseTransferab if (!JS_GetElement(cx, array, i, &v)) return false; if (!v.isObject()) return reportDataCloneError(JS_SCERR_TRANSFERABLE); tObj = &v.toObject(); + RootedObject unwrappedObj(cx, CheckedUnwrap(tObj)); + if (!unwrappedObj) { + ReportAccessDenied(cx); + return false; + } + // Shared memory cannot be transferred because it is not possible (nor // desirable) to detach the memory in agents that already hold a // reference to it. - if (tObj->is<SharedArrayBufferObject>()) + if (unwrappedObj->is<SharedArrayBufferObject>()) return reportDataCloneError(JS_SCERR_SHMEM_TRANSFERABLE); - if (tObj->is<WasmMemoryObject>() && tObj->as<WasmMemoryObject>().isShared()) - return reportDataCloneError(JS_SCERR_SHMEM_TRANSFERABLE); + else if (unwrappedObj->is<WasmMemoryObject>()) { + if (unwrappedObj->as<WasmMemoryObject>().isShared()) + return reportDataCloneError(JS_SCERR_SHMEM_TRANSFERABLE); + } // External array buffers may be able to be transferred in the future, // but that is not currently implemented. - if (tObj->is<ArrayBufferObject>() && tObj->as<ArrayBufferObject>().isExternal()) - return reportDataCloneError(JS_SCERR_TRANSFERABLE); + else if (unwrappedObj->is<ArrayBufferObject>()) { + if (unwrappedObj->as<ArrayBufferObject>().isExternal()) + return reportDataCloneError(JS_SCERR_TRANSFERABLE); + } + + else { + if (!callbacks || !callbacks->canTransfer) + return reportDataCloneError(JS_SCERR_TRANSFERABLE); + + JSAutoCompartment ac(cx, unwrappedObj); + if (!callbacks->canTransfer(cx, unwrappedObj, closure)) + return false; + } // No duplicates allowed auto p = transferableObjects.lookupForAdd(tObj); if (p) return reportDataCloneError(JS_SCERR_DUP_TRANSFERABLE); if (!transferableObjects.add(p, tObj)) return false; @@ -1681,16 +1700,22 @@ JSStructuredCloneWriter::transferOwnersh if (cls == ESClass::ArrayBuffer) { tag = SCTAG_TRANSFER_MAP_ARRAY_BUFFER; // The current setup of the array buffer inheritance hierarchy doesn't // lend itself well to generic manipulation via proxies. Rooted<ArrayBufferObject*> arrayBuffer(cx, &CheckedUnwrap(obj)->as<ArrayBufferObject>()); JSAutoCompartment ac(cx, arrayBuffer); + + if (arrayBuffer->isDetached()) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED); + return false; + } + size_t nbytes = arrayBuffer->byteLength(); if (arrayBuffer->isWasm() || arrayBuffer->isPreparedForAsmJS()) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_NO_TRANSFER); return false; } if (scope == JS::StructuredCloneScope::DifferentProcess) {
--- a/js/src/wasm/WasmBaselineCompile.cpp +++ b/js/src/wasm/WasmBaselineCompile.cpp @@ -7987,38 +7987,41 @@ BaseCompiler::emitSetGlobal() if (!iter_.readSetGlobal(&id, &unused_value)) return false; if (deadCode_) return true; const GlobalDesc& global = env_.globals[id]; - ScratchI32 tmp(*this); switch (global.type()) { case ValType::I32: { RegI32 rv = popI32(); + ScratchI32 tmp(*this); masm.store32(rv, addressOfGlobalVar(global, tmp)); freeI32(rv); break; } case ValType::I64: { RegI64 rv = popI64(); + ScratchI32 tmp(*this); masm.store64(rv, addressOfGlobalVar(global, tmp)); freeI64(rv); break; } case ValType::F32: { RegF32 rv = popF32(); + ScratchI32 tmp(*this); masm.storeFloat32(rv, addressOfGlobalVar(global, tmp)); freeF32(rv); break; } case ValType::F64: { RegF64 rv = popF64(); + ScratchI32 tmp(*this); masm.storeDouble(rv, addressOfGlobalVar(global, tmp)); freeF64(rv); break; } default: MOZ_CRASH("Global variable type"); break; }
--- a/layout/style/nsCSSAnonBoxList.h +++ b/layout/style/nsCSSAnonBoxList.h @@ -18,17 +18,18 @@ * The first argument to * CSS_ANON_BOX/CSS_WRAPPER_ANON_BOX/CSS_NON_INHERITING_ANON_BOX is the C++ * identifier of the atom. * * The second argument is the string value of the atom. * * CSS_NON_INHERITING_ANON_BOX is used for anon boxes that never inherit style * from anything. This means all their property values are the initial values - * of those properties. + * of those properties. These ones must come first! Code relies on this. + * If this macro is not defined, it will default to CSS_ANON_BOX. * * CSS_WRAPPER_ANON_BOX is used for anon boxes that are used as wrappers around * other frames during frametree fixup (e.g. table anonymous boxes, ruby * anonymous boxes, anonymous flex item blocks, etc). These are also inheriting * anon boxes, just like CSS_ANON_BOX. If this macro is not defined, it will * default to CSS_ANON_BOX. */ @@ -46,55 +47,67 @@ #ifndef CSS_WRAPPER_ANON_BOX # ifdef DEFINED_CSS_WRAPPER_ANON_BOX # error "Recursive includes of nsCSSAnonBoxList.h?" # endif /* DEFINED_CSS_WRAPPER_ANON_BOX */ # define CSS_WRAPPER_ANON_BOX(name_, value_) CSS_ANON_BOX(name_, value_) # define DEFINED_CSS_WRAPPER_ANON_BOX #endif /* CSS_WRAPPER_ANON_BOX */ +//--------------------------------------------------------------------------- +// Non-inheriting ones, which must come first +//--------------------------------------------------------------------------- + +// Placeholder frames for out of flows. Note that :-moz-placeholder is used for +// the pseudo-element that represents the placeholder text in <input +// placeholder="foo">, so we need a different string here. +CSS_NON_INHERITING_ANON_BOX(oofPlaceholder, ":-moz-oof-placeholder") + +// Framesets +CSS_NON_INHERITING_ANON_BOX(horizontalFramesetBorder, ":-moz-hframeset-border") +CSS_NON_INHERITING_ANON_BOX(verticalFramesetBorder, ":-moz-vframeset-border") + +CSS_NON_INHERITING_ANON_BOX(framesetBlank, ":-moz-frameset-blank") + +CSS_NON_INHERITING_ANON_BOX(tableColGroup, ":-moz-table-column-group") +CSS_NON_INHERITING_ANON_BOX(tableCol, ":-moz-table-column") + +CSS_NON_INHERITING_ANON_BOX(pageBreak, ":-moz-pagebreak") + +//--------------------------------------------------------------------------- +// Other ones +//--------------------------------------------------------------------------- + // ::-moz-text, ::-moz-oof-placeholder, and ::-moz-first-letter-continuation are // non-elements which no rule will match. CSS_ANON_BOX(mozText, ":-moz-text") -// placeholder frames for out of flows. Note that :-moz-placeholder is used for -// the pseudo-element that represents the placeholder text in <input -// placeholder="foo">, so we need a different string here. -CSS_NON_INHERITING_ANON_BOX(oofPlaceholder, ":-moz-oof-placeholder") // nsFirstLetterFrames for content outside the ::first-letter. CSS_ANON_BOX(firstLetterContinuation, ":-moz-first-letter-continuation") CSS_ANON_BOX(mozBlockInsideInlineWrapper, ":-moz-block-inside-inline-wrapper") CSS_WRAPPER_ANON_BOX(mozMathMLAnonymousBlock, ":-moz-mathml-anonymous-block") CSS_ANON_BOX(mozXULAnonymousBlock, ":-moz-xul-anonymous-block") -// Framesets -CSS_NON_INHERITING_ANON_BOX(horizontalFramesetBorder, ":-moz-hframeset-border") -CSS_NON_INHERITING_ANON_BOX(verticalFramesetBorder, ":-moz-vframeset-border") - CSS_ANON_BOX(mozLineFrame, ":-moz-line-frame") CSS_ANON_BOX(buttonContent, ":-moz-button-content") CSS_ANON_BOX(cellContent, ":-moz-cell-content") CSS_ANON_BOX(dropDownList, ":-moz-dropdown-list") CSS_ANON_BOX(fieldsetContent, ":-moz-fieldset-content") -CSS_NON_INHERITING_ANON_BOX(framesetBlank, ":-moz-frameset-blank") CSS_ANON_BOX(mozDisplayComboboxControlFrame, ":-moz-display-comboboxcontrol-frame") CSS_ANON_BOX(htmlCanvasContent, ":-moz-html-canvas-content") CSS_WRAPPER_ANON_BOX(inlineTable, ":-moz-inline-table") CSS_WRAPPER_ANON_BOX(table, ":-moz-table") CSS_WRAPPER_ANON_BOX(tableCell, ":-moz-table-cell") -CSS_NON_INHERITING_ANON_BOX(tableColGroup, ":-moz-table-column-group") -CSS_NON_INHERITING_ANON_BOX(tableCol, ":-moz-table-column") CSS_ANON_BOX(tableWrapper, ":-moz-table-wrapper") CSS_WRAPPER_ANON_BOX(tableRowGroup, ":-moz-table-row-group") CSS_WRAPPER_ANON_BOX(tableRow, ":-moz-table-row") CSS_ANON_BOX(canvas, ":-moz-canvas") -CSS_NON_INHERITING_ANON_BOX(pageBreak, ":-moz-pagebreak") CSS_ANON_BOX(page, ":-moz-page") CSS_ANON_BOX(pageContent, ":-moz-pagecontent") CSS_ANON_BOX(pageSequence, ":-moz-page-sequence") CSS_ANON_BOX(scrolledContent, ":-moz-scrolled-content") CSS_ANON_BOX(scrolledCanvas, ":-moz-scrolled-canvas") CSS_ANON_BOX(scrolledPageSequence, ":-moz-scrolled-page-sequence") CSS_ANON_BOX(columnContent, ":-moz-column-content") CSS_ANON_BOX(viewport, ":-moz-viewport")
--- a/layout/style/nsCSSAnonBoxes.cpp +++ b/layout/style/nsCSSAnonBoxes.cpp @@ -34,31 +34,22 @@ MOZ_POP_DISABLE_INTEGRAL_CONSTANT_OVERFL } // namespace mozilla #define CSS_ANON_BOX(name_, value_) \ NS_STATIC_ATOM_SUBCLASS_DEFN_PTR(nsICSSAnonBoxPseudo, nsCSSAnonBoxes, name_) #include "nsCSSAnonBoxList.h" #undef CSS_ANON_BOX static const nsStaticAtomSetup sCSSAnonBoxAtomSetup[] = { - // Put the non-inheriting anon boxes first, so we can index into them easily. - #define CSS_ANON_BOX(name_, value_) /* nothing */ - #define CSS_NON_INHERITING_ANON_BOX(name_, value_) \ + // Non-inheriting boxes must come first in nsCSSAnonBoxList.h so that + // `NonInheriting` values can index into this array and other similar arrays. + #define CSS_ANON_BOX(name_, value_) \ NS_STATIC_ATOM_SUBCLASS_SETUP( \ mozilla::detail::gCSSAnonBoxAtoms, nsCSSAnonBoxes, name_) #include "nsCSSAnonBoxList.h" - #undef CSS_NON_INHERITING_ANON_BOX - #undef CSS_ANON_BOX - - #define CSS_ANON_BOX(name_, value_) \ - NS_STATIC_ATOM_SUBCLASS_SETUP( \ - mozilla::detail::gCSSAnonBoxAtoms, nsCSSAnonBoxes, name_) - #define CSS_NON_INHERITING_ANON_BOX(name_, value_) /* nothing */ - #include "nsCSSAnonBoxList.h" - #undef CSS_NON_INHERITING_ANON_BOX #undef CSS_ANON_BOX }; void nsCSSAnonBoxes::RegisterStaticAtoms() { NS_RegisterStaticAtoms(sCSSAnonBoxAtomSetup); } @@ -81,15 +72,8 @@ nsCSSAnonBoxes::IsTreePseudoElement(nsAt nsCSSAnonBoxes::NonInheritingTypeForPseudoTag(nsAtom* aPseudo) { MOZ_ASSERT(IsNonInheritingAnonBox(aPseudo)); Maybe<uint32_t> index = nsStaticAtomUtils::Lookup(aPseudo, sCSSAnonBoxAtomSetup); MOZ_RELEASE_ASSERT(index.isSome()); return static_cast<NonInheriting>(*index); } - -/* static */ nsAtom* -nsCSSAnonBoxes::GetNonInheritingPseudoAtom(NonInheriting aBoxType) -{ - MOZ_ASSERT(aBoxType < NonInheriting::_Count); - return *sCSSAnonBoxAtomSetup[static_cast<NonInheritingBase>(aBoxType)].mAtomp; -}
--- a/layout/style/nsCSSAnonBoxes.h +++ b/layout/style/nsCSSAnonBoxes.h @@ -113,15 +113,11 @@ public: #undef CSS_WRAPPER_ANON_BOX #undef CSS_ANON_BOX false); } // Get the NonInheriting type for a given pseudo tag. The pseudo tag must // test true for IsNonInheritingAnonBox. static NonInheriting NonInheritingTypeForPseudoTag(nsAtom* aPseudo); - - // Get the atom for a given non-inheriting anon box type. aBoxType must be < - // NonInheriting::_Count. - static nsAtom* GetNonInheritingPseudoAtom(NonInheriting aBoxType); }; #endif /* nsCSSAnonBoxes_h___ */
--- a/mobile/android/installer/allowed-dupes.mn +++ b/mobile/android/installer/allowed-dupes.mn @@ -43,10 +43,9 @@ modules/devtools/shared/apps/Simulator.j res/table-remove-column-active.gif res/table-remove-column-hover.gif res/table-remove-column.gif res/table-remove-row-active.gif res/table-remove-row-hover.gif res/table-remove-row.gif res/multilocale.txt modules/commonjs/index.js -chrome/toolkit/content/global/XPCNativeWrapper.js update.locale
--- a/security/sandbox/chromium/base/win/scoped_handle.cc +++ b/security/sandbox/chromium/base/win/scoped_handle.cc @@ -108,17 +108,19 @@ ActiveVerifier* ActiveVerifier::Get() { if (!g_active_verifier) ActiveVerifier::InstallVerifier(); return g_active_verifier; } bool CloseHandleWrapper(HANDLE handle) { if (!::CloseHandle(handle)) - CHECK(false); // CloseHandle failed. + // Making this DCHECK as we are hitting this frequently, looks like we are + // closing handles twice somewhere. See bug 1449480. + DCHECK(false); // CloseHandle failed. return true; } // Assigns the g_active_verifier global within the GetLock() lock. // If |existing_verifier| is non-null then |enabled| is ignored. void ThreadSafeAssignOrCreateActiveVerifier(ActiveVerifier* existing_verifier, bool enabled) { AutoNativeLock lock(*GetLock());
--- a/testing/web-platform/meta/preload/link-header-preload-delay-onload.html.ini +++ b/testing/web-platform/meta/preload/link-header-preload-delay-onload.html.ini @@ -1,3 +1,4 @@ [link-header-preload-delay-onload.html] disabled: if os == "linux": https://bugzilla.mozilla.org/show_bug.cgi?id=1437081 + if os == "mac": https://bugzilla.mozilla.org/show_bug.cgi?id=1437081
--- a/testing/web-platform/tests/webmessaging/message-channels/close.html +++ b/testing/web-platform/tests/webmessaging/message-channels/close.html @@ -33,28 +33,16 @@ async_test(t => { e.ports[0].postMessage('TESTMSG'); setTimeout(t.step_func_done(), time_to_wait_for_messages); }); c2.port2.postMessage('TEST', [c.port2]); }, 'Message sent to closed port from transferred port should not arrive.'); async_test(t => { const c = new MessageChannel(); - c.port1.onmessage = t.unreached_func('Should not have delivered message'); - c.port2.close(); - const c2 = new MessageChannel(); - c2.port1.onmessage = t.step_func(e => { - e.ports[0].postMessage('TESTMSG'); - setTimeout(t.step_func_done(), time_to_wait_for_messages); - }); - c2.port2.postMessage('TEST', [c.port2]); - }, 'Message sent from transferred closed port should not arrive.'); - -async_test(t => { - const c = new MessageChannel(); let isClosed = false; c.port1.onmessage = t.step_func_done(e => { assert_true(isClosed); assert_equals(e.data, 'TEST'); }); c.port2.postMessage('TEST'); c.port2.close(); isClosed = true;
deleted file mode 100644 --- a/toolkit/content/XPCNativeWrapper.js +++ /dev/null @@ -1,7 +0,0 @@ -/* 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/. */ - -/* - * Moved to C++ implementation in XPConnect. See bug 281988. - */
--- a/toolkit/content/jar.mn +++ b/toolkit/content/jar.mn @@ -1,12 +1,11 @@ toolkit.jar: % content global %content/global/ contentaccessible=yes * content/global/license.html - content/global/XPCNativeWrapper.js content/global/minimal-xul.css * content/global/xul.css content/global/components.css content/global/textbox.css content/global/menulist.css content/global/autocomplete.css content/global/aboutAbout.js content/global/aboutAbout.xhtml
--- a/toolkit/content/tests/reftests/bug-442419-progressmeter-max-ref.xul +++ b/toolkit/content/tests/reftests/bug-442419-progressmeter-max-ref.xul @@ -1,7 +1,7 @@ <?xml version="1.0"?> <?xml-stylesheet href="chrome://global/skin/global.css" type="text/css"?> <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> - <progressmeter value="50"/> <!-- default is max = 100 --> + <progressmeter value="100"/> <!-- default is max = 100 --> </window>
--- a/toolkit/content/tests/reftests/bug-442419-progressmeter-max.xul +++ b/toolkit/content/tests/reftests/bug-442419-progressmeter-max.xul @@ -1,7 +1,7 @@ <?xml version="1.0"?> <?xml-stylesheet href="chrome://global/skin/global.css" type="text/css"?> <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> - <progressmeter max="198" value="99"/> <!-- 50% --> + <progressmeter max="198" value="198"/> <!-- 100% --> </window>
--- a/toolkit/content/tests/reftests/reftest.list +++ b/toolkit/content/tests/reftests/reftest.list @@ -1,6 +1,6 @@ -fails-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)&&!isDebugBuild) random-if(cocoaWidget||/^Windows\x20NT\x206\.2/.test(http.oscpu)||(/^Windows\x20NT\x2010\.0/.test(http.oscpu))) == bug-442419-progressmeter-max.xul bug-442419-progressmeter-max-ref.xul # fails most of the time on Mac because progress meter animates; Bug 1439988 +== bug-442419-progressmeter-max.xul bug-442419-progressmeter-max-ref.xul != textbox-multiline-default-value.xul textbox-multiline-empty.xul == videocontrols-dynamically-add-cc.html videocontrols-dynamically-add-cc-ref.html == audio-with-bogus-url.html audio-with-bogus-url-ref.html == audio-dynamically-change-small-width.html audio-dynamically-change-small-width-ref.html == audio-with-padding.html audio-with-padding-ref.html
--- a/xpcom/ds/nsBaseHashtable.h +++ b/xpcom/ds/nsBaseHashtable.h @@ -260,65 +260,75 @@ public: */ MOZ_MUST_USE LookupResult Lookup(KeyType aKey) { return LookupResult(this->GetEntry(aKey), *this); } struct EntryPtr { private: - EntryType& mEntry; + EntryType* mEntry; bool mExistingEntry; + nsBaseHashtable& mTable; // For debugging purposes #ifdef DEBUG - nsBaseHashtable& mTable; uint32_t mTableGeneration; bool mDidInitNewEntry; #endif public: EntryPtr(nsBaseHashtable& aTable, EntryType* aEntry, bool aExistingEntry) - : mEntry(*aEntry) + : mEntry(aEntry) , mExistingEntry(aExistingEntry) + , mTable(aTable) #ifdef DEBUG - , mTable(aTable) , mTableGeneration(aTable.GetGeneration()) , mDidInitNewEntry(false) #endif {} ~EntryPtr() { - MOZ_ASSERT(mExistingEntry || mDidInitNewEntry, - "Forgot to call OrInsert() on a new entry"); + MOZ_ASSERT(mExistingEntry || mDidInitNewEntry || !mEntry, + "Forgot to call OrInsert() or OrRemove() on a new entry"); } // Is there something stored in the table already? explicit operator bool() const { MOZ_ASSERT(mTableGeneration == mTable.GetGeneration()); return mExistingEntry; } template <class F> UserDataType OrInsert(F func) { MOZ_ASSERT(mTableGeneration == mTable.GetGeneration()); + MOZ_ASSERT(mEntry); if (!mExistingEntry) { - mEntry.mData = func(); + mEntry->mData = func(); #ifdef DEBUG mDidInitNewEntry = true; #endif } - return mEntry.mData; + return mEntry->mData; + } + + void OrRemove() + { + MOZ_ASSERT(mTableGeneration == mTable.GetGeneration()); + MOZ_ASSERT(mEntry); + mTable.RemoveEntry(mEntry); + mEntry = nullptr; } MOZ_MUST_USE DataType& Data() { MOZ_ASSERT(mTableGeneration == mTable.GetGeneration()); - return mEntry.mData; + MOZ_ASSERT(mEntry); + return mEntry->mData; } }; /** * Looks up aKey in the hashtable and returns an object that allows you to * insert a new entry into the hashtable for that key if an existing entry * isn't found for it. *
--- a/xpcom/tests/gtest/TestHashtables.cpp +++ b/xpcom/tests/gtest/TestHashtables.cpp @@ -469,16 +469,38 @@ TEST(Hashtables, DataHashtable_LookupFor // Lookup().Remove() should remove all entries. for (auto& entity : gEntities) { if (auto entry = UniToEntity.Lookup(entity.mUnicode)) { entry.Remove(); } } ASSERT_TRUE(0 == UniToEntity.Count()); + + // Remove newly added entries via OrRemove. + for (auto& entity : gEntities) { + auto entry = UniToEntity.LookupForAdd(entity.mUnicode); + ASSERT_FALSE(entry); + entry.OrRemove(); + } + ASSERT_TRUE(0 == UniToEntity.Count()); + + // Remove existing entries via OrRemove. + for (auto& entity : gEntities) { + auto entry = UniToEntity.LookupForAdd(entity.mUnicode); + const char* val = entry.OrInsert([&entity] () { return entity.mStr; }); + ASSERT_FALSE(entry); + ASSERT_TRUE(val == entity.mStr); + ASSERT_TRUE(entry.Data() == entity.mStr); + + auto entry2 = UniToEntity.LookupForAdd(entity.mUnicode); + ASSERT_TRUE(entry2); + entry2.OrRemove(); + } + ASSERT_TRUE(0 == UniToEntity.Count()); } TEST(Hashtables, ClassHashtable_LookupForAdd) { // check a class-hashtable LookupForAdd with null values nsClassHashtable<nsCStringHashKey,TestUniChar> EntToUniClass(ENTITY_COUNT); for (auto& entity : gEntities) { @@ -514,9 +536,31 @@ TEST(Hashtables, ClassHashtable_LookupFo // Lookup().Remove() should remove all entries. for (auto& entity : gEntities) { if (auto entry = EntToUniClass.Lookup(nsDependentCString(entity.mStr))) { entry.Remove(); } } ASSERT_TRUE(0 == EntToUniClass.Count()); + + // Remove newly added entries via OrRemove. + for (auto& entity : gEntities) { + auto entry = EntToUniClass.LookupForAdd(nsDependentCString(entity.mStr)); + ASSERT_FALSE(entry); + entry.OrRemove(); + } + ASSERT_TRUE(0 == EntToUniClass.Count()); + + // Remove existing entries via OrRemove. + for (auto& entity : gEntities) { + auto entry = EntToUniClass.LookupForAdd(nsDependentCString(entity.mStr)); + const TestUniChar* val = entry.OrInsert([] () { return nullptr; }); + ASSERT_FALSE(entry); + ASSERT_TRUE(val == nullptr); + ASSERT_TRUE(entry.Data() == nullptr); + + auto entry2 = EntToUniClass.LookupForAdd(nsDependentCString(entity.mStr)); + ASSERT_TRUE(entry2); + entry2.OrRemove(); + } + ASSERT_TRUE(0 == EntToUniClass.Count()); }