diff --git a/browser/components/newtab/RemoteAboutNewTab.jsm b/browser/components/newtab/RemoteAboutNewTab.jsm index 1f335511d2ad..4ef509633eaf 100644 --- a/browser/components/newtab/RemoteAboutNewTab.jsm +++ b/browser/components/newtab/RemoteAboutNewTab.jsm @@ -43,6 +43,7 @@ let RemoteAboutNewTab = { * Initialize the RemotePageManager and add all message listeners for this page */ init: function() { + RemoteNewTabLocation.init(); this.pageListener = new RemotePages("about:remote-newtab"); this.pageListener.addMessageListener("NewTab:InitializeGrid", this.initializeGrid.bind(this)); this.pageListener.addMessageListener("NewTab:UpdateGrid", this.updateGrid.bind(this)); @@ -292,6 +293,7 @@ let RemoteAboutNewTab = { Ci.nsISupportsWeakReference]), uninit: function() { + RemoteNewTabLocation.uninit(); this._removeObservers(); this.pageListener.destroy(); this.pageListener = null; diff --git a/browser/components/newtab/RemoteNewTabLocation.jsm b/browser/components/newtab/RemoteNewTabLocation.jsm index 9ec171987721..1c958a275f83 100644 --- a/browser/components/newtab/RemoteNewTabLocation.jsm +++ b/browser/components/newtab/RemoteNewTabLocation.jsm @@ -1,17 +1,49 @@ -/* globals Services */ +/* globals Services, UpdateUtils, XPCOMUtils, URL, NewTabPrefsProvider, Locale */ +/* exported RemoteNewTabLocation */ "use strict"; this.EXPORTED_SYMBOLS = ["RemoteNewTabLocation"]; -Components.utils.import("resource://gre/modules/Services.jsm"); -Components.utils.importGlobalProperties(["URL"]); +const {utils: Cu} = Components; +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.importGlobalProperties(["URL"]); -// TODO: will get dynamically set in bug 1210478 -const DEFAULT_PAGE_LOCATION = "https://newtab.cdn.mozilla.net/v0/nightly/en-US/index.html"; +XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils", + "resource://gre/modules/UpdateUtils.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "NewTabPrefsProvider", + "resource:///modules/NewTabPrefsProvider.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "Locale", + "resource://gre/modules/Locale.jsm"); -this.RemoteNewTabLocation = { - _url: new URL(DEFAULT_PAGE_LOCATION), +// The preference that tells whether to match the OS locale +const PREF_MATCH_OS_LOCALE = "intl.locale.matchOS"; + +// The preference that tells what locale the user selected +const PREF_SELECTED_LOCALE = "general.useragent.locale"; + +const DEFAULT_PAGE_LOCATION = "https://newtab.cdn.mozilla.net/" + + "v%VERSION%/%CHANNEL%/%LOCALE%/index.html"; + +const VALID_CHANNELS = new Set(["esr", "release", "beta", "aurora", "nightly"]); + +const NEWTAB_VERSION = "0"; + +let RemoteNewTabLocation = { + /* + * Generate a default url based on locale and update channel + */ + _generateDefaultURL() { + let releaseName = this._releaseFromUpdateChannel(UpdateUtils.UpdateChannel); + let uri = DEFAULT_PAGE_LOCATION + .replace("%VERSION%", this.version) + .replace("%LOCALE%", Locale.getLocale()) + .replace("%CHANNEL%", releaseName); + return new URL(uri); + }, + + _url: null, _overridden: false, get href() { @@ -26,17 +58,84 @@ this.RemoteNewTabLocation = { return this._overridden; }, - override: function(newURL) { - this._url = new URL(newURL); - this._overridden = true; - Services.obs.notifyObservers(null, "remote-new-tab-location-changed", - this._url.href); + get version() { + return NEWTAB_VERSION; }, - reset: function() { - this._url = new URL(DEFAULT_PAGE_LOCATION); + get channels() { + return VALID_CHANNELS; + }, + + /** + * Returns the release name from an Update Channel name + * + * @return {String} a release name based on the update channel. Defaults to nightly + */ + _releaseFromUpdateChannel(channel) { + let result = "nightly"; + if (VALID_CHANNELS.has(channel)) { + result = channel; + } + return result; + }, + + /* + * Updates the location when the page is not overriden. + * Useful when there is a pref change + */ + _updateMaybe() { + if (!this.overridden) { + let url = this._generateDefaultURL(); + if (url.href !== this._url.href) { + this._url = url; + Services.obs.notifyObservers(null, "remote-new-tab-location-changed", + this._url.href); + } + } + }, + + /* + * Override the Remote newtab page location. + */ + override(newURL) { + let url = new URL(newURL); + if (url.href !== this._url.href) { + this._overridden = true; + this._url = url; + Services.obs.notifyObservers(null, "remote-new-tab-location-changed", + this._url.href); + } + }, + + /* + * Reset the newtab page location to the default value + */ + reset() { + let url = this._generateDefaultURL(); + if (url.href !== this._url.href) { + this._url = url; + this._overridden = false; + Services.obs.notifyObservers(null, "remote-new-tab-location-changed", + this._url.href); + } + }, + + init() { + NewTabPrefsProvider.prefs.on( + PREF_SELECTED_LOCALE, + this._updateMaybe.bind(this)); + + NewTabPrefsProvider.prefs.on( + PREF_MATCH_OS_LOCALE, + this._updateMaybe.bind(this)); + + this._url = this._generateDefaultURL(); + }, + + uninit() { + this._url = null; this._overridden = false; - Services.obs.notifyObservers(null, "remote-new-tab-location-changed", - this._url.href); + NewTabPrefsProvider.prefs.off(PREF_SELECTED_LOCALE, this._updateMaybe); + NewTabPrefsProvider.prefs.off(PREF_MATCH_OS_LOCALE, this._updateMaybe); } }; diff --git a/browser/components/newtab/tests/xpcshell/test_RemoteNewTabLocation.js b/browser/components/newtab/tests/xpcshell/test_RemoteNewTabLocation.js index e280b03426a5..ad1b01de2e7d 100644 --- a/browser/components/newtab/tests/xpcshell/test_RemoteNewTabLocation.js +++ b/browser/components/newtab/tests/xpcshell/test_RemoteNewTabLocation.js @@ -1,39 +1,140 @@ -/* globals ok, equal, RemoteNewTabLocation, Services */ +/* globals ok, equal, RemoteNewTabLocation, NewTabPrefsProvider, Services, Preferences */ +/* jscs:disable requireCamelCaseOrUpperCaseIdentifiers */ "use strict"; Components.utils.import("resource:///modules/RemoteNewTabLocation.jsm"); +Components.utils.import("resource:///modules/NewTabPrefsProvider.jsm"); Components.utils.import("resource://gre/modules/Services.jsm"); +Components.utils.import("resource://gre/modules/Preferences.jsm"); Components.utils.importGlobalProperties(["URL"]); -add_task(function* () { - var notificationPromise; - let defaultHref = RemoteNewTabLocation.href; +RemoteNewTabLocation.init(); +const DEFAULT_HREF = RemoteNewTabLocation.href; +RemoteNewTabLocation.uninit(); +add_task(function* test_defaults() { + RemoteNewTabLocation.init(); ok(RemoteNewTabLocation.href, "Default location has an href"); ok(RemoteNewTabLocation.origin, "Default location has an origin"); ok(!RemoteNewTabLocation.overridden, "Default location is not overridden"); + RemoteNewTabLocation.uninit(); +}); +/** + * Tests the overriding of the default URL + */ +add_task(function* test_overrides() { + RemoteNewTabLocation.init(); let testURL = new URL("https://example.com/"); + let notificationPromise; - notificationPromise = changeNotificationPromise(testURL.href); + notificationPromise = nextChangeNotificationPromise( + testURL.href, "Remote Location should change"); RemoteNewTabLocation.override(testURL.href); yield notificationPromise; ok(RemoteNewTabLocation.overridden, "Remote location should be overridden"); - equal(RemoteNewTabLocation.href, testURL.href, "Remote href should be the custom URL"); - equal(RemoteNewTabLocation.origin, testURL.origin, "Remote origin should be the custom URL"); + equal(RemoteNewTabLocation.href, testURL.href, + "Remote href should be the custom URL"); + equal(RemoteNewTabLocation.origin, testURL.origin, + "Remote origin should be the custom URL"); - notificationPromise = changeNotificationPromise(defaultHref); + notificationPromise = nextChangeNotificationPromise( + DEFAULT_HREF, "Remote href should be reset"); RemoteNewTabLocation.reset(); yield notificationPromise; ok(!RemoteNewTabLocation.overridden, "Newtab URL should not be overridden"); - equal(RemoteNewTabLocation.href, defaultHref, "Remote href should be reset"); + RemoteNewTabLocation.uninit(); }); -function changeNotificationPromise(aNewURL) { +/** + * Tests how RemoteNewTabLocation responds to updates to prefs + */ +add_task(function* test_updates() { + RemoteNewTabLocation.init(); + let notificationPromise; + let expectedHref = "https://newtab.cdn.mozilla.net" + + `/v${RemoteNewTabLocation.version}` + + "/nightly" + + "/en-GB" + + "/index.html"; + Preferences.set("intl.locale.matchOS", true); + Preferences.set("general.useragent.locale", "en-GB"); + NewTabPrefsProvider.prefs.init(); + + // test update checks for prefs + notificationPromise = nextChangeNotificationPromise( + expectedHref, "Remote href should be updated"); + Preferences.set("intl.locale.matchOS", false); + yield notificationPromise; + + notificationPromise = nextChangeNotificationPromise( + DEFAULT_HREF, "Remote href changes back to default"); + Preferences.set("general.useragent.locale", "en-US"); + + yield notificationPromise; + + // test update fires on override and reset + let testURL = new URL("https://example.com/"); + notificationPromise = nextChangeNotificationPromise( + testURL.href, "a notification occurs on override"); + RemoteNewTabLocation.override(testURL.href); + yield notificationPromise; + + // from overridden to default + notificationPromise = nextChangeNotificationPromise( + DEFAULT_HREF, "a notification occurs on reset"); + RemoteNewTabLocation.reset(); + yield notificationPromise; + + // override to default URL from default URL + notificationPromise = nextChangeNotificationPromise( + testURL.href, "a notification only occurs for a change in overridden urls"); + RemoteNewTabLocation.override(DEFAULT_HREF); + RemoteNewTabLocation.override(testURL.href); + yield notificationPromise; + + // reset twice, only one notification for default URL + notificationPromise = nextChangeNotificationPromise( + DEFAULT_HREF, "reset occurs"); + RemoteNewTabLocation.reset(); + yield notificationPromise; + + notificationPromise = nextChangeNotificationPromise( + testURL.href, "a notification only occurs for a change in reset urls"); + RemoteNewTabLocation.reset(); + RemoteNewTabLocation.override(testURL.href); + yield notificationPromise; + + NewTabPrefsProvider.prefs.uninit(); + RemoteNewTabLocation.uninit(); +}); + +/** + * Verifies that RemoteNewTabLocation's _releaseFromUpdateChannel + * Returns the correct release names + */ +add_task(function* test_release_names() { + RemoteNewTabLocation.init(); + let valid_channels = RemoteNewTabLocation.channels; + let invalid_channels = new Set(["default", "invalid"]); + + for (let channel of valid_channels) { + equal(channel, RemoteNewTabLocation._releaseFromUpdateChannel(channel), + "release == channel name when valid"); + } + + for (let channel of invalid_channels) { + equal("nightly", RemoteNewTabLocation._releaseFromUpdateChannel(channel), + "release == nightly when invalid"); + } + RemoteNewTabLocation.uninit(); +}); + +function nextChangeNotificationPromise(aNewURL, testMessage) { return new Promise(resolve => { Services.obs.addObserver(function observer(aSubject, aTopic, aData) { // jshint ignore:line Services.obs.removeObserver(observer, aTopic); - equal(aData, aNewURL, "remote-new-tab-location-changed data should be new URL."); + equal(aData, aNewURL, testMessage); resolve(); }, "remote-new-tab-location-changed", false); }); diff --git a/browser/extensions/pdfjs/test/browser.ini b/browser/extensions/pdfjs/test/browser.ini index 1856a0472a75..fb4aa9afc2a5 100644 --- a/browser/extensions/pdfjs/test/browser.ini +++ b/browser/extensions/pdfjs/test/browser.ini @@ -1,12 +1,10 @@ [DEFAULT] -support-files = file_pdfjs_test.pdf +support-files = + file_pdfjs_test.pdf + head.js [browser_pdfjs_main.js] -skip-if = e10s # Bug 1159385 [browser_pdfjs_navigation.js] -skip-if = e10s # Bug 1159385 [browser_pdfjs_savedialog.js] [browser_pdfjs_views.js] -skip-if = e10s # Bug 1159385 [browser_pdfjs_zoom.js] -skip-if = e10s # Bug 1159385 diff --git a/browser/extensions/pdfjs/test/browser_pdfjs_main.js b/browser/extensions/pdfjs/test/browser_pdfjs_main.js index 0abfad679c10..789605a1208a 100644 --- a/browser/extensions/pdfjs/test/browser_pdfjs_main.js +++ b/browser/extensions/pdfjs/test/browser_pdfjs_main.js @@ -4,9 +4,7 @@ const RELATIVE_DIR = "browser/extensions/pdfjs/test/"; const TESTROOT = "http://example.com/browser/" + RELATIVE_DIR; -function test() { - var tab; - +add_task(function* test() { let handlerService = Cc["@mozilla.org/uriloader/handler-service;1"].getService(Ci.nsIHandlerService); let mimeService = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService); let handlerInfo = mimeService.getFromTypeAndExtension('application/pdf', 'pdf'); @@ -17,82 +15,53 @@ function test() { info('Pref action: ' + handlerInfo.preferredAction); - waitForExplicitFinish(); - registerCleanupFunction(function() { - gBrowser.removeTab(tab); - }); + yield BrowserTestUtils.withNewTab({ gBrowser: gBrowser, url: "about:blank" }, + function* (newTabBrowser) { + yield waitForPdfJS(newTabBrowser, TESTROOT + "file_pdfjs_test.pdf"); - tab = gBrowser.addTab(TESTROOT + "file_pdfjs_test.pdf"); - var newTabBrowser = gBrowser.getBrowserForTab(tab); - newTabBrowser.addEventListener("load", function eventHandler() { - newTabBrowser.removeEventListener("load", eventHandler, true); + ok(gBrowser.isFindBarInitialized(), "Browser FindBar initialized!"); - var document = newTabBrowser.contentDocument, - window = newTabBrowser.contentWindow; + yield ContentTask.spawn(newTabBrowser, null, function* () { + // + // Overall sanity tests + // + ok(content.document.querySelector('div#viewer'), "document content has viewer UI"); + ok('PDFJS' in content.wrappedJSObject, "window content has PDFJS object"); - // Runs tests after all 'load' event handlers have fired off - window.addEventListener("documentload", function() { - runTests(document, window, tab, function () { - closePDFViewer(window, finish); + // + // Sidebar: open + // + var sidebar = content.document.querySelector('button#sidebarToggle'), + outerContainer = content.document.querySelector('div#outerContainer'); + + sidebar.click(); + ok(outerContainer.classList.contains('sidebarOpen'), "sidebar opens on click"); + + // + // Sidebar: close + // + sidebar.click(); + ok(!outerContainer.classList.contains('sidebarOpen'), "sidebar closes on click"); + + // + // Page change from prev/next buttons + // + var prevPage = content.document.querySelector('button#previous'), + nextPage = content.document.querySelector('button#next'); + + var pgNumber = content.document.querySelector('input#pageNumber').value; + is(parseInt(pgNumber, 10), 1, 'initial page is 1'); + + // + // Bookmark button + // + var viewBookmark = content.document.querySelector('a#viewBookmark'); + viewBookmark.click(); + + ok(viewBookmark.href.length > 0, "viewBookmark button has href"); + + var viewer = content.wrappedJSObject.PDFViewerApplication; + yield viewer.close(); }); - }, false, true); - }, true); -} - - -function runTests(document, window, tab, callback) { - - // - // Overall sanity tests - // - ok(document.querySelector('div#viewer'), "document content has viewer UI"); - ok('PDFJS' in window.wrappedJSObject, "window content has PDFJS object"); - ok('PDFViewerApplication' in window.wrappedJSObject, - "window content has viewer object"); - - // - // Browser Find - // - ok(gBrowser.isFindBarInitialized(tab), "Browser FindBar initialized!"); - - // - // Sidebar: open - // - var sidebar = document.querySelector('button#sidebarToggle'), - outerContainer = document.querySelector('div#outerContainer'); - - sidebar.click(); - ok(outerContainer.classList.contains('sidebarOpen'), 'sidebar opens on click'); - - // - // Sidebar: close - // - sidebar.click(); - ok(!outerContainer.classList.contains('sidebarOpen'), 'sidebar closes on click'); - - // - // Page change from prev/next buttons - // - var prevPage = document.querySelector('button#previous'), - nextPage = document.querySelector('button#next'); - - var pageNumber = document.querySelector('input#pageNumber'); - is(parseInt(pageNumber.value), 1, 'initial page is 1'); - - // - // Bookmark button - // - var viewBookmark = document.querySelector('a#viewBookmark'); - viewBookmark.click(); - ok(viewBookmark.href.length > 0, 'viewBookmark button has href'); - - callback(); -} - -/** - * Destroys PDF.js viewer opened document. - */ -function closePDFViewer(window, callback) { - var viewer = window.wrappedJSObject.PDFViewerApplication; - viewer.close().then(callback); -} + }); +}); diff --git a/browser/extensions/pdfjs/test/browser_pdfjs_navigation.js b/browser/extensions/pdfjs/test/browser_pdfjs_navigation.js index a1a501a023f2..3a822366857c 100644 --- a/browser/extensions/pdfjs/test/browser_pdfjs_navigation.js +++ b/browser/extensions/pdfjs/test/browser_pdfjs_navigation.js @@ -139,9 +139,7 @@ const TESTS = [ } ]; -function test() { - var tab; - +add_task(function* test() { let mimeService = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService); let handlerInfo = mimeService.getFromTypeAndExtension('application/pdf', 'pdf'); @@ -151,51 +149,73 @@ function test() { info('Pref action: ' + handlerInfo.preferredAction); - waitForExplicitFinish(); - registerCleanupFunction(function() { - gBrowser.removeTab(tab); - }); + yield BrowserTestUtils.withNewTab({ gBrowser, url: "about:blank" }, + function* (newTabBrowser) { + yield waitForPdfJS(newTabBrowser, TESTROOT + "file_pdfjs_test.pdf"); - tab = gBrowser.addTab(TESTROOT + "file_pdfjs_test.pdf"); - gBrowser.selectedTab = tab; - - var newTabBrowser = gBrowser.getBrowserForTab(tab); - newTabBrowser.addEventListener("load", function eventHandler() { - newTabBrowser.removeEventListener("load", eventHandler, true); - - var document = newTabBrowser.contentDocument, - window = newTabBrowser.contentWindow; - - // Runs tests after all 'load' event handlers have fired off - window.addEventListener("documentload", function() { - runTests(document, window, function () { - var pageNumber = document.querySelector('input#pageNumber'); - is(pageNumber.value, pageNumber.max, "Document is left on the last page"); - finish(); + yield ContentTask.spawn(newTabBrowser, null, function* () { + // Check if PDF is opened with internal viewer + ok(content.document.querySelector('div#viewer'), "document content has viewer UI"); + ok('PDFJS' in content.wrappedJSObject, "window content has PDFJS object"); }); - }, false, true); - }, true); -} -function runTests(document, window, finish) { - // Check if PDF is opened with internal viewer - ok(document.querySelector('div#viewer'), "document content has viewer UI"); - ok('PDFJS' in window.wrappedJSObject, "window content has PDFJS object"); + yield ContentTask.spawn(newTabBrowser, null, contentSetUp); - // Wait for outline items, the start the navigation actions - waitForOutlineItems(document).then(function () { - // The key navigation has to happen in page-fit, otherwise it won't scroll - // trough a complete page - setZoomToPageFit(document).then(function () { - runNextTest(document, window, finish); - }, function () { - ok(false, "Current scale has been set to 'page-fit'"); - finish(); + yield Task.spawn(runTests(newTabBrowser)); + + yield ContentTask.spawn(newTabBrowser, null, function*() { + let pageNumber = content.document.querySelector('input#pageNumber'); + is(pageNumber.value, pageNumber.max, "Document is left on the last page"); + }); }); - }, function () { - ok(false, "Outline items have been found"); - finish(); - }); +}); + +function* contentSetUp() { + /** + * Outline Items gets appended to the document later on we have to + * wait for them before we start to navigate though document + * + * @param document + * @returns {deferred.promise|*} + */ + function waitForOutlineItems(document) { + return new Promise((resolve, reject) => { + document.addEventListener("outlineloaded", function outlineLoaded(evt) { + document.removeEventListener("outlineloaded", outlineLoaded); + var outlineCount = evt.detail.outlineCount; + + if (document.querySelectorAll(".outlineItem").length === outlineCount) { + resolve(); + } else { + reject(); + } + }); + }); + } + + /** + * The key navigation has to happen in page-fit, otherwise it won't scroll + * through a complete page + * + * @param document + * @returns {deferred.promise|*} + */ + function setZoomToPageFit(document) { + return new Promise((resolve) => { + document.addEventListener("pagerendered", function onZoom(e) { + document.removeEventListener("pagerendered", onZoom); + document.querySelector("#viewer").click(); + resolve(); + }); + + var select = document.querySelector("select#scaleSelect"); + select.selectedIndex = 2; + select.dispatchEvent(new Event("change")); + }); + } + + yield waitForOutlineItems(content.document); + yield setZoomToPageFit(content.document); } /** @@ -207,99 +227,55 @@ function runTests(document, window, finish) { * @param test * @param callback */ -function runNextTest(document, window, endCallback) { - var test = TESTS.shift(), - deferred = Promise.defer(), - pageNumber = document.querySelector('input#pageNumber'); +function* runTests(browser) { + yield ContentTask.spawn(browser, TESTS, function* (TESTS) { + let window = content; + let document = window.document; - // Add an event-listener to wait for page to change, afterwards resolve the promise - var timeout = window.setTimeout(() => deferred.reject(), 5000); - window.addEventListener('pagechange', function pageChange() { - if (pageNumber.value == test.expectedPage) { - window.removeEventListener('pagechange', pageChange); - window.clearTimeout(timeout); - deferred.resolve(pageNumber.value); + for (let test of TESTS) { + let deferred = {}; + deferred.promise = new Promise((resolve, reject) => { + deferred.resolve = resolve; + deferred.reject = reject; + }); + + let pageNumber = document.querySelector('input#pageNumber'); + + // Add an event-listener to wait for page to change, afterwards resolve the promise + let timeout = window.setTimeout(() => deferred.reject(), 5000); + window.addEventListener('pagechange', function pageChange() { + if (pageNumber.value == test.expectedPage) { + window.removeEventListener('pagechange', pageChange); + window.clearTimeout(timeout); + deferred.resolve(+pageNumber.value); + } + }); + + // Get the element and trigger the action for changing the page + var el = document.querySelector(test.action.selector); + ok(el, "Element '" + test.action.selector + "' has been found"); + + // The value option is for input case + if (test.action.value) + el.value = test.action.value; + + // Dispatch the event for changing the page + if (test.action.event == "keydown") { + var ev = document.createEvent("KeyboardEvent"); + ev.initKeyEvent("keydown", true, true, null, false, false, false, false, + test.action.keyCode, 0); + el.dispatchEvent(ev); + } + else { + var ev = new Event(test.action.event); + } + el.dispatchEvent(ev); + + let pgNumber = yield deferred.promise; + is(pgNumber, test.expectedPage, test.message); } - }); - // Get the element and trigger the action for changing the page - var el = document.querySelector(test.action.selector); - ok(el, "Element '" + test.action.selector + "' has been found"); - - // The value option is for input case - if (test.action.value) - el.value = test.action.value; - - // Dispatch the event for changing the page - if (test.action.event == "keydown") { - var ev = document.createEvent("KeyboardEvent"); - ev.initKeyEvent("keydown", true, true, null, false, false, false, false, - test.action.keyCode, 0); - el.dispatchEvent(ev); - } - else { - var ev = new Event(test.action.event); - } - el.dispatchEvent(ev); - - - // When the promise gets resolved we call the next test if there are any left - // or else we call the final callback which will end the test - deferred.promise.then(function (pgNumber) { - is(pgNumber, test.expectedPage, test.message); - - if (TESTS.length) - runNextTest(document, window, endCallback); - else - endCallback(); - }, function () { - ok(false, "Test '" + test.message + "' failed with timeout."); - endCallback(); + var viewer = content.wrappedJSObject.PDFViewerApplication; + yield viewer.close(); }); } - -/** - * Outline Items gets appended to the document latter on we have to - * wait for them before we start to navigate though document - * - * @param document - * @returns {deferred.promise|*} - */ -function waitForOutlineItems(document) { - var deferred = Promise.defer(); - document.addEventListener("outlineloaded", function outlineLoaded(evt) { - document.removeEventListener("outlineloaded", outlineLoaded); - var outlineCount = evt.detail.outlineCount; - - if (document.querySelectorAll(".outlineItem").length === outlineCount) { - deferred.resolve(); - } else { - deferred.reject(); - } - }); - - return deferred.promise; -} - -/** - * The key navigation has to happen in page-fit, otherwise it won't scroll - * trough a complete page - * - * @param document - * @returns {deferred.promise|*} - */ -function setZoomToPageFit(document) { - var deferred = Promise.defer(); - document.addEventListener("pagerendered", function onZoom(e) { - document.removeEventListener("pagerendered", onZoom); - document.querySelector("#viewer").click(); - deferred.resolve(); - }); - - var select = document.querySelector("select#scaleSelect"); - select.selectedIndex = 2; - select.dispatchEvent(new Event("change")); - - return deferred.promise; -} - diff --git a/browser/extensions/pdfjs/test/browser_pdfjs_views.js b/browser/extensions/pdfjs/test/browser_pdfjs_views.js index 77e1f737f7f1..ee6c416d5a5e 100644 --- a/browser/extensions/pdfjs/test/browser_pdfjs_views.js +++ b/browser/extensions/pdfjs/test/browser_pdfjs_views.js @@ -4,9 +4,7 @@ const RELATIVE_DIR = "browser/extensions/pdfjs/test/"; const TESTROOT = "http://example.com/browser/" + RELATIVE_DIR; -function test() { - var tab; - +add_task(function* test() { let handlerService = Cc["@mozilla.org/uriloader/handler-service;1"].getService(Ci.nsIHandlerService); let mimeService = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService); let handlerInfo = mimeService.getFromTypeAndExtension('application/pdf', 'pdf'); @@ -17,70 +15,47 @@ function test() { info('Pref action: ' + handlerInfo.preferredAction); - waitForExplicitFinish(); - registerCleanupFunction(function() { - gBrowser.removeTab(tab); - }); + yield BrowserTestUtils.withNewTab({ gBrowser, url: "about:blank" }, + function* (browser) { + // check that PDF is opened with internal viewer + yield waitForPdfJS(browser, TESTROOT + "file_pdfjs_test.pdf"); - tab = gBrowser.addTab(TESTROOT + "file_pdfjs_test.pdf"); - var newTabBrowser = gBrowser.getBrowserForTab(tab); - newTabBrowser.addEventListener("load", function eventHandler() { - newTabBrowser.removeEventListener("load", eventHandler, true); + yield ContentTask.spawn(browser, null, function* () { + ok(content.document.querySelector('div#viewer'), "document content has viewer UI"); + ok('PDFJS' in content.wrappedJSObject, "window content has PDFJS object"); - var document = newTabBrowser.contentDocument, - window = newTabBrowser.contentWindow; + //open sidebar + var sidebar = content.document.querySelector('button#sidebarToggle'); + var outerContainer = content.document.querySelector('div#outerContainer'); - // Runs tests after all 'load' event handlers have fired off - window.addEventListener("documentload", function() { - runTests(document, window, function () { - closePDFViewer(window, finish); + sidebar.click(); + ok(outerContainer.classList.contains('sidebarOpen'), 'sidebar opens on click'); + + // check that thumbnail view is open + var thumbnailView = content.document.querySelector('div#thumbnailView'); + var outlineView = content.document.querySelector('div#outlineView'); + + is(thumbnailView.getAttribute('class'), null, 'Initial view is thumbnail view'); + is(outlineView.getAttribute('class'), 'hidden', 'Outline view is hidden initially'); + + //switch to outline view + var viewOutlineButton = content.document.querySelector('button#viewOutline'); + viewOutlineButton.click(); + + is(thumbnailView.getAttribute('class'), 'hidden', 'Thumbnail view is hidden when outline is selected'); + is(outlineView.getAttribute('class'), '', 'Outline view is visible when selected'); + + //switch back to thumbnail view + var viewThumbnailButton = content.document.querySelector('button#viewThumbnail'); + viewThumbnailButton.click(); + + is(thumbnailView.getAttribute('class'), '', 'Thumbnail view is visible when selected'); + is(outlineView.getAttribute('class'), 'hidden', 'Outline view is hidden when thumbnail is selected'); + + sidebar.click(); + + var viewer = content.wrappedJSObject.PDFViewerApplication; + yield viewer.close(); }); - }, false, true); - }, true); -} - -function runTests(document, window, callback) { - // check that PDF is opened with internal viewer - ok(document.querySelector('div#viewer'), "document content has viewer UI"); - ok('PDFJS' in window.wrappedJSObject, "window content has PDFJS object"); - - //open sidebar - var sidebar = document.querySelector('button#sidebarToggle'); - var outerContainer = document.querySelector('div#outerContainer'); - - sidebar.click(); - ok(outerContainer.classList.contains('sidebarOpen'), 'sidebar opens on click'); - - // check that thumbnail view is open - var thumbnailView = document.querySelector('div#thumbnailView'); - var outlineView = document.querySelector('div#outlineView'); - - is(thumbnailView.getAttribute('class'), null, 'Initial view is thumbnail view'); - is(outlineView.getAttribute('class'), 'hidden', 'Outline view is hidden initially'); - - //switch to outline view - var viewOutlineButton = document.querySelector('button#viewOutline'); - viewOutlineButton.click(); - - is(outlineView.getAttribute('class'), '', 'Outline view is visible when selected'); - is(thumbnailView.getAttribute('class'), 'hidden', 'Thumbnail view is hidden when outline is selected'); - - //switch back to thumbnail view - var viewThumbnailButton = document.querySelector('button#viewThumbnail'); - viewThumbnailButton.click(); - - is(thumbnailView.getAttribute('class'), '', 'Thumbnail view is visible when selected'); - is(outlineView.getAttribute('class'), 'hidden', 'Outline view is hidden when thumbnail is selected'); - - sidebar.click(); - - callback(); -} - -/** - * Destroys PDF.js viewer opened document. - */ -function closePDFViewer(window, callback) { - var viewer = window.wrappedJSObject.PDFViewerApplication; - viewer.close().then(callback); -} + }); +}); diff --git a/browser/extensions/pdfjs/test/browser_pdfjs_zoom.js b/browser/extensions/pdfjs/test/browser_pdfjs_zoom.js index 05efa9dfe4ce..c06cf39c009a 100644 --- a/browser/extensions/pdfjs/test/browser_pdfjs_zoom.js +++ b/browser/extensions/pdfjs/test/browser_pdfjs_zoom.js @@ -28,6 +28,7 @@ const TESTS = [ { action: { keyboard: true, + keyCode: 61, event: "+" }, expectedZoom: 1, // 1 - zoom in @@ -37,6 +38,7 @@ const TESTS = [ { action: { keyboard: true, + keyCode: 109, event: "-" }, expectedZoom: -1, // -1 - zoom out @@ -54,11 +56,7 @@ const TESTS = [ } ]; -var initialWidth; // the initial width of the PDF document -var previousWidth; // the width of the PDF document at previous step/test - -function test() { - var tab; +add_task(function* test() { let handlerService = Cc["@mozilla.org/uriloader/handler-service;1"] .getService(Ci.nsIHandlerService); let mimeService = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService); @@ -72,114 +70,83 @@ function test() { info('Pref action: ' + handlerInfo.preferredAction); - waitForExplicitFinish(); - registerCleanupFunction(function() { - gBrowser.removeTab(tab); - }); + yield BrowserTestUtils.withNewTab({ gBrowser, url: "about:blank" }, + function* (newTabBrowser) { + yield waitForPdfJS(newTabBrowser, TESTROOT + "file_pdfjs_test.pdf" + "#zoom=100"); - tab = gBrowser.selectedTab = gBrowser.addTab(TESTROOT + "file_pdfjs_test.pdf"); - var newTabBrowser = gBrowser.getBrowserForTab(tab); + yield ContentTask.spawn(newTabBrowser, TESTS, function* (TESTS) { + let document = content.document; - newTabBrowser.addEventListener("load", function eventHandler() { - newTabBrowser.removeEventListener("load", eventHandler, true); + function waitForRender() { + return new Promise((resolve) => { + document.addEventListener("pagerendered", function onPageRendered(e) { + if(e.detail.pageNumber !== 1) { + return; + } - var document = newTabBrowser.contentDocument, - window = newTabBrowser.contentWindow; + document.removeEventListener("pagerendered", onPageRendered, true); + resolve(); + }, true); + }); + } - // Runs tests after all 'load' event handlers have fired off - window.addEventListener("documentload", function() { - initialWidth = parseInt(document.querySelector("div#pageContainer1").style.width); - previousWidth = initialWidth; - runTests(document, window, function () { - closePDFViewer(window, finish); + // check that PDF is opened with internal viewer + ok(content.document.querySelector('div#viewer'), "document content has viewer UI"); + ok('PDFJS' in content.wrappedJSObject, "window content has PDFJS object"); + + let initialWidth, previousWidth; + initialWidth = previousWidth = + parseInt(content.document.querySelector("div#pageContainer1").style.width); + + for (let test of TESTS) { + // We zoom using an UI element + var ev; + if (test.action.selector) { + // Get the element and trigger the action for changing the zoom + var el = document.querySelector(test.action.selector); + ok(el, "Element '" + test.action.selector + "' has been found"); + + if (test.action.index){ + el.selectedIndex = test.action.index; + } + + // Dispatch the event for changing the zoom + ev = new Event(test.action.event); + } + // We zoom using keyboard + else { + // Simulate key press + ev = new content.KeyboardEvent("keydown", + { key: test.action.event, + keyCode: test.action.keyCode, + ctrlKey: true }); + el = content; + } + + el.dispatchEvent(ev); + yield waitForRender(); + + var pageZoomScale = content.document.querySelector('select#scaleSelect'); + + // The zoom value displayed in the zoom select + var zoomValue = pageZoomScale.options[pageZoomScale.selectedIndex].innerHTML; + + let pageContainer = content.document.querySelector('div#pageContainer1'); + let actualWidth = parseInt(pageContainer.style.width); + + // the actual zoom of the PDF document + let computedZoomValue = parseInt(((actualWidth/initialWidth).toFixed(2))*100) + "%"; + is(computedZoomValue, zoomValue, "Content has correct zoom"); + + // Check that document zooms in the expected way (in/out) + let zoom = (actualWidth - previousWidth) * test.expectedZoom; + ok(zoom > 0, test.message); + + previousWidth = actualWidth; + } + + var viewer = content.wrappedJSObject.PDFViewerApplication; + yield viewer.close(); }); - }, false, true); - }, true); -} - -function runTests(document, window, callback) { - // check that PDF is opened with internal viewer - ok(document.querySelector('div#viewer'), "document content has viewer UI"); - ok('PDFJS' in window.wrappedJSObject, "window content has PDFJS object"); - - // Start the zooming tests after the document is loaded - waitForDocumentLoad(document).then(function () { - zoomPDF(document, window, TESTS.shift(), callback); - }); -} - -function waitForDocumentLoad(document) { - var deferred = Promise.defer(); - var interval = setInterval(function () { - if (document.querySelector("div#pageContainer1") != null){ - clearInterval(interval); - deferred.resolve(); - } - }, 500); - - return deferred.promise; -} - -function zoomPDF(document, window, test, endCallback) { - var renderedPage; - - document.addEventListener("pagerendered", function onPageRendered(e) { - if(e.detail.pageNumber !== 1) { - return; - } - - document.removeEventListener("pagerendered", onPageRendered, true); - - var pageZoomScale = document.querySelector('select#scaleSelect'); - - // The zoom value displayed in the zoom select - var zoomValue = pageZoomScale.options[pageZoomScale.selectedIndex].innerHTML; - - let pageContainer = document.querySelector('div#pageContainer1'); - let actualWidth = parseInt(pageContainer.style.width); - - // the actual zoom of the PDF document - let computedZoomValue = parseInt(((actualWidth/initialWidth).toFixed(2))*100) + "%"; - is(computedZoomValue, zoomValue, "Content has correct zoom"); - - // Check that document zooms in the expected way (in/out) - let zoom = (actualWidth - previousWidth) * test.expectedZoom; - ok(zoom > 0, test.message); - - // Go to next test (if there is any) or finish - var nextTest = TESTS.shift(); - if (nextTest) { - previousWidth = actualWidth; - zoomPDF(document, window, nextTest, endCallback); - } - else - endCallback(); - }, true); - - // We zoom using an UI element - if (test.action.selector) { - // Get the element and trigger the action for changing the zoom - var el = document.querySelector(test.action.selector); - ok(el, "Element '" + test.action.selector + "' has been found"); - - if (test.action.index){ - el.selectedIndex = test.action.index; - } - - // Dispatch the event for changing the zoom - el.dispatchEvent(new Event(test.action.event)); - } - // We zoom using keyboard - else { - // Simulate key press - EventUtils.synthesizeKey(test.action.event, { ctrlKey: true }); - } -} - -/** - * Destroys PDF.js viewer opened document. - */ -function closePDFViewer(window, callback) { - var viewer = window.wrappedJSObject.PDFViewerApplication; - viewer.close().then(callback); -} + }); +}); diff --git a/browser/extensions/pdfjs/test/head.js b/browser/extensions/pdfjs/test/head.js new file mode 100644 index 000000000000..d980bceb14e2 --- /dev/null +++ b/browser/extensions/pdfjs/test/head.js @@ -0,0 +1,15 @@ +function waitForPdfJS(browser, url) { + // Runs tests after all 'load' event handlers have fired off + return ContentTask.spawn(browser, url, function* (url) { + yield new Promise((resolve) => { + // NB: Add the listener to the global object so that we receive the + // event fired from the new window. + addEventListener("documentload", function listener() { + removeEventListener("documentload", listener, false); + resolve(); + }, false, true); + + content.location = url; + }); + }); +} diff --git a/chrome/nsChromeRegistryChrome.cpp b/chrome/nsChromeRegistryChrome.cpp index 08a72d9d9d0c..79075aa3f241 100644 --- a/chrome/nsChromeRegistryChrome.cpp +++ b/chrome/nsChromeRegistryChrome.cpp @@ -396,33 +396,6 @@ SerializeURI(nsIURI* aURI, aURI->GetOriginCharset(aSerializedURI.charset); } -static PLDHashOperator -EnumerateOverride(nsIURI* aURIKey, - nsIURI* aURI, - void* aArg) -{ - nsTArray* overrides = - static_cast*>(aArg); - - SerializedURI chromeURI, overrideURI; - - SerializeURI(aURIKey, chromeURI); - SerializeURI(aURI, overrideURI); - - OverrideMapping override = { - chromeURI, overrideURI - }; - overrides->AppendElement(override); - return (PLDHashOperator)PL_DHASH_NEXT; -} - -struct EnumerationArgs -{ - InfallibleTArray& packages; - const nsCString& selectedLocale; - const nsCString& selectedSkin; -}; - void nsChromeRegistryChrome::SendRegisteredChrome( mozilla::dom::PContentParent* aParent) @@ -431,10 +404,12 @@ nsChromeRegistryChrome::SendRegisteredChrome( InfallibleTArray resources; InfallibleTArray overrides; - EnumerationArgs args = { - packages, mSelectedLocale, mSelectedSkin - }; - mPackagesHash.EnumerateRead(CollectPackages, &args); + for (auto iter = mPackagesHash.Iter(); !iter.Done(); iter.Next()) { + ChromePackage chromePackage; + ChromePackageFromPackageEntry(iter.Key(), iter.UserData(), &chromePackage, + mSelectedLocale, mSelectedSkin); + packages.AppendElement(chromePackage); + } // If we were passed a parent then a new child process has been created and // has requested all of the chrome so send it the resources too. Otherwise @@ -452,7 +427,15 @@ nsChromeRegistryChrome::SendRegisteredChrome( rph->CollectSubstitutions(resources); } - mOverrideTable.EnumerateRead(&EnumerateOverride, &overrides); + for (auto iter = mOverrideTable.Iter(); !iter.Done(); iter.Next()) { + SerializedURI chromeURI, overrideURI; + + SerializeURI(iter.Key(), chromeURI); + SerializeURI(iter.UserData(), overrideURI); + + OverrideMapping override = { chromeURI, overrideURI }; + overrides.AppendElement(override); + } if (aParent) { bool success = aParent->SendRegisterChrome(packages, resources, overrides, @@ -490,20 +473,6 @@ nsChromeRegistryChrome::ChromePackageFromPackageEntry(const nsACString& aPackage aChromePackage->flags = aPackage->flags; } -PLDHashOperator -nsChromeRegistryChrome::CollectPackages(const nsACString &aKey, - PackageEntry *package, - void *arg) -{ - EnumerationArgs* args = static_cast(arg); - - ChromePackage chromePackage; - ChromePackageFromPackageEntry(aKey, package, &chromePackage, - args->selectedLocale, args->selectedSkin); - args->packages.AppendElement(chromePackage); - return PL_DHASH_NEXT; -} - static bool CanLoadResource(nsIURI* aResourceURI) { diff --git a/chrome/nsChromeRegistryChrome.h b/chrome/nsChromeRegistryChrome.h index 553c4e29174d..ba6eec9890b9 100644 --- a/chrome/nsChromeRegistryChrome.h +++ b/chrome/nsChromeRegistryChrome.h @@ -59,9 +59,6 @@ class nsChromeRegistryChrome : public nsChromeRegistry ChromePackage* aChromePackage, const nsCString& aSelectedLocale, const nsCString& aSelectedSkin); - static PLDHashOperator CollectPackages(const nsACString &aKey, - PackageEntry *package, - void *arg); nsresult OverrideLocalePackage(const nsACString& aPackage, nsACString& aOverride); diff --git a/dom/base/nsGkAtomList.h b/dom/base/nsGkAtomList.h index 482760cc7402..a27817652f91 100644 --- a/dom/base/nsGkAtomList.h +++ b/dom/base/nsGkAtomList.h @@ -910,6 +910,7 @@ GK_ATOM(onstatechange, "onstatechange") GK_ATOM(onstatuschanged, "onstatuschanged") GK_ATOM(onstkcommand, "onstkcommand") GK_ATOM(onstksessionend, "onstksessionend") +GK_ATOM(onstorage, "onstorage") GK_ATOM(onstorageareachanged, "onstorageareachanged") GK_ATOM(onsubmit, "onsubmit") GK_ATOM(onsuccess, "onsuccess") diff --git a/dom/base/nsScriptNameSpaceManager.h b/dom/base/nsScriptNameSpaceManager.h index d0d0a3662831..c7794945a5af 100644 --- a/dom/base/nsScriptNameSpaceManager.h +++ b/dom/base/nsScriptNameSpaceManager.h @@ -91,7 +91,7 @@ public: // Measurement of the following members may be added later if DMD finds it // is worthwhile: // - mGlobalName - return mKey.SizeOfExcludingThisMustBeUnshared(aMallocSizeOf); + return mKey.SizeOfExcludingThisIfUnshared(aMallocSizeOf); } }; diff --git a/dom/events/EventNameList.h b/dom/events/EventNameList.h index f047b1b3d94e..0479dbc06a26 100644 --- a/dom/events/EventNameList.h +++ b/dom/events/EventNameList.h @@ -527,8 +527,10 @@ WINDOW_EVENT(popstate, eBasicEventClass) // Not supported yet // WINDOW_EVENT(redo) -// Not supported yet -// WINDOW_EVENT(storage) +WINDOW_EVENT(storage, + eStorage, + EventNameType_HTMLBodyOrFramesetOnly, + eBasicEventClass) // Not supported yet // WINDOW_EVENT(undo) WINDOW_EVENT(unload, diff --git a/dom/imptests/failures/html/html/browsers/the-window-object/test_window-properties.html.json b/dom/imptests/failures/html/html/browsers/the-window-object/test_window-properties.html.json index 3a3aa7ffe992..b34f6573fa91 100644 --- a/dom/imptests/failures/html/html/browsers/the-window-object/test_window-properties.html.json +++ b/dom/imptests/failures/html/html/browsers/the-window-object/test_window-properties.html.json @@ -25,7 +25,6 @@ "Window attribute: onclose": true, "Window attribute: oncuechange": true, "Window attribute: onmousewheel": true, - "Window attribute: onstorage": true, "Window unforgeable attribute: window": true, "Window unforgeable attribute: document": true, "Window unforgeable attribute: top": true diff --git a/dom/locales/en-US/chrome/dom/dom.properties b/dom/locales/en-US/chrome/dom/dom.properties index b03d32662e12..96fabb4a2495 100644 --- a/dom/locales/en-US/chrome/dom/dom.properties +++ b/dom/locales/en-US/chrome/dom/dom.properties @@ -67,7 +67,7 @@ nsIDOMWindowInternalWarning=Use of nsIDOMWindowInternal is deprecated. Use nsIDO FullScreenDeniedDisabled=Request for full-screen was denied because full-screen API is disabled by user preference. FullScreenDeniedFocusedPlugin=Request for full-screen was denied because a windowed plugin is focused. FullScreenDeniedHidden=Request for full-screen was denied because the document is no longer visible. -FullScreenDeniedContainerNotAllowed=Request for full-screen was denied because at least one of the document's containing element is not iframe or does not have an "allowfullscreen" attribute. +FullScreenDeniedContainerNotAllowed=Request for full-screen was denied because at least one of the document's containing elements is not an iframe or does not have an "allowfullscreen" attribute. FullScreenDeniedNotInputDriven=Request for full-screen was denied because Element.mozRequestFullScreen() was not called from inside a short running user-generated event handler. FullScreenDeniedNotInDocument=Request for full-screen was denied because requesting element is no longer in its document. FullScreenDeniedMovedDocument=Request for full-screen was denied because requesting element has moved document. diff --git a/dom/media/AbstractMediaDecoder.h b/dom/media/AbstractMediaDecoder.h index 8405bb34e18d..deaa2c28ded2 100644 --- a/dom/media/AbstractMediaDecoder.h +++ b/dom/media/AbstractMediaDecoder.h @@ -10,6 +10,7 @@ #include "mozilla/Attributes.h" #include "mozilla/StateMirroring.h" +#include "MediaEventSource.h" #include "MediaInfo.h" #include "nsISupports.h" #include "nsDataHashtable.h" @@ -68,6 +69,15 @@ public: virtual AbstractCanonical* CanonicalDurationOrNull() { return nullptr; }; + // Return an event that will be notified when data arrives in MediaResource. + // MediaDecoderReader will register with this event to receive notifications + // in order to udpate buffer ranges. + // Return null if this decoder doesn't support the event. + virtual MediaEventSource* DataArrivedEvent() + { + return nullptr; + } + protected: virtual void UpdateEstimatedMediaDuration(int64_t aDuration) {}; public: diff --git a/dom/media/MediaDecoder.cpp b/dom/media/MediaDecoder.cpp index 6925cf0f32af..e8dbc08a0d36 100644 --- a/dom/media/MediaDecoder.cpp +++ b/dom/media/MediaDecoder.cpp @@ -1447,10 +1447,13 @@ void MediaDecoder::NotifyDataArrived() { MOZ_ASSERT(NS_IsMainThread()); - if (mDecoderStateMachine) { - mDecoderStateMachine->DispatchNotifyDataArrived(); + // Don't publish events since task queues might be shutting down. + if (mShuttingDown) { + return; } + mDataArrivedEvent.Notify(); + // ReadyState computation depends on MediaDecoder::CanPlayThrough, which // depends on the download rate. UpdateReadyState(); diff --git a/dom/media/MediaDecoder.h b/dom/media/MediaDecoder.h index 16fc3843eb39..64eaabb95972 100644 --- a/dom/media/MediaDecoder.h +++ b/dom/media/MediaDecoder.h @@ -820,6 +820,11 @@ protected: RefPtr mResource; private: + MediaEventSource* + DataArrivedEvent() override { return &mDataArrivedEvent; } + + MediaEventProducer mDataArrivedEvent; + // The state machine object for handling the decoding. It is safe to // call methods of this object from other threads. Its internal data // is synchronised on a monitor. The lifetime of this object is diff --git a/dom/media/MediaDecoderReader.cpp b/dom/media/MediaDecoderReader.cpp index 38a0d2b44452..2cd6333b0a48 100644 --- a/dom/media/MediaDecoderReader.cpp +++ b/dom/media/MediaDecoderReader.cpp @@ -79,6 +79,11 @@ MediaDecoderReader::MediaDecoderReader(AbstractMediaDecoder* aDecoder) MOZ_COUNT_CTOR(MediaDecoderReader); MOZ_ASSERT(NS_IsMainThread()); + if (mDecoder && mDecoder->DataArrivedEvent()) { + mDataArrivedListener = mDecoder->DataArrivedEvent()->Connect( + mTaskQueue, this, &MediaDecoderReader::NotifyDataArrived); + } + // Dispatch initialization that needs to happen on that task queue. nsCOMPtr r = NS_NewRunnableMethod(this, &MediaDecoderReader::InitializationTask); mTaskQueue->Dispatch(r.forget()); @@ -360,6 +365,8 @@ MediaDecoderReader::Shutdown() mBaseAudioPromise.RejectIfExists(END_OF_STREAM, __func__); mBaseVideoPromise.RejectIfExists(END_OF_STREAM, __func__); + mDataArrivedListener.DisconnectIfExists(); + ReleaseMediaResources(); mDuration.DisconnectIfConnected(); mBuffered.DisconnectAll(); diff --git a/dom/media/MediaDecoderReader.h b/dom/media/MediaDecoderReader.h index dbb5e8f349c4..cc7891d51aaf 100644 --- a/dom/media/MediaDecoderReader.h +++ b/dom/media/MediaDecoderReader.h @@ -223,15 +223,6 @@ public: virtual size_t SizeOfVideoQueueInFrames(); virtual size_t SizeOfAudioQueueInFrames(); - void DispatchNotifyDataArrived() - { - RefPtr r = NS_NewRunnableMethod( - this, &MediaDecoderReader::NotifyDataArrived); - - OwnerThread()->Dispatch( - r.forget(), AbstractThread::DontAssertDispatchSuccess); - } - void NotifyDataArrived() { MOZ_ASSERT(OnTaskQueue()); @@ -428,6 +419,8 @@ private: // "discontinuity" in the stream. For example after a seek. bool mAudioDiscontinuity; bool mVideoDiscontinuity; + + MediaEventListener mDataArrivedListener; }; } // namespace mozilla diff --git a/dom/media/MediaDecoderStateMachine.h b/dom/media/MediaDecoderStateMachine.h index 462d22230a16..2e755e588e6f 100644 --- a/dom/media/MediaDecoderStateMachine.h +++ b/dom/media/MediaDecoderStateMachine.h @@ -168,11 +168,6 @@ public: OwnerThread()->Dispatch(runnable.forget()); } - void DispatchNotifyDataArrived() - { - mReader->DispatchNotifyDataArrived(); - } - // Notifies the state machine that should minimize the number of samples // decoded we preroll, until playback starts. The first time playback starts // the state machine is free to return to prerolling normally. Note diff --git a/dom/media/MediaStreamGraph.cpp b/dom/media/MediaStreamGraph.cpp index 5079a8fb34d8..868b97b0de7a 100644 --- a/dom/media/MediaStreamGraph.cpp +++ b/dom/media/MediaStreamGraph.cpp @@ -1229,11 +1229,11 @@ MediaStreamGraphImpl::UpdateMainThreadState() bool MediaStreamGraphImpl::OneIteration(GraphTime aStateEnd) { - MaybeProduceMemoryReport(); - // Process graph message from the main thread for this iteration. RunMessagesInQueue(); + MaybeProduceMemoryReport(); + GraphTime stateEnd = std::min(aStateEnd, mEndTime); UpdateGraph(stateEnd); diff --git a/dom/media/gstreamer/GStreamerAllocator.cpp b/dom/media/gstreamer/GStreamerAllocator.cpp index 94c41ffe2939..c1c01d7a79fa 100644 --- a/dom/media/gstreamer/GStreamerAllocator.cpp +++ b/dom/media/gstreamer/GStreamerAllocator.cpp @@ -60,7 +60,7 @@ moz_gfx_memory_reset(MozGfxMemory *mem) mem->image->Release(); ImageContainer* container = ((MozGfxMemoryAllocator*) mem->memory.allocator)->reader->GetImageContainer(); - mem->image = reinterpret_cast(container->CreateImage(ImageFormat::PLANAR_YCBCR).take()); + mem->image = container->CreatePlanarYCbCrImage().forget().take(); mem->data = mem->image->AllocateAndGetNewBuffer(mem->memory.size); } diff --git a/dom/media/platforms/PDMFactory.cpp b/dom/media/platforms/PDMFactory.cpp index e3f9446e52f3..cf6bddcec01f 100644 --- a/dom/media/platforms/PDMFactory.cpp +++ b/dom/media/platforms/PDMFactory.cpp @@ -227,7 +227,7 @@ PDMFactory::CreateDecoderWithPDM(PlatformDecoderModule* aPDM, } bool -PDMFactory::SupportsMimeType(const nsACString& aMimeType) +PDMFactory::SupportsMimeType(const nsACString& aMimeType) const { if (mEMEPDM) { return mEMEPDM->SupportsMimeType(aMimeType); @@ -305,7 +305,7 @@ PDMFactory::StartupPDM(PlatformDecoderModule* aPDM) } already_AddRefed -PDMFactory::GetDecoder(const nsACString& aMimeType) +PDMFactory::GetDecoder(const nsACString& aMimeType) const { RefPtr pdm; for (auto& current : mCurrentPDMs) { diff --git a/dom/media/platforms/PDMFactory.h b/dom/media/platforms/PDMFactory.h index 76b60958ec89..f85bd2ca961f 100644 --- a/dom/media/platforms/PDMFactory.h +++ b/dom/media/platforms/PDMFactory.h @@ -35,7 +35,7 @@ public: layers::LayersBackend aLayersBackend = layers::LayersBackend::LAYERS_NONE, layers::ImageContainer* aImageContainer = nullptr); - bool SupportsMimeType(const nsACString& aMimeType); + bool SupportsMimeType(const nsACString& aMimeType) const; #ifdef MOZ_EME // Creates a PlatformDecoderModule that uses a CDMProxy to decrypt or @@ -52,7 +52,9 @@ private: // Startup the provided PDM and add it to our list if successful. bool StartupPDM(PlatformDecoderModule* aPDM); // Returns the first PDM in our list supporting the mimetype. - already_AddRefed GetDecoder(const nsACString& aMimeType); + already_AddRefed + GetDecoder(const nsACString& aMimeType) const; + already_AddRefed CreateDecoderWithPDM(PlatformDecoderModule* aPDM, const TrackInfo& aConfig, diff --git a/dom/media/platforms/PlatformDecoderModule.h b/dom/media/platforms/PlatformDecoderModule.h index 9ce212a4f4ae..0f68a9125b1e 100644 --- a/dom/media/platforms/PlatformDecoderModule.h +++ b/dom/media/platforms/PlatformDecoderModule.h @@ -53,7 +53,7 @@ public: virtual nsresult Startup() { return NS_OK; }; // Indicates if the PlatformDecoderModule supports decoding of aMimeType. - virtual bool SupportsMimeType(const nsACString& aMimeType) = 0; + virtual bool SupportsMimeType(const nsACString& aMimeType) const = 0; enum ConversionRequired { kNeedNone, diff --git a/dom/media/platforms/agnostic/AgnosticDecoderModule.cpp b/dom/media/platforms/agnostic/AgnosticDecoderModule.cpp index 39876f8989ec..64adc7e96e5b 100644 --- a/dom/media/platforms/agnostic/AgnosticDecoderModule.cpp +++ b/dom/media/platforms/agnostic/AgnosticDecoderModule.cpp @@ -12,7 +12,7 @@ namespace mozilla { bool -AgnosticDecoderModule::SupportsMimeType(const nsACString& aMimeType) +AgnosticDecoderModule::SupportsMimeType(const nsACString& aMimeType) const { return VPXDecoder::IsVPX(aMimeType) || OpusDataDecoder::IsOpus(aMimeType) || diff --git a/dom/media/platforms/agnostic/AgnosticDecoderModule.h b/dom/media/platforms/agnostic/AgnosticDecoderModule.h index 97cc23e799b4..ff0d0583c7b8 100644 --- a/dom/media/platforms/agnostic/AgnosticDecoderModule.h +++ b/dom/media/platforms/agnostic/AgnosticDecoderModule.h @@ -10,7 +10,7 @@ public: AgnosticDecoderModule() = default; virtual ~AgnosticDecoderModule() = default; - bool SupportsMimeType(const nsACString& aMimeType) override; + bool SupportsMimeType(const nsACString& aMimeType) const override; ConversionRequired DecoderNeedsConversion(const TrackInfo& aConfig) const override diff --git a/dom/media/platforms/agnostic/BlankDecoderModule.cpp b/dom/media/platforms/agnostic/BlankDecoderModule.cpp index 10e12c9d8802..8e93c27e3253 100644 --- a/dom/media/platforms/agnostic/BlankDecoderModule.cpp +++ b/dom/media/platforms/agnostic/BlankDecoderModule.cpp @@ -246,7 +246,7 @@ public: } bool - SupportsMimeType(const nsACString& aMimeType) override + SupportsMimeType(const nsACString& aMimeType) const override { return true; } diff --git a/dom/media/platforms/agnostic/eme/EMEDecoderModule.cpp b/dom/media/platforms/agnostic/eme/EMEDecoderModule.cpp index a13e4d500f50..79508e19d082 100644 --- a/dom/media/platforms/agnostic/eme/EMEDecoderModule.cpp +++ b/dom/media/platforms/agnostic/eme/EMEDecoderModule.cpp @@ -303,7 +303,7 @@ EMEDecoderModule::DecoderNeedsConversion(const TrackInfo& aConfig) const } bool -EMEDecoderModule::SupportsMimeType(const nsACString& aMimeType) +EMEDecoderModule::SupportsMimeType(const nsACString& aMimeType) const { Maybe gmp; gmp.emplace(NS_ConvertUTF16toUTF8(mProxy->KeySystem())); diff --git a/dom/media/platforms/agnostic/eme/EMEDecoderModule.h b/dom/media/platforms/agnostic/eme/EMEDecoderModule.h index b5cfa82b71cf..0fa26884bc9d 100644 --- a/dom/media/platforms/agnostic/eme/EMEDecoderModule.h +++ b/dom/media/platforms/agnostic/eme/EMEDecoderModule.h @@ -45,7 +45,7 @@ protected: DecoderNeedsConversion(const TrackInfo& aConfig) const override; bool - SupportsMimeType(const nsACString& aMimeType) override; + SupportsMimeType(const nsACString& aMimeType) const override; private: RefPtr mProxy; diff --git a/dom/media/platforms/agnostic/gmp/GMPDecoderModule.cpp b/dom/media/platforms/agnostic/gmp/GMPDecoderModule.cpp index 0f5b46dd052a..aa7da132e943 100644 --- a/dom/media/platforms/agnostic/gmp/GMPDecoderModule.cpp +++ b/dom/media/platforms/agnostic/gmp/GMPDecoderModule.cpp @@ -228,7 +228,7 @@ GMPDecoderModule::SupportsMimeType(const nsACString& aMimeType, } bool -GMPDecoderModule::SupportsMimeType(const nsACString& aMimeType) +GMPDecoderModule::SupportsMimeType(const nsACString& aMimeType) const { return SupportsMimeType(aMimeType, PreferredGMP(aMimeType)); } diff --git a/dom/media/platforms/agnostic/gmp/GMPDecoderModule.h b/dom/media/platforms/agnostic/gmp/GMPDecoderModule.h index 2444c7a57f54..ef5a0f553236 100644 --- a/dom/media/platforms/agnostic/gmp/GMPDecoderModule.h +++ b/dom/media/platforms/agnostic/gmp/GMPDecoderModule.h @@ -36,7 +36,7 @@ public: DecoderNeedsConversion(const TrackInfo& aConfig) const override; bool - SupportsMimeType(const nsACString& aMimeType) override; + SupportsMimeType(const nsACString& aMimeType) const override; // Main thread only. static void Init(); diff --git a/dom/media/platforms/android/AndroidDecoderModule.cpp b/dom/media/platforms/android/AndroidDecoderModule.cpp index 7352f7a784a3..57e6a7252e42 100644 --- a/dom/media/platforms/android/AndroidDecoderModule.cpp +++ b/dom/media/platforms/android/AndroidDecoderModule.cpp @@ -18,11 +18,28 @@ #include "nsAutoPtr.h" #include "nsPromiseFlatString.h" +#include "prlog.h" + #include +static PRLogModuleInfo* AndroidDecoderModuleLog() +{ + static PRLogModuleInfo* sLogModule = nullptr; + if (!sLogModule) { + sLogModule = PR_NewLogModule("AndroidDecoderModule"); + } + return sLogModule; +} + +#undef LOG +#define LOG(arg, ...) MOZ_LOG(AndroidDecoderModuleLog(), \ + mozilla::LogLevel::Debug, ("AndroidDecoderModule(%p)::%s: " arg, \ + this, __func__, ##__VA_ARGS__)) + using namespace mozilla; using namespace mozilla::gl; using namespace mozilla::widget::sdk; +using media::TimeUnit; namespace mozilla { @@ -30,10 +47,11 @@ namespace mozilla { if (mCallback) { \ mCallback->Func(__VA_ARGS__); \ } else { \ - NS_WARNING("callback not set"); \ + NS_WARNING("Callback not set"); \ } -static const char* TranslateMimeType(const nsACString& aMimeType) +static const char* +TranslateMimeType(const nsACString& aMimeType) { if (aMimeType.EqualsLiteral("video/webm; codecs=vp8")) { return "video/x-vnd.on2.vp8"; @@ -43,7 +61,8 @@ static const char* TranslateMimeType(const nsACString& aMimeType) return PromiseFlatCString(aMimeType).get(); } -static MediaCodec::LocalRef CreateDecoder(const nsACString& aMimeType) +static MediaCodec::LocalRef +CreateDecoder(const nsACString& aMimeType) { MediaCodec::LocalRef codec; NS_ENSURE_SUCCESS(MediaCodec::CreateDecoderByType(TranslateMimeType(aMimeType), @@ -51,19 +70,23 @@ static MediaCodec::LocalRef CreateDecoder(const nsACString& aMimeType) return codec; } -class VideoDataDecoder : public MediaCodecDataDecoder { +class VideoDataDecoder : public MediaCodecDataDecoder +{ public: VideoDataDecoder(const VideoInfo& aConfig, - MediaFormat::Param aFormat, MediaDataDecoderCallback* aCallback, + MediaFormat::Param aFormat, + MediaDataDecoderCallback* aCallback, layers::ImageContainer* aImageContainer) - : MediaCodecDataDecoder(MediaData::Type::VIDEO_DATA, aConfig.mMimeType, aFormat, aCallback) + : MediaCodecDataDecoder(MediaData::Type::VIDEO_DATA, aConfig.mMimeType, + aFormat, aCallback) , mImageContainer(aImageContainer) , mConfig(aConfig) { } - RefPtr Init() override { + RefPtr Init() override + { mSurfaceTexture = AndroidSurfaceTexture::Create(); if (!mSurfaceTexture) { NS_WARNING("Failed to create SurfaceTexture for video decode\n"); @@ -77,22 +100,26 @@ public: return InitPromise::CreateAndResolve(TrackInfo::kVideoTrack, __func__); } - void Cleanup() override { + void Cleanup() override + { mGLContext = nullptr; } - nsresult Input(MediaRawData* aSample) override { + nsresult Input(MediaRawData* aSample) override + { return MediaCodecDataDecoder::Input(aSample); } - bool WantCopy() { + bool WantCopy() const + { // Allocating a texture is incredibly slow on PowerVR and may fail on // emulators, see bug 1190379. return mGLContext->Vendor() != GLVendor::Imagination && mGLContext->Renderer() != GLRenderer::AndroidEmulator; } - EGLImage CopySurface(layers::Image* img) { + EGLImage CopySurface(layers::Image* img) + { mGLContext->MakeCurrent(); GLuint tex = CreateTextureForOffscreen(mGLContext, mGLContext->GetGLFormats(), @@ -100,8 +127,8 @@ public: auto helper = mGLContext->BlitHelper(); const gl::OriginPos destOrigin = gl::OriginPos::TopLeft; - if (!helper->BlitImageToTexture(img, img->GetSize(), tex, LOCAL_GL_TEXTURE_2D, - destOrigin)) + if (!helper->BlitImageToTexture(img, img->GetSize(), tex, + LOCAL_GL_TEXTURE_2D, destOrigin)) { mGLContext->fDeleteTextures(1, &tex); return nullptr; @@ -113,22 +140,24 @@ public: }; EGLContext eglContext = static_cast(mGLContext.get())->mContext; - EGLImage eglImage = sEGLLibrary.fCreateImage(EGL_DISPLAY(), eglContext, - LOCAL_EGL_GL_TEXTURE_2D_KHR, - (EGLClientBuffer)tex, attribs); + EGLImage eglImage = sEGLLibrary.fCreateImage( + EGL_DISPLAY(), eglContext, LOCAL_EGL_GL_TEXTURE_2D_KHR, + reinterpret_cast(tex), attribs); mGLContext->fDeleteTextures(1, &tex); return eglImage; } nsresult PostOutput(BufferInfo::Param aInfo, MediaFormat::Param aFormat, - const media::TimeUnit& aDuration) override { + const TimeUnit& aDuration) override + { if (!EnsureGLContext()) { return NS_ERROR_FAILURE; } RefPtr img = - new SurfaceTextureImage(mSurfaceTexture.get(), mConfig.mDisplay, gl::OriginPos::BottomLeft); + new SurfaceTextureImage(mSurfaceTexture.get(), mConfig.mDisplay, + gl::OriginPos::BottomLeft); if (WantCopy()) { EGLImage eglImage = CopySurface(img); @@ -150,10 +179,8 @@ public: NS_WARNING("No EGL fence support detected, rendering artifacts may occur!"); } - img = new layers::EGLImageImage( - eglImage, eglSync, - mConfig.mDisplay, gl::OriginPos::TopLeft, - true /* owns */); + img = new layers::EGLImageImage(eglImage, eglSync, mConfig.mDisplay, + gl::OriginPos::TopLeft, true /* owns */); } nsresult rv; @@ -185,7 +212,8 @@ public: } protected: - bool EnsureGLContext() { + bool EnsureGLContext() + { if (mGLContext) { return true; } @@ -200,29 +228,33 @@ protected: RefPtr mGLContext; }; -class AudioDataDecoder : public MediaCodecDataDecoder { - +class AudioDataDecoder : public MediaCodecDataDecoder +{ public: - AudioDataDecoder(const AudioInfo& aConfig, MediaFormat::Param aFormat, MediaDataDecoderCallback* aCallback) - : MediaCodecDataDecoder(MediaData::Type::AUDIO_DATA, aConfig.mMimeType, aFormat, aCallback) + AudioDataDecoder(const AudioInfo& aConfig, MediaFormat::Param aFormat, + MediaDataDecoderCallback* aCallback) + : MediaCodecDataDecoder(MediaData::Type::AUDIO_DATA, aConfig.mMimeType, + aFormat, aCallback) { JNIEnv* const env = jni::GetEnvForThread(); jni::Object::LocalRef buffer(env); - NS_ENSURE_SUCCESS_VOID(aFormat->GetByteBuffer(NS_LITERAL_STRING("csd-0"), &buffer)); + NS_ENSURE_SUCCESS_VOID(aFormat->GetByteBuffer(NS_LITERAL_STRING("csd-0"), + &buffer)); if (!buffer && aConfig.mCodecSpecificConfig->Length() >= 2) { - buffer = jni::Object::LocalRef::Adopt(env, env->NewDirectByteBuffer(aConfig.mCodecSpecificConfig->Elements(), - aConfig.mCodecSpecificConfig->Length())); - NS_ENSURE_SUCCESS_VOID(aFormat->SetByteBuffer(NS_LITERAL_STRING("csd-0"), buffer)); + buffer = jni::Object::LocalRef::Adopt( + env, env->NewDirectByteBuffer(aConfig.mCodecSpecificConfig->Elements(), + aConfig.mCodecSpecificConfig->Length())); + NS_ENSURE_SUCCESS_VOID(aFormat->SetByteBuffer(NS_LITERAL_STRING("csd-0"), + buffer)); } } nsresult Output(BufferInfo::Param aInfo, void* aBuffer, - MediaFormat::Param aFormat, - const media::TimeUnit& aDuration) { + MediaFormat::Param aFormat, const TimeUnit& aDuration) + { // The output on Android is always 16-bit signed - nsresult rv; int32_t numChannels; NS_ENSURE_SUCCESS(rv = @@ -239,7 +271,7 @@ public: NS_ENSURE_SUCCESS(rv = aInfo->Offset(&offset), rv); #ifdef MOZ_SAMPLE_TYPE_S16 - int32_t numSamples = size / 2; + const int32_t numSamples = size / 2; #else #error We only support 16-bit integer PCM #endif @@ -247,8 +279,9 @@ public: const int32_t numFrames = numSamples / numChannels; auto audio = MakeUnique(numSamples); - uint8_t* bufferStart = static_cast(aBuffer) + offset; - PodCopy(audio.get(), reinterpret_cast(bufferStart), numSamples); + const uint8_t* bufferStart = static_cast(aBuffer) + offset; + PodCopy(audio.get(), reinterpret_cast(bufferStart), + numSamples); int64_t presentationTimeUs; NS_ENSURE_SUCCESS(rv = aInfo->PresentationTimeUs(&presentationTimeUs), rv); @@ -264,10 +297,11 @@ public: } }; - -bool AndroidDecoderModule::SupportsMimeType(const nsACString& aMimeType) +bool +AndroidDecoderModule::SupportsMimeType(const nsACString& aMimeType) const { - if (!AndroidBridge::Bridge() || (AndroidBridge::Bridge()->GetAPIVersion() < 16)) { + if (!AndroidBridge::Bridge() || + (AndroidBridge::Bridge()->GetAPIVersion() < 16)) { return false; } @@ -286,11 +320,9 @@ bool AndroidDecoderModule::SupportsMimeType(const nsACString& aMimeType) already_AddRefed AndroidDecoderModule::CreateVideoDecoder( - const VideoInfo& aConfig, - layers::LayersBackend aLayersBackend, - layers::ImageContainer* aImageContainer, - FlushableTaskQueue* aVideoTaskQueue, - MediaDataDecoderCallback* aCallback) + const VideoInfo& aConfig, layers::LayersBackend aLayersBackend, + layers::ImageContainer* aImageContainer, FlushableTaskQueue* aVideoTaskQueue, + MediaDataDecoderCallback* aCallback) { MediaFormat::LocalRef format; @@ -307,9 +339,9 @@ AndroidDecoderModule::CreateVideoDecoder( } already_AddRefed -AndroidDecoderModule::CreateAudioDecoder(const AudioInfo& aConfig, - FlushableTaskQueue* aAudioTaskQueue, - MediaDataDecoderCallback* aCallback) +AndroidDecoderModule::CreateAudioDecoder( + const AudioInfo& aConfig, FlushableTaskQueue* aAudioTaskQueue, + MediaDataDecoderCallback* aCallback) { MOZ_ASSERT(aConfig.mBitDepth == 16, "We only handle 16-bit audio!"); @@ -325,7 +357,6 @@ AndroidDecoderModule::CreateAudioDecoder(const AudioInfo& aConfig, new AudioDataDecoder(aConfig, format, aCallback); return decoder.forget(); - } PlatformDecoderModule::ConversionRequired @@ -333,9 +364,8 @@ AndroidDecoderModule::DecoderNeedsConversion(const TrackInfo& aConfig) const { if (aConfig.IsVideo()) { return kNeedAnnexB; - } else { - return kNeedNone; } + return kNeedNone; } MediaCodecDataDecoder::MediaCodecDataDecoder(MediaData::Type aType, @@ -349,9 +379,7 @@ MediaCodecDataDecoder::MediaCodecDataDecoder(MediaData::Type aType, , mInputBuffers(nullptr) , mOutputBuffers(nullptr) , mMonitor("MediaCodecDataDecoder::mMonitor") - , mFlushing(false) - , mDraining(false) - , mStopping(false) + , mState(kDecoding) { } @@ -361,7 +389,8 @@ MediaCodecDataDecoder::~MediaCodecDataDecoder() Shutdown(); } -RefPtr MediaCodecDataDecoder::Init() +RefPtr +MediaCodecDataDecoder::Init() { nsresult rv = InitDecoder(nullptr); @@ -371,10 +400,12 @@ RefPtr MediaCodecDataDecoder::Init() return NS_SUCCEEDED(rv) ? InitPromise::CreateAndResolve(type, __func__) : - InitPromise::CreateAndReject(MediaDataDecoder::DecoderFailureReason::INIT_ERROR, __func__); + InitPromise::CreateAndReject( + MediaDataDecoder::DecoderFailureReason::INIT_ERROR, __func__); } -nsresult MediaCodecDataDecoder::InitDecoder(Surface::Param aSurface) +nsresult +MediaCodecDataDecoder::InitDecoder(Surface::Param aSurface) { mDecoder = CreateDecoder(mMimeType); if (!mDecoder) { @@ -395,224 +426,312 @@ nsresult MediaCodecDataDecoder::InitDecoder(Surface::Param aSurface) return NS_OK; } -// This is in usec, so that's 10ms -#define DECODER_TIMEOUT 10000 +// This is in usec, so that's 10ms. +static const int64_t kDecoderTimeout = 10000; -#define HANDLE_DECODER_ERROR() \ +#define BREAK_ON_DECODER_ERROR() \ if (NS_FAILED(res)) { \ - NS_WARNING("exiting decoder loop due to exception"); \ - if (mDraining) { \ + NS_WARNING("Exiting decoder loop due to exception"); \ + if (State() == kDrainDecoder) { \ INVOKE_CALLBACK(DrainComplete); \ - mDraining = false; \ + State(kDecoding); \ } \ INVOKE_CALLBACK(Error); \ break; \ } -nsresult MediaCodecDataDecoder::GetInputBuffer(JNIEnv* env, int index, jni::Object::LocalRef* buffer) +nsresult +MediaCodecDataDecoder::GetInputBuffer( + JNIEnv* aEnv, int aIndex, jni::Object::LocalRef* aBuffer) { - bool retried = false; - while (!*buffer) { - *buffer = jni::Object::LocalRef::Adopt(env->GetObjectArrayElement(mInputBuffers.Get(), index)); - if (!*buffer) { - if (!retried) { - // Reset the input buffers and then try again - nsresult res = ResetInputBuffers(); - if (NS_FAILED(res)) { - return res; - } - retried = true; - } else { - // We already tried resetting the input buffers, return an error - return NS_ERROR_FAILURE; - } + MOZ_ASSERT(aEnv); + MOZ_ASSERT(!*aBuffer); + + int numTries = 2; + + while (numTries--) { + *aBuffer = jni::Object::LocalRef::Adopt( + aEnv->GetObjectArrayElement(mInputBuffers.Get(), aIndex)); + if (*aBuffer) { + return NS_OK; + } + nsresult res = ResetInputBuffers(); + if (NS_FAILED(res)) { + return res; } } + return NS_ERROR_FAILURE; +} + +bool +MediaCodecDataDecoder::WaitForInput() +{ + MonitorAutoLock lock(mMonitor); + + while (State() == kDecoding && mQueue.empty()) { + // Signal that we require more input. + INVOKE_CALLBACK(InputExhausted); + lock.Wait(); + } + + return State() != kStopping; +} + + +MediaRawData* +MediaCodecDataDecoder::PeekNextSample() +{ + MonitorAutoLock lock(mMonitor); + + if (State() == kFlushing) { + mDecoder->Flush(); + ClearQueue(); + State(kDecoding); + lock.Notify(); + return nullptr; + } + + if (mQueue.empty()) { + if (State() == kDrainQueue) { + State(kDrainDecoder); + } + return nullptr; + } + + // We're not stopping or flushing, so try to get a sample. + return mQueue.front(); +} + +nsresult +MediaCodecDataDecoder::QueueSample(const MediaRawData* aSample) +{ + MOZ_ASSERT(aSample); + AutoLocalJNIFrame frame(jni::GetEnvForThread(), 1); + + // We have a sample, try to feed it to the decoder. + int32_t inputIndex = -1; + nsresult res = mDecoder->DequeueInputBuffer(kDecoderTimeout, &inputIndex); + if (NS_FAILED(res)) { + return res; + } + + if (inputIndex < 0) { + // There is no valid input buffer available. + return NS_ERROR_FAILURE; + } + + jni::Object::LocalRef buffer(frame.GetEnv()); + res = GetInputBuffer(frame.GetEnv(), inputIndex, &buffer); + if (NS_FAILED(res)) { + return res; + } + + void* directBuffer = frame.GetEnv()->GetDirectBufferAddress(buffer.Get()); + + MOZ_ASSERT(frame.GetEnv()->GetDirectBufferCapacity(buffer.Get()) >= + aSample->Size(), + "Decoder buffer is not large enough for sample"); + + PodCopy(static_cast(directBuffer), aSample->Data(), aSample->Size()); + + res = mDecoder->QueueInputBuffer(inputIndex, 0, aSample->Size(), + aSample->mTime, 0); + if (NS_FAILED(res)) { + return res; + } + + mDurations.push(TimeUnit::FromMicroseconds(aSample->mDuration)); + return NS_OK; +} + +nsresult +MediaCodecDataDecoder::QueueEOS() +{ + mMonitor.AssertCurrentThreadOwns(); + + nsresult res = NS_OK; + int32_t inputIndex = -1; + res = mDecoder->DequeueInputBuffer(kDecoderTimeout, &inputIndex); + if (NS_FAILED(res) || inputIndex < 0) { + return res; + } + + res = mDecoder->QueueInputBuffer(inputIndex, 0, 0, 0, + MediaCodec::BUFFER_FLAG_END_OF_STREAM); + if (NS_SUCCEEDED(res)) { + State(kDrainWaitEOS); + mMonitor.Notify(); + } + return res; +} + +void +MediaCodecDataDecoder::HandleEOS(int32_t aOutputStatus) +{ + MonitorAutoLock lock(mMonitor); + + if (State() == kDrainWaitEOS) { + State(kDecoding); + mMonitor.Notify(); + + INVOKE_CALLBACK(DrainComplete); + } + + mDecoder->ReleaseOutputBuffer(aOutputStatus, false); +} + +TimeUnit +MediaCodecDataDecoder::GetOutputDuration() +{ + MOZ_ASSERT(!mDurations.empty(), "Should have had a duration queued"); + const TimeUnit duration = mDurations.front(); + mDurations.pop(); + return duration; +} + +nsresult +MediaCodecDataDecoder::ProcessOutput( + BufferInfo::Param aInfo, MediaFormat::Param aFormat, int32_t aStatus) +{ + AutoLocalJNIFrame frame(jni::GetEnvForThread(), 1); + + const TimeUnit duration = GetOutputDuration(); + const auto buffer = jni::Object::LocalRef::Adopt( + frame.GetEnv()->GetObjectArrayElement(mOutputBuffers.Get(), aStatus)); + + if (buffer) { + // The buffer will be null on Android L if we are decoding to a Surface. + void* directBuffer = frame.GetEnv()->GetDirectBufferAddress(buffer.Get()); + Output(aInfo, directBuffer, aFormat, duration); + } + + // The Surface will be updated at this point (for video). + mDecoder->ReleaseOutputBuffer(aStatus, true); + PostOutput(aInfo, aFormat, duration); return NS_OK; } -void MediaCodecDataDecoder::DecoderLoop() +void +MediaCodecDataDecoder::DecoderLoop() { - bool outputDone = false; - - bool draining = false; - bool waitingEOF = false; - + bool isOutputDone = false; AutoLocalJNIFrame frame(jni::GetEnvForThread(), 1); RefPtr sample; - MediaFormat::LocalRef outputFormat(frame.GetEnv()); - nsresult res; + nsresult res = NS_OK; + + while (WaitForInput()) { + sample = PeekNextSample(); - for (;;) { { MonitorAutoLock lock(mMonitor); - while (!mStopping && !mDraining && !mFlushing && mQueue.empty()) { - if (mQueue.empty()) { - // We could be waiting here forever if we don't signal that we need more input - INVOKE_CALLBACK(InputExhausted); - } - lock.Wait(); - } - - if (mStopping) { - // Get out of the loop. This is the only exit point. - break; - } - - if (mFlushing) { - mDecoder->Flush(); - ClearQueue(); - mFlushing = false; - lock.Notify(); - continue; - } - - // We're not stopping or draining, so try to get a sample - if (!mQueue.empty()) { - sample = mQueue.front(); - } - - if (mDraining && !sample && !waitingEOF) { - draining = true; - } - } - - if (draining && !waitingEOF) { - MOZ_ASSERT(!sample, "Shouldn't have a sample when pushing EOF frame"); - - int32_t inputIndex; - res = mDecoder->DequeueInputBuffer(DECODER_TIMEOUT, &inputIndex); - HANDLE_DECODER_ERROR(); - - if (inputIndex >= 0) { - res = mDecoder->QueueInputBuffer(inputIndex, 0, 0, 0, MediaCodec::BUFFER_FLAG_END_OF_STREAM); - HANDLE_DECODER_ERROR(); - - waitingEOF = true; + if (State() == kDrainDecoder) { + MOZ_ASSERT(!sample, "Shouldn't have a sample when pushing EOF frame"); + res = QueueEOS(); + BREAK_ON_DECODER_ERROR(); } } if (sample) { - // We have a sample, try to feed it to the decoder - int inputIndex; - res = mDecoder->DequeueInputBuffer(DECODER_TIMEOUT, &inputIndex); - HANDLE_DECODER_ERROR(); - - if (inputIndex >= 0) { - jni::Object::LocalRef buffer(frame.GetEnv()); - res = GetInputBuffer(frame.GetEnv(), inputIndex, &buffer); - HANDLE_DECODER_ERROR(); - - void* directBuffer = frame.GetEnv()->GetDirectBufferAddress(buffer.Get()); - - MOZ_ASSERT(frame.GetEnv()->GetDirectBufferCapacity(buffer.Get()) >= sample->Size(), - "Decoder buffer is not large enough for sample"); - - { - // We're feeding this to the decoder, so remove it from the queue - MonitorAutoLock lock(mMonitor); - mQueue.pop(); - } - - PodCopy((uint8_t*)directBuffer, sample->Data(), sample->Size()); - - res = mDecoder->QueueInputBuffer(inputIndex, 0, sample->Size(), - sample->mTime, 0); - HANDLE_DECODER_ERROR(); - - mDurations.push(media::TimeUnit::FromMicroseconds(sample->mDuration)); - sample = nullptr; - outputDone = false; + res = QueueSample(sample); + if (NS_SUCCEEDED(res)) { + // We've fed this into the decoder, so remove it from the queue. + MonitorAutoLock lock(mMonitor); + mQueue.pop(); + isOutputDone = false; } } - if (!outputDone) { - BufferInfo::LocalRef bufferInfo; - res = BufferInfo::New(&bufferInfo); - HANDLE_DECODER_ERROR(); + if (isOutputDone) { + continue; + } - int32_t outputStatus; - res = mDecoder->DequeueOutputBuffer(bufferInfo, DECODER_TIMEOUT, &outputStatus); - HANDLE_DECODER_ERROR(); + BufferInfo::LocalRef bufferInfo; + nsresult res = BufferInfo::New(&bufferInfo); + BREAK_ON_DECODER_ERROR(); - if (outputStatus == MediaCodec::INFO_TRY_AGAIN_LATER) { - // We might want to call mCallback->InputExhausted() here, but there seems to be - // some possible bad interactions here with the threading - } else if (outputStatus == MediaCodec::INFO_OUTPUT_BUFFERS_CHANGED) { - res = ResetOutputBuffers(); - HANDLE_DECODER_ERROR(); - } else if (outputStatus == MediaCodec::INFO_OUTPUT_FORMAT_CHANGED) { - res = mDecoder->GetOutputFormat(ReturnTo(&outputFormat)); - HANDLE_DECODER_ERROR(); - } else if (outputStatus < 0) { - NS_WARNING("unknown error from decoder!"); - INVOKE_CALLBACK(Error); + int32_t outputStatus = -1; + res = mDecoder->DequeueOutputBuffer(bufferInfo, kDecoderTimeout, + &outputStatus); + BREAK_ON_DECODER_ERROR(); - // Don't break here just in case it's recoverable. If it's not, others stuff will fail later and - // we'll bail out. - } else { - int32_t flags; - res = bufferInfo->Flags(&flags); - HANDLE_DECODER_ERROR(); + if (outputStatus == MediaCodec::INFO_TRY_AGAIN_LATER) { + // We might want to call mCallback->InputExhausted() here, but there seems + // to be some possible bad interactions here with the threading. + } else if (outputStatus == MediaCodec::INFO_OUTPUT_BUFFERS_CHANGED) { + res = ResetOutputBuffers(); + BREAK_ON_DECODER_ERROR(); + } else if (outputStatus == MediaCodec::INFO_OUTPUT_FORMAT_CHANGED) { + res = mDecoder->GetOutputFormat(ReturnTo(&outputFormat)); + BREAK_ON_DECODER_ERROR(); + } else if (outputStatus < 0) { + NS_WARNING("Unknown error from decoder!"); + INVOKE_CALLBACK(Error); + // Don't break here just in case it's recoverable. If it's not, other + // stuff will fail later and we'll bail out. + } else { + // We have a valid buffer index >= 0 here. + int32_t flags; + nsresult res = bufferInfo->Flags(&flags); + BREAK_ON_DECODER_ERROR(); - // We have a valid buffer index >= 0 here - if (flags & MediaCodec::BUFFER_FLAG_END_OF_STREAM) { - if (draining) { - draining = false; - waitingEOF = false; - - mMonitor.Lock(); - mDraining = false; - mMonitor.Notify(); - mMonitor.Unlock(); - - INVOKE_CALLBACK(DrainComplete); - } - - mDecoder->ReleaseOutputBuffer(outputStatus, false); - outputDone = true; - - // We only queue empty EOF frames, so we're done for now - continue; - } - - MOZ_ASSERT(!mDurations.empty(), "Should have had a duration queued"); - - media::TimeUnit duration; - if (!mDurations.empty()) { - duration = mDurations.front(); - mDurations.pop(); - } - - auto buffer = jni::Object::LocalRef::Adopt( - frame.GetEnv()->GetObjectArrayElement(mOutputBuffers.Get(), outputStatus)); - if (buffer) { - // The buffer will be null on Android L if we are decoding to a Surface - void* directBuffer = frame.GetEnv()->GetDirectBufferAddress(buffer.Get()); - Output(bufferInfo, directBuffer, outputFormat, duration); - } - - // The Surface will be updated at this point (for video) - mDecoder->ReleaseOutputBuffer(outputStatus, true); - - PostOutput(bufferInfo, outputFormat, duration); + if (flags & MediaCodec::BUFFER_FLAG_END_OF_STREAM) { + HandleEOS(outputStatus); + isOutputDone = true; + // We only queue empty EOF frames, so we're done for now. + continue; } + + res = ProcessOutput(bufferInfo, outputFormat, outputStatus); + BREAK_ON_DECODER_ERROR(); } } Cleanup(); - // We're done + // We're done. MonitorAutoLock lock(mMonitor); - mStopping = false; + State(kShutdown); mMonitor.Notify(); } -void MediaCodecDataDecoder::ClearQueue() +const char* +MediaCodecDataDecoder::ModuleStateStr(ModuleState aState) { + static const char* kStr[] = { + "Decoding", "Flushing", "DrainQueue", "DrainDecoder", "DrainWaitEOS", + "Stopping", "Shutdown" + }; + + MOZ_ASSERT(aState < sizeof(kStr) / sizeof(kStr[0])); + return kStr[aState]; +} + +MediaCodecDataDecoder::ModuleState +MediaCodecDataDecoder::State() const +{ + return mState; +} + +void +MediaCodecDataDecoder::State(ModuleState aState) +{ + LOG("%s -> %s", ModuleStateStr(mState), ModuleStateStr(aState)); + + if (aState == kDrainDecoder) { + MOZ_ASSERT(mState == kDrainQueue); + } else if (aState == kDrainWaitEOS) { + MOZ_ASSERT(mState == kDrainDecoder); + } + + mState = aState; +} + +void +MediaCodecDataDecoder::ClearQueue() { mMonitor.AssertCurrentThreadOwns(); + while (!mQueue.empty()) { mQueue.pop(); } @@ -621,7 +740,9 @@ void MediaCodecDataDecoder::ClearQueue() } } -nsresult MediaCodecDataDecoder::Input(MediaRawData* aSample) { +nsresult +MediaCodecDataDecoder::Input(MediaRawData* aSample) +{ MonitorAutoLock lock(mMonitor); mQueue.push(aSample); lock.NotifyAll(); @@ -629,53 +750,61 @@ nsresult MediaCodecDataDecoder::Input(MediaRawData* aSample) { return NS_OK; } -nsresult MediaCodecDataDecoder::ResetInputBuffers() +nsresult +MediaCodecDataDecoder::ResetInputBuffers() { return mDecoder->GetInputBuffers(ReturnTo(&mInputBuffers)); } -nsresult MediaCodecDataDecoder::ResetOutputBuffers() +nsresult +MediaCodecDataDecoder::ResetOutputBuffers() { return mDecoder->GetOutputBuffers(ReturnTo(&mOutputBuffers)); } -nsresult MediaCodecDataDecoder::Flush() { +nsresult +MediaCodecDataDecoder::Flush() +{ MonitorAutoLock lock(mMonitor); - mFlushing = true; + State(kFlushing); lock.Notify(); - while (mFlushing) { + while (State() == kFlushing) { lock.Wait(); } return NS_OK; } -nsresult MediaCodecDataDecoder::Drain() { +nsresult +MediaCodecDataDecoder::Drain() +{ MonitorAutoLock lock(mMonitor); - if (mDraining) { + if (State() == kDrainDecoder || State() == kDrainQueue) { return NS_OK; } - mDraining = true; + State(kDrainQueue); lock.Notify(); return NS_OK; } -nsresult MediaCodecDataDecoder::Shutdown() { +nsresult +MediaCodecDataDecoder::Shutdown() +{ MonitorAutoLock lock(mMonitor); - if (!mThread || mStopping) { + if (!mThread || State() == kStopping) { // Already shutdown or in the process of doing so return NS_OK; } - mStopping = true; + State(kStopping); lock.Notify(); - while (mStopping) { + while (State() == kStopping) { lock.Wait(); } diff --git a/dom/media/platforms/android/AndroidDecoderModule.h b/dom/media/platforms/android/AndroidDecoderModule.h index 28ff19639e76..d3f216d1725c 100644 --- a/dom/media/platforms/android/AndroidDecoderModule.h +++ b/dom/media/platforms/android/AndroidDecoderModule.h @@ -36,7 +36,7 @@ public: AndroidDecoderModule() {} virtual ~AndroidDecoderModule() {} - bool SupportsMimeType(const nsACString& aMimeType) override; + bool SupportsMimeType(const nsACString& aMimeType) const override; ConversionRequired DecoderNeedsConversion(const TrackInfo& aConfig) const override; @@ -59,8 +59,55 @@ public: nsresult Input(MediaRawData* aSample) override; protected: + enum ModuleState { + kDecoding = 0, + kFlushing, + kDrainQueue, + kDrainDecoder, + kDrainWaitEOS, + kStopping, + kShutdown + }; + friend class AndroidDecoderModule; + static const char* ModuleStateStr(ModuleState aState); + + virtual nsresult InitDecoder(widget::sdk::Surface::Param aSurface); + + virtual nsresult Output(widget::sdk::BufferInfo::Param aInfo, void* aBuffer, + widget::sdk::MediaFormat::Param aFormat, const media::TimeUnit& aDuration) + { + return NS_OK; + } + + virtual nsresult PostOutput(widget::sdk::BufferInfo::Param aInfo, + widget::sdk::MediaFormat::Param aFormat, const media::TimeUnit& aDuration) + { + return NS_OK; + } + + virtual void Cleanup() {}; + + nsresult ResetInputBuffers(); + nsresult ResetOutputBuffers(); + + nsresult GetInputBuffer(JNIEnv* env, int index, jni::Object::LocalRef* buffer); + bool WaitForInput(); + MediaRawData* PeekNextSample(); + nsresult QueueSample(const MediaRawData* aSample); + nsresult QueueEOS(); + void HandleEOS(int32_t aOutputStatus); + media::TimeUnit GetOutputDuration(); + nsresult ProcessOutput(widget::sdk::BufferInfo::Param aInfo, + widget::sdk::MediaFormat::Param aFormat, + int32_t aStatus); + ModuleState State() const; + void State(ModuleState aState); + void DecoderLoop(); + + virtual void ClearQueue(); + MediaData::Type mType; nsAutoCString mMimeType; @@ -77,28 +124,14 @@ protected: // Only these members are protected by mMonitor. Monitor mMonitor; - bool mFlushing; - bool mDraining; - bool mStopping; + + ModuleState mState; SampleQueue mQueue; // Durations are stored in microseconds. std::queue mDurations; - - virtual nsresult InitDecoder(widget::sdk::Surface::Param aSurface); - - virtual nsresult Output(widget::sdk::BufferInfo::Param aInfo, void* aBuffer, widget::sdk::MediaFormat::Param aFormat, const media::TimeUnit& aDuration) { return NS_OK; } - virtual nsresult PostOutput(widget::sdk::BufferInfo::Param aInfo, widget::sdk::MediaFormat::Param aFormat, const media::TimeUnit& aDuration) { return NS_OK; } - virtual void Cleanup() {}; - - nsresult ResetInputBuffers(); - nsresult ResetOutputBuffers(); - - void DecoderLoop(); - nsresult GetInputBuffer(JNIEnv* env, int index, jni::Object::LocalRef* buffer); - virtual void ClearQueue(); }; -} // namwspace mozilla +} // namespace mozilla #endif diff --git a/dom/media/platforms/apple/AppleDecoderModule.cpp b/dom/media/platforms/apple/AppleDecoderModule.cpp index cc4e6b83a67e..a1980c3fc9a3 100644 --- a/dom/media/platforms/apple/AppleDecoderModule.cpp +++ b/dom/media/platforms/apple/AppleDecoderModule.cpp @@ -117,7 +117,7 @@ AppleDecoderModule::CreateAudioDecoder(const AudioInfo& aConfig, } bool -AppleDecoderModule::SupportsMimeType(const nsACString& aMimeType) +AppleDecoderModule::SupportsMimeType(const nsACString& aMimeType) const { return (sIsCoreMediaAvailable && (aMimeType.EqualsLiteral("audio/mpeg") || diff --git a/dom/media/platforms/apple/AppleDecoderModule.h b/dom/media/platforms/apple/AppleDecoderModule.h index fb7bb7465d19..56d61b841f30 100644 --- a/dom/media/platforms/apple/AppleDecoderModule.h +++ b/dom/media/platforms/apple/AppleDecoderModule.h @@ -32,7 +32,7 @@ public: FlushableTaskQueue* aAudioTaskQueue, MediaDataDecoderCallback* aCallback) override; - bool SupportsMimeType(const nsACString& aMimeType) override; + bool SupportsMimeType(const nsACString& aMimeType) const override; ConversionRequired DecoderNeedsConversion(const TrackInfo& aConfig) const override; diff --git a/dom/media/platforms/ffmpeg/FFmpegDecoderModule.h b/dom/media/platforms/ffmpeg/FFmpegDecoderModule.h index fae53dfb7577..dbda9e7c8b14 100644 --- a/dom/media/platforms/ffmpeg/FFmpegDecoderModule.h +++ b/dom/media/platforms/ffmpeg/FFmpegDecoderModule.h @@ -61,7 +61,7 @@ public: return decoder.forget(); } - bool SupportsMimeType(const nsACString& aMimeType) override + bool SupportsMimeType(const nsACString& aMimeType) const override { AVCodecID audioCodec = FFmpegAudioDecoder::GetCodecId(aMimeType); AVCodecID videoCodec = FFmpegH264Decoder::GetCodecId(aMimeType); diff --git a/dom/media/platforms/gonk/GonkDecoderModule.cpp b/dom/media/platforms/gonk/GonkDecoderModule.cpp index 4d9026144776..60d0a3166cfc 100644 --- a/dom/media/platforms/gonk/GonkDecoderModule.cpp +++ b/dom/media/platforms/gonk/GonkDecoderModule.cpp @@ -61,7 +61,7 @@ GonkDecoderModule::DecoderNeedsConversion(const TrackInfo& aConfig) const } bool -GonkDecoderModule::SupportsMimeType(const nsACString& aMimeType) +GonkDecoderModule::SupportsMimeType(const nsACString& aMimeType) const { return aMimeType.EqualsLiteral("audio/mp4a-latm") || aMimeType.EqualsLiteral("audio/3gpp") || diff --git a/dom/media/platforms/gonk/GonkDecoderModule.h b/dom/media/platforms/gonk/GonkDecoderModule.h index efb80572bc1a..5034f3ca2ba4 100644 --- a/dom/media/platforms/gonk/GonkDecoderModule.h +++ b/dom/media/platforms/gonk/GonkDecoderModule.h @@ -35,7 +35,7 @@ public: ConversionRequired DecoderNeedsConversion(const TrackInfo& aConfig) const override; - bool SupportsMimeType(const nsACString& aMimeType) override; + bool SupportsMimeType(const nsACString& aMimeType) const override; }; diff --git a/dom/media/platforms/wmf/WMFDecoderModule.cpp b/dom/media/platforms/wmf/WMFDecoderModule.cpp index 6fb1ab7bb62d..875e206162bd 100644 --- a/dom/media/platforms/wmf/WMFDecoderModule.cpp +++ b/dom/media/platforms/wmf/WMFDecoderModule.cpp @@ -164,7 +164,7 @@ CanCreateWMFDecoder() } bool -WMFDecoderModule::SupportsMimeType(const nsACString& aMimeType) +WMFDecoderModule::SupportsMimeType(const nsACString& aMimeType) const { if ((aMimeType.EqualsLiteral("audio/mp4a-latm") || aMimeType.EqualsLiteral("audio/mp4")) && diff --git a/dom/media/platforms/wmf/WMFDecoderModule.h b/dom/media/platforms/wmf/WMFDecoderModule.h index 6ebb83f65003..a959e26091fc 100644 --- a/dom/media/platforms/wmf/WMFDecoderModule.h +++ b/dom/media/platforms/wmf/WMFDecoderModule.h @@ -31,7 +31,7 @@ public: FlushableTaskQueue* aAudioTaskQueue, MediaDataDecoderCallback* aCallback) override; - bool SupportsMimeType(const nsACString& aMimeType) override; + bool SupportsMimeType(const nsACString& aMimeType) const override; ConversionRequired DecoderNeedsConversion(const TrackInfo& aConfig) const override; diff --git a/dom/media/webaudio/AudioContext.cpp b/dom/media/webaudio/AudioContext.cpp index 1d52ac34655a..b699bef6ba49 100644 --- a/dom/media/webaudio/AudioContext.cpp +++ b/dom/media/webaudio/AudioContext.cpp @@ -74,7 +74,9 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_ADDREF_INHERITED(AudioContext, DOMEventTargetHelper) NS_IMPL_RELEASE_INHERITED(AudioContext, DOMEventTargetHelper) + NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(AudioContext) + NS_INTERFACE_MAP_ENTRY(nsIMemoryReporter) NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) static float GetSampleRateForAudioContext(bool aIsOffline, float aSampleRate) diff --git a/dom/media/webaudio/AudioDestinationNode.cpp b/dom/media/webaudio/AudioDestinationNode.cpp index 329e8d88f6d2..9b7d8501b64a 100644 --- a/dom/media/webaudio/AudioDestinationNode.cpp +++ b/dom/media/webaudio/AudioDestinationNode.cpp @@ -178,7 +178,9 @@ public: virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override { size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf); - amount += mBuffer->SizeOfIncludingThis(aMallocSizeOf); + if (mBuffer) { + amount += mBuffer->SizeOfIncludingThis(aMallocSizeOf); + } return amount; } diff --git a/dom/media/webaudio/MediaBufferDecoder.cpp b/dom/media/webaudio/MediaBufferDecoder.cpp index bb1eb60169fe..a2b1b7365811 100644 --- a/dom/media/webaudio/MediaBufferDecoder.cpp +++ b/dom/media/webaudio/MediaBufferDecoder.cpp @@ -600,7 +600,9 @@ WebAudioDecodeJob::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const if (mOutput) { amount += mOutput->SizeOfIncludingThis(aMallocSizeOf); } - amount += mBuffer->SizeOfIncludingThis(aMallocSizeOf); + if (mBuffer) { + amount += mBuffer->SizeOfIncludingThis(aMallocSizeOf); + } return amount; } diff --git a/dom/media/webaudio/test/mochitest.ini b/dom/media/webaudio/test/mochitest.ini index bf497a493030..08935990a38f 100644 --- a/dom/media/webaudio/test/mochitest.ini +++ b/dom/media/webaudio/test/mochitest.ini @@ -183,4 +183,3 @@ skip-if = (toolkit == 'gonk' && !debug) || android_version == '10' || android_ve [test_waveShaperPassThrough.html] [test_waveShaperInvalidLengthCurve.html] [test_WebAudioMemoryReporting.html] -skip-if = debug # assertion failures: bug 1222202 diff --git a/dom/media/webaudio/test/test_WebAudioMemoryReporting.html b/dom/media/webaudio/test/test_WebAudioMemoryReporting.html index 60db95ccf47e..c753756e79fb 100644 --- a/dom/media/webaudio/test/test_WebAudioMemoryReporting.html +++ b/dom/media/webaudio/test/test_WebAudioMemoryReporting.html @@ -13,10 +13,14 @@ var ac = new AudioContext(); var sp = ac.createScriptProcessor(4096, 1, 1); sp.connect(ac.destination); +// Not started so as to test +// https://bugzilla.mozilla.org/show_bug.cgi?id=1225003#c2 +var oac = new OfflineAudioContext(1, 1, 48000); + var nodeTypes = ["ScriptProcessorNode", "AudioDestinationNode"]; var objectTypes = ["dom-nodes", "engine-objects", "stream-objects"]; -var usages = {}; +var usages = { "explicit/webaudio/audiocontext": 0 }; for (var i = 0; i < nodeTypes.length; ++i) { for (var j = 0; j < objectTypes.length; ++j) { @@ -43,5 +47,8 @@ SpecialPowers.Cc["@mozilla.org/memory-reporter-manager;1"]. getService(SpecialPowers.Ci.nsIMemoryReporterManager). getReports(handleReport, null, finished, null, /* anonymized = */ false); +// To test bug 1225003, run a failing decodeAudioData() job over a time when +// the tasks from getReports() are expected to run. +ac.decodeAudioData(new ArrayBuffer(4), function(){}, function(){}); diff --git a/dom/messagechannel/MessagePort.cpp b/dom/messagechannel/MessagePort.cpp index d7d5202e7522..967b6655440e 100644 --- a/dom/messagechannel/MessagePort.cpp +++ b/dom/messagechannel/MessagePort.cpp @@ -149,6 +149,10 @@ public: bool dummy; mPort->DispatchEvent(static_cast(event.get()), &dummy); + + // We must check if we were waiting for this message in order to shutdown + // the port. + mPort->UpdateMustKeepAlive(); return NS_OK; } @@ -216,8 +220,10 @@ public: virtual bool Notify(JSContext* aCx, workers::Status aStatus) override { - if (mPort && aStatus > Running) { - mPort->Close(); + if (aStatus > Running) { + // We cannot process messages anymore because we cannot dispatch new + // runnables. Let's force a Close(). + mPort->CloseForced(); } return true; @@ -289,7 +295,7 @@ MessagePort::MessagePort(nsPIDOMWindow* aWindow) MessagePort::~MessagePort() { - Close(); + CloseForced(); MOZ_ASSERT(!mWorkerFeature); } @@ -336,7 +342,6 @@ MessagePort::Initialize(const nsID& aUUID, mIdentifier->sequenceId() = aSequenceID; mState = aState; - mNextStep = eNextStepNone; if (mNeutered) { // If this port is neutered we don't want to keep it alive artificially nor @@ -452,8 +457,9 @@ MessagePort::PostMessage(JSContext* aCx, JS::Handle aMessage, return; } - // Not entangled yet, but already closed. - if (mNextStep != eNextStepNone) { + // Not entangled yet, but already closed/disentangled. + if (mState == eStateEntanglingForDisentangle || + mState == eStateEntanglingForClose) { return; } @@ -490,11 +496,55 @@ MessagePort::Start() void MessagePort::Dispatch() { - if (!mMessageQueueEnabled || mMessages.IsEmpty() || mDispatchRunnable || - mState > eStateEntangled || mNextStep != eNextStepNone) { + if (!mMessageQueueEnabled || mMessages.IsEmpty() || mDispatchRunnable) { return; } + switch (mState) { + case eStateUnshippedEntangled: + // Everything is fine here. We have messages because the other + // port populates our queue directly. + break; + + case eStateEntangling: + // Everything is fine here as well. We have messages because the other + // port populated our queue directly when we were in the + // eStateUnshippedEntangled state. + break; + + case eStateEntanglingForDisentangle: + // Here we don't want to ship messages because these messages must be + // delivered by the cloned version of this one. They will be sent in the + // SendDisentangle(). + return; + + case eStateEntanglingForClose: + // We still want to deliver messages if we are closing. These messages + // are here from the previous eStateUnshippedEntangled state. + break; + + case eStateEntangled: + // This port is up and running. + break; + + case eStateDisentangling: + // If we are in the process to disentangle the port, we cannot dispatch + // messages. They will be sent to the cloned version of this port via + // SendDisentangle(); + return; + + case eStateDisentangled: + MOZ_CRASH("This cannot happen."); + // It cannot happen because Disentangle should take off all the pending + // messages. + break; + + case eStateDisentangledForClose: + // If we are here is because the port has been closed. We can still + // process the pending messages. + break; + } + RefPtr data = mMessages.ElementAt(0); mMessages.RemoveElementAt(0); @@ -510,6 +560,24 @@ MessagePort::Dispatch() void MessagePort::Close() { + CloseInternal(true /* aSoftly */); +} + +void +MessagePort::CloseForced() +{ + CloseInternal(false /* aSoftly */); +} + +void +MessagePort::CloseInternal(bool aSoftly) +{ + // If we have some messages to send but we don't want a 'soft' close, we have + // to flush them now. + if (!aSoftly) { + mMessages.Clear(); + } + if (mState == eStateUnshippedEntangled) { MOZ_ASSERT(mUnshippedEntangledPort); @@ -517,8 +585,8 @@ MessagePort::Close() RefPtr port = Move(mUnshippedEntangledPort); MOZ_ASSERT(mUnshippedEntangledPort == nullptr); - mState = eStateDisentangled; - port->Close(); + mState = eStateDisentangledForClose; + port->CloseInternal(aSoftly); UpdateMustKeepAlive(); return; @@ -526,7 +594,20 @@ MessagePort::Close() // Not entangled yet, we have to wait. if (mState == eStateEntangling) { - mNextStep = eNextStepClose; + mState = eStateEntanglingForClose; + return; + } + + // Not entangled but already cloned or closed + if (mState == eStateEntanglingForDisentangle || + mState == eStateEntanglingForClose) { + return; + } + + // Maybe we were already closing the port but softly. In this case we call + // UpdateMustKeepAlive() to consider the empty pending message queue. + if (mState == eStateDisentangledForClose && !aSoftly) { + UpdateMustKeepAlive(); return; } @@ -536,7 +617,7 @@ MessagePort::Close() // We don't care about stopping the sending of messages because from now all // the incoming messages will be ignored. - mState = eStateDisentangled; + mState = eStateDisentangledForClose; MOZ_ASSERT(mActor); @@ -576,8 +657,11 @@ MessagePort::SetOnmessage(EventHandlerNonNull* aCallback) void MessagePort::Entangled(nsTArray& aMessages) { - MOZ_ASSERT(mState == eStateEntangling); + MOZ_ASSERT(mState == eStateEntangling || + mState == eStateEntanglingForDisentangle || + mState == eStateEntanglingForClose); + State oldState = mState; mState = eStateEntangled; // If we have pending messages, these have to be sent. @@ -598,8 +682,10 @@ MessagePort::Entangled(nsTArray& aMessages) return; } - if (mNextStep == eNextStepClose) { - Close(); + // If the next step is to close the port, we do it ignoring the received + // messages. + if (oldState == eStateEntanglingForClose) { + CloseForced(); return; } @@ -607,12 +693,11 @@ MessagePort::Entangled(nsTArray& aMessages) // We were waiting for the entangling callback in order to disentangle this // port immediately after. - if (mNextStep == eNextStepDisentangle) { + if (oldState == eStateEntanglingForDisentangle) { StartDisentangling(); return; } - MOZ_ASSERT(mNextStep == eNextStepNone); Dispatch(); } @@ -623,7 +708,6 @@ MessagePort::StartDisentangling() MOZ_ASSERT(mState == eStateEntangled); mState = eStateDisentangling; - mNextStep = eNextStepNone; // Sending this message we communicate to the parent actor that we don't want // to receive any new messages. It is possible that a message has been @@ -635,8 +719,12 @@ MessagePort::StartDisentangling() void MessagePort::MessagesReceived(nsTArray& aMessages) { - MOZ_ASSERT(mState == eStateEntangled || mState == eStateDisentangling); - MOZ_ASSERT(mNextStep == eNextStepNone); + MOZ_ASSERT(mState == eStateEntangled || + mState == eStateDisentangling || + // This last step can happen only if Close() has been called + // manually. At this point SendClose() is sent but we can still + // receive something until the Closing request is processed. + mState == eStateDisentangledForClose); MOZ_ASSERT(mMessagesForTheOtherPort.IsEmpty()); RemoveDocFromBFCache(); @@ -700,7 +788,8 @@ MessagePort::CloneAndDisentangle(MessagePortIdentifier& aIdentifier) // We already have a 'next step'. We have to consider this port as already // cloned/closed/disentangled. - if (mNextStep != eNextStepNone) { + if (mState == eStateEntanglingForDisentangle || + mState == eStateEntanglingForClose) { return; } @@ -730,27 +819,28 @@ MessagePort::CloneAndDisentangle(MessagePortIdentifier& aIdentifier) // Register this component to PBackground. ConnectToPBackground(); - mNextStep = eNextStepDisentangle; + mState = eStateEntanglingForDisentangle; return; } // Not entangled yet, we have to wait. - if (mState < eStateEntangled) { - mNextStep = eNextStepDisentangle; + if (mState == eStateEntangling) { + mState = eStateEntanglingForDisentangle; return; } + MOZ_ASSERT(mState == eStateEntangled); StartDisentangling(); } void MessagePort::Closed() { - if (mState == eStateDisentangled) { + if (mState >= eStateDisentangled) { return; } - mState = eStateDisentangled; + mState = eStateDisentangledForClose; if (mActor) { mActor->SetPort(nullptr); @@ -789,7 +879,9 @@ MessagePort::ActorCreated(mozilla::ipc::PBackgroundChild* aActor) MOZ_ASSERT(aActor); MOZ_ASSERT(!mActor); MOZ_ASSERT(mIdentifier); - MOZ_ASSERT(mState == eStateEntangling); + MOZ_ASSERT(mState == eStateEntangling || + mState == eStateEntanglingForDisentangle || + mState == eStateEntanglingForClose); PMessagePortChild* actor = aActor->SendPMessagePortConstructor(mIdentifier->uuid(), @@ -805,7 +897,9 @@ MessagePort::ActorCreated(mozilla::ipc::PBackgroundChild* aActor) void MessagePort::UpdateMustKeepAlive() { - if (mState == eStateDisentangled && mIsKeptAlive) { + if (mState >= eStateDisentangled && + mMessages.IsEmpty() && + mIsKeptAlive) { mIsKeptAlive = false; if (mWorkerFeature) { @@ -859,7 +953,7 @@ MessagePort::Observe(nsISupports* aSubject, const char* aTopic, NS_ENSURE_SUCCESS(rv, rv); if (innerID == mInnerID) { - Close(); + CloseForced(); } return NS_OK; diff --git a/dom/messagechannel/MessagePort.h b/dom/messagechannel/MessagePort.h index 34e3731bef97..a9600d4fece3 100644 --- a/dom/messagechannel/MessagePort.h +++ b/dom/messagechannel/MessagePort.h @@ -25,6 +25,7 @@ class DispatchEventRunnable; class MessagePortChild; class MessagePortIdentifier; class MessagePortMessage; +class PostMessageRunnable; class SharedMessagePortMessage; namespace workers { @@ -36,6 +37,7 @@ class MessagePort final : public DOMEventTargetHelper , public nsIObserver { friend class DispatchEventRunnable; + friend class PostMessageRunnable; public: NS_DECL_NSIIPCBACKGROUNDCHILDCREATECALLBACK @@ -52,6 +54,7 @@ public: Create(nsPIDOMWindow* aWindow, const MessagePortIdentifier& aIdentifier, ErrorResult& aRv); + // For IPC. static void ForceClose(const MessagePortIdentifier& aIdentifier); @@ -77,6 +80,8 @@ public: void CloneAndDisentangle(MessagePortIdentifier& aIdentifier); + void CloseForced(); + // These methods are useful for MessagePortChild void Entangled(nsTArray& aMessages); @@ -96,11 +101,16 @@ private: // StateEntangling. eStateUnshippedEntangled, - // If the port is closed or cloned when we are in this state, we set the - // mNextStep. This 'next' operation will be done when entangled() message - // is received. + // If the port is closed or cloned when we are in this state, we go in one + // of the following 2 steps. EntanglingForClose or ForDisentangle. eStateEntangling, + // We are not fully entangled yet but are already disentangled. + eStateEntanglingForDisentangle, + + // We are not fully entangled yet but are already closed. + eStateEntanglingForClose, + // When entangled() is received we send all the messages in the // mMessagesForTheOtherPort to the actor and we change the state to // StateEntangled. At this point the port is entangled with the other. We @@ -121,7 +131,11 @@ private: // don't receive any other message, so nothing will be lost. // Disentangling the port we send all the messages from the mMessages // though the actor. - eStateDisentangled + eStateDisentangled, + + // We are here if Close() has been called. We are disentangled but we can + // still send pending messages. + eStateDisentangledForClose }; void Initialize(const nsID& aUUID, const nsID& aDestinationUUID, @@ -138,6 +152,8 @@ private: void RemoveDocFromBFCache(); + void CloseInternal(bool aSoftly); + // This method is meant to keep alive the MessagePort when this object is // creating the actor and until the actor is entangled. // We release the object when the port is closed or disentangled. @@ -165,14 +181,6 @@ private: State mState; - // This 'nextStep' is used when we are waiting to be entangled but the - // content has called Clone() or Close(). - enum { - eNextStepNone, - eNextStepDisentangle, - eNextStepClose - } mNextStep; - bool mMessageQueueEnabled; bool mIsKeptAlive; diff --git a/dom/messagechannel/MessagePortService.cpp b/dom/messagechannel/MessagePortService.cpp index 809b4a8a0f08..6f95ec340478 100644 --- a/dom/messagechannel/MessagePortService.cpp +++ b/dom/messagechannel/MessagePortService.cpp @@ -36,6 +36,9 @@ public: : mDestinationUUID(aDestinationUUID) , mSequenceID(1) , mParent(nullptr) + // By default we don't know the next parent. + , mWaitingForNewParent(true) + , mNextStepCloseAll(false) { MOZ_COUNT_CTOR(MessagePortServiceData); } @@ -62,6 +65,9 @@ public: FallibleTArray mNextParents; FallibleTArray> mMessages; + + bool mWaitingForNewParent; + bool mNextStepCloseAll; }; /* static */ MessagePortService* @@ -113,31 +119,49 @@ MessagePortService::RequestEntangling(MessagePortParent* aParent, // This is a security check. if (!data->mDestinationUUID.Equals(aDestinationUUID)) { MOZ_ASSERT(false, "DestinationUUIDs do not match!"); + CloseAll(aParent->ID()); return false; } if (aSequenceID < data->mSequenceID) { MOZ_ASSERT(false, "Invalid sequence ID!"); + CloseAll(aParent->ID()); return false; } if (aSequenceID == data->mSequenceID) { if (data->mParent) { MOZ_ASSERT(false, "Two ports cannot have the same sequenceID."); + CloseAll(aParent->ID()); return false; } // We activate this port, sending all the messages. data->mParent = aParent; + data->mWaitingForNewParent = false; FallibleTArray array; if (!SharedMessagePortMessage::FromSharedToMessagesParent(aParent, data->mMessages, array)) { + CloseAll(aParent->ID()); return false; } data->mMessages.Clear(); - return aParent->Entangled(array); + + // We can entangle the port. + if (!aParent->Entangled(array)) { + CloseAll(aParent->ID()); + return false; + } + + // If we were waiting for this parent in order to close this channel, this + // is the time to do it. + if (data->mNextStepCloseAll) { + CloseAll(aParent->ID()); + } + + return true; } // This new parent will be the next one when a Disentangle request is @@ -145,6 +169,7 @@ MessagePortService::RequestEntangling(MessagePortParent* aParent, MessagePortServiceData::NextParent* nextParent = data->mNextParents.AppendElement(mozilla::fallible); if (!nextParent) { + CloseAll(aParent->ID()); return false; } @@ -193,6 +218,7 @@ MessagePortService::DisentanglePort( // We didn't find the parent. if (!nextParent) { data->mMessages.SwapElements(aMessages); + data->mWaitingForNewParent = true; data->mParent = nullptr; return true; } @@ -250,7 +276,7 @@ MessagePortService::CloseAllDebugCheck(const nsID& aID, #endif void -MessagePortService::CloseAll(const nsID& aUUID) +MessagePortService::CloseAll(const nsID& aUUID, bool aForced) { MessagePortServiceData* data; if (!mPorts.Get(aUUID, &data)) { @@ -267,9 +293,24 @@ MessagePortService::CloseAll(const nsID& aUUID) } nsID destinationUUID = data->mDestinationUUID; + + // If we have informations about the other port and that port has some + // pending messages to deliver but the parent has not processed them yet, + // because its entangling request didn't arrive yet), we cannot close this + // channel. + MessagePortServiceData* destinationData; + if (!aForced && + mPorts.Get(destinationUUID, &destinationData) && + !destinationData->mMessages.IsEmpty() && + destinationData->mWaitingForNewParent) { + MOZ_ASSERT(!destinationData->mNextStepCloseAll); + destinationData->mNextStepCloseAll = true; + return; + } + mPorts.Remove(aUUID); - CloseAll(destinationUUID); + CloseAll(destinationUUID, aForced); // CloseAll calls itself recursively and it can happen that it deletes // itself. Before continuing we must check if we are still alive. @@ -370,7 +411,7 @@ MessagePortService::ForceClose(const nsID& aUUID, return false; } - CloseAll(aUUID); + CloseAll(aUUID, true); return true; } diff --git a/dom/messagechannel/MessagePortService.h b/dom/messagechannel/MessagePortService.h index 7c09dd26264b..c90fb0e899ad 100644 --- a/dom/messagechannel/MessagePortService.h +++ b/dom/messagechannel/MessagePortService.h @@ -46,7 +46,7 @@ public: private: ~MessagePortService() {} - void CloseAll(const nsID& aUUID); + void CloseAll(const nsID& aUUID, bool aForced = false); void MaybeShutdown(); class MessagePortServiceData; diff --git a/dom/messagechannel/tests/mochitest.ini b/dom/messagechannel/tests/mochitest.ini index 3839c7c74f1a..67d18d06fb61 100644 --- a/dom/messagechannel/tests/mochitest.ini +++ b/dom/messagechannel/tests/mochitest.ini @@ -24,3 +24,5 @@ support-files = [test_messageChannel_any.html] [test_messageChannel_forceClose.html] [test_messageChannel_bug1178076.html] +[test_messageChannel_bug1224825.html] +[test_messageChannel_worker_forceClose.html] diff --git a/dom/messagechannel/tests/test_messageChannel_bug1224825.html b/dom/messagechannel/tests/test_messageChannel_bug1224825.html new file mode 100644 index 000000000000..f501155479bd --- /dev/null +++ b/dom/messagechannel/tests/test_messageChannel_bug1224825.html @@ -0,0 +1,94 @@ + + + + + + Test for Bug 1224825 + + + + +Mozilla Bug 1224825 +
+
+
+ + + diff --git a/dom/messagechannel/tests/test_messageChannel_worker_forceClose.html b/dom/messagechannel/tests/test_messageChannel_worker_forceClose.html new file mode 100644 index 000000000000..1610fc265208 --- /dev/null +++ b/dom/messagechannel/tests/test_messageChannel_worker_forceClose.html @@ -0,0 +1,27 @@ + + + + + Test for forcing the closing of the port in workers + + + + +
+
+
+ + + diff --git a/dom/push/PushRecord.jsm b/dom/push/PushRecord.jsm index 35d0f79a1e80..76bbac09af15 100644 --- a/dom/push/PushRecord.jsm +++ b/dom/push/PushRecord.jsm @@ -209,6 +209,11 @@ PushRecord.prototype = { return this.quota === 0; }, + matchesOriginAttributes(pattern) { + return ChromeUtils.originAttributesMatchPattern( + this.principal.originAttributes, pattern); + }, + toSubscription() { return { pushEndpoint: this.pushEndpoint, diff --git a/dom/push/PushService.jsm b/dom/push/PushService.jsm index f83b32e48dcd..d6e2f7e4e2fa 100644 --- a/dom/push/PushService.jsm +++ b/dom/push/PushService.jsm @@ -277,37 +277,31 @@ this.PushService = { }) break; - case "webapps-clear-data": - console.debug("webapps-clear-data"); - - let data = aSubject - .QueryInterface(Ci.mozIApplicationClearPrivateDataParams); - if (!data) { - console.error("webapps-clear-data: Failed to get information " + - "about application"); - return; - } - - var originAttributes = - ChromeUtils.originAttributesToSuffix({ appId: data.appId, - inBrowser: data.browserOnly }); - this._db.getAllByOriginAttributes(originAttributes) - .then(records => Promise.all(records.map(record => - this._db.delete(record.keyID) - .catch(err => { - console.error("webapps-clear-data: Error removing record", - record, err); - // This is the record we were unable to delete. - return record; - }) - .then(maybeDeleted => this._backgroundUnregister(maybeDeleted)) - ) - )); - + case "clear-origin-data": + this._clearOriginData(data).catch(error => { + console.error("clearOriginData: Error clearing origin data:", error); + }); break; } }, + _clearOriginData: function(data) { + console.log("clearOriginData()"); + + if (!data) { + return Promise.resolve(); + } + + let pattern = JSON.parse(data); + return this._db.clearIf(record => { + if (!record.matchesOriginAttributes(pattern)) { + return false; + } + this._backgroundUnregister(record); + return true; + }); + }, + /** * Sends an unregister request to the server in the background. If the * service is not connected, this function is a no-op. @@ -487,7 +481,7 @@ this.PushService = { return; } - Services.obs.addObserver(this, "webapps-clear-data", false); + Services.obs.addObserver(this, "clear-origin-data", false); // On B2G the NetworkManager interface fires a network-active-changed // event. @@ -614,7 +608,7 @@ this.PushService = { prefs.ignore("connection.enabled", this); Services.obs.removeObserver(this, this._networkStateChangeEventName); - Services.obs.removeObserver(this, "webapps-clear-data"); + Services.obs.removeObserver(this, "clear-origin-data"); Services.obs.removeObserver(this, "idle-daily"); Services.obs.removeObserver(this, "perm-changed"); }, diff --git a/dom/push/test/xpcshell/head.js b/dom/push/test/xpcshell/head.js index 0a98e6b8de94..63fc2809690d 100644 --- a/dom/push/test/xpcshell/head.js +++ b/dom/push/test/xpcshell/head.js @@ -12,6 +12,7 @@ Cu.import('resource://gre/modules/Timer.jsm'); Cu.import('resource://gre/modules/Promise.jsm'); Cu.import('resource://gre/modules/Preferences.jsm'); Cu.import('resource://gre/modules/PlacesUtils.jsm'); +Cu.import('resource://gre/modules/ObjectUtils.jsm'); const serviceExports = Cu.import('resource://gre/modules/PushService.jsm', {}); const servicePrefs = new Preferences('dom.push.'); diff --git a/dom/push/test/xpcshell/test_clear_origin_data.js b/dom/push/test/xpcshell/test_clear_origin_data.js new file mode 100644 index 000000000000..a7f448a6dba3 --- /dev/null +++ b/dom/push/test/xpcshell/test_clear_origin_data.js @@ -0,0 +1,144 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +'use strict'; + +const {PushDB, PushService, PushServiceWebSocket} = serviceExports; + +const userAgentID = 'bd744428-f125-436a-b6d0-dd0c9845837f'; + +let clearForPattern = Task.async(function* (testRecords, pattern) { + let patternString = JSON.stringify(pattern); + yield PushService._clearOriginData(patternString); + + for (let length = testRecords.length; length--;) { + let test = testRecords[length]; + let originSuffix = ChromeUtils.originAttributesToSuffix( + test.originAttributes); + + let registration = yield PushNotificationService.registration( + test.scope, + originSuffix + ); + + let url = test.scope + originSuffix; + + if (ObjectUtils.deepEqual(test.clearIf, pattern)) { + ok(!registration, 'Should clear registration ' + url + + ' for pattern ' + patternString); + testRecords.splice(length, 1); + } else { + ok(registration, 'Should not clear registration ' + url + + ' for pattern ' + patternString); + } + } +}); + +function run_test() { + do_get_profile(); + setPrefs({ + userAgentID, + requestTimeout: 1000, + retryBaseInterval: 150 + }); + disableServiceWorkerEvents( + 'https://example.org/1' + ); + run_next_test(); +} + +add_task(function* test_webapps_cleardata() { + let db = PushServiceWebSocket.newPushDB(); + do_register_cleanup(() => {return db.drop().then(_ => db.close());}); + + let testRecords = [{ + scope: 'https://example.org/1', + originAttributes: { appId: 1 }, + clearIf: { appId: 1, inBrowser: false }, + }, { + scope: 'https://example.org/1', + originAttributes: { appId: 1, inBrowser: true }, + clearIf: { appId: 1 }, + }, { + scope: 'https://example.org/1', + originAttributes: { appId: 2, inBrowser: true }, + clearIf: { appId: 2, inBrowser: true }, + }, { + scope: 'https://example.org/2', + originAttributes: { appId: 1 }, + clearIf: { appId: 1, inBrowser: false }, + }, { + scope: 'https://example.org/2', + originAttributes: { appId: 2, inBrowser: true }, + clearIf: { appId: 2, inBrowser: true }, + }, { + scope: 'https://example.org/3', + originAttributes: { appId: 3, inBrowser: true }, + clearIf: { inBrowser: true }, + }, { + scope: 'https://example.org/3', + originAttributes: { appId: 4, inBrowser: true }, + clearIf: { inBrowser: true }, + }]; + + let unregisterDone; + let unregisterPromise = new Promise(resolve => + unregisterDone = after(testRecords.length, resolve)); + + PushService.init({ + serverURI: "wss://push.example.org", + networkInfo: new MockDesktopNetworkInfo(), + db, + makeWebSocket(uri) { + return new MockWebSocket(uri, { + onHello(data) { + equal(data.messageType, 'hello', 'Handshake: wrong message type'); + equal(data.uaid, userAgentID, 'Handshake: wrong device ID'); + this.serverSendMsg(JSON.stringify({ + messageType: 'hello', + status: 200, + uaid: userAgentID + })); + }, + onRegister(data) { + equal(data.messageType, 'register', 'Register: wrong message type'); + this.serverSendMsg(JSON.stringify({ + messageType: 'register', + status: 200, + channelID: data.channelID, + uaid: userAgentID, + pushEndpoint: 'https://example.com/update/' + Math.random(), + })); + }, + onUnregister(data) { + unregisterDone(); + }, + }); + } + }); + + yield Promise.all(testRecords.map(test => + PushNotificationService.register( + test.scope, + ChromeUtils.originAttributesToSuffix(test.originAttributes) + ) + )); + + // Removes records for all scopes with the same app ID. Excludes records + // where `inBrowser` is true. + yield clearForPattern(testRecords, { appId: 1, inBrowser: false }); + + // Removes the remaining record for app ID 1, where `inBrowser` is true. + yield clearForPattern(testRecords, { appId: 1 }); + + // Removes all records for all scopes with the same app ID, where + // `inBrowser` is true. + yield clearForPattern(testRecords, { appId: 2, inBrowser: true }); + + // Removes all records where `inBrowser` is true. + yield clearForPattern(testRecords, { inBrowser: true }); + + equal(testRecords.length, 0, 'Should remove all test records'); + yield waitForPromise(unregisterPromise, DEFAULT_TIMEOUT, + 'Timed out waiting for unregister'); +}); diff --git a/dom/push/test/xpcshell/test_webapps_cleardata.js b/dom/push/test/xpcshell/test_webapps_cleardata.js deleted file mode 100644 index a985a95ed919..000000000000 --- a/dom/push/test/xpcshell/test_webapps_cleardata.js +++ /dev/null @@ -1,95 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ - -'use strict'; - -const {PushDB, PushService, PushServiceWebSocket} = serviceExports; - -const userAgentID = 'bd744428-f125-436a-b6d0-dd0c9845837f'; - -function run_test() { - do_get_profile(); - setPrefs({ - userAgentID, - requestTimeout: 1000, - retryBaseInterval: 150 - }); - disableServiceWorkerEvents( - 'https://example.org/1' - ); - run_next_test(); -} - -add_task(function* test_webapps_cleardata() { - let db = PushServiceWebSocket.newPushDB(); - do_register_cleanup(() => {return db.drop().then(_ => db.close());}); - - let unregisterDone; - let unregisterPromise = new Promise(resolve => unregisterDone = resolve); - - PushService.init({ - serverURI: "wss://push.example.org", - networkInfo: new MockDesktopNetworkInfo(), - db, - makeWebSocket(uri) { - return new MockWebSocket(uri, { - onHello(data) { - equal(data.messageType, 'hello', 'Handshake: wrong message type'); - equal(data.uaid, userAgentID, 'Handshake: wrong device ID'); - this.serverSendMsg(JSON.stringify({ - messageType: 'hello', - status: 200, - uaid: userAgentID - })); - }, - onRegister(data) { - equal(data.messageType, 'register', 'Register: wrong message type'); - this.serverSendMsg(JSON.stringify({ - messageType: 'register', - status: 200, - channelID: data.channelID, - uaid: userAgentID, - pushEndpoint: 'https://example.com/update/' + Math.random(), - })); - }, - onUnregister(data) { - unregisterDone(); - }, - }); - } - }); - - let registers = yield Promise.all([ - PushNotificationService.register( - 'https://example.org/1', - ChromeUtils.originAttributesToSuffix({ appId: 1, inBrowser: false })), - PushNotificationService.register( - 'https://example.org/1', - ChromeUtils.originAttributesToSuffix({ appId: 1, inBrowser: true })), - ]); - - Services.obs.notifyObservers( - { appId: 1, browserOnly: false, - QueryInterface: XPCOMUtils.generateQI([Ci.mozIApplicationClearPrivateDataParams])}, - "webapps-clear-data", ""); - - let waitAWhile = new Promise(function(res) { - setTimeout(res, 2000); - }); - yield waitAWhile; - - let registration; - registration = yield PushNotificationService.registration( - 'https://example.org/1', - ChromeUtils.originAttributesToSuffix({ appId: 1, inBrowser: false })); - ok(!registration, 'Registration for { 1, false } should not exist.'); - - registration = yield PushNotificationService.registration( - 'https://example.org/1', - ChromeUtils.originAttributesToSuffix({ appId: 1, inBrowser: true })); - ok(registration, 'Registration for { 1, true } should still exist.'); - - yield waitForPromise(unregisterPromise, DEFAULT_TIMEOUT, - 'Timed out waiting for unregister'); -}); - diff --git a/dom/push/test/xpcshell/xpcshell.ini b/dom/push/test/xpcshell/xpcshell.ini index cf60c9f4b664..c8464d4e624f 100644 --- a/dom/push/test/xpcshell/xpcshell.ini +++ b/dom/push/test/xpcshell/xpcshell.ini @@ -4,6 +4,7 @@ tail = # Push notifications and alarms are currently disabled on Android. skip-if = toolkit == 'android' +[test_clear_origin_data.js] [test_drop_expired.js] [test_notification_ack.js] [test_notification_data.js] @@ -39,7 +40,6 @@ run-sequentially = This will delete all existing push subscriptions. [test_unregister_invalid_json.js] [test_unregister_not_found.js] [test_unregister_success.js] -[test_webapps_cleardata.js] [test_updateRecordNoEncryptionKeys_ws.js] [test_reconnect_retry.js] [test_retry_ws.js] diff --git a/dom/webidl/EventHandler.webidl b/dom/webidl/EventHandler.webidl index add56fe30cd3..feef81522fa4 100644 --- a/dom/webidl/EventHandler.webidl +++ b/dom/webidl/EventHandler.webidl @@ -134,7 +134,7 @@ interface WindowEventHandlers { attribute EventHandler onpagehide; attribute EventHandler onpageshow; attribute EventHandler onpopstate; - //(Not implemented)attribute EventHandler onstorage; + attribute EventHandler onstorage; attribute EventHandler onunload; }; diff --git a/dom/webidl/Navigator.webidl b/dom/webidl/Navigator.webidl index e0ca51c4e43d..4cca68cc1b94 100644 --- a/dom/webidl/Navigator.webidl +++ b/dom/webidl/Navigator.webidl @@ -54,8 +54,10 @@ interface NavigatorID { [NoInterfaceObject, Exposed=(Window,Worker)] interface NavigatorLanguage { - // These 2 values are cached. They are updated when pref - // intl.accept_languages is changed. + // These two attributes are cached because this interface is also implemented + // by Workernavigator and this way we don't have to go back to the + // main-thread from the worker thread anytime we need to retrieve them. They + // are updated when pref intl.accept_languages is changed. [Pure, Cached] readonly attribute DOMString? language; diff --git a/dom/workers/DataStore.cpp b/dom/workers/DataStore.cpp index 3ccb000a7fb9..be69f40a0817 100644 --- a/dom/workers/DataStore.cpp +++ b/dom/workers/DataStore.cpp @@ -79,18 +79,15 @@ class DataStoreGetStringRunnable final : public DataStoreRunnable FuncType mFunc; nsAString& mString; - ErrorResult& mRv; public: DataStoreGetStringRunnable(WorkerPrivate* aWorkerPrivate, const nsMainThreadPtrHandle& aBackingStore, FuncType aFunc, - nsAString& aString, - ErrorResult& aRv) + nsAString& aString) : DataStoreRunnable(aWorkerPrivate, aBackingStore) , mFunc(aFunc) , mString(aString) - , mRv(aRv) { MOZ_ASSERT(aWorkerPrivate); aWorkerPrivate->AssertIsOnWorkerThread(); @@ -102,9 +99,15 @@ protected: { AssertIsOnMainThread(); + ErrorResult rv; nsString string; - (mBackingStore.get()->*mFunc)(string, mRv); + (mBackingStore.get()->*mFunc)(string, rv); mString.Assign(string); + + if (NS_WARN_IF(rv.Failed())) { + rv.SuppressException(); + } + return true; } }; @@ -113,17 +116,12 @@ protected: // thread. class DataStoreGetReadOnlyRunnable final : public DataStoreRunnable { - ErrorResult& mRv; - public: bool mReadOnly; -public: DataStoreGetReadOnlyRunnable(WorkerPrivate* aWorkerPrivate, - const nsMainThreadPtrHandle& aBackingStore, - ErrorResult& aRv) + const nsMainThreadPtrHandle& aBackingStore) : DataStoreRunnable(aWorkerPrivate, aBackingStore) - , mRv(aRv) { MOZ_ASSERT(aWorkerPrivate); aWorkerPrivate->AssertIsOnWorkerThread(); @@ -135,7 +133,12 @@ protected: { AssertIsOnMainThread(); - mReadOnly = mBackingStore->GetReadOnly(mRv); + ErrorResult rv; + mReadOnly = mBackingStore->GetReadOnly(rv); + if (NS_WARN_IF(rv.Failed())) { + rv.SuppressException(); + } + return true; } }; @@ -147,6 +150,7 @@ public: const nsMainThreadPtrHandle& aBackingStore, Promise* aWorkerPromise) : DataStoreRunnable(aWorkerPrivate, aBackingStore) + , mFailed(false) { MOZ_ASSERT(aWorkerPrivate); aWorkerPrivate->AssertIsOnWorkerThread(); @@ -167,31 +171,34 @@ public: return true; } + bool Failed() const + { + return mFailed; + } + protected: RefPtr mPromiseWorkerProxy; + bool mFailed; }; // A DataStoreRunnable to run DataStore::Get(...) on the main thread. class DataStoreGetRunnable final : public DataStoreProxyRunnable { Sequence mId; - ErrorResult& mRv; public: DataStoreGetRunnable(WorkerPrivate* aWorkerPrivate, const nsMainThreadPtrHandle& aBackingStore, - Promise* aWorkerPromise, - const Sequence& aId, - ErrorResult& aRv) + Promise* aWorkerPromise) : DataStoreProxyRunnable(aWorkerPrivate, aBackingStore, aWorkerPromise) - , mRv(aRv) { MOZ_ASSERT(aWorkerPrivate); aWorkerPrivate->AssertIsOnWorkerThread(); + } - if (!mId.AppendElements(aId, fallible)) { - mRv.Throw(NS_ERROR_OUT_OF_MEMORY); - } + Sequence& Id() + { + return mId; } protected: @@ -200,8 +207,14 @@ protected: { AssertIsOnMainThread(); - RefPtr promise = mBackingStore->Get(mId, mRv); + ErrorResult rv; + RefPtr promise = mBackingStore->Get(mId, rv); promise->AppendNativeHandler(mPromiseWorkerProxy); + if (NS_WARN_IF(rv.Failed())) { + rv.SuppressException(); + mFailed = true; + } + return true; } }; @@ -212,30 +225,28 @@ class DataStorePutRunnable final : public DataStoreProxyRunnable { const StringOrUnsignedLong& mId; const nsString mRevisionId; - ErrorResult& mRv; + nsresult mError; public: DataStorePutRunnable(WorkerPrivate* aWorkerPrivate, const nsMainThreadPtrHandle& aBackingStore, Promise* aWorkerPromise, - JSContext* aCx, - JS::Handle aObj, const StringOrUnsignedLong& aId, - const nsAString& aRevisionId, - ErrorResult& aRv) + const nsAString& aRevisionId) : DataStoreProxyRunnable(aWorkerPrivate, aBackingStore, aWorkerPromise) , StructuredCloneHolder(CloningNotSupported, TransferringNotSupported, SameProcessDifferentThread) , mId(aId) , mRevisionId(aRevisionId) - , mRv(aRv) + , mError(NS_OK) { MOZ_ASSERT(aWorkerPrivate); aWorkerPrivate->AssertIsOnWorkerThread(); + } - // This needs to be structured cloned while it's still on the worker thread. - Write(aCx, aObj, mRv); - NS_WARN_IF(mRv.Failed()); + nsresult ErrorCode() const + { + return mError; } protected: @@ -247,22 +258,28 @@ protected: // Initialise an AutoJSAPI with the target window. AutoJSAPI jsapi; if (NS_WARN_IF(!jsapi.Init(mBackingStore->GetParentObject()))) { - mRv.Throw(NS_ERROR_UNEXPECTED); + mError = NS_ERROR_UNEXPECTED; return true; } JSContext* cx = jsapi.cx(); + ErrorResult rv; JS::Rooted value(cx); - Read(mBackingStore->GetParentObject(), cx, &value, mRv); - if (NS_WARN_IF(mRv.Failed())) { + Read(mBackingStore->GetParentObject(), cx, &value, rv); + if (NS_WARN_IF(rv.Failed())) { + rv.SuppressException(); + mError = NS_ERROR_DOM_DATA_CLONE_ERR; + return true; + } + + RefPtr promise = mBackingStore->Put(cx, value, mId, + mRevisionId, rv); + if (NS_WARN_IF(rv.Failed())) { + rv.SuppressException(); + mError = NS_ERROR_FAILURE; return true; } - RefPtr promise = mBackingStore->Put(cx, - value, - mId, - mRevisionId, - mRv); promise->AppendNativeHandler(mPromiseWorkerProxy); return true; } @@ -274,30 +291,28 @@ class DataStoreAddRunnable final : public DataStoreProxyRunnable { const Optional& mId; const nsString mRevisionId; - ErrorResult& mRv; + nsresult mResult; public: DataStoreAddRunnable(WorkerPrivate* aWorkerPrivate, const nsMainThreadPtrHandle& aBackingStore, Promise* aWorkerPromise, - JSContext* aCx, - JS::Handle aObj, const Optional& aId, - const nsAString& aRevisionId, - ErrorResult& aRv) + const nsAString& aRevisionId) : DataStoreProxyRunnable(aWorkerPrivate, aBackingStore, aWorkerPromise) , StructuredCloneHolder(CloningNotSupported, TransferringNotSupported, SameProcessDifferentThread) , mId(aId) , mRevisionId(aRevisionId) - , mRv(aRv) + , mResult(NS_OK) { MOZ_ASSERT(aWorkerPrivate); aWorkerPrivate->AssertIsOnWorkerThread(); + } - // This needs to be structured cloned while it's still on the worker thread. - Write(aCx, aObj, mRv); - NS_WARN_IF(mRv.Failed()); + nsresult ErrorCode() const + { + return mResult; } protected: @@ -309,22 +324,28 @@ protected: // Initialise an AutoJSAPI with the target window. AutoJSAPI jsapi; if (NS_WARN_IF(!jsapi.Init(mBackingStore->GetParentObject()))) { - mRv.Throw(NS_ERROR_UNEXPECTED); + mResult = NS_ERROR_UNEXPECTED; return true; } JSContext* cx = jsapi.cx(); + ErrorResult rv; JS::Rooted value(cx); - Read(mBackingStore->GetParentObject(), cx, &value, mRv); - if (NS_WARN_IF(mRv.Failed())) { + Read(mBackingStore->GetParentObject(), cx, &value, rv); + if (NS_WARN_IF(rv.Failed())) { + rv.SuppressException(); + mResult = NS_ERROR_DOM_DATA_CLONE_ERR; + return true; + } + + RefPtr promise = mBackingStore->Add(cx, value, mId, + mRevisionId, rv); + if (NS_WARN_IF(rv.Failed())) { + rv.SuppressException(); + mResult = NS_ERROR_FAILURE; return true; } - RefPtr promise = mBackingStore->Add(cx, - value, - mId, - mRevisionId, - mRv); promise->AppendNativeHandler(mPromiseWorkerProxy); return true; } @@ -336,19 +357,16 @@ class DataStoreRemoveRunnable final : public DataStoreProxyRunnable { const StringOrUnsignedLong& mId; const nsString mRevisionId; - ErrorResult& mRv; public: DataStoreRemoveRunnable(WorkerPrivate* aWorkerPrivate, const nsMainThreadPtrHandle& aBackingStore, Promise* aWorkerPromise, const StringOrUnsignedLong& aId, - const nsAString& aRevisionId, - ErrorResult& aRv) + const nsAString& aRevisionId) : DataStoreProxyRunnable(aWorkerPrivate, aBackingStore, aWorkerPromise) , mId(aId) , mRevisionId(aRevisionId) - , mRv(aRv) { MOZ_ASSERT(aWorkerPrivate); aWorkerPrivate->AssertIsOnWorkerThread(); @@ -360,7 +378,14 @@ protected: { AssertIsOnMainThread(); - RefPtr promise = mBackingStore->Remove(mId, mRevisionId, mRv); + ErrorResult rv; + RefPtr promise = mBackingStore->Remove(mId, mRevisionId, rv); + if (NS_WARN_IF(rv.Failed())) { + rv.SuppressException(); + mFailed = true; + return true; + } + promise->AppendNativeHandler(mPromiseWorkerProxy); return true; } @@ -370,17 +395,14 @@ protected: class DataStoreClearRunnable final : public DataStoreProxyRunnable { const nsString mRevisionId; - ErrorResult& mRv; public: DataStoreClearRunnable(WorkerPrivate* aWorkerPrivate, const nsMainThreadPtrHandle& aBackingStore, Promise* aWorkerPromise, - const nsAString& aRevisionId, - ErrorResult& aRv) + const nsAString& aRevisionId) : DataStoreProxyRunnable(aWorkerPrivate, aBackingStore, aWorkerPromise) , mRevisionId(aRevisionId) - , mRv(aRv) { MOZ_ASSERT(aWorkerPrivate); aWorkerPrivate->AssertIsOnWorkerThread(); @@ -392,7 +414,14 @@ protected: { AssertIsOnMainThread(); - RefPtr promise = mBackingStore->Clear(mRevisionId, mRv); + ErrorResult rv; + RefPtr promise = mBackingStore->Clear(mRevisionId, rv); + if (NS_WARN_IF(rv.Failed())) { + rv.SuppressException(); + mFailed = true; + return true; + } + promise->AppendNativeHandler(mPromiseWorkerProxy); return true; } @@ -403,23 +432,27 @@ class DataStoreSyncStoreRunnable final : public DataStoreRunnable { WorkerDataStoreCursor* mWorkerCursor; const nsString mRevisionId; - ErrorResult& mRv; + bool mFailed; public: DataStoreSyncStoreRunnable(WorkerPrivate* aWorkerPrivate, const nsMainThreadPtrHandle& aBackingStore, WorkerDataStoreCursor* aWorkerCursor, - const nsAString& aRevisionId, - ErrorResult& aRv) + const nsAString& aRevisionId) : DataStoreRunnable(aWorkerPrivate, aBackingStore) , mWorkerCursor(aWorkerCursor) , mRevisionId(aRevisionId) - , mRv(aRv) + , mFailed(false) { MOZ_ASSERT(aWorkerPrivate); aWorkerPrivate->AssertIsOnWorkerThread(); } + bool Failed() const + { + return mFailed; + } + protected: virtual bool MainThreadRun() override @@ -427,7 +460,14 @@ protected: AssertIsOnMainThread(); // Point WorkerDataStoreCursor to DataStoreCursor. - RefPtr cursor = mBackingStore->Sync(mRevisionId, mRv); + ErrorResult rv; + RefPtr cursor = mBackingStore->Sync(mRevisionId, rv); + if (NS_WARN_IF(rv.Failed())) { + rv.SuppressException(); + mFailed = true; + return true; + } + nsMainThreadPtrHandle backingCursor( new nsMainThreadPtrHolder(cursor)); mWorkerCursor->SetBackingDataStoreCursor(backingCursor); @@ -447,8 +487,7 @@ WorkerDataStore::GetName(JSContext* aCx, nsAString& aName, ErrorResult& aRv) new DataStoreGetStringRunnable(workerPrivate, mBackingStore, &DataStore::GetName, - aName, - aRv); + aName); runnable->Dispatch(aCx); } @@ -463,8 +502,7 @@ WorkerDataStore::GetOwner(JSContext* aCx, nsAString& aOwner, ErrorResult& aRv) new DataStoreGetStringRunnable(workerPrivate, mBackingStore, &DataStore::GetOwner, - aOwner, - aRv); + aOwner); runnable->Dispatch(aCx); } @@ -476,7 +514,7 @@ WorkerDataStore::GetReadOnly(JSContext* aCx, ErrorResult& aRv) workerPrivate->AssertIsOnWorkerThread(); RefPtr runnable = - new DataStoreGetReadOnlyRunnable(workerPrivate, mBackingStore, aRv); + new DataStoreGetReadOnlyRunnable(workerPrivate, mBackingStore); runnable->Dispatch(aCx); return runnable->mReadOnly; @@ -499,11 +537,20 @@ WorkerDataStore::Get(JSContext* aCx, RefPtr runnable = new DataStoreGetRunnable(workerPrivate, mBackingStore, - promise, - aId, - aRv); + promise); + + if (!runnable->Id().AppendElements(aId, fallible)) { + aRv.Throw(NS_ERROR_OUT_OF_MEMORY); + return nullptr; + } + runnable->Dispatch(aCx); + if (runnable->Failed()) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + return promise.forget(); } @@ -527,13 +574,20 @@ WorkerDataStore::Put(JSContext* aCx, new DataStorePutRunnable(workerPrivate, mBackingStore, promise, - aCx, - aObj, aId, - aRevisionId, - aRv); + aRevisionId); + runnable->Write(aCx, aObj, aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + runnable->Dispatch(aCx); + if (NS_FAILED(runnable->ErrorCode())) { + aRv.Throw(runnable->ErrorCode()); + return nullptr; + } + return promise.forget(); } @@ -557,13 +611,20 @@ WorkerDataStore::Add(JSContext* aCx, new DataStoreAddRunnable(workerPrivate, mBackingStore, promise, - aCx, - aObj, aId, - aRevisionId, - aRv); + aRevisionId); + runnable->Write(aCx, aObj, aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + runnable->Dispatch(aCx); + if (NS_FAILED(runnable->ErrorCode())) { + aRv.Throw(runnable->ErrorCode()); + return nullptr; + } + return promise.forget(); } @@ -587,10 +648,14 @@ WorkerDataStore::Remove(JSContext* aCx, mBackingStore, promise, aId, - aRevisionId, - aRv); + aRevisionId); runnable->Dispatch(aCx); + if (runnable->Failed()) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + return promise.forget(); } @@ -612,10 +677,14 @@ WorkerDataStore::Clear(JSContext* aCx, new DataStoreClearRunnable(workerPrivate, mBackingStore, promise, - aRevisionId, - aRv); + aRevisionId); runnable->Dispatch(aCx); + if (runnable->Failed()) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + return promise.forget(); } @@ -632,23 +701,18 @@ WorkerDataStore::GetRevisionId(JSContext* aCx, new DataStoreGetStringRunnable(workerPrivate, mBackingStore, &DataStore::GetRevisionId, - aRevisionId, - aRv); + aRevisionId); runnable->Dispatch(aCx); } // A DataStoreRunnable to run DataStore::GetLength(...) on the main thread. class DataStoreGetLengthRunnable final : public DataStoreProxyRunnable { - ErrorResult& mRv; - public: DataStoreGetLengthRunnable(WorkerPrivate* aWorkerPrivate, const nsMainThreadPtrHandle& aBackingStore, - Promise* aWorkerPromise, - ErrorResult& aRv) + Promise* aWorkerPromise) : DataStoreProxyRunnable(aWorkerPrivate, aBackingStore, aWorkerPromise) - , mRv(aRv) { MOZ_ASSERT(aWorkerPrivate); aWorkerPrivate->AssertIsOnWorkerThread(); @@ -660,8 +724,14 @@ protected: { AssertIsOnMainThread(); - RefPtr promise = mBackingStore->GetLength(mRv); + ErrorResult rv; + RefPtr promise = mBackingStore->GetLength(rv); promise->AppendNativeHandler(mPromiseWorkerProxy); + if (NS_WARN_IF(rv.Failed())) { + rv.SuppressException(); + mFailed = true; + } + return true; } }; @@ -681,10 +751,14 @@ WorkerDataStore::GetLength(JSContext* aCx, ErrorResult& aRv) RefPtr runnable = new DataStoreGetLengthRunnable(workerPrivate, mBackingStore, - promise, - aRv); + promise); runnable->Dispatch(aCx); + if (runnable->Failed()) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + return promise.forget(); } @@ -709,10 +783,14 @@ WorkerDataStore::Sync(JSContext* aCx, new DataStoreSyncStoreRunnable(workerPrivate, mBackingStore, workerCursor, - aRevisionId, - aRv); + aRevisionId); runnable->Dispatch(aCx); + if (runnable->Failed()) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + return workerCursor.forget(); } diff --git a/dom/workers/SharedWorker.cpp b/dom/workers/SharedWorker.cpp index 568844ac3fd8..f50c2687fe67 100644 --- a/dom/workers/SharedWorker.cpp +++ b/dom/workers/SharedWorker.cpp @@ -136,7 +136,6 @@ SharedWorker::Close() if (mMessagePort) { mMessagePort->Close(); - mMessagePort = nullptr; } } diff --git a/dom/workers/test/sharedWorker_ports.js b/dom/workers/test/sharedWorker_ports.js index a0d29e29a2dc..64672e6abb27 100644 --- a/dom/workers/test/sharedWorker_ports.js +++ b/dom/workers/test/sharedWorker_ports.js @@ -17,6 +17,7 @@ onconnect = function(evt) { test: (evtFromPort2.data.type == "connected"), msg: "The original message received" }); port.postMessage({type: "finish"}); + close(); } } } diff --git a/dom/workers/test/test_sharedWorker_ports.html b/dom/workers/test/test_sharedWorker_ports.html index 8233b3f71f38..32698ab5277e 100644 --- a/dom/workers/test/test_sharedWorker_ports.html +++ b/dom/workers/test/test_sharedWorker_ports.html @@ -28,6 +28,9 @@ sw1.port.onmessage = function(event) { } if (event.data.type == "finish") { + info("Finished!"); + ok(sw1.port, "The port still exists"); + sw1.port.foo = sw1; // Just a test to see if we leak. SimpleTest.finish(); } } diff --git a/gfx/tests/crashtests/1225125-1.html b/gfx/tests/crashtests/1225125-1.html new file mode 100644 index 000000000000..6632dff283e4 --- /dev/null +++ b/gfx/tests/crashtests/1225125-1.html @@ -0,0 +1,11 @@ + + + + +
+
+
+
+ + + diff --git a/gfx/tests/crashtests/crashtests.list b/gfx/tests/crashtests/crashtests.list index 26d3441c242a..09eda9693f5e 100644 --- a/gfx/tests/crashtests/crashtests.list +++ b/gfx/tests/crashtests/crashtests.list @@ -126,3 +126,4 @@ load 944579.html pref(security.fileuri.strict_origin_policy,false) load 950000.html load 1034403-1.html load balinese-letter-spacing.html +load 1225125-1.html diff --git a/js/public/TrackedOptimizationInfo.h b/js/public/TrackedOptimizationInfo.h index eff485509cf1..58253e780181 100644 --- a/js/public/TrackedOptimizationInfo.h +++ b/js/public/TrackedOptimizationInfo.h @@ -25,6 +25,7 @@ namespace JS { _(GetProp_InlineAccess) \ _(GetProp_Innerize) \ _(GetProp_InlineCache) \ + _(GetProp_SharedCache) \ \ _(SetProp_CommonSetter) \ _(SetProp_TypedObject) \ diff --git a/js/public/UbiNodeDominatorTree.h b/js/public/UbiNodeDominatorTree.h new file mode 100644 index 000000000000..b010dd14d477 --- /dev/null +++ b/js/public/UbiNodeDominatorTree.h @@ -0,0 +1,354 @@ +/* -*- 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/. */ + +#ifndef js_UbiNodeDominatorTree_h +#define js_UbiNodeDominatorTree_h + +#include "mozilla/DebugOnly.h" +#include "mozilla/Maybe.h" +#include "mozilla/Move.h" +#include "mozilla/UniquePtr.h" + +#include "jsalloc.h" + +#include "js/UbiNode.h" +#include "js/UbiNodePostOrder.h" +#include "js/Utility.h" +#include "js/Vector.h" + +namespace JS { +namespace ubi { + +/** + * In a directed graph with a root node `R`, a node `A` is said to "dominate" a + * node `B` iff every path from `R` to `B` contains `A`. A node `A` is said to + * be the "immediate dominator" of a node `B` iff it dominates `B`, is not `B` + * itself, and does not dominate any other nodes which also dominate `B` in + * turn. + * + * If we take every node from a graph `G` and create a new graph `T` with edges + * to each node from its immediate dominator, then `T` is a tree (each node has + * only one immediate dominator, or none if it is the root). This tree is called + * a "dominator tree". + * + * This class represents a dominator tree constructed from a `JS::ubi::Node` + * heap graph. The domination relationship and dominator trees are useful tools + * for analyzing heap graphs because they tell you: + * + * - Exactly what could be reclaimed by the GC if some node `A` became + * unreachable: those nodes which are dominated by `A`, + * + * - The "retained size" of a node in the heap graph, in contrast to its + * "shallow size". The "shallow size" is the space taken by a node itself, + * not counting anything it references. The "retained size" of a node is its + * shallow size plus the size of all the things that would be collected if + * the original node wasn't (directly or indirectly) referencing them. In + * other words, the retained size is the shallow size of a node plus the + * shallow sizes of every other node it dominates. For example, the root + * node in a binary tree might have a small shallow size that does not take + * up much space itself, but it dominates the rest of the binary tree and + * its retained size is therefore significant (assuming no external + * references into the tree). + * + * The simple, engineered algorithm presented in "A Simple, Fast Dominance + * Algorithm" by Cooper el al[0] is used to find dominators and construct the + * dominator tree. This algorithm runs in O(n^2) time, but is faster in practice + * than alternative algorithms with better theoretical running times, such as + * Lengauer-Tarjan which runs in O(e * log(n)). The big caveat to that statement + * is that Cooper et al found it is faster in practice *on control flow graphs* + * and I'm not convinced that this property also holds on *heap* graphs. That + * said, the implementation of this algorithm is *much* simpler than + * Lengauer-Tarjan and has been found to be fast enough at least for the time + * being. + * + * [0]: http://www.cs.rice.edu/~keith/EMBED/dom.pdf + */ +class JS_PUBLIC_API(DominatorTree) +{ + private: + // Type aliases. + using NodeSet = js::HashSet, js::SystemAllocPolicy>; + using NodeSetPtr = mozilla::UniquePtr>; + using PredecessorSets = js::HashMap, + js::SystemAllocPolicy>; + using NodeToIndexMap = js::HashMap, + js::SystemAllocPolicy>; + + private: + // Data members. + mozilla::Vector postOrder; + NodeToIndexMap nodeToPostOrderIndex; + mozilla::Vector doms; + + private: + // We use `UNDEFINED` as a sentinel value in the `doms` vector to signal + // that we haven't found any dominators for the node at the corresponding + // index in `postOrder` yet. + static const uint32_t UNDEFINED = UINT32_MAX; + + DominatorTree(mozilla::Vector&& postOrder, NodeToIndexMap&& nodeToPostOrderIndex, + mozilla::Vector&& doms) + : postOrder(mozilla::Move(postOrder)) + , nodeToPostOrderIndex(mozilla::Move(nodeToPostOrderIndex)) + , doms(mozilla::Move(doms)) + { } + + static uint32_t intersect(mozilla::Vector& doms, uint32_t finger1, uint32_t finger2) { + while (finger1 != finger2) { + if (finger1 < finger2) + finger1 = doms[finger1]; + else if (finger2 < finger1) + finger2 = doms[finger2]; + } + return finger1; + } + + // Do the post order traversal of the heap graph and populate our + // predecessor sets. + static bool doTraversal(JSRuntime* rt, AutoCheckCannotGC& noGC, const Node& root, + mozilla::Vector& postOrder, PredecessorSets& predecessorSets) { + uint32_t nodeCount = 0; + auto onNode = [&](const Node& node) { + nodeCount++; + if (MOZ_UNLIKELY(nodeCount == UINT32_MAX)) + return false; + return postOrder.append(node); + }; + + auto onEdge = [&](const Node& origin, const Edge& edge) { + auto p = predecessorSets.lookupForAdd(edge.referent); + if (!p) { + auto set = rt->make_unique(); + if (!set || + !set->init() || + !predecessorSets.add(p, edge.referent, mozilla::Move(set))) + { + return false; + } + } + MOZ_ASSERT(p && p->value()); + return p->value()->put(origin); + }; + + PostOrder traversal(rt, noGC); + return traversal.init() && + traversal.addStart(root) && + traversal.traverse(onNode, onEdge); + } + + // Populates the given `map` with an entry for each node to its index in + // `postOrder`. + static bool mapNodesToTheirIndices(mozilla::Vector& postOrder, NodeToIndexMap& map) { + MOZ_ASSERT(!map.initialized()); + MOZ_ASSERT(postOrder.length() < UINT32_MAX); + uint32_t length = postOrder.length(); + if (!map.init(length)) + return false; + for (uint32_t i = 0; i < length; i++) + map.putNewInfallible(postOrder[i], i); + return true; + } + + // Convert the Node -> NodeSet predecessorSets to a index -> Vector + // form. + static bool convertPredecessorSetsToVectors( + const Node& root, + mozilla::Vector& postOrder, + PredecessorSets& predecessorSets, + NodeToIndexMap& nodeToPostOrderIndex, + mozilla::Vector>& predecessorVectors) + { + MOZ_ASSERT(postOrder.length() < UINT32_MAX); + uint32_t length = postOrder.length(); + + MOZ_ASSERT(predecessorVectors.length() == 0); + if (!predecessorVectors.growBy(length)) + return false; + + for (uint32_t i = 0; i < length - 1; i++) { + auto& node = postOrder[i]; + MOZ_ASSERT(node != root, + "Only the last node should be root, since this was a post order traversal."); + + auto ptr = predecessorSets.lookup(node); + MOZ_ASSERT(ptr, + "Because this isn't the root, it had better have predecessors, or else how " + "did we even find it."); + + auto& predecessors = ptr->value(); + if (!predecessorVectors[i].reserve(predecessors->count())) + return false; + for (auto range = predecessors->all(); !range.empty(); range.popFront()) { + auto ptr = nodeToPostOrderIndex.lookup(range.front()); + MOZ_ASSERT(ptr); + predecessorVectors[i].infallibleAppend(ptr->value()); + } + } + predecessorSets.finish(); + return true; + } + + // Initialize `doms` such that the immediate dominator of the `root` is the + // `root` itself and all others are `UNDEFINED`. + static bool initializeDominators(mozilla::Vector& doms, uint32_t length) { + MOZ_ASSERT(doms.length() == 0); + if (!doms.growByUninitialized(length)) + return false; + doms[length - 1] = length - 1; + for (uint32_t i = 0; i < length - 1; i++) + doms[i] = UNDEFINED; + return true; + } + + void assertSanity() const { + MOZ_ASSERT(postOrder.length() == doms.length()); + MOZ_ASSERT(postOrder.length() == nodeToPostOrderIndex.count()); + } + + public: + // DominatorTree is not copy-able. + DominatorTree(const DominatorTree&) = delete; + DominatorTree& operator=(const DominatorTree&) = delete; + + // DominatorTree is move-able. + DominatorTree(DominatorTree&& rhs) + : postOrder(mozilla::Move(rhs.postOrder)) + , nodeToPostOrderIndex(mozilla::Move(rhs.nodeToPostOrderIndex)) + , doms(mozilla::Move(rhs.doms)) + { + MOZ_ASSERT(this != &rhs, "self-move is not allowed"); + } + DominatorTree& operator=(DominatorTree&& rhs) { + this->~DominatorTree(); + new (this) DominatorTree(mozilla::Move(rhs)); + return *this; + } + + /** + * Construct a `DominatorTree` of the heap graph visible from `root`. The + * `root` is also used as the root of the resulting dominator tree. + * + * The resulting `DominatorTree` instance must not outlive the + * `JS::ubi::Node` graph it was constructed from. + * + * - For `JS::ubi::Node` graphs backed by the live heap graph, this means + * that the `DominatorTree`'s lifetime _must_ be contained within the + * scope of the provided `AutoCheckCannotGC` reference because a GC will + * invalidate the nodes. + * + * - For `JS::ubi::Node` graphs backed by some other offline structure + * provided by the embedder, the resulting `DominatorTree`'s lifetime is + * bounded by that offline structure's lifetime. + * + * In practice, this means that within SpiderMonkey we must treat + * `DominatorTree` as if it were backed by the live heap graph and trust + * that embedders with knowledge of the graph's implementation will do the + * Right Thing. + * + * Returns `mozilla::Nothing()` on OOM failure. It is the caller's + * responsibility to handle and report the OOM. + */ + static mozilla::Maybe + Create(JSRuntime* rt, AutoCheckCannotGC& noGC, const Node& root) { + mozilla::Vector postOrder; + PredecessorSets predecessorSets; + if (!predecessorSets.init() || !doTraversal(rt, noGC, root, postOrder, predecessorSets)) + return mozilla::Nothing(); + + MOZ_ASSERT(postOrder.length() < UINT32_MAX); + uint32_t length = postOrder.length(); + MOZ_ASSERT(postOrder[length - 1] == root); + + // From here on out we wish to avoid hash table lookups, and we use + // indices into `postOrder` instead of actual nodes wherever + // possible. This greatly improves the performance of this + // implementation, but we have to pay a little bit of upfront cost to + // convert our data structures to play along first. + + NodeToIndexMap nodeToPostOrderIndex; + if (!mapNodesToTheirIndices(postOrder, nodeToPostOrderIndex)) + return mozilla::Nothing(); + + mozilla::Vector> predecessorVectors; + if (!convertPredecessorSetsToVectors(root, postOrder, predecessorSets, nodeToPostOrderIndex, + predecessorVectors)) + return mozilla::Nothing(); + + mozilla::Vector doms; + if (!initializeDominators(doms, length)) + return mozilla::Nothing(); + + bool changed = true; + while (changed) { + changed = false; + + // Iterate over the non-root nodes in reverse post order. + for (uint32_t indexPlusOne = length - 1; indexPlusOne > 0; indexPlusOne--) { + MOZ_ASSERT(postOrder[indexPlusOne - 1] != root); + + // Take the intersection of every predecessor's dominator set; + // that is the current best guess at the immediate dominator for + // this node. + + uint32_t newIDomIdx = UNDEFINED; + + auto& predecessors = predecessorVectors[indexPlusOne - 1]; + auto range = predecessors.all(); + for ( ; !range.empty(); range.popFront()) { + auto idx = range.front(); + if (doms[idx] != UNDEFINED) { + newIDomIdx = idx; + break; + } + } + + MOZ_ASSERT(newIDomIdx != UNDEFINED, + "Because the root is initialized to dominate itself and is the first " + "node in every path, there must exist a predecessor to this node that " + "also has a dominator."); + + for ( ; !range.empty(); range.popFront()) { + auto idx = range.front(); + if (doms[idx] != UNDEFINED) + newIDomIdx = intersect(doms, newIDomIdx, idx); + } + + // If the immediate dominator changed, we will have to do + // another pass of the outer while loop to continue the forward + // dataflow. + if (newIDomIdx != doms[indexPlusOne - 1]) { + doms[indexPlusOne - 1] = newIDomIdx; + changed = true; + } + } + } + + return mozilla::Some(DominatorTree(mozilla::Move(postOrder), + mozilla::Move(nodeToPostOrderIndex), + mozilla::Move(doms))); + } + + /** + * Return the immediate dominator of the given `node`. If `node` was not + * reachable from the `root` that this dominator tree was constructed from, + * then return the null `JS::ubi::Node`. + */ + Node getImmediateDominator(const Node& node) const { + assertSanity(); + auto ptr = nodeToPostOrderIndex.lookup(node); + if (!ptr) + return Node(); + + auto idx = ptr->value(); + MOZ_ASSERT(idx < postOrder.length()); + return postOrder[doms[idx]]; + } +}; + +} // namespace ubi +} // namespace JS + +#endif // js_UbiNodeDominatorTree_h diff --git a/js/public/UbiNodePostOrder.h b/js/public/UbiNodePostOrder.h index ed35e71ad515..5943ee6505fa 100644 --- a/js/public/UbiNodePostOrder.h +++ b/js/public/UbiNodePostOrder.h @@ -8,6 +8,7 @@ #define js_UbiNodePostOrder_h #include "mozilla/DebugOnly.h" +#include "mozilla/Maybe.h" #include "mozilla/Move.h" #include "jsalloc.h" @@ -19,32 +20,43 @@ namespace JS { namespace ubi { -// A post-order depth-first traversal of `ubi::Node` graphs. -// -// NB: This traversal visits each node reachable from the start set exactly -// once, and does not visit edges at all. Therefore, this traversal would be a -// very poor choice for recording multiple paths to the same node, for example. -// If your analysis needs to consider edges, use `JS::ubi::BreadthFirst` -// instead. -// -// No GC may occur while an instance of `PostOrder` is live. -// -// The `Visitor` type provided to `PostOrder::traverse` must have the following -// member: -// -// bool operator()(Node& node) -// -// The visitor method. This method is called once for each `node` reachable -// from the start set in post-order. -// -// The visitor function should return true on success, or false if an error -// occurs. A false return value terminates the traversal immediately, and -// causes `PostOrder::traverse` to return false. +/** + * A post-order depth-first traversal of `ubi::Node` graphs. + * + * No GC may occur while an instance of `PostOrder` is live. + * + * The `NodeVisitor` type provided to `PostOrder::traverse` must have the + * following member: + * + * bool operator()(Node& node) + * + * The node visitor method. This method is called once for each `node` + * reachable from the start set in post-order. + * + * This visitor function should return true on success, or false if an error + * occurs. A false return value terminates the traversal immediately, and + * causes `PostOrder::traverse` to return false. + * + * The `EdgeVisitor` type provided to `PostOrder::traverse` must have the + * following member: + * + * bool operator()(Node& origin, Edge& edge) + * + * The edge visitor method. This method is called once for each outgoing + * `edge` from `origin` that is reachable from the start set. + * + * NB: UNLIKE NODES, THERE IS NO GUARANTEED ORDER IN WHICH EDGES AND THEIR + * ORIGINS ARE VISITED! + * + * This visitor function should return true on success, or false if an error + * occurs. A false return value terminates the traversal immediately, and + * causes `PostOrder::traverse` to return false. + */ struct PostOrder { private: struct OriginAndEdges { - Node origin; - EdgeVector edges; + Node origin; + EdgeVector edges; OriginAndEdges(const Node& node, EdgeVector&& edges) : origin(node) @@ -120,14 +132,16 @@ struct PostOrder { } // Traverse the graph in post-order, starting with the set of nodes passed - // to `addStart` and applying `visitor::operator()` for each node in - // the graph, as described above. + // to `addStart` and applying `onNode::operator()` for each node in the + // graph and `onEdge::operator()` for each edge in the graph, as described + // above. // // This should be called only once per instance of this class. // - // Return false on OOM or error return from `visitor::operator()`. - template - bool traverse(Visitor visitor) { + // Return false on OOM or error return from `onNode::operator()` or + // `onEdge::operator()`. + template + bool traverse(NodeVisitor onNode, EdgeVisitor onEdge) { MOZ_ASSERT(!traversed, "Can only traverse() once!"); traversed = true; @@ -136,7 +150,7 @@ struct PostOrder { auto& edges = stack.back().edges; if (edges.empty()) { - if (!visitor(origin)) + if (!onNode(origin)) return false; stack.popBack(); continue; @@ -145,14 +159,20 @@ struct PostOrder { Edge edge = mozilla::Move(edges.back()); edges.popBack(); + if (!onEdge(origin, edge)) + return false; + auto ptr = seen.lookupForAdd(edge.referent); // We've already seen this node, don't follow its edges. if (ptr) continue; // Mark the referent as seen and follow its edges. - if (!seen.add(ptr, edge.referent) || !pushForTraversing(edge.referent)) + if (!seen.add(ptr, edge.referent) || + !pushForTraversing(edge.referent)) + { return false; + } } return true; diff --git a/js/public/WeakMapPtr.h b/js/public/WeakMapPtr.h index 84810b977592..41860551a072 100644 --- a/js/public/WeakMapPtr.h +++ b/js/public/WeakMapPtr.h @@ -33,8 +33,6 @@ class JS_PUBLIC_API(WeakMapPtr) V lookup(const K& key); bool put(JSContext* cx, const K& key, const V& value); - static void keyMarkCallback(JSTracer* trc, K key, void* data); - private: void* ptr; diff --git a/js/src/gc/Zone.h b/js/src/gc/Zone.h index 8cdb1d65f1df..af3da791284c 100644 --- a/js/src/gc/Zone.h +++ b/js/src/gc/Zone.h @@ -408,9 +408,16 @@ struct Zone : public JS::shadow::Zone, uniqueIds_.remove(cell); } - // Off-thread parsing should not result in any UIDs being created. - void assertNoUniqueIdsInZone() const { - MOZ_ASSERT(uniqueIds_.count() == 0); + // When finished parsing off-thread, transfer any UIDs we created in the + // off-thread zone into the target zone. + void adoptUniqueIds(JS::Zone* source) { + js::AutoEnterOOMUnsafeRegion oomUnsafe; + for (js::gc::UniqueIdMap::Enum e(source->uniqueIds_); !e.empty(); e.popFront()) { + MOZ_ASSERT(!uniqueIds_.has(e.front().key())); + if (!uniqueIds_.put(e.front().key(), e.front().value())) + oomUnsafe.crash("failed to transfer unique ids from off-main-thread"); + } + source->uniqueIds_.clear(); } #ifdef JSGC_HASH_TABLE_CHECKS diff --git a/js/src/jit/BaselineCompiler.cpp b/js/src/jit/BaselineCompiler.cpp index d5212b05fff4..8a1693d02587 100644 --- a/js/src/jit/BaselineCompiler.cpp +++ b/js/src/jit/BaselineCompiler.cpp @@ -766,7 +766,8 @@ BaselineCompiler::emitArgumentTypeChecks() frame.pushThis(); frame.popRegsAndSync(1); - ICTypeMonitor_Fallback::Compiler compiler(cx, (uint32_t) 0); + ICTypeMonitor_Fallback::Compiler compiler(cx, ICStubCompiler::Engine::Baseline, + (uint32_t) 0); if (!emitNonOpIC(compiler.getStub(&stubSpace_))) return false; @@ -774,7 +775,8 @@ BaselineCompiler::emitArgumentTypeChecks() frame.pushArg(i); frame.popRegsAndSync(1); - ICTypeMonitor_Fallback::Compiler compiler(cx, i + 1); + ICTypeMonitor_Fallback::Compiler compiler(cx, ICStubCompiler::Engine::Baseline, + i + 1); if (!emitNonOpIC(compiler.getStub(&stubSpace_))) return false; } @@ -2258,7 +2260,7 @@ BaselineCompiler::emit_JSOP_GETPROP() frame.popRegsAndSync(1); // Call IC. - ICGetProp_Fallback::Compiler compiler(cx); + ICGetProp_Fallback::Compiler compiler(cx, ICStubCompiler::Engine::Baseline); if (!emitOpIC(compiler.getStub(&stubSpace_))) return false; @@ -2361,7 +2363,8 @@ BaselineCompiler::emit_JSOP_GETALIASEDVAR() if (ionCompileable_) { // No need to monitor types if we know Ion can't compile this script. - ICTypeMonitor_Fallback::Compiler compiler(cx, (ICMonitoredFallbackStub*) nullptr); + ICTypeMonitor_Fallback::Compiler compiler(cx, ICStubCompiler::Engine::Baseline, + (ICMonitoredFallbackStub*) nullptr); if (!emitOpIC(compiler.getStub(&stubSpace_))) return false; } diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp index 247faac70f27..7f26548dbe01 100644 --- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -46,44 +46,6 @@ using mozilla::DebugOnly; namespace js { namespace jit { -static void -GuardReceiverObject(MacroAssembler& masm, ReceiverGuard guard, - Register object, Register scratch, - size_t receiverGuardOffset, Label* failure) -{ - Address groupAddress(ICStubReg, receiverGuardOffset + HeapReceiverGuard::offsetOfGroup()); - Address shapeAddress(ICStubReg, receiverGuardOffset + HeapReceiverGuard::offsetOfShape()); - Address expandoAddress(object, UnboxedPlainObject::offsetOfExpando()); - - if (guard.group) { - masm.loadPtr(groupAddress, scratch); - masm.branchTestObjGroup(Assembler::NotEqual, object, scratch, failure); - - if (guard.group->clasp() == &UnboxedPlainObject::class_ && !guard.shape) { - // Guard the unboxed object has no expando object. - masm.branchPtr(Assembler::NotEqual, expandoAddress, ImmWord(0), failure); - } - } - - if (guard.shape) { - masm.loadPtr(shapeAddress, scratch); - if (guard.group && guard.group->clasp() == &UnboxedPlainObject::class_) { - // Guard the unboxed object has a matching expando object. - masm.branchPtr(Assembler::Equal, expandoAddress, ImmWord(0), failure); - Label done; - masm.push(object); - masm.loadPtr(expandoAddress, object); - masm.branchTestObjShape(Assembler::Equal, object, scratch, &done); - masm.pop(object); - masm.jump(failure); - masm.bind(&done); - masm.pop(object); - } else { - masm.branchTestObjShape(Assembler::NotEqual, object, scratch, failure); - } - } -} - // // WarmUpCounter_Fallback // @@ -377,369 +339,6 @@ ICWarmUpCounter_Fallback::Compiler::generateStubCode(MacroAssembler& masm) return true; } - -// -// TypeMonitor_Fallback -// - -bool -ICTypeMonitor_Fallback::addMonitorStubForValue(JSContext* cx, JSScript* script, HandleValue val) -{ - bool wasDetachedMonitorChain = lastMonitorStubPtrAddr_ == nullptr; - MOZ_ASSERT_IF(wasDetachedMonitorChain, numOptimizedMonitorStubs_ == 0); - - if (numOptimizedMonitorStubs_ >= MAX_OPTIMIZED_STUBS) { - // TODO: if the TypeSet becomes unknown or has the AnyObject type, - // replace stubs with a single stub to handle these. - return true; - } - - if (val.isPrimitive()) { - if (val.isMagic(JS_UNINITIALIZED_LEXICAL)) - return true; - MOZ_ASSERT(!val.isMagic()); - JSValueType type = val.isDouble() ? JSVAL_TYPE_DOUBLE : val.extractNonDoubleType(); - - // Check for existing TypeMonitor stub. - ICTypeMonitor_PrimitiveSet* existingStub = nullptr; - for (ICStubConstIterator iter(firstMonitorStub()); !iter.atEnd(); iter++) { - if (iter->isTypeMonitor_PrimitiveSet()) { - existingStub = iter->toTypeMonitor_PrimitiveSet(); - if (existingStub->containsType(type)) - return true; - } - } - - ICTypeMonitor_PrimitiveSet::Compiler compiler(cx, existingStub, type); - ICStub* stub = existingStub ? compiler.updateStub() - : compiler.getStub(compiler.getStubSpace(script)); - if (!stub) { - ReportOutOfMemory(cx); - return false; - } - - JitSpew(JitSpew_BaselineIC, " %s TypeMonitor stub %p for primitive type %d", - existingStub ? "Modified existing" : "Created new", stub, type); - - if (!existingStub) { - MOZ_ASSERT(!hasStub(TypeMonitor_PrimitiveSet)); - addOptimizedMonitorStub(stub); - } - - } else if (val.toObject().isSingleton()) { - RootedObject obj(cx, &val.toObject()); - - // Check for existing TypeMonitor stub. - for (ICStubConstIterator iter(firstMonitorStub()); !iter.atEnd(); iter++) { - if (iter->isTypeMonitor_SingleObject() && - iter->toTypeMonitor_SingleObject()->object() == obj) - { - return true; - } - } - - ICTypeMonitor_SingleObject::Compiler compiler(cx, obj); - ICStub* stub = compiler.getStub(compiler.getStubSpace(script)); - if (!stub) { - ReportOutOfMemory(cx); - return false; - } - - JitSpew(JitSpew_BaselineIC, " Added TypeMonitor stub %p for singleton %p", - stub, obj.get()); - - addOptimizedMonitorStub(stub); - - } else { - RootedObjectGroup group(cx, val.toObject().group()); - - // Check for existing TypeMonitor stub. - for (ICStubConstIterator iter(firstMonitorStub()); !iter.atEnd(); iter++) { - if (iter->isTypeMonitor_ObjectGroup() && - iter->toTypeMonitor_ObjectGroup()->group() == group) - { - return true; - } - } - - ICTypeMonitor_ObjectGroup::Compiler compiler(cx, group); - ICStub* stub = compiler.getStub(compiler.getStubSpace(script)); - if (!stub) { - ReportOutOfMemory(cx); - return false; - } - - JitSpew(JitSpew_BaselineIC, " Added TypeMonitor stub %p for ObjectGroup %p", - stub, group.get()); - - addOptimizedMonitorStub(stub); - } - - bool firstMonitorStubAdded = wasDetachedMonitorChain && (numOptimizedMonitorStubs_ > 0); - - if (firstMonitorStubAdded) { - // Was an empty monitor chain before, but a new stub was added. This is the - // only time that any main stubs' firstMonitorStub fields need to be updated to - // refer to the newly added monitor stub. - ICStub* firstStub = mainFallbackStub_->icEntry()->firstStub(); - for (ICStubConstIterator iter(firstStub); !iter.atEnd(); iter++) { - // Non-monitored stubs are used if the result has always the same type, - // e.g. a StringLength stub will always return int32. - if (!iter->isMonitored()) - continue; - - // Since we just added the first optimized monitoring stub, any - // existing main stub's |firstMonitorStub| MUST be pointing to the fallback - // monitor stub (i.e. this stub). - MOZ_ASSERT(iter->toMonitoredStub()->firstMonitorStub() == this); - iter->toMonitoredStub()->updateFirstMonitorStub(firstMonitorStub_); - } - } - - return true; -} - -static bool -DoTypeMonitorFallback(JSContext* cx, BaselineFrame* frame, ICTypeMonitor_Fallback* stub, - HandleValue value, MutableHandleValue res) -{ - // It's possible that we arrived here from bailing out of Ion, and that - // Ion proved that the value is dead and optimized out. In such cases, do - // nothing. However, it's also possible that we have an uninitialized this, - // in which case we should not look for other magic values. - if (stub->monitorsThis()) { - MOZ_ASSERT_IF(value.isMagic(), value.isMagic(JS_UNINITIALIZED_LEXICAL)); - } else { - if (value.isMagic(JS_OPTIMIZED_OUT)) { - res.set(value); - return true; - } - } - - RootedScript script(cx, frame->script()); - jsbytecode* pc = stub->icEntry()->pc(script); - TypeFallbackICSpew(cx, stub, "TypeMonitor"); - - uint32_t argument; - if (stub->monitorsThis()) { - MOZ_ASSERT(pc == script->code()); - if (value.isMagic(JS_UNINITIALIZED_LEXICAL)) - TypeScript::SetThis(cx, script, TypeSet::UnknownType()); - else - TypeScript::SetThis(cx, script, value); - } else if (stub->monitorsArgument(&argument)) { - MOZ_ASSERT(pc == script->code()); - TypeScript::SetArgument(cx, script, argument, value); - } else { - TypeScript::Monitor(cx, script, pc, value); - } - - if (!stub->addMonitorStubForValue(cx, script, value)) - return false; - - // Copy input value to res. - res.set(value); - return true; -} - -typedef bool (*DoTypeMonitorFallbackFn)(JSContext*, BaselineFrame*, ICTypeMonitor_Fallback*, - HandleValue, MutableHandleValue); -static const VMFunction DoTypeMonitorFallbackInfo = - FunctionInfo(DoTypeMonitorFallback, TailCall); - -bool -ICTypeMonitor_Fallback::Compiler::generateStubCode(MacroAssembler& masm) -{ - MOZ_ASSERT(engine_ == Engine::Baseline); - MOZ_ASSERT(R0 == JSReturnOperand); - - // Restore the tail call register. - EmitRestoreTailCallReg(masm); - - masm.pushValue(R0); - masm.push(ICStubReg); - pushFramePtr(masm, R0.scratchReg()); - - return tailCallVM(DoTypeMonitorFallbackInfo, masm); -} - -bool -ICTypeMonitor_PrimitiveSet::Compiler::generateStubCode(MacroAssembler& masm) -{ - MOZ_ASSERT(engine_ == Engine::Baseline); - - Label success; - if ((flags_ & TypeToFlag(JSVAL_TYPE_INT32)) && !(flags_ & TypeToFlag(JSVAL_TYPE_DOUBLE))) - masm.branchTestInt32(Assembler::Equal, R0, &success); - - if (flags_ & TypeToFlag(JSVAL_TYPE_DOUBLE)) - masm.branchTestNumber(Assembler::Equal, R0, &success); - - if (flags_ & TypeToFlag(JSVAL_TYPE_UNDEFINED)) - masm.branchTestUndefined(Assembler::Equal, R0, &success); - - if (flags_ & TypeToFlag(JSVAL_TYPE_BOOLEAN)) - masm.branchTestBoolean(Assembler::Equal, R0, &success); - - if (flags_ & TypeToFlag(JSVAL_TYPE_STRING)) - masm.branchTestString(Assembler::Equal, R0, &success); - - if (flags_ & TypeToFlag(JSVAL_TYPE_SYMBOL)) - masm.branchTestSymbol(Assembler::Equal, R0, &success); - - // Currently, we will never generate primitive stub checks for object. However, - // when we do get to the point where we want to collapse our monitor chains of - // objects and singletons down (when they get too long) to a generic "any object" - // in coordination with the typeset doing the same thing, this will need to - // be re-enabled. - /* - if (flags_ & TypeToFlag(JSVAL_TYPE_OBJECT)) - masm.branchTestObject(Assembler::Equal, R0, &success); - */ - MOZ_ASSERT(!(flags_ & TypeToFlag(JSVAL_TYPE_OBJECT))); - - if (flags_ & TypeToFlag(JSVAL_TYPE_NULL)) - masm.branchTestNull(Assembler::Equal, R0, &success); - - EmitStubGuardFailure(masm); - - masm.bind(&success); - EmitReturnFromIC(masm); - return true; -} - -bool -ICTypeMonitor_SingleObject::Compiler::generateStubCode(MacroAssembler& masm) -{ - MOZ_ASSERT(engine_ == Engine::Baseline); - - Label failure; - masm.branchTestObject(Assembler::NotEqual, R0, &failure); - - // Guard on the object's identity. - Register obj = masm.extractObject(R0, ExtractTemp0); - Address expectedObject(ICStubReg, ICTypeMonitor_SingleObject::offsetOfObject()); - masm.branchPtr(Assembler::NotEqual, expectedObject, obj, &failure); - - EmitReturnFromIC(masm); - - masm.bind(&failure); - EmitStubGuardFailure(masm); - return true; -} - -bool -ICTypeMonitor_ObjectGroup::Compiler::generateStubCode(MacroAssembler& masm) -{ - MOZ_ASSERT(engine_ == Engine::Baseline); - - Label failure; - masm.branchTestObject(Assembler::NotEqual, R0, &failure); - - // Guard on the object's ObjectGroup. - Register obj = masm.extractObject(R0, ExtractTemp0); - masm.loadPtr(Address(obj, JSObject::offsetOfGroup()), R1.scratchReg()); - - Address expectedGroup(ICStubReg, ICTypeMonitor_ObjectGroup::offsetOfGroup()); - masm.branchPtr(Assembler::NotEqual, expectedGroup, R1.scratchReg(), &failure); - - EmitReturnFromIC(masm); - - masm.bind(&failure); - EmitStubGuardFailure(masm); - return true; -} - -bool -ICUpdatedStub::addUpdateStubForValue(JSContext* cx, HandleScript script, HandleObject obj, - HandleId id, HandleValue val) -{ - if (numOptimizedStubs_ >= MAX_OPTIMIZED_STUBS) { - // TODO: if the TypeSet becomes unknown or has the AnyObject type, - // replace stubs with a single stub to handle these. - return true; - } - - EnsureTrackPropertyTypes(cx, obj, id); - - // Make sure that undefined values are explicitly included in the property - // types for an object if generating a stub to write an undefined value. - if (val.isUndefined() && CanHaveEmptyPropertyTypesForOwnProperty(obj)) - AddTypePropertyId(cx, obj, id, val); - - if (val.isPrimitive()) { - JSValueType type = val.isDouble() ? JSVAL_TYPE_DOUBLE : val.extractNonDoubleType(); - - // Check for existing TypeUpdate stub. - ICTypeUpdate_PrimitiveSet* existingStub = nullptr; - for (ICStubConstIterator iter(firstUpdateStub_); !iter.atEnd(); iter++) { - if (iter->isTypeUpdate_PrimitiveSet()) { - existingStub = iter->toTypeUpdate_PrimitiveSet(); - if (existingStub->containsType(type)) - return true; - } - } - - ICTypeUpdate_PrimitiveSet::Compiler compiler(cx, existingStub, type); - ICStub* stub = existingStub ? compiler.updateStub() - : compiler.getStub(compiler.getStubSpace(script)); - if (!stub) - return false; - if (!existingStub) { - MOZ_ASSERT(!hasTypeUpdateStub(TypeUpdate_PrimitiveSet)); - addOptimizedUpdateStub(stub); - } - - JitSpew(JitSpew_BaselineIC, " %s TypeUpdate stub %p for primitive type %d", - existingStub ? "Modified existing" : "Created new", stub, type); - - } else if (val.toObject().isSingleton()) { - RootedObject obj(cx, &val.toObject()); - - // Check for existing TypeUpdate stub. - for (ICStubConstIterator iter(firstUpdateStub_); !iter.atEnd(); iter++) { - if (iter->isTypeUpdate_SingleObject() && - iter->toTypeUpdate_SingleObject()->object() == obj) - { - return true; - } - } - - ICTypeUpdate_SingleObject::Compiler compiler(cx, obj); - ICStub* stub = compiler.getStub(compiler.getStubSpace(script)); - if (!stub) - return false; - - JitSpew(JitSpew_BaselineIC, " Added TypeUpdate stub %p for singleton %p", stub, obj.get()); - - addOptimizedUpdateStub(stub); - - } else { - RootedObjectGroup group(cx, val.toObject().group()); - - // Check for existing TypeUpdate stub. - for (ICStubConstIterator iter(firstUpdateStub_); !iter.atEnd(); iter++) { - if (iter->isTypeUpdate_ObjectGroup() && - iter->toTypeUpdate_ObjectGroup()->group() == group) - { - return true; - } - } - - ICTypeUpdate_ObjectGroup::Compiler compiler(cx, group); - ICStub* stub = compiler.getStub(compiler.getStubSpace(script)); - if (!stub) - return false; - - JitSpew(JitSpew_BaselineIC, " Added TypeUpdate stub %p for ObjectGroup %p", - stub, group.get()); - - addOptimizedUpdateStub(stub); - } - - return true; -} - // // TypeUpdate_Fallback // @@ -909,28 +508,6 @@ ICTypeUpdate_ObjectGroup::Compiler::generateStubCode(MacroAssembler& masm) return true; } -// -// VM function to help call native getters. -// - -static bool -DoCallNativeGetter(JSContext* cx, HandleFunction callee, HandleObject obj, - MutableHandleValue result) -{ - MOZ_ASSERT(callee->isNative()); - JSNative natfun = callee->native(); - - JS::AutoValueArray<2> vp(cx); - vp[0].setObject(*callee.get()); - vp[1].setObject(*obj.get()); - - if (!natfun(cx, 0, vp.begin())) - return false; - - result.set(vp[0]); - return true; -} - typedef bool (*DoCallNativeGetterFn)(JSContext*, HandleFunction, HandleObject, MutableHandleValue); static const VMFunction DoCallNativeGetterInfo = FunctionInfo(DoCallNativeGetter); @@ -1403,274 +980,6 @@ ICToNumber_Fallback::Compiler::generateStubCode(MacroAssembler& masm) // GetElem_Fallback // -static void GetFixedOrDynamicSlotOffset(Shape* shape, bool* isFixed, uint32_t* offset) -{ - MOZ_ASSERT(isFixed); - MOZ_ASSERT(offset); - *isFixed = shape->slot() < shape->numFixedSlots(); - *offset = *isFixed ? NativeObject::getFixedSlotOffset(shape->slot()) - : (shape->slot() - shape->numFixedSlots()) * sizeof(Value); -} - -static JSObject* -GetDOMProxyProto(JSObject* obj) -{ - MOZ_ASSERT(IsCacheableDOMProxy(obj)); - return obj->getTaggedProto().toObjectOrNull(); -} - -// Callers are expected to have already guarded on the shape of the -// object, which guarantees the object is a DOM proxy. -static void -CheckDOMProxyExpandoDoesNotShadow(JSContext* cx, MacroAssembler& masm, Register object, - const Address& checkExpandoShapeAddr, - Address* expandoAndGenerationAddr, - Address* generationAddr, - Register scratch, - AllocatableGeneralRegisterSet& domProxyRegSet, - Label* checkFailed) -{ - // Guard that the object does not have expando properties, or has an expando - // which is known to not have the desired property. - - // For the remaining code, we need to reserve some registers to load a value. - // This is ugly, but unavoidable. - ValueOperand tempVal = domProxyRegSet.takeAnyValue(); - masm.pushValue(tempVal); - - Label failDOMProxyCheck; - Label domProxyOk; - - masm.loadPtr(Address(object, ProxyObject::offsetOfValues()), scratch); - Address expandoAddr(scratch, ProxyObject::offsetOfExtraSlotInValues(GetDOMProxyExpandoSlot())); - - if (expandoAndGenerationAddr) { - MOZ_ASSERT(generationAddr); - - masm.loadPtr(*expandoAndGenerationAddr, tempVal.scratchReg()); - masm.branchPrivatePtr(Assembler::NotEqual, expandoAddr, tempVal.scratchReg(), - &failDOMProxyCheck); - - masm.load32(*generationAddr, scratch); - masm.branch32(Assembler::NotEqual, - Address(tempVal.scratchReg(), offsetof(ExpandoAndGeneration, generation)), - scratch, &failDOMProxyCheck); - - masm.loadValue(Address(tempVal.scratchReg(), 0), tempVal); - } else { - masm.loadValue(expandoAddr, tempVal); - } - - // If the incoming object does not have an expando object then we're sure we're not - // shadowing. - masm.branchTestUndefined(Assembler::Equal, tempVal, &domProxyOk); - - // The reference object used to generate this check may not have had an - // expando object at all, in which case the presence of a non-undefined - // expando value in the incoming object is automatically a failure. - masm.loadPtr(checkExpandoShapeAddr, scratch); - masm.branchPtr(Assembler::Equal, scratch, ImmPtr(nullptr), &failDOMProxyCheck); - - // Otherwise, ensure that the incoming object has an object for its expando value and that - // the shape matches. - masm.branchTestObject(Assembler::NotEqual, tempVal, &failDOMProxyCheck); - Register objReg = masm.extractObject(tempVal, tempVal.scratchReg()); - masm.branchTestObjShape(Assembler::Equal, objReg, scratch, &domProxyOk); - - // Failure case: restore the tempVal registers and jump to failures. - masm.bind(&failDOMProxyCheck); - masm.popValue(tempVal); - masm.jump(checkFailed); - - // Success case: restore the tempval and proceed. - masm.bind(&domProxyOk); - masm.popValue(tempVal); -} - -// Look up a property's shape on an object, being careful never to do any effectful -// operations. This procedure not yielding a shape should not be taken as a lack of -// existence of the property on the object. -static bool -EffectlesslyLookupProperty(JSContext* cx, HandleObject obj, HandleId id, - MutableHandleObject holder, MutableHandleShape shape, - bool* checkDOMProxy=nullptr, - DOMProxyShadowsResult* shadowsResult=nullptr, - bool* domProxyHasGeneration=nullptr) -{ - shape.set(nullptr); - holder.set(nullptr); - - if (checkDOMProxy) { - *checkDOMProxy = false; - *shadowsResult = ShadowCheckFailed; - } - - // Check for list base if asked to. - RootedObject checkObj(cx, obj); - if (checkDOMProxy && IsCacheableDOMProxy(obj)) { - MOZ_ASSERT(domProxyHasGeneration); - MOZ_ASSERT(shadowsResult); - - *checkDOMProxy = true; - if (obj->hasUncacheableProto()) - return true; - - *shadowsResult = GetDOMProxyShadowsCheck()(cx, obj, id); - if (*shadowsResult == ShadowCheckFailed) - return false; - - if (DOMProxyIsShadowing(*shadowsResult)) { - holder.set(obj); - return true; - } - - *domProxyHasGeneration = (*shadowsResult == DoesntShadowUnique); - - checkObj = GetDOMProxyProto(obj); - if (!checkObj) - return true; - } - - if (LookupPropertyPure(cx, checkObj, id, holder.address(), shape.address())) - return true; - - holder.set(nullptr); - shape.set(nullptr); - return true; -} - -static bool -CheckHasNoSuchProperty(JSContext* cx, HandleObject obj, HandlePropertyName name, - MutableHandleObject lastProto, size_t* protoChainDepthOut) -{ - MOZ_ASSERT(protoChainDepthOut != nullptr); - - size_t depth = 0; - RootedObject curObj(cx, obj); - while (curObj) { - if (curObj->isNative()) { - // Don't handle proto chains with resolve hooks. - if (ClassMayResolveId(cx->names(), curObj->getClass(), NameToId(name), curObj)) - return false; - if (curObj->as().contains(cx, NameToId(name))) - return false; - } else if (curObj != obj) { - // Non-native objects are only handled as the original receiver. - return false; - } else if (curObj->is()) { - if (curObj->as().containsUnboxedOrExpandoProperty(cx, NameToId(name))) - return false; - } else if (curObj->is()) { - if (name == cx->names().length) - return false; - } else if (curObj->is()) { - if (curObj->as().typeDescr().hasProperty(cx->names(), NameToId(name))) - return false; - } else { - return false; - } - - JSObject* proto = curObj->getTaggedProto().toObjectOrNull(); - if (!proto) - break; - - curObj = proto; - depth++; - } - - lastProto.set(curObj); - *protoChainDepthOut = depth; - return true; -} - -static bool -IsCacheableProtoChain(JSObject* obj, JSObject* holder, bool isDOMProxy=false) -{ - MOZ_ASSERT_IF(isDOMProxy, IsCacheableDOMProxy(obj)); - - if (!isDOMProxy && !obj->isNative()) { - if (obj == holder) - return false; - if (!obj->is() && - !obj->is() && - !obj->is()) - { - return false; - } - } - - // Don't handle objects which require a prototype guard. This should - // be uncommon so handling it is likely not worth the complexity. - if (obj->hasUncacheableProto()) - return false; - - JSObject* cur = obj; - while (cur != holder) { - // We cannot assume that we find the holder object on the prototype - // chain and must check for null proto. The prototype chain can be - // altered during the lookupProperty call. - JSObject* proto; - if (isDOMProxy && cur == obj) - proto = cur->getTaggedProto().toObjectOrNull(); - else - proto = cur->getProto(); - - if (!proto || !proto->isNative()) - return false; - - if (proto->hasUncacheableProto()) - return false; - - cur = proto; - } - return true; -} - -static bool -IsCacheableGetPropReadSlot(JSObject* obj, JSObject* holder, Shape* shape, bool isDOMProxy=false) -{ - if (!shape || !IsCacheableProtoChain(obj, holder, isDOMProxy)) - return false; - - if (!shape->hasSlot() || !shape->hasDefaultGetter()) - return false; - - return true; -} - -static bool -IsCacheableGetPropCall(JSContext* cx, JSObject* obj, JSObject* holder, Shape* shape, - bool* isScripted, bool* isTemporarilyUnoptimizable, bool isDOMProxy=false) -{ - MOZ_ASSERT(isScripted); - - if (!shape || !IsCacheableProtoChain(obj, holder, isDOMProxy)) - return false; - - if (shape->hasSlot() || shape->hasDefaultGetter()) - return false; - - if (!shape->hasGetterValue()) - return false; - - if (!shape->getterValue().isObject() || !shape->getterObject()->is()) - return false; - - JSFunction* func = &shape->getterObject()->as(); - if (func->isNative()) { - *isScripted = false; - return true; - } - - if (!func->hasJITCode()) { - *isTemporarilyUnoptimizable = true; - return false; - } - - *isScripted = true; - return true; -} - static Shape* LastPropertyForSetProp(JSObject* obj) { @@ -2530,8 +1839,11 @@ DoGetElemFallback(JSContext* cx, BaselineFrame* frame, ICGetElem_Fallback* stub_ return true; // Add a type monitor stub for the resulting value. - if (!stub->addMonitorStubForValue(cx, frame->script(), res)) + if (!stub->addMonitorStubForValue(cx, frame->script(), res, + ICStubCompiler::Engine::Baseline)) + { return false; + } if (attached) return true; @@ -3085,34 +2397,6 @@ LoadTypedThingLength(MacroAssembler& masm, TypedThingLayout layout, Register obj } } -static void -LoadTypedThingData(MacroAssembler& masm, TypedThingLayout layout, Register obj, Register result) -{ - switch (layout) { - case Layout_TypedArray: - masm.loadPtr(Address(obj, TypedArrayLayout::dataOffset()), result); - break; - case Layout_OutlineTypedObject: - masm.loadPtr(Address(obj, OutlineTypedObject::offsetOfData()), result); - break; - case Layout_InlineTypedObject: - masm.computeEffectiveAddress(Address(obj, InlineTypedObject::offsetOfDataStart()), result); - break; - default: - MOZ_CRASH(); - } -} - -static void -CheckForNeuteredTypedObject(JSContext* cx, MacroAssembler& masm, Label* failure) -{ - // All stubs which manipulate typed objects need to check the compartment - // wide flag indicating whether the objects are neutered, and bail out in - // this case. - int32_t* address = &cx->compartment()->neuteredTypedObjects; - masm.branch32(Assembler::NotEqual, AbsoluteAddress(address), Imm32(0), failure); -} - bool ICGetElem_TypedArray::Compiler::generateStubCode(MacroAssembler& masm) { @@ -3858,19 +3142,6 @@ ICSetElem_DenseOrUnboxedArray::Compiler::generateStubCode(MacroAssembler& masm) return true; } -static bool -GetProtoShapes(JSObject* obj, size_t protoChainDepth, MutableHandle shapes) -{ - JSObject* curProto = obj->getProto(); - for (size_t i = 0; i < protoChainDepth; i++) { - if (!shapes.append(curProto->as().lastProperty())) - return false; - curProto = curProto->getProto(); - } - MOZ_ASSERT(!curProto); - return true; -} - // // SetElem_DenseOrUnboxedArrayAdd // @@ -4600,70 +3871,6 @@ ICIn_Dense::Compiler::generateStubCode(MacroAssembler& masm) return true; } -// Try to update all existing GetProp/GetName getter call stubs that match the -// given holder in place with a new shape and getter. fallbackStub can be -// either an ICGetProp_Fallback or an ICGetName_Fallback. -// -// If 'getter' is an own property, holder == receiver must be true. -static bool -UpdateExistingGetPropCallStubs(ICFallbackStub* fallbackStub, - ICStub::Kind kind, - HandleNativeObject holder, - HandleObject receiver, - HandleFunction getter) -{ - MOZ_ASSERT(kind == ICStub::GetProp_CallScripted || - kind == ICStub::GetProp_CallNative || - kind == ICStub::GetProp_CallNativeGlobal); - MOZ_ASSERT(fallbackStub->isGetName_Fallback() || - fallbackStub->isGetProp_Fallback()); - MOZ_ASSERT(holder); - MOZ_ASSERT(receiver); - - bool isOwnGetter = (holder == receiver); - bool foundMatchingStub = false; - ReceiverGuard receiverGuard(receiver); - for (ICStubConstIterator iter = fallbackStub->beginChainConst(); !iter.atEnd(); iter++) { - if (iter->kind() == kind) { - ICGetPropCallGetter* getPropStub = static_cast(*iter); - if (getPropStub->holder() == holder && getPropStub->isOwnGetter() == isOwnGetter) { - // If this is an own getter, update the receiver guard as well, - // since that's the shape we'll be guarding on. Furthermore, - // isOwnGetter() relies on holderShape_ and receiverGuard_ being - // the same shape. - if (isOwnGetter) - getPropStub->receiverGuard().update(receiverGuard); - - MOZ_ASSERT(getPropStub->holderShape() != holder->lastProperty() || - !getPropStub->receiverGuard().matches(receiverGuard) || - getPropStub->toGetProp_CallNativeGlobal()->globalShape() != - receiver->as().global().lastProperty(), - "Why didn't we end up using this stub?"); - - // We want to update the holder shape to match the new one no - // matter what, even if the receiver shape is different. - getPropStub->holderShape() = holder->lastProperty(); - - // Make sure to update the getter, since a shape change might - // have changed which getter we want to use. - getPropStub->getter() = getter; - - if (getPropStub->isGetProp_CallNativeGlobal()) { - ICGetProp_CallNativeGlobal* globalStub = - getPropStub->toGetProp_CallNativeGlobal(); - globalStub->globalShape() = - receiver->as().global().lastProperty(); - } - - if (getPropStub->receiverGuard().matches(receiverGuard)) - foundMatchingStub = true; - } - } - } - - return foundMatchingStub; -} - // Try to update existing SetProp setter call stubs for the given holder in // place with a new shape and setter. static bool @@ -4770,7 +3977,8 @@ TryAttachGlobalNameValueStub(JSContext* cx, HandleScript script, jsbytecode* pc, return true; JitSpew(JitSpew_BaselineIC, " Generating GetName(GlobalName non-lexical) stub"); - ICGetPropNativeCompiler compiler(cx, ICStub::GetName_Global, monitorStub, + ICGetPropNativeCompiler compiler(cx, ICStub::GetName_Global, + ICStubCompiler::Engine::Baseline, monitorStub, globalLexical, current, name, isFixedSlot, offset, /* inputDefinitelyObject = */ true); newStub = compiler.getStub(compiler.getStubSpace(script)); @@ -4847,6 +4055,7 @@ TryAttachGlobalNameAccessorStub(JSContext* cx, HandleScript script, jsbytecode* return true; } ICGetPropCallNativeCompiler compiler(cx, ICStub::GetProp_CallNativeGlobal, + ICStubCompiler::Engine::Baseline, monitorStub, globalLexical, current, getter, script->pcToOffset(pc), /* outerClass = */ nullptr, @@ -5016,7 +4225,7 @@ DoGetNameFallback(JSContext* cx, BaselineFrame* frame, ICGetName_Fallback* stub_ return true; // Add a type monitor stub for the resulting value. - if (!stub->addMonitorStubForValue(cx, script, res)) + if (!stub->addMonitorStubForValue(cx, script, res, ICStubCompiler::Engine::Baseline)) return false; if (attached) return true; @@ -5239,1795 +4448,6 @@ ICGetIntrinsic_Constant::Compiler::generateStubCode(MacroAssembler& masm) return true; } -// -// GetProp_Fallback -// - -static bool -TryAttachMagicArgumentsGetPropStub(JSContext* cx, JSScript* script, ICGetProp_Fallback* stub, - HandlePropertyName name, HandleValue val, HandleValue res, - bool* attached) -{ - MOZ_ASSERT(!*attached); - - if (!val.isMagic(JS_OPTIMIZED_ARGUMENTS)) - return true; - - // Try handling arguments.callee on optimized arguments. - if (name == cx->names().callee) { - MOZ_ASSERT(script->hasMappedArgsObj()); - - JitSpew(JitSpew_BaselineIC, " Generating GetProp(MagicArgs.callee) stub"); - - // Unlike ICGetProp_ArgumentsLength, only magic argument stubs are - // supported at the moment. - ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub(); - ICGetProp_ArgumentsCallee::Compiler compiler(cx, monitorStub); - ICStub* newStub = compiler.getStub(compiler.getStubSpace(script)); - if (!newStub) - return false; - stub->addNewStub(newStub); - - *attached = true; - return true; - } - - return true; -} - -static bool -TryAttachLengthStub(JSContext* cx, JSScript* script, ICGetProp_Fallback* stub, HandleValue val, - HandleValue res, bool* attached) -{ - MOZ_ASSERT(!*attached); - - if (val.isString()) { - MOZ_ASSERT(res.isInt32()); - JitSpew(JitSpew_BaselineIC, " Generating GetProp(String.length) stub"); - ICGetProp_StringLength::Compiler compiler(cx); - ICStub* newStub = compiler.getStub(compiler.getStubSpace(script)); - if (!newStub) - return false; - - *attached = true; - stub->addNewStub(newStub); - return true; - } - - if (val.isMagic(JS_OPTIMIZED_ARGUMENTS) && res.isInt32()) { - JitSpew(JitSpew_BaselineIC, " Generating GetProp(MagicArgs.length) stub"); - ICGetProp_ArgumentsLength::Compiler compiler(cx, ICGetProp_ArgumentsLength::Magic); - ICStub* newStub = compiler.getStub(compiler.getStubSpace(script)); - if (!newStub) - return false; - - *attached = true; - stub->addNewStub(newStub); - return true; - } - - if (!val.isObject()) - return true; - - RootedObject obj(cx, &val.toObject()); - - if (obj->is() && res.isInt32()) { - JitSpew(JitSpew_BaselineIC, " Generating GetProp(Array.length) stub"); - ICGetProp_ArrayLength::Compiler compiler(cx); - ICStub* newStub = compiler.getStub(compiler.getStubSpace(script)); - if (!newStub) - return false; - - *attached = true; - stub->addNewStub(newStub); - return true; - } - - if (obj->is() && res.isInt32()) { - JitSpew(JitSpew_BaselineIC, " Generating GetProp(UnboxedArray.length) stub"); - ICGetProp_UnboxedArrayLength::Compiler compiler(cx); - ICStub* newStub = compiler.getStub(compiler.getStubSpace(script)); - if (!newStub) - return false; - - *attached = true; - stub->addNewStub(newStub); - return true; - } - - if (obj->is() && res.isInt32()) { - JitSpew(JitSpew_BaselineIC, " Generating GetProp(ArgsObj.length %s) stub", - obj->is() ? "Mapped" : "Unmapped"); - ICGetProp_ArgumentsLength::Which which = ICGetProp_ArgumentsLength::Mapped; - if (obj->is()) - which = ICGetProp_ArgumentsLength::Unmapped; - ICGetProp_ArgumentsLength::Compiler compiler(cx, which); - ICStub* newStub = compiler.getStub(compiler.getStubSpace(script)); - if (!newStub) - return false; - - *attached = true; - stub->addNewStub(newStub); - return true; - } - - return true; -} - -static bool -UpdateExistingGenerationalDOMProxyStub(ICGetProp_Fallback* stub, - HandleObject obj) -{ - Value expandoSlot = GetProxyExtra(obj, GetDOMProxyExpandoSlot()); - MOZ_ASSERT(!expandoSlot.isObject() && !expandoSlot.isUndefined()); - ExpandoAndGeneration* expandoAndGeneration = (ExpandoAndGeneration*)expandoSlot.toPrivate(); - for (ICStubConstIterator iter = stub->beginChainConst(); !iter.atEnd(); iter++) { - if (iter->isGetProp_CallDOMProxyWithGenerationNative()) { - ICGetProp_CallDOMProxyWithGenerationNative* updateStub = - iter->toGetProp_CallDOMProxyWithGenerationNative(); - if (updateStub->expandoAndGeneration() == expandoAndGeneration) { - // Update generation - uint32_t generation = expandoAndGeneration->generation; - JitSpew(JitSpew_BaselineIC, - " Updating existing stub with generation, old value: %i, " - "new value: %i", updateStub->generation(), - generation); - updateStub->setGeneration(generation); - return true; - } - } - } - return false; -} - -// Return whether obj is in some PreliminaryObjectArray and has a structure -// that might change in the future. -static bool -IsPreliminaryObject(JSObject* obj) -{ - if (obj->isSingleton()) - return false; - - TypeNewScript* newScript = obj->group()->newScript(); - if (newScript && !newScript->analyzed()) - return true; - - if (obj->group()->maybePreliminaryObjects()) - return true; - - return false; -} - -static void -StripPreliminaryObjectStubs(JSContext* cx, ICFallbackStub* stub) -{ - // Before the new script properties analysis has been performed on a type, - // all instances of that type have the maximum number of fixed slots. - // Afterwards, the objects (even the preliminary ones) might be changed - // to reduce the number of fixed slots they have. If we generate stubs for - // both the old and new number of fixed slots, the stub will look - // polymorphic to IonBuilder when it is actually monomorphic. To avoid - // this, strip out any stubs for preliminary objects before attaching a new - // stub which isn't on a preliminary object. - - for (ICStubIterator iter = stub->beginChain(); !iter.atEnd(); iter++) { - if (iter->isGetProp_Native() && iter->toGetProp_Native()->hasPreliminaryObject()) - iter.unlink(cx); - else if (iter->isSetProp_Native() && iter->toSetProp_Native()->hasPreliminaryObject()) - iter.unlink(cx); - } -} - -static bool -TryAttachNativeGetValuePropStub(JSContext* cx, HandleScript script, jsbytecode* pc, - ICGetProp_Fallback* stub, HandlePropertyName name, - HandleValue val, HandleShape oldShape, - HandleValue res, bool* attached) -{ - MOZ_ASSERT(!*attached); - - if (!val.isObject()) - return true; - - RootedObject obj(cx, &val.toObject()); - - if (obj->isNative() && oldShape != obj->as().lastProperty()) { - // No point attaching anything, since we know the shape guard will fail - return true; - } - - RootedShape shape(cx); - RootedObject holder(cx); - RootedId id(cx, NameToId(name)); - if (!EffectlesslyLookupProperty(cx, obj, id, &holder, &shape)) - return false; - - ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub(); - if (IsCacheableGetPropReadSlot(obj, holder, shape)) { - bool isFixedSlot; - uint32_t offset; - GetFixedOrDynamicSlotOffset(shape, &isFixedSlot, &offset); - - // Instantiate this property for singleton holders, for use during Ion compilation. - if (IsIonEnabled(cx)) - EnsureTrackPropertyTypes(cx, holder, NameToId(name)); - - ICStub::Kind kind = - (obj == holder) ? ICStub::GetProp_Native : ICStub::GetProp_NativePrototype; - - JitSpew(JitSpew_BaselineIC, " Generating GetProp(Native %s) stub", - (obj == holder) ? "direct" : "prototype"); - ICGetPropNativeCompiler compiler(cx, kind, monitorStub, obj, holder, - name, isFixedSlot, offset); - ICGetPropNativeStub* newStub = compiler.getStub(compiler.getStubSpace(script)); - if (!newStub) - return false; - - if (IsPreliminaryObject(obj)) - newStub->notePreliminaryObject(); - else - StripPreliminaryObjectStubs(cx, stub); - - stub->addNewStub(newStub); - *attached = true; - return true; - } - return true; -} - - -static bool -TryAttachNativeGetAccessorPropStub(JSContext* cx, HandleScript script, jsbytecode* pc, - ICGetProp_Fallback* stub, HandlePropertyName name, - HandleValue val, HandleValue res, bool* attached, - bool* isTemporarilyUnoptimizable) -{ - MOZ_ASSERT(!*attached); - MOZ_ASSERT(!*isTemporarilyUnoptimizable); - - if (!val.isObject()) - return true; - - RootedObject obj(cx, &val.toObject()); - - bool isDOMProxy; - bool domProxyHasGeneration; - DOMProxyShadowsResult domProxyShadowsResult; - RootedShape shape(cx); - RootedObject holder(cx); - RootedId id(cx, NameToId(name)); - if (!EffectlesslyLookupProperty(cx, obj, id, &holder, &shape, &isDOMProxy, - &domProxyShadowsResult, &domProxyHasGeneration)) - { - return false; - } - - ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub(); - - bool isScripted = false; - bool cacheableCall = IsCacheableGetPropCall(cx, obj, holder, shape, &isScripted, - isTemporarilyUnoptimizable); - - // Try handling scripted getters. - if (cacheableCall && isScripted && !isDOMProxy) { - RootedFunction callee(cx, &shape->getterObject()->as()); - MOZ_ASSERT(callee->hasScript()); - - if (UpdateExistingGetPropCallStubs(stub, ICStub::GetProp_CallScripted, - holder.as(), obj, callee)) { - *attached = true; - return true; - } - - JitSpew(JitSpew_BaselineIC, " Generating GetProp(NativeObj/ScriptedGetter %s:%" PRIuSIZE ") stub", - callee->nonLazyScript()->filename(), callee->nonLazyScript()->lineno()); - - ICGetProp_CallScripted::Compiler compiler(cx, monitorStub, obj, holder, callee, - script->pcToOffset(pc)); - ICStub* newStub = compiler.getStub(compiler.getStubSpace(script)); - if (!newStub) - return false; - - stub->addNewStub(newStub); - *attached = true; - return true; - } - - // If it's a shadowed listbase proxy property, attach stub to call Proxy::get instead. - if (isDOMProxy && DOMProxyIsShadowing(domProxyShadowsResult)) { - MOZ_ASSERT(obj == holder); - - JitSpew(JitSpew_BaselineIC, " Generating GetProp(DOMProxyProxy) stub"); - Rooted proxy(cx, &obj->as()); - ICGetProp_DOMProxyShadowed::Compiler compiler(cx, monitorStub, proxy, name, - script->pcToOffset(pc)); - ICStub* newStub = compiler.getStub(compiler.getStubSpace(script)); - if (!newStub) - return false; - stub->addNewStub(newStub); - *attached = true; - return true; - } - - const Class* outerClass = nullptr; - if (!isDOMProxy && !obj->isNative()) { - outerClass = obj->getClass(); - if (!IsWindowProxy(obj)) - return true; - - // This must be a WindowProxy for the current Window/global. Else it'd - // be a cross-compartment wrapper and IsWindowProxy returns false for - // those. - MOZ_ASSERT(ToWindowIfWindowProxy(obj) == cx->global()); - obj = cx->global(); - - if (!EffectlesslyLookupProperty(cx, obj, id, &holder, &shape, &isDOMProxy, - &domProxyShadowsResult, &domProxyHasGeneration)) - { - return false; - } - cacheableCall = IsCacheableGetPropCall(cx, obj, holder, shape, &isScripted, - isTemporarilyUnoptimizable, isDOMProxy); - } - - // Try handling JSNative getters. - if (!cacheableCall || isScripted) - return true; - - if (!shape || !shape->hasGetterValue() || !shape->getterValue().isObject() || - !shape->getterObject()->is()) - { - return true; - } - - RootedFunction callee(cx, &shape->getterObject()->as()); - MOZ_ASSERT(callee->isNative()); - - if (outerClass && (!callee->jitInfo() || callee->jitInfo()->needsOuterizedThisObject())) - return true; - - JitSpew(JitSpew_BaselineIC, " Generating GetProp(%s%s/NativeGetter %p) stub", - isDOMProxy ? "DOMProxyObj" : "NativeObj", - isDOMProxy && domProxyHasGeneration ? "WithGeneration" : "", - callee->native()); - - ICStub* newStub = nullptr; - if (isDOMProxy) { - MOZ_ASSERT(obj != holder); - ICStub::Kind kind; - if (domProxyHasGeneration) { - if (UpdateExistingGenerationalDOMProxyStub(stub, obj)) { - *attached = true; - return true; - } - kind = ICStub::GetProp_CallDOMProxyWithGenerationNative; - } else { - kind = ICStub::GetProp_CallDOMProxyNative; - } - Rooted proxy(cx, &obj->as()); - ICGetPropCallDOMProxyNativeCompiler compiler(cx, kind, monitorStub, proxy, holder, callee, - script->pcToOffset(pc)); - newStub = compiler.getStub(compiler.getStubSpace(script)); - } else { - if (UpdateExistingGetPropCallStubs(stub, ICStub::GetProp_CallNative, - holder.as(), obj, callee)) - { - *attached = true; - return true; - } - - ICGetPropCallNativeCompiler compiler(cx, ICStub::GetProp_CallNative, - monitorStub, obj, holder, callee, - script->pcToOffset(pc), outerClass); - newStub = compiler.getStub(compiler.getStubSpace(script)); - } - if (!newStub) - return false; - stub->addNewStub(newStub); - *attached = true; - return true; -} - -static bool -TryAttachUnboxedGetPropStub(JSContext* cx, HandleScript script, - ICGetProp_Fallback* stub, HandlePropertyName name, HandleValue val, - bool* attached) -{ - MOZ_ASSERT(!*attached); - - if (!cx->runtime()->jitSupportsFloatingPoint) - return true; - - if (!val.isObject() || !val.toObject().is()) - return true; - Rooted obj(cx, &val.toObject().as()); - - const UnboxedLayout::Property* property = obj->layout().lookup(name); - if (!property) - return true; - - ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub(); - - ICGetProp_Unboxed::Compiler compiler(cx, monitorStub, obj->group(), - property->offset + UnboxedPlainObject::offsetOfData(), - property->type); - ICStub* newStub = compiler.getStub(compiler.getStubSpace(script)); - if (!newStub) - return false; - stub->addNewStub(newStub); - - StripPreliminaryObjectStubs(cx, stub); - - *attached = true; - return true; -} - -static bool -TryAttachUnboxedExpandoGetPropStub(JSContext* cx, HandleScript script, jsbytecode* pc, - ICGetProp_Fallback* stub, HandlePropertyName name, HandleValue val, - bool* attached) -{ - MOZ_ASSERT(!*attached); - - if (!val.isObject() || !val.toObject().is()) - return true; - Rooted obj(cx, &val.toObject().as()); - - Rooted expando(cx, obj->maybeExpando()); - if (!expando) - return true; - - Shape* shape = expando->lookup(cx, name); - if (!shape || !shape->hasDefaultGetter() || !shape->hasSlot()) - return true; - - bool isFixedSlot; - uint32_t offset; - GetFixedOrDynamicSlotOffset(shape, &isFixedSlot, &offset); - - ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub(); - - ICGetPropNativeCompiler compiler(cx, ICStub::GetProp_Native, monitorStub, obj, obj, - name, isFixedSlot, offset); - ICGetPropNativeStub* newStub = compiler.getStub(compiler.getStubSpace(script)); - if (!newStub) - return false; - - StripPreliminaryObjectStubs(cx, stub); - - stub->addNewStub(newStub); - *attached = true; - return true; -} - -static bool -TryAttachTypedObjectGetPropStub(JSContext* cx, HandleScript script, - ICGetProp_Fallback* stub, HandlePropertyName name, HandleValue val, - bool* attached) -{ - MOZ_ASSERT(!*attached); - - if (!cx->runtime()->jitSupportsFloatingPoint) - return true; - - if (!val.isObject() || !val.toObject().is()) - return true; - Rooted obj(cx, &val.toObject().as()); - - if (!obj->typeDescr().is()) - return true; - Rooted structDescr(cx, &obj->typeDescr().as()); - - size_t fieldIndex; - if (!structDescr->fieldIndex(NameToId(name), &fieldIndex)) - return true; - - Rooted fieldDescr(cx, &structDescr->fieldDescr(fieldIndex)); - if (!fieldDescr->is()) - return true; - - uint32_t fieldOffset = structDescr->fieldOffset(fieldIndex); - ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub(); - - ICGetProp_TypedObject::Compiler compiler(cx, monitorStub, obj->maybeShape(), - fieldOffset, &fieldDescr->as()); - ICStub* newStub = compiler.getStub(compiler.getStubSpace(script)); - if (!newStub) - return false; - stub->addNewStub(newStub); - - *attached = true; - return true; -} - -static bool -TryAttachPrimitiveGetPropStub(JSContext* cx, HandleScript script, jsbytecode* pc, - ICGetProp_Fallback* stub, HandlePropertyName name, HandleValue val, - HandleValue res, bool* attached) -{ - MOZ_ASSERT(!*attached); - - JSValueType primitiveType; - RootedNativeObject proto(cx); - Rooted global(cx, &script->global()); - if (val.isString()) { - primitiveType = JSVAL_TYPE_STRING; - proto = GlobalObject::getOrCreateStringPrototype(cx, global); - } else if (val.isSymbol()) { - primitiveType = JSVAL_TYPE_SYMBOL; - proto = GlobalObject::getOrCreateSymbolPrototype(cx, global); - } else if (val.isNumber()) { - primitiveType = JSVAL_TYPE_DOUBLE; - proto = GlobalObject::getOrCreateNumberPrototype(cx, global); - } else { - MOZ_ASSERT(val.isBoolean()); - primitiveType = JSVAL_TYPE_BOOLEAN; - proto = GlobalObject::getOrCreateBooleanPrototype(cx, global); - } - if (!proto) - return false; - - // Instantiate this property, for use during Ion compilation. - RootedId id(cx, NameToId(name)); - if (IsIonEnabled(cx)) - EnsureTrackPropertyTypes(cx, proto, id); - - // For now, only look for properties directly set on the prototype. - RootedShape shape(cx, proto->lookup(cx, id)); - if (!shape || !shape->hasSlot() || !shape->hasDefaultGetter()) - return true; - - bool isFixedSlot; - uint32_t offset; - GetFixedOrDynamicSlotOffset(shape, &isFixedSlot, &offset); - - ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub(); - - JitSpew(JitSpew_BaselineIC, " Generating GetProp_Primitive stub"); - ICGetProp_Primitive::Compiler compiler(cx, monitorStub, primitiveType, proto, - isFixedSlot, offset); - ICStub* newStub = compiler.getStub(compiler.getStubSpace(script)); - if (!newStub) - return false; - - stub->addNewStub(newStub); - *attached = true; - return true; -} - -static bool -TryAttachNativeGetPropDoesNotExistStub(JSContext* cx, HandleScript script, - jsbytecode* pc, ICGetProp_Fallback* stub, - HandlePropertyName name, HandleValue val, - bool* attached) -{ - MOZ_ASSERT(!*attached); - - if (!val.isObject()) - return true; - - RootedObject obj(cx, &val.toObject()); - - // Don't attach stubs for CALLPROP since those need NoSuchMethod handling. - if (JSOp(*pc) == JSOP_CALLPROP) - return true; - - // Check if does-not-exist can be confirmed on property. - RootedObject lastProto(cx); - size_t protoChainDepth = SIZE_MAX; - if (!CheckHasNoSuchProperty(cx, obj, name, &lastProto, &protoChainDepth)) - return true; - MOZ_ASSERT(protoChainDepth < SIZE_MAX); - - if (protoChainDepth > ICGetProp_NativeDoesNotExist::MAX_PROTO_CHAIN_DEPTH) - return true; - - ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub(); - - // Confirmed no-such-property. Add stub. - JitSpew(JitSpew_BaselineIC, " Generating GetProp_NativeDoesNotExist stub"); - ICGetPropNativeDoesNotExistCompiler compiler(cx, monitorStub, obj, protoChainDepth); - ICStub* newStub = compiler.getStub(compiler.getStubSpace(script)); - if (!newStub) - return false; - - stub->addNewStub(newStub); - *attached = true; - return true; -} - -static bool -ComputeGetPropResult(JSContext* cx, BaselineFrame* frame, JSOp op, HandlePropertyName name, - MutableHandleValue val, MutableHandleValue res) -{ - // Handle arguments.length and arguments.callee on optimized arguments, as - // it is not an object. - if (val.isMagic(JS_OPTIMIZED_ARGUMENTS) && IsOptimizedArguments(frame, val)) { - if (op == JSOP_LENGTH) { - res.setInt32(frame->numActualArgs()); - } else { - MOZ_ASSERT(name == cx->names().callee); - MOZ_ASSERT(frame->script()->hasMappedArgsObj()); - res.setObject(*frame->callee()); - } - } else { - if (op == JSOP_GETXPROP) { - RootedObject obj(cx, &val.toObject()); - RootedId id(cx, NameToId(name)); - if (!GetPropertyForNameLookup(cx, obj, id, res)) - return false; - } else { - MOZ_ASSERT(op == JSOP_GETPROP || op == JSOP_CALLPROP || op == JSOP_LENGTH); - if (!GetProperty(cx, val, name, res)) - return false; - } - } - - return true; -} - -static bool -DoGetPropFallback(JSContext* cx, BaselineFrame* frame, ICGetProp_Fallback* stub_, - MutableHandleValue val, MutableHandleValue res) -{ - // This fallback stub may trigger debug mode toggling. - DebugModeOSRVolatileStub stub(frame, stub_); - - jsbytecode* pc = stub->icEntry()->pc(frame->script()); - JSOp op = JSOp(*pc); - FallbackICSpew(cx, stub, "GetProp(%s)", js_CodeName[op]); - - MOZ_ASSERT(op == JSOP_GETPROP || op == JSOP_CALLPROP || op == JSOP_LENGTH || op == JSOP_GETXPROP); - - // Grab our old shape before it goes away. - RootedShape oldShape(cx); - if (val.isObject()) - oldShape = val.toObject().maybeShape(); - - bool attached = false; - // There are some reasons we can fail to attach a stub that are temporary. - // We want to avoid calling noteUnoptimizableAccess() if the reason we - // failed to attach a stub is one of those temporary reasons, since we might - // end up attaching a stub for the exact same access later. - bool isTemporarilyUnoptimizable = false; - - RootedScript script(cx, frame->script()); - RootedPropertyName name(cx, frame->script()->getName(pc)); - - // After the Genericstub was added, we should never reach the Fallbackstub again. - MOZ_ASSERT(!stub->hasStub(ICStub::GetProp_Generic)); - - if (stub->numOptimizedStubs() >= ICGetProp_Fallback::MAX_OPTIMIZED_STUBS) { - // Discard all stubs in this IC and replace with generic getprop stub. - for(ICStubIterator iter = stub->beginChain(); !iter.atEnd(); iter++) - iter.unlink(cx); - ICGetProp_Generic::Compiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub()); - ICStub* newStub = compiler.getStub(compiler.getStubSpace(script)); - if (!newStub) - return false; - stub->addNewStub(newStub); - attached = true; - } - - if (!attached && !TryAttachNativeGetAccessorPropStub(cx, script, pc, stub, name, val, res, - &attached, &isTemporarilyUnoptimizable)) - { - return false; - } - - if (!ComputeGetPropResult(cx, frame, op, name, val, res)) - return false; - - TypeScript::Monitor(cx, script, pc, res); - - // Check if debug mode toggling made the stub invalid. - if (stub.invalid()) - return true; - - // Add a type monitor stub for the resulting value. - if (!stub->addMonitorStubForValue(cx, script, res)) - return false; - - if (attached) - return true; - - if (op == JSOP_LENGTH) { - if (!TryAttachLengthStub(cx, script, stub, val, res, &attached)) - return false; - if (attached) - return true; - } - - if (!TryAttachMagicArgumentsGetPropStub(cx, script, stub, name, val, res, &attached)) - return false; - if (attached) - return true; - - if (!TryAttachNativeGetValuePropStub(cx, script, pc, stub, name, val, oldShape, - res, &attached)) - return false; - if (attached) - return true; - - if (!TryAttachUnboxedGetPropStub(cx, script, stub, name, val, &attached)) - return false; - if (attached) - return true; - - if (!TryAttachUnboxedExpandoGetPropStub(cx, script, pc, stub, name, val, &attached)) - return false; - if (attached) - return true; - - if (!TryAttachTypedObjectGetPropStub(cx, script, stub, name, val, &attached)) - return false; - if (attached) - return true; - - if (val.isString() || val.isNumber() || val.isBoolean()) { - if (!TryAttachPrimitiveGetPropStub(cx, script, pc, stub, name, val, res, &attached)) - return false; - if (attached) - return true; - } - - if (res.isUndefined()) { - // Try attaching property-not-found optimized stub for undefined results. - if (!TryAttachNativeGetPropDoesNotExistStub(cx, script, pc, stub, name, val, &attached)) - return false; - if (attached) - return true; - } - - MOZ_ASSERT(!attached); - if (!isTemporarilyUnoptimizable) - stub->noteUnoptimizableAccess(); - - return true; -} - -typedef bool (*DoGetPropFallbackFn)(JSContext*, BaselineFrame*, ICGetProp_Fallback*, - MutableHandleValue, MutableHandleValue); -static const VMFunction DoGetPropFallbackInfo = - FunctionInfo(DoGetPropFallback, TailCall, PopValues(1)); - -bool -ICGetProp_Fallback::Compiler::generateStubCode(MacroAssembler& masm) -{ - MOZ_ASSERT(engine_ == Engine::Baseline); - MOZ_ASSERT(R0 == JSReturnOperand); - - EmitRestoreTailCallReg(masm); - - // Ensure stack is fully synced for the expression decompiler. - masm.pushValue(R0); - - // Push arguments. - masm.pushValue(R0); - masm.push(ICStubReg); - pushFramePtr(masm, R0.scratchReg()); - - if (!tailCallVM(DoGetPropFallbackInfo, masm)) - return false; - - // What follows is bailout for inlined scripted getters. - // The return address pointed to by the baseline stack points here. - returnOffset_ = masm.currentOffset(); - - // Even though the fallback frame doesn't enter a stub frame, the CallScripted - // frame that we are emulating does. Again, we lie. - inStubFrame_ = true; -#ifdef DEBUG - entersStubFrame_ = true; -#endif - - leaveStubFrame(masm, true); - - // When we get here, ICStubReg contains the ICGetProp_Fallback stub, - // which we can't use to enter the TypeMonitor IC, because it's a MonitoredFallbackStub - // instead of a MonitoredStub. So, we cheat. - masm.loadPtr(Address(ICStubReg, ICMonitoredFallbackStub::offsetOfFallbackMonitorStub()), - ICStubReg); - EmitEnterTypeMonitorIC(masm, ICTypeMonitor_Fallback::offsetOfFirstMonitorStub()); - - return true; -} - -void -ICGetProp_Fallback::Compiler::postGenerateStubCode(MacroAssembler& masm, Handle code) -{ - cx->compartment()->jitCompartment()->initBaselineGetPropReturnAddr(code->raw() + returnOffset_); -} - -bool -ICGetProp_ArrayLength::Compiler::generateStubCode(MacroAssembler& masm) -{ - MOZ_ASSERT(engine_ == Engine::Baseline); - - Label failure; - masm.branchTestObject(Assembler::NotEqual, R0, &failure); - - Register scratch = R1.scratchReg(); - - // Unbox R0 and guard it's an array. - Register obj = masm.extractObject(R0, ExtractTemp0); - masm.branchTestObjClass(Assembler::NotEqual, obj, scratch, &ArrayObject::class_, &failure); - - // Load obj->elements->length. - masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratch); - masm.load32(Address(scratch, ObjectElements::offsetOfLength()), scratch); - - // Guard length fits in an int32. - masm.branchTest32(Assembler::Signed, scratch, scratch, &failure); - - masm.tagValue(JSVAL_TYPE_INT32, scratch, R0); - EmitReturnFromIC(masm); - - // Failure case - jump to next stub - masm.bind(&failure); - EmitStubGuardFailure(masm); - return true; -} - -bool -ICGetProp_UnboxedArrayLength::Compiler::generateStubCode(MacroAssembler& masm) -{ - MOZ_ASSERT(engine_ == Engine::Baseline); - - Label failure; - masm.branchTestObject(Assembler::NotEqual, R0, &failure); - - Register scratch = R1.scratchReg(); - - // Unbox R0 and guard it's an unboxed array. - Register obj = masm.extractObject(R0, ExtractTemp0); - masm.branchTestObjClass(Assembler::NotEqual, obj, scratch, &UnboxedArrayObject::class_, &failure); - - // Load obj->length. - masm.load32(Address(obj, UnboxedArrayObject::offsetOfLength()), scratch); - - masm.tagValue(JSVAL_TYPE_INT32, scratch, R0); - EmitReturnFromIC(masm); - - // Failure case - jump to next stub - masm.bind(&failure); - EmitStubGuardFailure(masm); - return true; -} - -bool -ICGetProp_StringLength::Compiler::generateStubCode(MacroAssembler& masm) -{ - MOZ_ASSERT(engine_ == Engine::Baseline); - - Label failure; - masm.branchTestString(Assembler::NotEqual, R0, &failure); - - // Unbox string and load its length. - Register string = masm.extractString(R0, ExtractTemp0); - masm.loadStringLength(string, string); - - masm.tagValue(JSVAL_TYPE_INT32, string, R0); - EmitReturnFromIC(masm); - - // Failure case - jump to next stub - masm.bind(&failure); - EmitStubGuardFailure(masm); - return true; -} - -bool -ICGetProp_Primitive::Compiler::generateStubCode(MacroAssembler& masm) -{ - MOZ_ASSERT(engine_ == Engine::Baseline); - - Label failure; - switch (primitiveType_) { - case JSVAL_TYPE_STRING: - masm.branchTestString(Assembler::NotEqual, R0, &failure); - break; - case JSVAL_TYPE_SYMBOL: - masm.branchTestSymbol(Assembler::NotEqual, R0, &failure); - break; - case JSVAL_TYPE_DOUBLE: // Also used for int32. - masm.branchTestNumber(Assembler::NotEqual, R0, &failure); - break; - case JSVAL_TYPE_BOOLEAN: - masm.branchTestBoolean(Assembler::NotEqual, R0, &failure); - break; - default: - MOZ_CRASH("unexpected type"); - } - - AllocatableGeneralRegisterSet regs(availableGeneralRegs(1)); - Register holderReg = regs.takeAny(); - Register scratchReg = regs.takeAny(); - - // Verify the shape of the prototype. - masm.movePtr(ImmGCPtr(prototype_.get()), holderReg); - - Address shapeAddr(ICStubReg, ICGetProp_Primitive::offsetOfProtoShape()); - masm.loadPtr(Address(holderReg, JSObject::offsetOfShape()), scratchReg); - masm.branchPtr(Assembler::NotEqual, shapeAddr, scratchReg, &failure); - - if (!isFixedSlot_) - masm.loadPtr(Address(holderReg, NativeObject::offsetOfSlots()), holderReg); - - masm.load32(Address(ICStubReg, ICGetProp_Primitive::offsetOfOffset()), scratchReg); - masm.loadValue(BaseIndex(holderReg, scratchReg, TimesOne), R0); - - // Enter type monitor IC to type-check result. - EmitEnterTypeMonitorIC(masm); - - // Failure case - jump to next stub - masm.bind(&failure); - EmitStubGuardFailure(masm); - return true; -} - -ICGetPropNativeStub* -ICGetPropNativeCompiler::getStub(ICStubSpace* space) -{ - ReceiverGuard guard(obj_); - - switch (kind) { - case ICStub::GetProp_Native: { - MOZ_ASSERT(obj_ == holder_); - return newStub(space, getStubCode(), firstMonitorStub_, guard, offset_); - } - - case ICStub::GetProp_NativePrototype: { - MOZ_ASSERT(obj_ != holder_); - Shape* holderShape = holder_->as().lastProperty(); - return newStub(space, getStubCode(), firstMonitorStub_, guard, - offset_, holder_, holderShape); - } - - case ICStub::GetName_Global: { - MOZ_ASSERT(obj_ != holder_); - Shape* holderShape = holder_->as().lastProperty(); - Shape* globalShape = obj_->as().global().lastProperty(); - return newStub(space, getStubCode(), firstMonitorStub_, guard, - offset_, holder_, holderShape, globalShape); - } - - default: - MOZ_CRASH("Bad stub kind"); - } -} - -static void -GuardGlobalObject(MacroAssembler& masm, HandleObject holder, Register globalLexicalReg, - Register holderReg, Register scratch, size_t globalShapeOffset, Label* failure) -{ - if (holder->is()) - return; - masm.extractObject(Address(globalLexicalReg, ScopeObject::offsetOfEnclosingScope()), - holderReg); - masm.loadPtr(Address(ICStubReg, globalShapeOffset), scratch); - masm.branchTestObjShape(Assembler::NotEqual, holderReg, scratch, failure); -} - -bool -ICGetPropNativeCompiler::generateStubCode(MacroAssembler& masm) -{ - MOZ_ASSERT(engine_ == Engine::Baseline); - - Label failure; - AllocatableGeneralRegisterSet regs(availableGeneralRegs(0)); - Register objReg = InvalidReg; - - if (inputDefinitelyObject_) { - objReg = R0.scratchReg(); - } else { - regs.take(R0); - // Guard input is an object and unbox. - masm.branchTestObject(Assembler::NotEqual, R0, &failure); - objReg = masm.extractObject(R0, ExtractTemp0); - } - regs.takeUnchecked(objReg); - - Register scratch = regs.takeAnyExcluding(ICTailCallReg); - - // Shape/group guard. - GuardReceiverObject(masm, ReceiverGuard(obj_), objReg, scratch, - ICGetPropNativeStub::offsetOfReceiverGuard(), &failure); - - Register holderReg; - if (obj_ == holder_) { - MOZ_ASSERT(kind != ICStub::GetName_Global); - if (obj_->is()) { - // We are loading off the expando object, so use that for the holder. - holderReg = regs.takeAny(); - masm.loadPtr(Address(objReg, UnboxedPlainObject::offsetOfExpando()), holderReg); - } else { - holderReg = objReg; - } - } else { - holderReg = regs.takeAny(); - - // If we are generating a non-lexical GETGNAME stub, we must also - // guard on the shape of the GlobalObject. - if (kind == ICStub::GetName_Global) { - MOZ_ASSERT(obj_->is() && obj_->as().isGlobal()); - GuardGlobalObject(masm, holder_, objReg, holderReg, scratch, - ICGetName_Global::offsetOfGlobalShape(), &failure); - } - - // Shape guard holder. - masm.loadPtr(Address(ICStubReg, ICGetProp_NativePrototype::offsetOfHolder()), - holderReg); - masm.loadPtr(Address(ICStubReg, ICGetProp_NativePrototype::offsetOfHolderShape()), - scratch); - masm.branchTestObjShape(Assembler::NotEqual, holderReg, scratch, &failure); - } - - if (!isFixedSlot_) { - // Don't overwrite actual holderReg if we need to load a dynamic slots object. - // May need to preserve object for noSuchMethod check later. - Register nextHolder = regs.takeAny(); - masm.loadPtr(Address(holderReg, NativeObject::offsetOfSlots()), nextHolder); - holderReg = nextHolder; - } - - masm.load32(Address(ICStubReg, ICGetPropNativeStub::offsetOfOffset()), scratch); - BaseIndex result(holderReg, scratch, TimesOne); - - masm.loadValue(result, R0); - - // Enter type monitor IC to type-check result. - EmitEnterTypeMonitorIC(masm); - - // Failure case - jump to next stub - masm.bind(&failure); - EmitStubGuardFailure(masm); - return true; -} - -ICStub* -ICGetPropNativeDoesNotExistCompiler::getStub(ICStubSpace* space) -{ - Rooted shapes(cx, ShapeVector(cx)); - - if (!GetProtoShapes(obj_, protoChainDepth_, &shapes)) - return nullptr; - - JS_STATIC_ASSERT(ICGetProp_NativeDoesNotExist::MAX_PROTO_CHAIN_DEPTH == 8); - - ICStub* stub = nullptr; - switch(protoChainDepth_) { - case 0: stub = getStubSpecific<0>(space, shapes); break; - case 1: stub = getStubSpecific<1>(space, shapes); break; - case 2: stub = getStubSpecific<2>(space, shapes); break; - case 3: stub = getStubSpecific<3>(space, shapes); break; - case 4: stub = getStubSpecific<4>(space, shapes); break; - case 5: stub = getStubSpecific<5>(space, shapes); break; - case 6: stub = getStubSpecific<6>(space, shapes); break; - case 7: stub = getStubSpecific<7>(space, shapes); break; - case 8: stub = getStubSpecific<8>(space, shapes); break; - default: MOZ_CRASH("ProtoChainDepth too high."); - } - if (!stub) - return nullptr; - return stub; -} - -bool -ICGetPropNativeDoesNotExistCompiler::generateStubCode(MacroAssembler& masm) -{ - MOZ_ASSERT(engine_ == Engine::Baseline); - - Label failure; - - AllocatableGeneralRegisterSet regs(availableGeneralRegs(1)); - Register scratch = regs.takeAny(); - -#ifdef DEBUG - // Ensure that protoChainDepth_ matches the protoChainDepth stored on the stub. - { - Label ok; - masm.load16ZeroExtend(Address(ICStubReg, ICStub::offsetOfExtra()), scratch); - masm.branch32(Assembler::Equal, scratch, Imm32(protoChainDepth_), &ok); - masm.assumeUnreachable("Non-matching proto chain depth on stub."); - masm.bind(&ok); - } -#endif // DEBUG - - // Guard input is an object. - masm.branchTestObject(Assembler::NotEqual, R0, &failure); - - // Unbox and guard against old shape/group. - Register objReg = masm.extractObject(R0, ExtractTemp0); - GuardReceiverObject(masm, ReceiverGuard(obj_), objReg, scratch, - ICGetProp_NativeDoesNotExist::offsetOfGuard(), &failure); - - Register protoReg = regs.takeAny(); - // Check the proto chain. - for (size_t i = 0; i < protoChainDepth_; i++) { - masm.loadObjProto(i == 0 ? objReg : protoReg, protoReg); - masm.branchTestPtr(Assembler::Zero, protoReg, protoReg, &failure); - size_t shapeOffset = ICGetProp_NativeDoesNotExistImpl<0>::offsetOfShape(i); - masm.loadPtr(Address(ICStubReg, shapeOffset), scratch); - masm.branchTestObjShape(Assembler::NotEqual, protoReg, scratch, &failure); - } - - // Shape and type checks succeeded, ok to proceed. - masm.moveValue(UndefinedValue(), R0); - - // Normally for this op, the result would have to be monitored by TI. - // However, since this stub ALWAYS returns UndefinedValue(), and we can be sure - // that undefined is already registered with the type-set, this can be avoided. - EmitReturnFromIC(masm); - - masm.bind(&failure); - EmitStubGuardFailure(masm); - return true; -} - -bool -ICGetProp_CallScripted::Compiler::generateStubCode(MacroAssembler& masm) -{ - MOZ_ASSERT(engine_ == Engine::Baseline); - - Label failure; - Label failureLeaveStubFrame; - AllocatableGeneralRegisterSet regs(availableGeneralRegs(1)); - Register scratch = regs.takeAnyExcluding(ICTailCallReg); - - // Guard input is an object. - masm.branchTestObject(Assembler::NotEqual, R0, &failure); - - // Unbox and shape guard. - Register objReg = masm.extractObject(R0, ExtractTemp0); - GuardReceiverObject(masm, ReceiverGuard(receiver_), objReg, scratch, - ICGetProp_CallScripted::offsetOfReceiverGuard(), &failure); - - if (receiver_ != holder_) { - Register holderReg = regs.takeAny(); - masm.loadPtr(Address(ICStubReg, ICGetProp_CallScripted::offsetOfHolder()), holderReg); - masm.loadPtr(Address(ICStubReg, ICGetProp_CallScripted::offsetOfHolderShape()), scratch); - masm.branchTestObjShape(Assembler::NotEqual, holderReg, scratch, &failure); - regs.add(holderReg); - } - - // Push a stub frame so that we can perform a non-tail call. - enterStubFrame(masm, scratch); - - // Load callee function and code. To ensure that |code| doesn't end up being - // ArgumentsRectifierReg, if it's available we assign it to |callee| instead. - Register callee; - if (regs.has(ArgumentsRectifierReg)) { - callee = ArgumentsRectifierReg; - regs.take(callee); - } else { - callee = regs.takeAny(); - } - Register code = regs.takeAny(); - masm.loadPtr(Address(ICStubReg, ICGetProp_CallScripted::offsetOfGetter()), callee); - masm.branchIfFunctionHasNoScript(callee, &failureLeaveStubFrame); - masm.loadPtr(Address(callee, JSFunction::offsetOfNativeOrScript()), code); - masm.loadBaselineOrIonRaw(code, code, &failureLeaveStubFrame); - - // Align the stack such that the JitFrameLayout is aligned on - // JitStackAlignment. - masm.alignJitStackBasedOnNArgs(0); - - // Getter is called with 0 arguments, just |obj| as thisv. - // Note that we use Push, not push, so that callJit will align the stack - // properly on ARM. - masm.Push(R0); - EmitBaselineCreateStubFrameDescriptor(masm, scratch); - masm.Push(Imm32(0)); // ActualArgc is 0 - masm.Push(callee); - masm.Push(scratch); - - // Handle arguments underflow. - Label noUnderflow; - masm.load16ZeroExtend(Address(callee, JSFunction::offsetOfNargs()), scratch); - masm.branch32(Assembler::Equal, scratch, Imm32(0), &noUnderflow); - { - // Call the arguments rectifier. - MOZ_ASSERT(ArgumentsRectifierReg != code); - - JitCode* argumentsRectifier = - cx->runtime()->jitRuntime()->getArgumentsRectifier(); - - masm.movePtr(ImmGCPtr(argumentsRectifier), code); - masm.loadPtr(Address(code, JitCode::offsetOfCode()), code); - masm.movePtr(ImmWord(0), ArgumentsRectifierReg); - } - - masm.bind(&noUnderflow); - masm.callJit(code); - - leaveStubFrame(masm, true); - - // Enter type monitor IC to type-check result. - EmitEnterTypeMonitorIC(masm); - - // Leave stub frame and go to next stub. - masm.bind(&failureLeaveStubFrame); - inStubFrame_ = true; - leaveStubFrame(masm, false); - - // Failure case - jump to next stub - masm.bind(&failure); - EmitStubGuardFailure(masm); - return true; -} - -bool -ICGetPropCallNativeCompiler::generateStubCode(MacroAssembler& masm) -{ - MOZ_ASSERT(engine_ == Engine::Baseline); - - Label failure; - - AllocatableGeneralRegisterSet regs(availableGeneralRegs(1)); - Register objReg = InvalidReg; - - MOZ_ASSERT(!(inputDefinitelyObject_ && outerClass_)); - if (inputDefinitelyObject_) { - objReg = R0.scratchReg(); - } else { - // Guard input is an object and unbox. - masm.branchTestObject(Assembler::NotEqual, R0, &failure); - objReg = masm.extractObject(R0, ExtractTemp0); - if (outerClass_) { - Register tmp = regs.takeAny(); - masm.branchTestObjClass(Assembler::NotEqual, objReg, tmp, outerClass_, &failure); - masm.movePtr(ImmGCPtr(cx->global()), objReg); - regs.add(tmp); - } - } - - Register scratch = regs.takeAnyExcluding(ICTailCallReg); - - // Shape guard. - GuardReceiverObject(masm, ReceiverGuard(receiver_), objReg, scratch, - ICGetPropCallGetter::offsetOfReceiverGuard(), &failure); - - if (receiver_ != holder_) { - Register holderReg = regs.takeAny(); - - // If we are generating a non-lexical GETGNAME stub, we must also - // guard on the shape of the GlobalObject. - if (kind == ICStub::GetProp_CallNativeGlobal) { - MOZ_ASSERT(receiver_->is() && - receiver_->as().isGlobal()); - GuardGlobalObject(masm, holder_, objReg, holderReg, scratch, - ICGetProp_CallNativeGlobal::offsetOfGlobalShape(), &failure); - } - - masm.loadPtr(Address(ICStubReg, ICGetPropCallGetter::offsetOfHolder()), holderReg); - masm.loadPtr(Address(ICStubReg, ICGetPropCallGetter::offsetOfHolderShape()), scratch); - masm.branchTestObjShape(Assembler::NotEqual, holderReg, scratch, &failure); - regs.add(holderReg); - } - - // Box and push obj onto baseline frame stack for decompiler - if (inputDefinitelyObject_) - masm.tagValue(JSVAL_TYPE_OBJECT, objReg, R0); - EmitStowICValues(masm, 1); - if (inputDefinitelyObject_) - objReg = masm.extractObject(R0, ExtractTemp0); - - // Push a stub frame so that we can perform a non-tail call. - enterStubFrame(masm, scratch); - - // Load callee function. - Register callee = regs.takeAny(); - masm.loadPtr(Address(ICStubReg, ICGetPropCallGetter::offsetOfGetter()), callee); - - // If we're calling a getter on the global, inline the logic for the - // 'this' hook on the global lexical scope and manually push the global. - if (kind == ICStub::GetProp_CallNativeGlobal) - masm.extractObject(Address(objReg, ScopeObject::offsetOfEnclosingScope()), objReg); - - // Push args for vm call. - masm.push(objReg); - masm.push(callee); - - regs.add(R0); - - if (!callVM(DoCallNativeGetterInfo, masm)) - return false; - leaveStubFrame(masm); - - EmitUnstowICValues(masm, 1, /* discard = */true); - - // Enter type monitor IC to type-check result. - EmitEnterTypeMonitorIC(masm); - - // Failure case - jump to next stub - masm.bind(&failure); - EmitStubGuardFailure(masm); - return true; -} - -ICStub* -ICGetPropCallNativeCompiler::getStub(ICStubSpace* space) -{ - ReceiverGuard guard(receiver_); - Shape* holderShape = holder_->as().lastProperty(); - - switch (kind) { - case ICStub::GetProp_CallNative: - return newStub(space, getStubCode(), firstMonitorStub_, - guard, holder_, holderShape, - getter_, pcOffset_); - - case ICStub::GetProp_CallNativeGlobal: { - Shape* globalShape = receiver_->as().global().lastProperty(); - return newStub(space, getStubCode(), firstMonitorStub_, - guard, holder_, holderShape, globalShape, - getter_, pcOffset_); - } - - default: - MOZ_CRASH("Bad stub kind"); - } -} - -bool -ICGetPropCallDOMProxyNativeCompiler::generateStubCode(MacroAssembler& masm, - Address* expandoAndGenerationAddr, - Address* generationAddr) -{ - MOZ_ASSERT(engine_ == Engine::Baseline); - - Label failure; - AllocatableGeneralRegisterSet regs(availableGeneralRegs(1)); - Register scratch = regs.takeAnyExcluding(ICTailCallReg); - - // Guard input is an object. - masm.branchTestObject(Assembler::NotEqual, R0, &failure); - - // Unbox. - Register objReg = masm.extractObject(R0, ExtractTemp0); - - // Shape guard. - static const size_t receiverShapeOffset = - ICGetProp_CallDOMProxyNative::offsetOfReceiverGuard() + - HeapReceiverGuard::offsetOfShape(); - masm.loadPtr(Address(ICStubReg, receiverShapeOffset), scratch); - masm.branchTestObjShape(Assembler::NotEqual, objReg, scratch, &failure); - - // Guard that our expando object hasn't started shadowing this property. - { - AllocatableGeneralRegisterSet domProxyRegSet(GeneralRegisterSet::All()); - domProxyRegSet.take(ICStubReg); - domProxyRegSet.take(objReg); - domProxyRegSet.take(scratch); - Address expandoShapeAddr(ICStubReg, ICGetProp_CallDOMProxyNative::offsetOfExpandoShape()); - CheckDOMProxyExpandoDoesNotShadow( - cx, masm, objReg, - expandoShapeAddr, expandoAndGenerationAddr, generationAddr, - scratch, - domProxyRegSet, - &failure); - } - - Register holderReg = regs.takeAny(); - masm.loadPtr(Address(ICStubReg, ICGetProp_CallDOMProxyNative::offsetOfHolder()), - holderReg); - masm.loadPtr(Address(ICStubReg, ICGetProp_CallDOMProxyNative::offsetOfHolderShape()), - scratch); - masm.branchTestObjShape(Assembler::NotEqual, holderReg, scratch, &failure); - regs.add(holderReg); - - // Push a stub frame so that we can perform a non-tail call. - enterStubFrame(masm, scratch); - - // Load callee function. - Register callee = regs.takeAny(); - masm.loadPtr(Address(ICStubReg, ICGetProp_CallDOMProxyNative::offsetOfGetter()), callee); - - // Push args for vm call. - masm.push(objReg); - masm.push(callee); - - // Don't have to preserve R0 anymore. - regs.add(R0); - - if (!callVM(DoCallNativeGetterInfo, masm)) - return false; - leaveStubFrame(masm); - - // Enter type monitor IC to type-check result. - EmitEnterTypeMonitorIC(masm); - - // Failure case - jump to next stub - masm.bind(&failure); - EmitStubGuardFailure(masm); - return true; -} - -bool -ICGetPropCallDOMProxyNativeCompiler::generateStubCode(MacroAssembler& masm) -{ - MOZ_ASSERT(engine_ == Engine::Baseline); - - if (kind == ICStub::GetProp_CallDOMProxyNative) - return generateStubCode(masm, nullptr, nullptr); - - Address internalStructAddress(ICStubReg, - ICGetProp_CallDOMProxyWithGenerationNative::offsetOfInternalStruct()); - Address generationAddress(ICStubReg, - ICGetProp_CallDOMProxyWithGenerationNative::offsetOfGeneration()); - return generateStubCode(masm, &internalStructAddress, &generationAddress); -} - -ICStub* -ICGetPropCallDOMProxyNativeCompiler::getStub(ICStubSpace* space) -{ - RootedShape shape(cx, proxy_->maybeShape()); - RootedShape holderShape(cx, holder_->as().lastProperty()); - - Value expandoSlot = GetProxyExtra(proxy_, GetDOMProxyExpandoSlot()); - RootedShape expandoShape(cx, nullptr); - ExpandoAndGeneration* expandoAndGeneration; - int32_t generation; - Value expandoVal; - if (kind == ICStub::GetProp_CallDOMProxyNative) { - expandoVal = expandoSlot; - expandoAndGeneration = nullptr; // initialize to silence GCC warning - generation = 0; // initialize to silence GCC warning - } else { - MOZ_ASSERT(kind == ICStub::GetProp_CallDOMProxyWithGenerationNative); - MOZ_ASSERT(!expandoSlot.isObject() && !expandoSlot.isUndefined()); - expandoAndGeneration = (ExpandoAndGeneration*)expandoSlot.toPrivate(); - expandoVal = expandoAndGeneration->expando; - generation = expandoAndGeneration->generation; - } - - if (expandoVal.isObject()) - expandoShape = expandoVal.toObject().as().lastProperty(); - - if (kind == ICStub::GetProp_CallDOMProxyNative) { - return newStub( - space, getStubCode(), firstMonitorStub_, shape, - expandoShape, holder_, holderShape, getter_, pcOffset_); - } - - return newStub( - space, getStubCode(), firstMonitorStub_, shape, - expandoAndGeneration, generation, expandoShape, holder_, holderShape, getter_, - pcOffset_); -} - -ICStub* -ICGetProp_DOMProxyShadowed::Compiler::getStub(ICStubSpace* space) -{ - RootedShape shape(cx, proxy_->maybeShape()); - return New(cx, space, getStubCode(), firstMonitorStub_, shape, - proxy_->handler(), name_, pcOffset_); -} - -static bool -ProxyGet(JSContext* cx, HandleObject proxy, HandlePropertyName name, MutableHandleValue vp) -{ - RootedValue receiver(cx, ObjectValue(*proxy)); - RootedId id(cx, NameToId(name)); - return Proxy::get(cx, proxy, receiver, id, vp); -} - -typedef bool (*ProxyGetFn)(JSContext* cx, HandleObject proxy, HandlePropertyName name, - MutableHandleValue vp); -static const VMFunction ProxyGetInfo = FunctionInfo(ProxyGet); - -bool -ICGetProp_DOMProxyShadowed::Compiler::generateStubCode(MacroAssembler& masm) -{ - MOZ_ASSERT(engine_ == Engine::Baseline); - - Label failure; - - AllocatableGeneralRegisterSet regs(availableGeneralRegs(1)); - // Need to reserve a scratch register, but the scratch register should not be - // ICTailCallReg, because it's used for |enterStubFrame| which needs a - // non-ICTailCallReg scratch reg. - Register scratch = regs.takeAnyExcluding(ICTailCallReg); - - // Guard input is an object. - masm.branchTestObject(Assembler::NotEqual, R0, &failure); - - // Unbox. - Register objReg = masm.extractObject(R0, ExtractTemp0); - - // Shape guard. - masm.loadPtr(Address(ICStubReg, ICGetProp_DOMProxyShadowed::offsetOfShape()), scratch); - masm.branchTestObjShape(Assembler::NotEqual, objReg, scratch, &failure); - - // No need to do any more guards; it's safe to call ProxyGet even - // if we've since stopped shadowing. - - // Call ProxyGet(JSContext* cx, HandleObject proxy, HandlePropertyName name, MutableHandleValue vp); - - // Push a stub frame so that we can perform a non-tail call. - enterStubFrame(masm, scratch); - - // Push property name and proxy object. - masm.loadPtr(Address(ICStubReg, ICGetProp_DOMProxyShadowed::offsetOfName()), scratch); - masm.push(scratch); - masm.push(objReg); - - // Don't have to preserve R0 anymore. - regs.add(R0); - - if (!callVM(ProxyGetInfo, masm)) - return false; - leaveStubFrame(masm); - - // Enter type monitor IC to type-check result. - EmitEnterTypeMonitorIC(masm); - - // Failure case - jump to next stub - masm.bind(&failure); - EmitStubGuardFailure(masm); - return true; -} - -bool -ICGetProp_ArgumentsLength::Compiler::generateStubCode(MacroAssembler& masm) -{ - MOZ_ASSERT(engine_ == Engine::Baseline); - - Label failure; - if (which_ == ICGetProp_ArgumentsLength::Magic) { - // Ensure that this is lazy arguments. - masm.branchTestMagicValue(Assembler::NotEqual, R0, JS_OPTIMIZED_ARGUMENTS, &failure); - - // Ensure that frame has not loaded different arguments object since. - masm.branchTest32(Assembler::NonZero, - Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfFlags()), - Imm32(BaselineFrame::HAS_ARGS_OBJ), - &failure); - - Address actualArgs(BaselineFrameReg, BaselineFrame::offsetOfNumActualArgs()); - masm.loadPtr(actualArgs, R0.scratchReg()); - masm.tagValue(JSVAL_TYPE_INT32, R0.scratchReg(), R0); - EmitReturnFromIC(masm); - - masm.bind(&failure); - EmitStubGuardFailure(masm); - return true; - } - MOZ_ASSERT(which_ == ICGetProp_ArgumentsLength::Mapped || - which_ == ICGetProp_ArgumentsLength::Unmapped); - - const Class* clasp = (which_ == ICGetProp_ArgumentsLength::Mapped) - ? &MappedArgumentsObject::class_ - : &UnmappedArgumentsObject::class_; - - Register scratchReg = R1.scratchReg(); - - // Guard on input being an arguments object. - masm.branchTestObject(Assembler::NotEqual, R0, &failure); - Register objReg = masm.extractObject(R0, ExtractTemp0); - masm.branchTestObjClass(Assembler::NotEqual, objReg, scratchReg, clasp, &failure); - - // Get initial length value. - masm.unboxInt32(Address(objReg, ArgumentsObject::getInitialLengthSlotOffset()), scratchReg); - - // Test if length has been overridden. - masm.branchTest32(Assembler::NonZero, - scratchReg, - Imm32(ArgumentsObject::LENGTH_OVERRIDDEN_BIT), - &failure); - - // Nope, shift out arguments length and return it. - // No need to type monitor because this stub always returns Int32. - masm.rshiftPtr(Imm32(ArgumentsObject::PACKED_BITS_COUNT), scratchReg); - masm.tagValue(JSVAL_TYPE_INT32, scratchReg, R0); - EmitReturnFromIC(masm); - - masm.bind(&failure); - EmitStubGuardFailure(masm); - return true; -} - -ICGetProp_ArgumentsCallee::ICGetProp_ArgumentsCallee(JitCode* stubCode, ICStub* firstMonitorStub) - : ICMonitoredStub(GetProp_ArgumentsCallee, stubCode, firstMonitorStub) -{ } - -bool -ICGetProp_ArgumentsCallee::Compiler::generateStubCode(MacroAssembler& masm) -{ - MOZ_ASSERT(engine_ == Engine::Baseline); - - Label failure; - - // Ensure that this is lazy arguments. - masm.branchTestMagicValue(Assembler::NotEqual, R0, JS_OPTIMIZED_ARGUMENTS, &failure); - - // Ensure that frame has not loaded different arguments object since. - masm.branchTest32(Assembler::NonZero, - Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfFlags()), - Imm32(BaselineFrame::HAS_ARGS_OBJ), - &failure); - - Address callee(BaselineFrameReg, BaselineFrame::offsetOfCalleeToken()); - masm.loadFunctionFromCalleeToken(callee, R0.scratchReg()); - masm.tagValue(JSVAL_TYPE_OBJECT, R0.scratchReg(), R0); - - EmitEnterTypeMonitorIC(masm); - - masm.bind(&failure); - EmitStubGuardFailure(masm); - return true; -} - -/* static */ ICGetProp_Generic* -ICGetProp_Generic::Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub, - ICGetProp_Generic& other) -{ - return New(cx, space, other.jitCode(), firstMonitorStub); -} - -static bool -DoGetPropGeneric(JSContext* cx, BaselineFrame* frame, ICGetProp_Generic* stub, MutableHandleValue val, MutableHandleValue res) -{ - jsbytecode* pc = stub->getChainFallback()->icEntry()->pc(frame->script()); - JSOp op = JSOp(*pc); - RootedPropertyName name(cx, frame->script()->getName(pc)); - return ComputeGetPropResult(cx, frame, op, name, val, res); -} - -typedef bool (*DoGetPropGenericFn)(JSContext*, BaselineFrame*, ICGetProp_Generic*, MutableHandleValue, MutableHandleValue); -static const VMFunction DoGetPropGenericInfo = FunctionInfo(DoGetPropGeneric); - -bool -ICGetProp_Generic::Compiler::generateStubCode(MacroAssembler& masm) -{ - MOZ_ASSERT(engine_ == Engine::Baseline); - - AllocatableGeneralRegisterSet regs(availableGeneralRegs(1)); - - Register scratch = regs.takeAnyExcluding(ICTailCallReg); - - // Sync for the decompiler. - EmitStowICValues(masm, 1); - - enterStubFrame(masm, scratch); - - // Push arguments. - masm.pushValue(R0); - masm.push(ICStubReg); - pushFramePtr(masm, R0.scratchReg()); - - if(!callVM(DoGetPropGenericInfo, masm)) - return false; - - leaveStubFrame(masm); - EmitUnstowICValues(masm, 1, /* discard = */ true); - EmitEnterTypeMonitorIC(masm); - return true; -} - -bool -ICGetProp_Unboxed::Compiler::generateStubCode(MacroAssembler& masm) -{ - MOZ_ASSERT(engine_ == Engine::Baseline); - - Label failure; - - AllocatableGeneralRegisterSet regs(availableGeneralRegs(1)); - - Register scratch = regs.takeAnyExcluding(ICTailCallReg); - - // Object and group guard. - masm.branchTestObject(Assembler::NotEqual, R0, &failure); - Register object = masm.extractObject(R0, ExtractTemp0); - masm.loadPtr(Address(ICStubReg, ICGetProp_Unboxed::offsetOfGroup()), scratch); - masm.branchPtr(Assembler::NotEqual, Address(object, JSObject::offsetOfGroup()), scratch, - &failure); - - // Get the address being read from. - masm.load32(Address(ICStubReg, ICGetProp_Unboxed::offsetOfFieldOffset()), scratch); - - masm.loadUnboxedProperty(BaseIndex(object, scratch, TimesOne), fieldType_, TypedOrValueRegister(R0)); - - // Only monitor the result if its type might change. - if (fieldType_ == JSVAL_TYPE_OBJECT) - EmitEnterTypeMonitorIC(masm); - else - EmitReturnFromIC(masm); - - masm.bind(&failure); - EmitStubGuardFailure(masm); - - return true; -} - -bool -ICGetProp_TypedObject::Compiler::generateStubCode(MacroAssembler& masm) -{ - MOZ_ASSERT(engine_ == Engine::Baseline); - - Label failure; - - CheckForNeuteredTypedObject(cx, masm, &failure); - - AllocatableGeneralRegisterSet regs(availableGeneralRegs(1)); - - Register scratch1 = regs.takeAnyExcluding(ICTailCallReg); - Register scratch2 = regs.takeAnyExcluding(ICTailCallReg); - - // Object and shape guard. - masm.branchTestObject(Assembler::NotEqual, R0, &failure); - Register object = masm.extractObject(R0, ExtractTemp0); - masm.loadPtr(Address(ICStubReg, ICGetProp_TypedObject::offsetOfShape()), scratch1); - masm.branchTestObjShape(Assembler::NotEqual, object, scratch1, &failure); - - // Get the object's data pointer. - LoadTypedThingData(masm, layout_, object, scratch1); - - // Get the address being written to. - masm.load32(Address(ICStubReg, ICGetProp_TypedObject::offsetOfFieldOffset()), scratch2); - masm.addPtr(scratch2, scratch1); - - // Only monitor the result if the type produced by this stub might vary. - bool monitorLoad; - - if (fieldDescr_->is()) { - Scalar::Type type = fieldDescr_->as().type(); - monitorLoad = type == Scalar::Uint32; - - masm.loadFromTypedArray(type, Address(scratch1, 0), R0, /* allowDouble = */ true, - scratch2, nullptr); - } else { - ReferenceTypeDescr::Type type = fieldDescr_->as().type(); - monitorLoad = type != ReferenceTypeDescr::TYPE_STRING; - - switch (type) { - case ReferenceTypeDescr::TYPE_ANY: - masm.loadValue(Address(scratch1, 0), R0); - break; - - case ReferenceTypeDescr::TYPE_OBJECT: { - Label notNull, done; - masm.loadPtr(Address(scratch1, 0), scratch1); - masm.branchTestPtr(Assembler::NonZero, scratch1, scratch1, ¬Null); - masm.moveValue(NullValue(), R0); - masm.jump(&done); - masm.bind(¬Null); - masm.tagValue(JSVAL_TYPE_OBJECT, scratch1, R0); - masm.bind(&done); - break; - } - - case ReferenceTypeDescr::TYPE_STRING: - masm.loadPtr(Address(scratch1, 0), scratch1); - masm.tagValue(JSVAL_TYPE_STRING, scratch1, R0); - break; - - default: - MOZ_CRASH(); - } - } - - if (monitorLoad) - EmitEnterTypeMonitorIC(masm); - else - EmitReturnFromIC(masm); - - masm.bind(&failure); - EmitStubGuardFailure(masm); - - return true; -} - -void -BaselineScript::noteAccessedGetter(uint32_t pcOffset) -{ - ICEntry& entry = icEntryFromPCOffset(pcOffset); - ICFallbackStub* stub = entry.fallbackStub(); - - if (stub->isGetProp_Fallback()) - stub->toGetProp_Fallback()->noteAccessedGetter(); -} - // // SetProp_Fallback // @@ -7454,17 +4874,20 @@ ICSetProp_Fallback::Compiler::generateStubCode(MacroAssembler& masm) if (!tailCallVM(DoSetPropFallbackInfo, masm)) return false; + // Even though the fallback frame doesn't enter a stub frame, the CallScripted + // frame that we are emulating does. Again, we lie. +#ifdef DEBUG + EmitRepushTailCallReg(masm); + EmitStowICValues(masm, 1); + enterStubFrame(masm, R1.scratchReg()); +#else + inStubFrame_ = true; +#endif + // What follows is bailout-only code for inlined script getters. // The return address pointed to by the baseline stack points here. returnOffset_ = masm.currentOffset(); - // Even though the fallback frame doesn't enter a stub frame, the CallScripted - // frame that we are emulating does. Again, we lie. - inStubFrame_ = true; -#ifdef DEBUG - entersStubFrame_ = true; -#endif - leaveStubFrame(masm, true); // Retrieve the stashed initial argument from the caller's frame before returning @@ -7950,6 +5373,7 @@ ICSetProp_CallScripted::Compiler::generateStubCode(MacroAssembler& masm) Register scratch = regs.takeAnyExcluding(ICTailCallReg); // Unbox and shape guard. + uint32_t framePushed = masm.framePushed(); Register objReg = masm.extractObject(R0, ExtractTemp0); GuardReceiverObject(masm, ReceiverGuard(receiver_), objReg, scratch, ICSetProp_CallScripted::offsetOfReceiverGuard(), &failureUnstow); @@ -8016,6 +5440,8 @@ ICSetProp_CallScripted::Compiler::generateStubCode(MacroAssembler& masm) masm.bind(&noUnderflow); masm.callJit(code); + uint32_t framePushedAfterCall = masm.framePushed(); + leaveStubFrame(masm, true); // Do not care about return value from function. The original RHS should be returned // as the result of this operation. @@ -8025,11 +5451,13 @@ ICSetProp_CallScripted::Compiler::generateStubCode(MacroAssembler& masm) // Leave stub frame and go to next stub. masm.bind(&failureLeaveStubFrame); + masm.setFramePushed(framePushedAfterCall); inStubFrame_ = true; leaveStubFrame(masm, false); // Unstow R0 and R1 masm.bind(&failureUnstow); + masm.setFramePushed(framePushed); EmitUnstowICValues(masm, 2); // Failure case - jump to next stub @@ -8074,6 +5502,7 @@ ICSetProp_CallNative::Compiler::generateStubCode(MacroAssembler& masm) Register scratch = regs.takeAnyExcluding(ICTailCallReg); // Unbox and shape guard. + uint32_t framePushed = masm.framePushed(); Register objReg = masm.extractObject(R0, ExtractTemp0); GuardReceiverObject(masm, ReceiverGuard(receiver_), objReg, scratch, ICSetProp_CallNative::offsetOfReceiverGuard(), &failureUnstow); @@ -8116,6 +5545,7 @@ ICSetProp_CallNative::Compiler::generateStubCode(MacroAssembler& masm) // Unstow R0 and R1 masm.bind(&failureUnstow); + masm.setFramePushed(framePushed); EmitUnstowICValues(masm, 2); // Failure case - jump to next stub @@ -8758,10 +6188,14 @@ DoCallFallback(JSContext* cx, BaselineFrame* frame, ICCall_Fallback* stub_, uint // Attach a new TypeMonitor stub for this value. ICTypeMonitor_Fallback* typeMonFbStub = stub->fallbackMonitorStub(); - if (!typeMonFbStub->addMonitorStubForValue(cx, script, res)) + if (!typeMonFbStub->addMonitorStubForValue(cx, script, res, + ICStubCompiler::Engine::Baseline)) + { return false; + } + // Add a type monitor stub for the resulting value. - if (!stub->addMonitorStubForValue(cx, script, res)) + if (!stub->addMonitorStubForValue(cx, script, res, ICStubCompiler::Engine::Baseline)) return false; // If 'callee' is a potential Call_StringSplit, try to attach an @@ -8813,10 +6247,13 @@ DoSpreadCallFallback(JSContext* cx, BaselineFrame* frame, ICCall_Fallback* stub_ // Attach a new TypeMonitor stub for this value. ICTypeMonitor_Fallback* typeMonFbStub = stub->fallbackMonitorStub(); - if (!typeMonFbStub->addMonitorStubForValue(cx, script, res)) + if (!typeMonFbStub->addMonitorStubForValue(cx, script, res, + ICStubCompiler::Engine::Baseline)) + { return false; + } // Add a type monitor stub for the resulting value. - if (!stub->addMonitorStubForValue(cx, script, res)) + if (!stub->addMonitorStubForValue(cx, script, res, ICStubCompiler::Engine::Baseline)) return false; if (!handled) @@ -9174,7 +6611,7 @@ ICCall_Fallback::Compiler::generateStubCode(MacroAssembler& masm) masm.push(masm.getStackPointer()); masm.push(ICStubReg); - pushFramePtr(masm, R0.scratchReg()); + PushFramePtr(masm, R0.scratchReg()); if (!callVM(DoSpreadCallFallbackInfo, masm)) return false; @@ -9198,11 +6635,12 @@ ICCall_Fallback::Compiler::generateStubCode(MacroAssembler& masm) masm.push(R0.scratchReg()); masm.push(ICStubReg); - pushFramePtr(masm, R0.scratchReg()); + PushFramePtr(masm, R0.scratchReg()); if (!callVM(DoCallFallbackInfo, masm)) return false; + uint32_t framePushed = masm.framePushed(); leaveStubFrame(masm); EmitReturnFromIC(masm); @@ -9213,6 +6651,7 @@ ICCall_Fallback::Compiler::generateStubCode(MacroAssembler& masm) // Here we are again in a stub frame. Marking as so. inStubFrame_ = true; + masm.setFramePushed(framePushed); // Load passed-in ThisV into R1 just in case it's needed. Need to do this before // we leave the stub frame since that info will be lost. @@ -11147,51 +8586,6 @@ ICGetIntrinsic_Constant::ICGetIntrinsic_Constant(JitCode* stubCode, const Value& ICGetIntrinsic_Constant::~ICGetIntrinsic_Constant() { } -ICGetProp_Primitive::ICGetProp_Primitive(JitCode* stubCode, ICStub* firstMonitorStub, - JSValueType primitiveType, Shape* protoShape, - uint32_t offset) - : ICMonitoredStub(GetProp_Primitive, stubCode, firstMonitorStub), - protoShape_(protoShape), - offset_(offset) -{ - extra_ = uint16_t(primitiveType); - MOZ_ASSERT(JSValueType(extra_) == primitiveType); -} - -ICGetPropNativeStub::ICGetPropNativeStub(ICStub::Kind kind, JitCode* stubCode, - ICStub* firstMonitorStub, - ReceiverGuard guard, uint32_t offset) - : ICMonitoredStub(kind, stubCode, firstMonitorStub), - receiverGuard_(guard), - offset_(offset) -{ } - -/* static */ ICGetProp_Native* -ICGetProp_Native::Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub, - ICGetProp_Native& other) -{ - return New(cx, space, other.jitCode(), firstMonitorStub, - other.receiverGuard(), other.offset()); -} - -ICGetPropNativePrototypeStub::ICGetPropNativePrototypeStub(ICStub::Kind kind, JitCode* stubCode, - ICStub* firstMonitorStub, - ReceiverGuard guard, uint32_t offset, - JSObject* holder, Shape* holderShape) - : ICGetPropNativeStub(kind, stubCode, firstMonitorStub, guard, offset), - holder_(holder), - holderShape_(holderShape) -{ } - -/* static */ ICGetProp_NativePrototype* -ICGetProp_NativePrototype::Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub, - ICGetProp_NativePrototype& other) -{ - return New(cx, space, other.jitCode(), firstMonitorStub, - other.receiverGuard(), other.offset(), - other.holder(), other.holderShape()); -} - ICGetName_Global::ICGetName_Global(JitCode* stubCode, ICStub* firstMonitorStub, ReceiverGuard guard, uint32_t offset, JSObject* holder, Shape* holderShape, Shape* globalShape) @@ -11209,66 +8603,6 @@ ICGetName_Global::Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorS other.holder(), other.holderShape(), other.globalShape()); } -ICGetProp_NativeDoesNotExist::ICGetProp_NativeDoesNotExist( - JitCode* stubCode, ICStub* firstMonitorStub, ReceiverGuard guard, - size_t protoChainDepth) - : ICMonitoredStub(GetProp_NativeDoesNotExist, stubCode, firstMonitorStub), - guard_(guard) -{ - MOZ_ASSERT(protoChainDepth <= MAX_PROTO_CHAIN_DEPTH); - extra_ = protoChainDepth; -} - -/* static */ size_t -ICGetProp_NativeDoesNotExist::offsetOfShape(size_t idx) -{ - MOZ_ASSERT(ICGetProp_NativeDoesNotExistImpl<0>::offsetOfShape(idx) == - ICGetProp_NativeDoesNotExistImpl< - ICGetProp_NativeDoesNotExist::MAX_PROTO_CHAIN_DEPTH>::offsetOfShape(idx)); - return ICGetProp_NativeDoesNotExistImpl<0>::offsetOfShape(idx); -} - -template -ICGetProp_NativeDoesNotExistImpl::ICGetProp_NativeDoesNotExistImpl( - JitCode* stubCode, ICStub* firstMonitorStub, ReceiverGuard guard, - Handle shapes) - : ICGetProp_NativeDoesNotExist(stubCode, firstMonitorStub, guard, ProtoChainDepth) -{ - MOZ_ASSERT(shapes.length() == NumShapes); - - // Note: using int32_t here to avoid gcc warning. - for (int32_t i = 0; i < int32_t(NumShapes); i++) - shapes_[i].init(shapes[i]); -} - -ICGetPropNativeDoesNotExistCompiler::ICGetPropNativeDoesNotExistCompiler( - JSContext* cx, ICStub* firstMonitorStub, HandleObject obj, size_t protoChainDepth) - : ICStubCompiler(cx, ICStub::GetProp_NativeDoesNotExist, Engine::Baseline), - firstMonitorStub_(firstMonitorStub), - obj_(cx, obj), - protoChainDepth_(protoChainDepth) -{ - MOZ_ASSERT(protoChainDepth_ <= ICGetProp_NativeDoesNotExist::MAX_PROTO_CHAIN_DEPTH); -} - -ICGetPropCallGetter::ICGetPropCallGetter(Kind kind, JitCode* stubCode, ICStub* firstMonitorStub, - ReceiverGuard receiverGuard, JSObject* holder, - Shape* holderShape, JSFunction* getter, - uint32_t pcOffset) - : ICMonitoredStub(kind, stubCode, firstMonitorStub), - receiverGuard_(receiverGuard), - holder_(holder), - holderShape_(holderShape), - getter_(getter), - pcOffset_(pcOffset) -{ - MOZ_ASSERT(kind == ICStub::GetProp_CallScripted || - kind == ICStub::GetProp_CallNative || - kind == ICStub::GetProp_CallNativeGlobal || - kind == ICStub::GetProp_CallDOMProxyNative || - kind == ICStub::GetProp_CallDOMProxyWithGenerationNative); -} - ICInstanceOf_Function::ICInstanceOf_Function(JitCode* stubCode, Shape* shape, JSObject* prototypeObj, uint32_t slot) : ICStub(InstanceOf_Function, stubCode), @@ -11277,35 +8611,6 @@ ICInstanceOf_Function::ICInstanceOf_Function(JitCode* stubCode, Shape* shape, slot_(slot) { } -/* static */ ICGetProp_CallScripted* -ICGetProp_CallScripted::Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub, - ICGetProp_CallScripted& other) -{ - return New(cx, space, other.jitCode(), firstMonitorStub, - other.receiverGuard(), - other.holder_, other.holderShape_, - other.getter_, other.pcOffset_); -} - -/* static */ ICGetProp_CallNative* -ICGetProp_CallNative::Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub, - ICGetProp_CallNative& other) -{ - return New(cx, space, other.jitCode(), firstMonitorStub, - other.receiverGuard(), other.holder_, - other.holderShape_, other.getter_, other.pcOffset_); -} - -/* static */ ICGetProp_CallNativeGlobal* -ICGetProp_CallNativeGlobal::Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub, - ICGetProp_CallNativeGlobal& other) -{ - return New(cx, space, other.jitCode(), firstMonitorStub, - other.receiverGuard(), other.holder_, - other.holderShape_, other.globalShape_, - other.getter_, other.pcOffset_); -} - ICSetProp_Native::ICSetProp_Native(JitCode* stubCode, ObjectGroup* group, Shape* shape, uint32_t offset) : ICUpdatedStub(SetProp_Native, stubCode), @@ -11508,86 +8813,6 @@ ICCall_ScriptedFunCall::Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMo other.pcOffset_); } -ICGetPropCallDOMProxyNativeStub::ICGetPropCallDOMProxyNativeStub(Kind kind, JitCode* stubCode, - ICStub* firstMonitorStub, - Shape* shape, - Shape* expandoShape, - JSObject* holder, - Shape* holderShape, - JSFunction* getter, - uint32_t pcOffset) - : ICGetPropCallGetter(kind, stubCode, firstMonitorStub, ReceiverGuard(nullptr, shape), - holder, holderShape, getter, pcOffset), - expandoShape_(expandoShape) -{ } - -ICGetPropCallDOMProxyNativeCompiler::ICGetPropCallDOMProxyNativeCompiler(JSContext* cx, - ICStub::Kind kind, - ICStub* firstMonitorStub, - Handle proxy, - HandleObject holder, - HandleFunction getter, - uint32_t pcOffset) - : ICStubCompiler(cx, kind, Engine::Baseline), - firstMonitorStub_(firstMonitorStub), - proxy_(cx, proxy), - holder_(cx, holder), - getter_(cx, getter), - pcOffset_(pcOffset) -{ - MOZ_ASSERT(kind == ICStub::GetProp_CallDOMProxyNative || - kind == ICStub::GetProp_CallDOMProxyWithGenerationNative); - MOZ_ASSERT(proxy_->handler()->family() == GetDOMProxyHandlerFamily()); -} - -/* static */ ICGetProp_CallDOMProxyNative* -ICGetProp_CallDOMProxyNative::Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub, - ICGetProp_CallDOMProxyNative& other) -{ - return New(cx, space, other.jitCode(), firstMonitorStub, - other.receiverGuard_.shape(), other.expandoShape_, - other.holder_, other.holderShape_, other.getter_, - other.pcOffset_); -} - -/* static */ ICGetProp_CallDOMProxyWithGenerationNative* -ICGetProp_CallDOMProxyWithGenerationNative::Clone(JSContext* cx, - ICStubSpace* space, - ICStub* firstMonitorStub, - ICGetProp_CallDOMProxyWithGenerationNative& other) -{ - return New(cx, space, other.jitCode(), - firstMonitorStub, - other.receiverGuard_.shape(), - other.expandoAndGeneration_, - other.generation_, - other.expandoShape_, other.holder_, - other.holderShape_, other.getter_, - other.pcOffset_); -} - -ICGetProp_DOMProxyShadowed::ICGetProp_DOMProxyShadowed(JitCode* stubCode, - ICStub* firstMonitorStub, - Shape* shape, - const BaseProxyHandler* proxyHandler, - PropertyName* name, - uint32_t pcOffset) - : ICMonitoredStub(ICStub::GetProp_DOMProxyShadowed, stubCode, firstMonitorStub), - shape_(shape), - proxyHandler_(proxyHandler), - name_(name), - pcOffset_(pcOffset) -{ } - -/* static */ ICGetProp_DOMProxyShadowed* -ICGetProp_DOMProxyShadowed::Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub, - ICGetProp_DOMProxyShadowed& other) -{ - return New(cx, space, other.jitCode(), firstMonitorStub, - other.shape_, other.proxyHandler_, other.name_, - other.pcOffset_); -} - // // Rest_Fallback // diff --git a/js/src/jit/BaselineIC.h b/js/src/jit/BaselineIC.h index bbe37375468d..40868556c0d9 100644 --- a/js/src/jit/BaselineIC.h +++ b/js/src/jit/BaselineIC.h @@ -21,8 +21,6 @@ #include "jit/SharedICRegisters.h" #include "js/TraceableVector.h" #include "vm/ArrayObject.h" -#include "vm/ReceiverGuard.h" -#include "vm/TypedArrayCommon.h" #include "vm/UnboxedObject.h" namespace js { @@ -57,356 +55,6 @@ class ICWarmUpCounter_Fallback : public ICFallbackStub }; -// TypeCheckPrimitiveSetStub -// Base class for IC stubs (TypeUpdate or TypeMonitor) that check that a given -// value's type falls within a set of primitive types. - -class TypeCheckPrimitiveSetStub : public ICStub -{ - friend class ICStubSpace; - protected: - inline static uint16_t TypeToFlag(JSValueType type) { - return 1u << static_cast(type); - } - - inline static uint16_t ValidFlags() { - return ((TypeToFlag(JSVAL_TYPE_OBJECT) << 1) - 1) & ~TypeToFlag(JSVAL_TYPE_MAGIC); - } - - TypeCheckPrimitiveSetStub(Kind kind, JitCode* stubCode, uint16_t flags) - : ICStub(kind, stubCode) - { - MOZ_ASSERT(kind == TypeMonitor_PrimitiveSet || kind == TypeUpdate_PrimitiveSet); - MOZ_ASSERT(flags && !(flags & ~ValidFlags())); - extra_ = flags; - } - - TypeCheckPrimitiveSetStub* updateTypesAndCode(uint16_t flags, JitCode* code) { - MOZ_ASSERT(flags && !(flags & ~ValidFlags())); - if (!code) - return nullptr; - extra_ = flags; - updateCode(code); - return this; - } - - public: - uint16_t typeFlags() const { - return extra_; - } - - bool containsType(JSValueType type) const { - MOZ_ASSERT(type <= JSVAL_TYPE_OBJECT); - MOZ_ASSERT(type != JSVAL_TYPE_MAGIC); - return extra_ & TypeToFlag(type); - } - - ICTypeMonitor_PrimitiveSet* toMonitorStub() { - return toTypeMonitor_PrimitiveSet(); - } - - ICTypeUpdate_PrimitiveSet* toUpdateStub() { - return toTypeUpdate_PrimitiveSet(); - } - - class Compiler : public ICStubCompiler { - protected: - TypeCheckPrimitiveSetStub* existingStub_; - uint16_t flags_; - - virtual int32_t getKey() const { - return static_cast(engine_) | - (static_cast(kind) << 1) | - (static_cast(flags_) << 17); - } - - public: - Compiler(JSContext* cx, Kind kind, TypeCheckPrimitiveSetStub* existingStub, - JSValueType type) - : ICStubCompiler(cx, kind, Engine::Baseline), - existingStub_(existingStub), - flags_((existingStub ? existingStub->typeFlags() : 0) | TypeToFlag(type)) - { - MOZ_ASSERT_IF(existingStub_, flags_ != existingStub_->typeFlags()); - } - - TypeCheckPrimitiveSetStub* updateStub() { - MOZ_ASSERT(existingStub_); - return existingStub_->updateTypesAndCode(flags_, getStubCode()); - } - }; -}; - -// TypeMonitor - -// The TypeMonitor fallback stub is not always a regular fallback stub. When -// used for monitoring the values pushed by a bytecode it doesn't hold a -// pointer to the IC entry, but rather back to the main fallback stub for the -// IC (from which a pointer to the IC entry can be retrieved). When monitoring -// the types of 'this', arguments or other values with no associated IC, there -// is no main fallback stub, and the IC entry is referenced directly. -class ICTypeMonitor_Fallback : public ICStub -{ - friend class ICStubSpace; - - static const uint32_t MAX_OPTIMIZED_STUBS = 8; - - // Pointer to the main fallback stub for the IC or to the main IC entry, - // depending on hasFallbackStub. - union { - ICMonitoredFallbackStub* mainFallbackStub_; - ICEntry* icEntry_; - }; - - // Pointer to the first monitor stub. - ICStub* firstMonitorStub_; - - // Address of the last monitor stub's field pointing to this - // fallback monitor stub. This will get updated when new - // monitor stubs are created and added. - ICStub** lastMonitorStubPtrAddr_; - - // Count of optimized type monitor stubs in this chain. - uint32_t numOptimizedMonitorStubs_ : 8; - - // Whether this has a fallback stub referring to the IC entry. - bool hasFallbackStub_ : 1; - - // Index of 'this' or argument which is being monitored, or BYTECODE_INDEX - // if this is monitoring the types of values pushed at some bytecode. - uint32_t argumentIndex_ : 23; - - static const uint32_t BYTECODE_INDEX = (1 << 23) - 1; - - ICTypeMonitor_Fallback(JitCode* stubCode, ICMonitoredFallbackStub* mainFallbackStub, - uint32_t argumentIndex) - : ICStub(ICStub::TypeMonitor_Fallback, stubCode), - mainFallbackStub_(mainFallbackStub), - firstMonitorStub_(thisFromCtor()), - lastMonitorStubPtrAddr_(nullptr), - numOptimizedMonitorStubs_(0), - hasFallbackStub_(mainFallbackStub != nullptr), - argumentIndex_(argumentIndex) - { } - - ICTypeMonitor_Fallback* thisFromCtor() { - return this; - } - - void addOptimizedMonitorStub(ICStub* stub) { - stub->setNext(this); - - MOZ_ASSERT((lastMonitorStubPtrAddr_ != nullptr) == - (numOptimizedMonitorStubs_ || !hasFallbackStub_)); - - if (lastMonitorStubPtrAddr_) - *lastMonitorStubPtrAddr_ = stub; - - if (numOptimizedMonitorStubs_ == 0) { - MOZ_ASSERT(firstMonitorStub_ == this); - firstMonitorStub_ = stub; - } else { - MOZ_ASSERT(firstMonitorStub_ != nullptr); - } - - lastMonitorStubPtrAddr_ = stub->addressOfNext(); - numOptimizedMonitorStubs_++; - } - - public: - bool hasStub(ICStub::Kind kind) { - ICStub* stub = firstMonitorStub_; - do { - if (stub->kind() == kind) - return true; - - stub = stub->next(); - } while (stub); - - return false; - } - - inline ICFallbackStub* mainFallbackStub() const { - MOZ_ASSERT(hasFallbackStub_); - return mainFallbackStub_; - } - - inline ICEntry* icEntry() const { - return hasFallbackStub_ ? mainFallbackStub()->icEntry() : icEntry_; - } - - inline ICStub* firstMonitorStub() const { - return firstMonitorStub_; - } - - static inline size_t offsetOfFirstMonitorStub() { - return offsetof(ICTypeMonitor_Fallback, firstMonitorStub_); - } - - inline uint32_t numOptimizedMonitorStubs() const { - return numOptimizedMonitorStubs_; - } - - inline bool monitorsThis() const { - return argumentIndex_ == 0; - } - - inline bool monitorsArgument(uint32_t* pargument) const { - if (argumentIndex_ > 0 && argumentIndex_ < BYTECODE_INDEX) { - *pargument = argumentIndex_ - 1; - return true; - } - return false; - } - - inline bool monitorsBytecode() const { - return argumentIndex_ == BYTECODE_INDEX; - } - - // Fixup the IC entry as for a normal fallback stub, for this/arguments. - void fixupICEntry(ICEntry* icEntry) { - MOZ_ASSERT(!hasFallbackStub_); - MOZ_ASSERT(icEntry_ == nullptr); - MOZ_ASSERT(lastMonitorStubPtrAddr_ == nullptr); - icEntry_ = icEntry; - lastMonitorStubPtrAddr_ = icEntry_->addressOfFirstStub(); - } - - // Create a new monitor stub for the type of the given value, and - // add it to this chain. - bool addMonitorStubForValue(JSContext* cx, JSScript* script, HandleValue val); - - void resetMonitorStubChain(Zone* zone); - - // Compiler for this stub kind. - class Compiler : public ICStubCompiler { - ICMonitoredFallbackStub* mainFallbackStub_; - uint32_t argumentIndex_; - - protected: - bool generateStubCode(MacroAssembler& masm); - - public: - Compiler(JSContext* cx, ICMonitoredFallbackStub* mainFallbackStub) - : ICStubCompiler(cx, ICStub::TypeMonitor_Fallback, Engine::Baseline), - mainFallbackStub_(mainFallbackStub), - argumentIndex_(BYTECODE_INDEX) - { } - - Compiler(JSContext* cx, uint32_t argumentIndex) - : ICStubCompiler(cx, ICStub::TypeMonitor_Fallback, Engine::Baseline), - mainFallbackStub_(nullptr), - argumentIndex_(argumentIndex) - { } - - ICTypeMonitor_Fallback* getStub(ICStubSpace* space) { - return newStub(space, getStubCode(), mainFallbackStub_, - argumentIndex_); - } - }; -}; - -class ICTypeMonitor_PrimitiveSet : public TypeCheckPrimitiveSetStub -{ - friend class ICStubSpace; - - ICTypeMonitor_PrimitiveSet(JitCode* stubCode, uint16_t flags) - : TypeCheckPrimitiveSetStub(TypeMonitor_PrimitiveSet, stubCode, flags) - {} - - public: - class Compiler : public TypeCheckPrimitiveSetStub::Compiler { - protected: - bool generateStubCode(MacroAssembler& masm); - - public: - Compiler(JSContext* cx, ICTypeMonitor_PrimitiveSet* existingStub, JSValueType type) - : TypeCheckPrimitiveSetStub::Compiler(cx, TypeMonitor_PrimitiveSet, existingStub, type) - {} - - ICTypeMonitor_PrimitiveSet* updateStub() { - TypeCheckPrimitiveSetStub* stub = - this->TypeCheckPrimitiveSetStub::Compiler::updateStub(); - if (!stub) - return nullptr; - return stub->toMonitorStub(); - } - - ICTypeMonitor_PrimitiveSet* getStub(ICStubSpace* space) { - MOZ_ASSERT(!existingStub_); - return newStub(space, getStubCode(), flags_); - } - }; -}; - -class ICTypeMonitor_SingleObject : public ICStub -{ - friend class ICStubSpace; - - HeapPtrObject obj_; - - ICTypeMonitor_SingleObject(JitCode* stubCode, JSObject* obj); - - public: - HeapPtrObject& object() { - return obj_; - } - - static size_t offsetOfObject() { - return offsetof(ICTypeMonitor_SingleObject, obj_); - } - - class Compiler : public ICStubCompiler { - protected: - HandleObject obj_; - bool generateStubCode(MacroAssembler& masm); - - public: - Compiler(JSContext* cx, HandleObject obj) - : ICStubCompiler(cx, TypeMonitor_SingleObject, Engine::Baseline), - obj_(obj) - { } - - ICTypeMonitor_SingleObject* getStub(ICStubSpace* space) { - return newStub(space, getStubCode(), obj_); - } - }; -}; - -class ICTypeMonitor_ObjectGroup : public ICStub -{ - friend class ICStubSpace; - - HeapPtrObjectGroup group_; - - ICTypeMonitor_ObjectGroup(JitCode* stubCode, ObjectGroup* group); - - public: - HeapPtrObjectGroup& group() { - return group_; - } - - static size_t offsetOfGroup() { - return offsetof(ICTypeMonitor_ObjectGroup, group_); - } - - class Compiler : public ICStubCompiler { - protected: - HandleObjectGroup group_; - bool generateStubCode(MacroAssembler& masm); - - public: - Compiler(JSContext* cx, HandleObjectGroup group) - : ICStubCompiler(cx, TypeMonitor_ObjectGroup, Engine::Baseline), - group_(group) - { } - - ICTypeMonitor_ObjectGroup* getStub(ICStubSpace* space) { - return newStub(space, getStubCode(), group_); - } - }; -}; - // TypeUpdate extern const VMFunction DoTypeUpdateFallbackInfo; @@ -453,7 +101,8 @@ class ICTypeUpdate_PrimitiveSet : public TypeCheckPrimitiveSetStub public: Compiler(JSContext* cx, ICTypeUpdate_PrimitiveSet* existingStub, JSValueType type) - : TypeCheckPrimitiveSetStub::Compiler(cx, TypeUpdate_PrimitiveSet, existingStub, type) + : TypeCheckPrimitiveSetStub::Compiler(cx, TypeUpdate_PrimitiveSet, + Engine::Baseline, existingStub, type) {} ICTypeUpdate_PrimitiveSet* updateStub() { @@ -880,7 +529,7 @@ class ICGetElem_Fallback : public ICMonitoredFallbackStub ICGetElem_Fallback* stub = newStub(space, getStubCode()); if (!stub) return nullptr; - if (!stub->initMonitoringChain(cx, space)) + if (!stub->initMonitoringChain(cx, space, engine_)) return nullptr; return stub; } @@ -1406,25 +1055,6 @@ class ICGetElem_UnboxedArray : public ICMonitoredStub }; }; -// Enum for stubs handling a combination of typed arrays and typed objects. -enum TypedThingLayout { - Layout_TypedArray, - Layout_OutlineTypedObject, - Layout_InlineTypedObject -}; - -static inline TypedThingLayout -GetTypedThingLayout(const Class* clasp) -{ - if (IsAnyTypedArrayClass(clasp)) - return Layout_TypedArray; - if (IsOutlineTypedObjectClass(clasp)) - return Layout_OutlineTypedObject; - if (IsInlineTypedObjectClass(clasp)) - return Layout_InlineTypedObject; - MOZ_CRASH("Bad object class"); -} - // Accesses scalar elements of a typed array or typed object. class ICGetElem_TypedArray : public ICStub { @@ -2069,7 +1699,7 @@ class ICGetName_Fallback : public ICMonitoredFallbackStub ICStub* getStub(ICStubSpace* space) { ICGetName_Fallback* stub = newStub(space, getStubCode()); - if (!stub || !stub->initMonitoringChain(cx, space)) + if (!stub || !stub->initMonitoringChain(cx, space, engine_)) return nullptr; return stub; } @@ -2229,7 +1859,7 @@ class ICGetIntrinsic_Fallback : public ICMonitoredFallbackStub ICStub* getStub(ICStubSpace* space) { ICGetIntrinsic_Fallback* stub = newStub(space, getStubCode()); - if (!stub || !stub->initMonitoringChain(cx, space)) + if (!stub || !stub->initMonitoringChain(cx, space, engine_)) return nullptr; return stub; } @@ -2271,1062 +1901,6 @@ class ICGetIntrinsic_Constant : public ICStub }; }; -class ICGetProp_Fallback : public ICMonitoredFallbackStub -{ - friend class ICStubSpace; - - explicit ICGetProp_Fallback(JitCode* stubCode) - : ICMonitoredFallbackStub(ICStub::GetProp_Fallback, stubCode) - { } - - public: - static const uint32_t MAX_OPTIMIZED_STUBS = 16; - static const size_t UNOPTIMIZABLE_ACCESS_BIT = 0; - static const size_t ACCESSED_GETTER_BIT = 1; - - void noteUnoptimizableAccess() { - extra_ |= (1u << UNOPTIMIZABLE_ACCESS_BIT); - } - bool hadUnoptimizableAccess() const { - return extra_ & (1u << UNOPTIMIZABLE_ACCESS_BIT); - } - - void noteAccessedGetter() { - extra_ |= (1u << ACCESSED_GETTER_BIT); - } - bool hasAccessedGetter() const { - return extra_ & (1u << ACCESSED_GETTER_BIT); - } - - class Compiler : public ICStubCompiler { - public: - static const int32_t BASELINE_KEY = - (static_cast(Engine::Baseline)) | - (static_cast(ICStub::GetProp_Fallback) << 1); - - protected: - uint32_t returnOffset_; - bool generateStubCode(MacroAssembler& masm); - void postGenerateStubCode(MacroAssembler& masm, Handle code); - - public: - explicit Compiler(JSContext* cx) - : ICStubCompiler(cx, ICStub::GetProp_Fallback, Engine::Baseline) - { } - - ICStub* getStub(ICStubSpace* space) { - ICGetProp_Fallback* stub = newStub(space, getStubCode()); - if (!stub || !stub->initMonitoringChain(cx, space)) - return nullptr; - return stub; - } - }; -}; - -// Stub for sites, which are too polymorphic (i.e. MAX_OPTIMIZED_STUBS was reached) -class ICGetProp_Generic : public ICMonitoredStub -{ - friend class ICStubSpace; - - protected: - explicit ICGetProp_Generic(JitCode* stubCode, ICStub* firstMonitorStub) - : ICMonitoredStub(ICStub::GetProp_Generic, stubCode, firstMonitorStub) {} - - public: - static ICGetProp_Generic* Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub, - ICGetProp_Generic& other); - - class Compiler : public ICStubCompiler { - protected: - bool generateStubCode(MacroAssembler& masm); - ICStub* firstMonitorStub_; - public: - explicit Compiler(JSContext* cx, ICStub* firstMonitorStub) - : ICStubCompiler(cx, ICStub::GetProp_Generic, Engine::Baseline), - firstMonitorStub_(firstMonitorStub) - {} - - ICStub* getStub(ICStubSpace* space) { - return newStub(space, getStubCode(), firstMonitorStub_); - } - }; -}; - -// Stub for accessing a dense array's length. -class ICGetProp_ArrayLength : public ICStub -{ - friend class ICStubSpace; - - explicit ICGetProp_ArrayLength(JitCode* stubCode) - : ICStub(GetProp_ArrayLength, stubCode) - {} - - public: - class Compiler : public ICStubCompiler { - bool generateStubCode(MacroAssembler& masm); - - public: - explicit Compiler(JSContext* cx) - : ICStubCompiler(cx, ICStub::GetProp_ArrayLength, Engine::Baseline) - {} - - ICStub* getStub(ICStubSpace* space) { - return newStub(space, getStubCode()); - } - }; -}; - -// Stub for accessing an unboxed array's length. -class ICGetProp_UnboxedArrayLength : public ICStub -{ - friend class ICStubSpace; - - explicit ICGetProp_UnboxedArrayLength(JitCode* stubCode) - : ICStub(GetProp_UnboxedArrayLength, stubCode) - {} - - public: - class Compiler : public ICStubCompiler { - bool generateStubCode(MacroAssembler& masm); - - public: - explicit Compiler(JSContext* cx) - : ICStubCompiler(cx, ICStub::GetProp_UnboxedArrayLength, Engine::Baseline) - {} - - ICStub* getStub(ICStubSpace* space) { - return newStub(space, getStubCode()); - } - }; -}; - -// Stub for accessing a property on a primitive's prototype. -class ICGetProp_Primitive : public ICMonitoredStub -{ - friend class ICStubSpace; - - protected: // Protected to silence Clang warning. - // Shape of String.prototype/Number.prototype to check for. - HeapPtrShape protoShape_; - - // Fixed or dynamic slot offset. - uint32_t offset_; - - ICGetProp_Primitive(JitCode* stubCode, ICStub* firstMonitorStub, JSValueType primitiveType, - Shape* protoShape, uint32_t offset); - - public: - HeapPtrShape& protoShape() { - return protoShape_; - } - JSValueType primitiveType() const { - return JSValueType(extra_); - } - - static size_t offsetOfProtoShape() { - return offsetof(ICGetProp_Primitive, protoShape_); - } - - static size_t offsetOfOffset() { - return offsetof(ICGetProp_Primitive, offset_); - } - - class Compiler : public ICStubCompiler { - ICStub* firstMonitorStub_; - JSValueType primitiveType_; - RootedObject prototype_; - bool isFixedSlot_; - uint32_t offset_; - - bool generateStubCode(MacroAssembler& masm); - - protected: - virtual int32_t getKey() const { - static_assert(sizeof(JSValueType) == 1, "JSValueType should fit in one byte"); - return static_cast(engine_) | - (static_cast(kind) << 1) | - (static_cast(isFixedSlot_) << 17) | - (static_cast(primitiveType_) << 25); - } - - public: - Compiler(JSContext* cx, ICStub* firstMonitorStub, JSValueType primitiveType, - HandleObject prototype, bool isFixedSlot, uint32_t offset) - : ICStubCompiler(cx, ICStub::GetProp_Primitive, Engine::Baseline), - firstMonitorStub_(firstMonitorStub), - primitiveType_(primitiveType), - prototype_(cx, prototype), - isFixedSlot_(isFixedSlot), - offset_(offset) - {} - - ICStub* getStub(ICStubSpace* space) { - RootedShape protoShape(cx, prototype_->as().lastProperty()); - return newStub(space, getStubCode(), firstMonitorStub_, - primitiveType_, protoShape, offset_); - } - }; -}; - -// Stub for accessing a string's length. -class ICGetProp_StringLength : public ICStub -{ - friend class ICStubSpace; - - explicit ICGetProp_StringLength(JitCode* stubCode) - : ICStub(GetProp_StringLength, stubCode) - {} - - public: - class Compiler : public ICStubCompiler { - bool generateStubCode(MacroAssembler& masm); - - public: - explicit Compiler(JSContext* cx) - : ICStubCompiler(cx, ICStub::GetProp_StringLength, Engine::Baseline) - {} - - ICStub* getStub(ICStubSpace* space) { - return newStub(space, getStubCode()); - } - }; -}; - -// Base class for native GetProp stubs. -class ICGetPropNativeStub : public ICMonitoredStub -{ - // Object shape/group. - HeapReceiverGuard receiverGuard_; - - // Fixed or dynamic slot offset. - uint32_t offset_; - - protected: - ICGetPropNativeStub(ICStub::Kind kind, JitCode* stubCode, ICStub* firstMonitorStub, - ReceiverGuard guard, uint32_t offset); - - public: - HeapReceiverGuard& receiverGuard() { - return receiverGuard_; - } - uint32_t offset() const { - return offset_; - } - - void notePreliminaryObject() { - extra_ = 1; - } - bool hasPreliminaryObject() const { - return extra_; - } - - static size_t offsetOfReceiverGuard() { - return offsetof(ICGetPropNativeStub, receiverGuard_); - } - static size_t offsetOfOffset() { - return offsetof(ICGetPropNativeStub, offset_); - } -}; - -// Stub for accessing an own property on a native object. -class ICGetProp_Native : public ICGetPropNativeStub -{ - friend class ICStubSpace; - - ICGetProp_Native(JitCode* stubCode, ICStub* firstMonitorStub, ReceiverGuard guard, - uint32_t offset) - : ICGetPropNativeStub(GetProp_Native, stubCode, firstMonitorStub, guard, offset) - {} - - public: - static ICGetProp_Native* Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub, - ICGetProp_Native& other); -}; - -class ICGetPropNativePrototypeStub : public ICGetPropNativeStub -{ - // Holder and its shape. - HeapPtrObject holder_; - HeapPtrShape holderShape_; - - protected: - ICGetPropNativePrototypeStub(ICStub::Kind kind, JitCode* stubCode, ICStub* firstMonitorStub, - ReceiverGuard guard, uint32_t offset, JSObject* holder, - Shape* holderShape); - - public: - HeapPtrObject& holder() { - return holder_; - } - HeapPtrShape& holderShape() { - return holderShape_; - } - static size_t offsetOfHolder() { - return offsetof(ICGetPropNativePrototypeStub, holder_); - } - static size_t offsetOfHolderShape() { - return offsetof(ICGetPropNativePrototypeStub, holderShape_); - } -}; - -// Stub for accessing a property on the native prototype of a native or unboxed -// object. Note that due to the shape teleporting optimization, we only have to -// guard on the object's shape/group and the holder's shape. -class ICGetProp_NativePrototype : public ICGetPropNativePrototypeStub -{ - friend class ICStubSpace; - - protected: - ICGetProp_NativePrototype(JitCode* stubCode, ICStub* firstMonitorStub, ReceiverGuard guard, - uint32_t offset, JSObject* holder, Shape* holderShape) - : ICGetPropNativePrototypeStub(GetProp_NativePrototype, stubCode, firstMonitorStub, guard, - offset, holder, holderShape) - { } - - public: - static ICGetProp_NativePrototype* Clone(JSContext* cx, - ICStubSpace* space, - ICStub* firstMonitorStub, - ICGetProp_NativePrototype& other); -}; - -// Stub for accessing a non-lexical global name. Semantically, it is really a -// getprop: the name is either on the GlobalObject or its prototype chain. We -// teleport to the object that has the name, but we also need to guard on the -// shape of the global object. -// -// The receiver object is the global lexical scope. -class ICGetName_Global : public ICGetPropNativePrototypeStub -{ - friend class ICStubSpace; - - protected: - HeapPtrShape globalShape_; - - ICGetName_Global(JitCode* stubCode, ICStub* firstMonitorStub, ReceiverGuard guard, - uint32_t slot, JSObject* holder, Shape* holderShape, Shape* globalShape); - - public: - static ICGetName_Global* Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub, - ICGetName_Global& other); - - HeapPtrShape& globalShape() { - return globalShape_; - } - static size_t offsetOfGlobalShape() { - return offsetof(ICGetName_Global, globalShape_); - } -}; - -// Compiler for native GetProp stubs. -class ICGetPropNativeCompiler : public ICStubCompiler -{ - ICStub* firstMonitorStub_; - HandleObject obj_; - HandleObject holder_; - HandlePropertyName propName_; - bool isFixedSlot_; - uint32_t offset_; - bool inputDefinitelyObject_; - - bool generateStubCode(MacroAssembler& masm); - - protected: - virtual int32_t getKey() const { - return static_cast(engine_) | - (static_cast(kind) << 1) | - (static_cast(isFixedSlot_) << 17) | - (static_cast(inputDefinitelyObject_) << 18) | - (HeapReceiverGuard::keyBits(obj_) << 19); - } - - public: - ICGetPropNativeCompiler(JSContext* cx, ICStub::Kind kind, - ICStub* firstMonitorStub, HandleObject obj, HandleObject holder, - HandlePropertyName propName, bool isFixedSlot, uint32_t offset, - bool inputDefinitelyObject = false) - : ICStubCompiler(cx, kind, Engine::Baseline), - firstMonitorStub_(firstMonitorStub), - obj_(obj), - holder_(holder), - propName_(propName), - isFixedSlot_(isFixedSlot), - offset_(offset), - inputDefinitelyObject_(inputDefinitelyObject) - {} - - ICGetPropNativeStub* getStub(ICStubSpace* space); -}; - -template class ICGetProp_NativeDoesNotExistImpl; - -class ICGetProp_NativeDoesNotExist : public ICMonitoredStub -{ - friend class ICStubSpace; - public: - HeapReceiverGuard guard_; - - static const size_t MAX_PROTO_CHAIN_DEPTH = 8; - - protected: - ICGetProp_NativeDoesNotExist(JitCode* stubCode, ICStub* firstMonitorStub, - ReceiverGuard guard, - size_t protoChainDepth); - - public: - size_t protoChainDepth() const { - MOZ_ASSERT(extra_ <= MAX_PROTO_CHAIN_DEPTH); - return extra_; - } - - template - ICGetProp_NativeDoesNotExistImpl* toImpl() { - MOZ_ASSERT(ProtoChainDepth == protoChainDepth()); - return static_cast*>(this); - } - - HeapReceiverGuard& guard() { - return guard_; - } - - static size_t offsetOfGuard() { - return offsetof(ICGetProp_NativeDoesNotExist, guard_); - } - - static size_t offsetOfShape(size_t idx); -}; - -template -class ICGetProp_NativeDoesNotExistImpl : public ICGetProp_NativeDoesNotExist -{ - friend class ICStubSpace; - public: - static const size_t MAX_PROTO_CHAIN_DEPTH = 8; - static const size_t NumShapes = ProtoChainDepth; - - private: - mozilla::Array shapes_; - - ICGetProp_NativeDoesNotExistImpl(JitCode* stubCode, ICStub* firstMonitorStub, - ReceiverGuard guard, - Handle shapes); - - public: - void traceShapes(JSTracer* trc) { - // Note: using int32_t here to avoid gcc warning. - for (int32_t i = 0; i < int32_t(NumShapes); i++) - TraceEdge(trc, &shapes_[i], "baseline-getpropnativedoesnotexist-stub-shape"); - } - - static size_t offsetOfShape(size_t idx) { - return offsetof(ICGetProp_NativeDoesNotExistImpl, shapes_) + (idx * sizeof(HeapPtrShape)); - } -}; - -class ICGetPropNativeDoesNotExistCompiler : public ICStubCompiler -{ - ICStub* firstMonitorStub_; - RootedObject obj_; - size_t protoChainDepth_; - - protected: - virtual int32_t getKey() const { - return static_cast(engine_) | - (static_cast(kind) << 1) | - (HeapReceiverGuard::keyBits(obj_) << 17) | - (static_cast(protoChainDepth_) << 19); - } - - bool generateStubCode(MacroAssembler& masm); - - public: - ICGetPropNativeDoesNotExistCompiler(JSContext* cx, ICStub* firstMonitorStub, - HandleObject obj, size_t protoChainDepth); - - template - ICStub* getStubSpecific(ICStubSpace* space, Handle shapes) { - ReceiverGuard guard(obj_); - return newStub> - (space, getStubCode(), firstMonitorStub_, guard, shapes); - } - - ICStub* getStub(ICStubSpace* space); -}; - -class ICGetProp_Unboxed : public ICMonitoredStub -{ - friend class ICStubSpace; - - HeapPtrObjectGroup group_; - uint32_t fieldOffset_; - - ICGetProp_Unboxed(JitCode* stubCode, ICStub* firstMonitorStub, ObjectGroup* group, - uint32_t fieldOffset) - : ICMonitoredStub(ICStub::GetProp_Unboxed, stubCode, firstMonitorStub), - group_(group), fieldOffset_(fieldOffset) - { - (void) fieldOffset_; // Silence clang warning - } - - public: - HeapPtrObjectGroup& group() { - return group_; - } - - static size_t offsetOfGroup() { - return offsetof(ICGetProp_Unboxed, group_); - } - static size_t offsetOfFieldOffset() { - return offsetof(ICGetProp_Unboxed, fieldOffset_); - } - - class Compiler : public ICStubCompiler { - protected: - ICStub* firstMonitorStub_; - RootedObjectGroup group_; - uint32_t fieldOffset_; - JSValueType fieldType_; - - bool generateStubCode(MacroAssembler& masm); - - virtual int32_t getKey() const { - return static_cast(engine_) | - (static_cast(kind) << 1) | - (static_cast(fieldType_) << 17); - } - - public: - Compiler(JSContext* cx, ICStub* firstMonitorStub, - ObjectGroup* group, uint32_t fieldOffset, JSValueType fieldType) - : ICStubCompiler(cx, ICStub::GetProp_Unboxed, Engine::Baseline), - firstMonitorStub_(firstMonitorStub), - group_(cx, group), - fieldOffset_(fieldOffset), - fieldType_(fieldType) - {} - - ICStub* getStub(ICStubSpace* space) { - return newStub(space, getStubCode(), firstMonitorStub_, group_, - fieldOffset_); - } - }; -}; - -static uint32_t -SimpleTypeDescrKey(SimpleTypeDescr* descr) -{ - if (descr->is()) - return uint32_t(descr->as().type()) << 1; - return (uint32_t(descr->as().type()) << 1) | 1; -} - -class ICGetProp_TypedObject : public ICMonitoredStub -{ - friend class ICStubSpace; - - HeapPtrShape shape_; - uint32_t fieldOffset_; - - ICGetProp_TypedObject(JitCode* stubCode, ICStub* firstMonitorStub, Shape* shape, - uint32_t fieldOffset) - : ICMonitoredStub(ICStub::GetProp_TypedObject, stubCode, firstMonitorStub), - shape_(shape), fieldOffset_(fieldOffset) - { - (void) fieldOffset_; // Silence clang warning - } - - public: - HeapPtrShape& shape() { - return shape_; - } - - static size_t offsetOfShape() { - return offsetof(ICGetProp_TypedObject, shape_); - } - static size_t offsetOfFieldOffset() { - return offsetof(ICGetProp_TypedObject, fieldOffset_); - } - - class Compiler : public ICStubCompiler { - protected: - ICStub* firstMonitorStub_; - RootedShape shape_; - uint32_t fieldOffset_; - TypedThingLayout layout_; - Rooted fieldDescr_; - - bool generateStubCode(MacroAssembler& masm); - - virtual int32_t getKey() const { - return static_cast(engine_) | - (static_cast(kind) << 1) | - (static_cast(SimpleTypeDescrKey(fieldDescr_)) << 17) | - (static_cast(layout_) << 25); - } - - public: - Compiler(JSContext* cx, ICStub* firstMonitorStub, - Shape* shape, uint32_t fieldOffset, SimpleTypeDescr* fieldDescr) - : ICStubCompiler(cx, ICStub::GetProp_TypedObject, Engine::Baseline), - firstMonitorStub_(firstMonitorStub), - shape_(cx, shape), - fieldOffset_(fieldOffset), - layout_(GetTypedThingLayout(shape->getObjectClass())), - fieldDescr_(cx, fieldDescr) - {} - - ICStub* getStub(ICStubSpace* space) { - return newStub(space, getStubCode(), firstMonitorStub_, shape_, - fieldOffset_); - } - }; -}; - -class ICGetPropCallGetter : public ICMonitoredStub -{ - friend class ICStubSpace; - - protected: - // Shape/group of receiver object. Used for both own and proto getters. - // In the GetPropCallDOMProxyNative case, the receiver guard enforces - // the proxy handler, because Shape implies Class. - HeapReceiverGuard receiverGuard_; - - // Holder and holder shape. For own getters, guarding on receiverGuard_ is - // sufficient, although Ion may use holder_ and holderShape_ even for own - // getters. In this case holderShape_ == receiverGuard_.shape_ (isOwnGetter - // below relies on this). - HeapPtrObject holder_; - - HeapPtrShape holderShape_; - - // Function to call. - HeapPtrFunction getter_; - - // PC offset of call - uint32_t pcOffset_; - - ICGetPropCallGetter(Kind kind, JitCode* stubCode, ICStub* firstMonitorStub, - ReceiverGuard receiverGuard, JSObject* holder, - Shape* holderShape, JSFunction* getter, uint32_t pcOffset); - - public: - HeapPtrObject& holder() { - return holder_; - } - HeapPtrShape& holderShape() { - return holderShape_; - } - HeapPtrFunction& getter() { - return getter_; - } - HeapReceiverGuard& receiverGuard() { - return receiverGuard_; - } - - bool isOwnGetter() const { - MOZ_ASSERT(holder_->isNative()); - MOZ_ASSERT(holderShape_); - return receiverGuard_.shape() == holderShape_; - } - - static size_t offsetOfHolder() { - return offsetof(ICGetPropCallGetter, holder_); - } - static size_t offsetOfHolderShape() { - return offsetof(ICGetPropCallGetter, holderShape_); - } - static size_t offsetOfGetter() { - return offsetof(ICGetPropCallGetter, getter_); - } - static size_t offsetOfPCOffset() { - return offsetof(ICGetPropCallGetter, pcOffset_); - } - static size_t offsetOfReceiverGuard() { - return offsetof(ICGetPropCallGetter, receiverGuard_); - } - - class Compiler : public ICStubCompiler { - protected: - ICStub* firstMonitorStub_; - RootedObject receiver_; - RootedObject holder_; - RootedFunction getter_; - uint32_t pcOffset_; - const Class* outerClass_; - - virtual int32_t getKey() const { - // ICGetPropCallNativeCompiler::getKey adds more bits to our - // return value, so be careful when making changes here. - return static_cast(engine_) | - (static_cast(kind) << 1) | - (HeapReceiverGuard::keyBits(receiver_) << 17) | - (static_cast(!!outerClass_) << 19) | - (static_cast(receiver_ != holder_) << 20); - } - - public: - Compiler(JSContext* cx, ICStub::Kind kind, ICStub* firstMonitorStub, - HandleObject receiver, HandleObject holder, HandleFunction getter, - uint32_t pcOffset, const Class* outerClass) - : ICStubCompiler(cx, kind, Engine::Baseline), - firstMonitorStub_(firstMonitorStub), - receiver_(cx, receiver), - holder_(cx, holder), - getter_(cx, getter), - pcOffset_(pcOffset), - outerClass_(outerClass) - { - MOZ_ASSERT(kind == ICStub::GetProp_CallScripted || - kind == ICStub::GetProp_CallNative || - kind == ICStub::GetProp_CallNativeGlobal); - } - }; -}; - -// Stub for calling a scripted getter on a native object when the getter is kept on the -// proto-chain. -class ICGetProp_CallScripted : public ICGetPropCallGetter -{ - friend class ICStubSpace; - - protected: - ICGetProp_CallScripted(JitCode* stubCode, ICStub* firstMonitorStub, - ReceiverGuard receiverGuard, - JSObject* holder, Shape* holderShape, - JSFunction* getter, uint32_t pcOffset) - : ICGetPropCallGetter(GetProp_CallScripted, stubCode, firstMonitorStub, - receiverGuard, holder, holderShape, getter, pcOffset) - {} - - public: - static ICGetProp_CallScripted* Clone(JSContext* cx, ICStubSpace* space, - ICStub* firstMonitorStub, ICGetProp_CallScripted& other); - - class Compiler : public ICGetPropCallGetter::Compiler { - protected: - bool generateStubCode(MacroAssembler& masm); - - public: - Compiler(JSContext* cx, ICStub* firstMonitorStub, HandleObject obj, - HandleObject holder, HandleFunction getter, uint32_t pcOffset) - : ICGetPropCallGetter::Compiler(cx, ICStub::GetProp_CallScripted, - firstMonitorStub, obj, holder, - getter, pcOffset, /* outerClass = */ nullptr) - {} - - ICStub* getStub(ICStubSpace* space) { - ReceiverGuard guard(receiver_); - Shape* holderShape = holder_->as().lastProperty(); - return newStub(space, getStubCode(), firstMonitorStub_, - guard, holder_, holderShape, getter_, - pcOffset_); - } - }; -}; - -// Stub for calling a native getter on a native object. -class ICGetProp_CallNative : public ICGetPropCallGetter -{ - friend class ICStubSpace; - - protected: - - ICGetProp_CallNative(JitCode* stubCode, ICStub* firstMonitorStub, - ReceiverGuard receiverGuard, - JSObject* holder, Shape* holderShape, - JSFunction* getter, uint32_t pcOffset) - : ICGetPropCallGetter(GetProp_CallNative, stubCode, firstMonitorStub, - receiverGuard, holder, holderShape, getter, pcOffset) - {} - - public: - static ICGetProp_CallNative* Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub, - ICGetProp_CallNative& other); - -}; - -// Stub for calling a native getter on the GlobalObject. -class ICGetProp_CallNativeGlobal : public ICGetPropCallGetter -{ - friend class ICStubSpace; - - protected: - HeapPtrShape globalShape_; - - ICGetProp_CallNativeGlobal(JitCode* stubCode, ICStub* firstMonitorStub, - ReceiverGuard receiverGuard, - JSObject* holder, Shape* holderShape, Shape* globalShape, - JSFunction* getter, uint32_t pcOffset) - : ICGetPropCallGetter(GetProp_CallNativeGlobal, stubCode, firstMonitorStub, - receiverGuard, holder, holderShape, getter, pcOffset), - globalShape_(globalShape) - { } - - public: - static ICGetProp_CallNativeGlobal* Clone(JSContext* cx, ICStubSpace* space, - ICStub* firstMonitorStub, - ICGetProp_CallNativeGlobal& other); - - HeapPtrShape& globalShape() { - return globalShape_; - } - static size_t offsetOfGlobalShape() { - return offsetof(ICGetProp_CallNativeGlobal, globalShape_); - } -}; - -class ICGetPropCallNativeCompiler : public ICGetPropCallGetter::Compiler -{ - bool inputDefinitelyObject_; - protected: - bool generateStubCode(MacroAssembler& masm); - - virtual int32_t getKey() const { - int32_t baseKey = ICGetPropCallGetter::Compiler::getKey(); - MOZ_ASSERT((baseKey >> 21) == 0); - return baseKey | (static_cast(inputDefinitelyObject_) << 21); - } - - public: - ICGetPropCallNativeCompiler(JSContext* cx, ICStub::Kind kind, ICStub* firstMonitorStub, - HandleObject receiver, HandleObject holder, HandleFunction getter, - uint32_t pcOffset, const Class* outerClass, - bool inputDefinitelyObject = false) - : ICGetPropCallGetter::Compiler(cx, kind, firstMonitorStub, receiver, holder, - getter, pcOffset, outerClass), - inputDefinitelyObject_(inputDefinitelyObject) - {} - - ICStub* getStub(ICStubSpace* space); -}; - -class ICGetPropCallDOMProxyNativeStub : public ICGetPropCallGetter -{ - friend class ICStubSpace; - protected: - // Object shape of expected expando object. (nullptr if no expando object should be there) - HeapPtrShape expandoShape_; - - ICGetPropCallDOMProxyNativeStub(ICStub::Kind kind, JitCode* stubCode, - ICStub* firstMonitorStub, Shape* shape, - Shape* expandoShape, - JSObject* holder, Shape* holderShape, - JSFunction* getter, uint32_t pcOffset); - - public: - HeapPtrShape& expandoShape() { - return expandoShape_; - } - static size_t offsetOfExpandoShape() { - return offsetof(ICGetPropCallDOMProxyNativeStub, expandoShape_); - } -}; - -class ICGetProp_CallDOMProxyNative : public ICGetPropCallDOMProxyNativeStub -{ - friend class ICStubSpace; - ICGetProp_CallDOMProxyNative(JitCode* stubCode, ICStub* firstMonitorStub, Shape* shape, - Shape* expandoShape, - JSObject* holder, Shape* holderShape, - JSFunction* getter, uint32_t pcOffset) - : ICGetPropCallDOMProxyNativeStub(ICStub::GetProp_CallDOMProxyNative, stubCode, - firstMonitorStub, shape, expandoShape, - holder, holderShape, getter, pcOffset) - {} - - public: - static ICGetProp_CallDOMProxyNative* Clone(JSContext* cx, - ICStubSpace* space, - ICStub* firstMonitorStub, - ICGetProp_CallDOMProxyNative& other); -}; - -class ICGetProp_CallDOMProxyWithGenerationNative : public ICGetPropCallDOMProxyNativeStub -{ - protected: - ExpandoAndGeneration* expandoAndGeneration_; - uint32_t generation_; - - public: - ICGetProp_CallDOMProxyWithGenerationNative(JitCode* stubCode, ICStub* firstMonitorStub, - Shape* shape, - ExpandoAndGeneration* expandoAndGeneration, - uint32_t generation, Shape* expandoShape, - JSObject* holder, Shape* holderShape, - JSFunction* getter, uint32_t pcOffset) - : ICGetPropCallDOMProxyNativeStub(ICStub::GetProp_CallDOMProxyWithGenerationNative, - stubCode, firstMonitorStub, shape, - expandoShape, holder, holderShape, getter, pcOffset), - expandoAndGeneration_(expandoAndGeneration), - generation_(generation) - { - } - - static ICGetProp_CallDOMProxyWithGenerationNative* - Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub, - ICGetProp_CallDOMProxyWithGenerationNative& other); - - void* expandoAndGeneration() const { - return expandoAndGeneration_; - } - uint32_t generation() const { - return generation_; - } - - void setGeneration(uint32_t value) { - generation_ = value; - } - - static size_t offsetOfInternalStruct() { - return offsetof(ICGetProp_CallDOMProxyWithGenerationNative, expandoAndGeneration_); - } - static size_t offsetOfGeneration() { - return offsetof(ICGetProp_CallDOMProxyWithGenerationNative, generation_); - } -}; - -class ICGetPropCallDOMProxyNativeCompiler : public ICStubCompiler { - ICStub* firstMonitorStub_; - Rooted proxy_; - RootedObject holder_; - RootedFunction getter_; - uint32_t pcOffset_; - - bool generateStubCode(MacroAssembler& masm, Address* internalStructAddr, - Address* generationAddr); - bool generateStubCode(MacroAssembler& masm); - - public: - ICGetPropCallDOMProxyNativeCompiler(JSContext* cx, ICStub::Kind kind, - ICStub* firstMonitorStub, Handle proxy, - HandleObject holder, HandleFunction getter, - uint32_t pcOffset); - - ICStub* getStub(ICStubSpace* space); -}; - -class ICGetProp_DOMProxyShadowed : public ICMonitoredStub -{ - friend class ICStubSpace; - protected: - HeapPtrShape shape_; - const BaseProxyHandler* proxyHandler_; - HeapPtrPropertyName name_; - uint32_t pcOffset_; - - ICGetProp_DOMProxyShadowed(JitCode* stubCode, ICStub* firstMonitorStub, Shape* shape, - const BaseProxyHandler* proxyHandler, PropertyName* name, - uint32_t pcOffset); - - public: - static ICGetProp_DOMProxyShadowed* Clone(JSContext* cx, ICStubSpace* space, - ICStub* firstMonitorStub, - ICGetProp_DOMProxyShadowed& other); - - HeapPtrShape& shape() { - return shape_; - } - HeapPtrPropertyName& name() { - return name_; - } - - static size_t offsetOfShape() { - return offsetof(ICGetProp_DOMProxyShadowed, shape_); - } - static size_t offsetOfProxyHandler() { - return offsetof(ICGetProp_DOMProxyShadowed, proxyHandler_); - } - static size_t offsetOfName() { - return offsetof(ICGetProp_DOMProxyShadowed, name_); - } - static size_t offsetOfPCOffset() { - return offsetof(ICGetProp_DOMProxyShadowed, pcOffset_); - } - - class Compiler : public ICStubCompiler { - ICStub* firstMonitorStub_; - Rooted proxy_; - RootedPropertyName name_; - uint32_t pcOffset_; - - bool generateStubCode(MacroAssembler& masm); - - public: - Compiler(JSContext* cx, ICStub* firstMonitorStub, Handle proxy, - HandlePropertyName name, uint32_t pcOffset) - : ICStubCompiler(cx, ICStub::GetProp_CallNative, Engine::Baseline), - firstMonitorStub_(firstMonitorStub), - proxy_(cx, proxy), - name_(cx, name), - pcOffset_(pcOffset) - {} - - ICStub* getStub(ICStubSpace* space); - }; -}; - -class ICGetProp_ArgumentsLength : public ICStub -{ - friend class ICStubSpace; - public: - enum Which { Mapped, Unmapped, Magic }; - - protected: - explicit ICGetProp_ArgumentsLength(JitCode* stubCode) - : ICStub(ICStub::GetProp_ArgumentsLength, stubCode) - { } - - public: - class Compiler : public ICStubCompiler { - protected: - Which which_; - - bool generateStubCode(MacroAssembler& masm); - - virtual int32_t getKey() const { - return static_cast(engine_) | - (static_cast(kind) << 1) | - (static_cast(which_) << 17); - } - - public: - Compiler(JSContext* cx, Which which) - : ICStubCompiler(cx, ICStub::GetProp_ArgumentsLength, Engine::Baseline), - which_(which) - {} - - ICStub* getStub(ICStubSpace* space) { - return newStub(space, getStubCode()); - } - }; -}; - -class ICGetProp_ArgumentsCallee : public ICMonitoredStub -{ - friend class ICStubSpace; - - protected: - ICGetProp_ArgumentsCallee(JitCode* stubCode, ICStub* firstMonitorStub); - - public: - class Compiler : public ICStubCompiler { - protected: - ICStub* firstMonitorStub_; - bool generateStubCode(MacroAssembler& masm); - - public: - Compiler(JSContext* cx, ICStub* firstMonitorStub) - : ICStubCompiler(cx, ICStub::GetProp_ArgumentsCallee, Engine::Baseline), - firstMonitorStub_(firstMonitorStub) - {} - - ICStub* getStub(ICStubSpace* space) { - return newStub(space, getStubCode(), firstMonitorStub_); - } - }; -}; - // SetProp // JSOP_SETPROP // JSOP_SETNAME @@ -3986,7 +2560,7 @@ class ICCall_Fallback : public ICMonitoredFallbackStub ICStub* getStub(ICStubSpace* space) { ICCall_Fallback* stub = newStub(space, getStubCode()); - if (!stub || !stub->initMonitoringChain(cx, space)) + if (!stub || !stub->initMonitoringChain(cx, space, engine_)) return nullptr; return stub; } diff --git a/js/src/jit/BaselineICList.h b/js/src/jit/BaselineICList.h index 1d17e8e2a212..f4710c4602eb 100644 --- a/js/src/jit/BaselineICList.h +++ b/js/src/jit/BaselineICList.h @@ -93,26 +93,6 @@ namespace jit { _(GetIntrinsic_Fallback) \ _(GetIntrinsic_Constant) \ \ - _(GetProp_Fallback) \ - _(GetProp_ArrayLength) \ - _(GetProp_UnboxedArrayLength) \ - _(GetProp_Primitive) \ - _(GetProp_StringLength) \ - _(GetProp_Native) \ - _(GetProp_NativeDoesNotExist) \ - _(GetProp_NativePrototype) \ - _(GetProp_Unboxed) \ - _(GetProp_TypedObject) \ - _(GetProp_CallScripted) \ - _(GetProp_CallNative) \ - _(GetProp_CallNativeGlobal) \ - _(GetProp_CallDOMProxyNative) \ - _(GetProp_CallDOMProxyWithGenerationNative) \ - _(GetProp_DOMProxyShadowed) \ - _(GetProp_ArgumentsLength) \ - _(GetProp_ArgumentsCallee) \ - _(GetProp_Generic) \ - \ _(SetProp_Fallback) \ _(SetProp_Native) \ _(SetProp_NativeAdd) \ diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 0690155795ba..2e3a67004586 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -1758,6 +1758,11 @@ CodeGenerator::visitUnarySharedStub(LUnarySharedStub* lir) case JSOP_NEG: emitSharedStub(ICStub::Kind::UnaryArith_Fallback, lir); break; + case JSOP_CALLPROP: + case JSOP_GETPROP: + case JSOP_LENGTH: + emitSharedStub(ICStub::Kind::GetProp_Fallback, lir); + break; default: MOZ_CRASH("Unsupported jsop in shared stubs."); } @@ -7969,6 +7974,11 @@ CodeGenerator::linkSharedStubs(JSContext* cx) stub = stubCompiler.getStub(&stubSpace_); break; } + case ICStub::Kind::GetProp_Fallback: { + ICGetProp_Fallback::Compiler stubCompiler(cx, ICStubCompiler::Engine::IonMonkey); + stub = stubCompiler.getStub(&stubSpace_); + break; + } default: MOZ_CRASH("Unsupported shared stub."); } diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index 9f4e3400c379..5ce139aff79f 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -10888,6 +10888,11 @@ IonBuilder::jsop_getprop(PropertyName* name) if (!getPropTryCache(&emitted, obj, name, barrier, types) || emitted) return emitted; + // Try to emit a shared stub. + trackOptimizationAttempt(TrackedStrategy::GetProp_SharedCache); + if (!getPropTrySharedStub(&emitted, obj) || emitted) + return emitted; + // Emit a call. MCallGetProperty* call = MCallGetProperty::New(alloc(), obj, name); current->add(call); @@ -11838,6 +11843,27 @@ IonBuilder::getPropTryCache(bool* emitted, MDefinition* obj, PropertyName* name, return true; } +bool +IonBuilder::getPropTrySharedStub(bool* emitted, MDefinition* obj) +{ + MOZ_ASSERT(*emitted == false); + + // Try to emit a shared stub cache. + + if (js_JitOptions.disableSharedStubs) + return true; + + MInstruction* stub = MUnarySharedStub::New(alloc(), obj); + current->add(stub); + current->push(stub); + + if (!resumeAfter(stub)) + return false; + + *emitted = true; + return true; +} + MDefinition* IonBuilder::tryInnerizeWindow(MDefinition* obj) { diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h index e562c35b328c..4a695f642981 100644 --- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -455,6 +455,7 @@ class IonBuilder TemporaryTypeSet* types); bool getPropTryCache(bool* emitted, MDefinition* obj, PropertyName* name, BarrierKind barrier, TemporaryTypeSet* types); + bool getPropTrySharedStub(bool* emitted, MDefinition* obj); // jsop_setprop() helpers. bool setPropTryCommonSetter(bool* emitted, MDefinition* obj, diff --git a/js/src/jit/JitFrameIterator.h b/js/src/jit/JitFrameIterator.h index 4c31eb9b17ad..9d9b7eef43f6 100644 --- a/js/src/jit/JitFrameIterator.h +++ b/js/src/jit/JitFrameIterator.h @@ -171,6 +171,9 @@ class JitFrameIterator bool isIonStub() const { return type_ == JitFrame_IonStub; } + bool isIonStubMaybeUnwound() const { + return type_ == JitFrame_IonStub || type_ == JitFrame_Unwound_IonStub; + } bool isBailoutJS() const { return type_ == JitFrame_Bailout; } diff --git a/js/src/jit/JitFrames.cpp b/js/src/jit/JitFrames.cpp index 4dd77763caf7..518cf51d27f2 100644 --- a/js/src/jit/JitFrames.cpp +++ b/js/src/jit/JitFrames.cpp @@ -1624,10 +1624,13 @@ GetPcScript(JSContext* cx, JSScript** scriptRes, jsbytecode** pcRes) MOZ_ASSERT(it.isBaselineStub() || it.isBaselineJS() || it.isIonJS()); } - // Skip Baseline stub frames. + // Skip Baseline or Ion stub frames. if (it.isBaselineStubMaybeUnwound()) { ++it; MOZ_ASSERT(it.isBaselineJS()); + } else if (it.isIonStubMaybeUnwound()) { + ++it; + MOZ_ASSERT(it.isIonJS()); } MOZ_ASSERT(it.isBaselineJS() || it.isIonJS()); diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp index 10876d2909ee..e18b757aa902 100644 --- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -5547,10 +5547,13 @@ jit::PropertyWriteNeedsTypeBarrier(TempAllocator& alloc, CompilerConstraintList* implicitType); break; } + } - // Perform additional filtering to make sure that any unboxed property - // being written can accommodate the value. - if (key->isGroup() && key->group()->maybeUnboxedLayout()) { + // Perform additional filtering to make sure that any unboxed property + // being written can accommodate the value. + for (size_t i = 0; i < types->getObjectCount(); i++) { + TypeSet::ObjectKey* key = types->getObject(i); + if (key && key->isGroup() && key->group()->maybeUnboxedLayout()) { const UnboxedLayout& layout = key->group()->unboxedLayout(); if (name) { const UnboxedLayout::Property* property = layout.lookup(name); diff --git a/js/src/jit/MacroAssembler.h b/js/src/jit/MacroAssembler.h index 180cb242d45b..7a44435fa71e 100644 --- a/js/src/jit/MacroAssembler.h +++ b/js/src/jit/MacroAssembler.h @@ -439,6 +439,7 @@ class MacroAssembler : public MacroAssemblerSpecific void Push(const Operand op) DEFINED_ON(x86_shared); void Push(Register reg) PER_SHARED_ARCH; + void Push(Register reg1, Register reg2, Register reg3, Register reg4) DEFINED_ON(arm64); void Push(const Imm32 imm) PER_SHARED_ARCH; void Push(const ImmWord imm) PER_SHARED_ARCH; void Push(const ImmPtr imm) PER_SHARED_ARCH; diff --git a/js/src/jit/SharedIC.cpp b/js/src/jit/SharedIC.cpp index 1f1b19fbb5e9..279e7191a7f7 100644 --- a/js/src/jit/SharedIC.cpp +++ b/js/src/jit/SharedIC.cpp @@ -7,6 +7,7 @@ #include "jit/SharedIC.h" #include "mozilla/Casting.h" +#include "mozilla/DebugOnly.h" #include "mozilla/SizePrintfMacros.h" #include "jslibmath.h" @@ -27,6 +28,7 @@ #include "vm/Interpreter-inl.h" using mozilla::BitwiseCast; +using mozilla::DebugOnly; namespace js { namespace jit { @@ -673,11 +675,12 @@ ICMonitoredStub::ICMonitoredStub(Kind kind, JitCode* stubCode, ICStub* firstMoni } bool -ICMonitoredFallbackStub::initMonitoringChain(JSContext* cx, ICStubSpace* space) +ICMonitoredFallbackStub::initMonitoringChain(JSContext* cx, ICStubSpace* space, + ICStubCompiler::Engine engine) { MOZ_ASSERT(fallbackMonitorStub_ == nullptr); - ICTypeMonitor_Fallback::Compiler compiler(cx, this); + ICTypeMonitor_Fallback::Compiler compiler(cx, engine, this); ICTypeMonitor_Fallback* stub = compiler.getStub(space); if (!stub) return false; @@ -686,9 +689,9 @@ ICMonitoredFallbackStub::initMonitoringChain(JSContext* cx, ICStubSpace* space) } bool -ICMonitoredFallbackStub::addMonitorStubForValue(JSContext* cx, JSScript* script, HandleValue val) +ICMonitoredFallbackStub::addMonitorStubForValue(JSContext* cx, JSScript* script, HandleValue val, ICStubCompiler::Engine engine) { - return fallbackMonitorStub_->addMonitorStubForValue(cx, script, val); + return fallbackMonitorStub_->addMonitorStubForValue(cx, script, val, engine); } bool @@ -719,6 +722,11 @@ ICStubCompiler::getStubCode() // Compile new stubcode. JitContext jctx(cx, nullptr); MacroAssembler masm; +#ifndef JS_USE_LINK_REGISTER + // The first value contains the return addres, + // which we pull into ICTailCallReg for tail calls. + masm.adjustFrame(sizeof(intptr_t)); +#endif #ifdef JS_CODEGEN_ARM masm.setSecondScratchReg(BaselineSecondScratchReg); #endif @@ -802,10 +810,14 @@ ICStubCompiler::callTypeUpdateIC(MacroAssembler& masm, uint32_t objectOffset) void ICStubCompiler::enterStubFrame(MacroAssembler& masm, Register scratch) { - if (engine_ == Engine::Baseline) + if (engine_ == Engine::Baseline) { EmitBaselineEnterStubFrame(masm, scratch); - else +#ifdef DEBUG + framePushedAtEnterStubFrame_ = masm.framePushed(); +#endif + } else { EmitIonEnterStubFrame(masm, scratch); + } MOZ_ASSERT(!inStubFrame_); inStubFrame_ = true; @@ -821,10 +833,17 @@ ICStubCompiler::leaveStubFrame(MacroAssembler& masm, bool calledIntoIon) MOZ_ASSERT(entersStubFrame_ && inStubFrame_); inStubFrame_ = false; - if (engine_ == Engine::Baseline) + if (engine_ == Engine::Baseline) { +#ifdef DEBUG + masm.setFramePushed(framePushedAtEnterStubFrame_); + if (calledIntoIon) + masm.adjustFrame(sizeof(intptr_t)); // Calls into ion have this extra. +#endif + EmitBaselineLeaveStubFrame(masm, calledIntoIon); - else + } else { EmitIonLeaveStubFrame(masm); + } } void @@ -843,6 +862,13 @@ ICStubCompiler::pushFramePtr(MacroAssembler& masm, Register scratch) } } +void +ICStubCompiler::PushFramePtr(MacroAssembler& masm, Register scratch) +{ + pushFramePtr(masm, scratch); + masm.adjustFrame(sizeof(intptr_t)); +} + bool ICStubCompiler::emitPostWriteBarrierSlot(MacroAssembler& masm, Register obj, ValueOperand val, Register scratch, LiveGeneralRegisterSet saveRegs) @@ -874,8 +900,9 @@ SharedStubEngine(BaselineFrame* frame) return frame ? ICStubCompiler::Engine::Baseline : ICStubCompiler::Engine::IonMonkey; } +template static JSScript* -SharedStubScript(BaselineFrame* frame, ICFallbackStub* stub) +SharedStubScript(BaselineFrame* frame, T* stub) { ICStubCompiler::Engine engine = SharedStubEngine(frame); if (engine == ICStubCompiler::Engine::Baseline) @@ -2031,5 +2058,2799 @@ ICCompare_Int32WithBoolean::Compiler::generateStubCode(MacroAssembler& masm) return true; } +// +// GetProp_Fallback +// + +static bool +TryAttachMagicArgumentsGetPropStub(JSContext* cx, JSScript* script, ICGetProp_Fallback* stub, + ICStubCompiler::Engine engine, HandlePropertyName name, + HandleValue val, HandleValue res, + bool* attached) +{ + MOZ_ASSERT(!*attached); + + if (!val.isMagic(JS_OPTIMIZED_ARGUMENTS)) + return true; + + // Try handling arguments.callee on optimized arguments. + if (name == cx->names().callee) { + MOZ_ASSERT(script->hasMappedArgsObj()); + + JitSpew(JitSpew_BaselineIC, " Generating GetProp(MagicArgs.callee) stub"); + + // Unlike ICGetProp_ArgumentsLength, only magic argument stubs are + // supported at the moment. + ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub(); + ICGetProp_ArgumentsCallee::Compiler compiler(cx, engine, monitorStub); + ICStub* newStub = compiler.getStub(compiler.getStubSpace(script)); + if (!newStub) + return false; + stub->addNewStub(newStub); + + *attached = true; + return true; + } + + return true; +} + +static bool +TryAttachLengthStub(JSContext* cx, JSScript* script, ICGetProp_Fallback* stub, + ICStubCompiler::Engine engine, HandleValue val, + HandleValue res, bool* attached) +{ + MOZ_ASSERT(!*attached); + + if (val.isString()) { + MOZ_ASSERT(res.isInt32()); + JitSpew(JitSpew_BaselineIC, " Generating GetProp(String.length) stub"); + ICGetProp_StringLength::Compiler compiler(cx, engine); + ICStub* newStub = compiler.getStub(compiler.getStubSpace(script)); + if (!newStub) + return false; + + *attached = true; + stub->addNewStub(newStub); + return true; + } + + if (val.isMagic(JS_OPTIMIZED_ARGUMENTS) && res.isInt32()) { + JitSpew(JitSpew_BaselineIC, " Generating GetProp(MagicArgs.length) stub"); + ICGetProp_ArgumentsLength::Compiler compiler(cx, engine, ICGetProp_ArgumentsLength::Magic); + ICStub* newStub = compiler.getStub(compiler.getStubSpace(script)); + if (!newStub) + return false; + + *attached = true; + stub->addNewStub(newStub); + return true; + } + + if (!val.isObject()) + return true; + + RootedObject obj(cx, &val.toObject()); + + if (obj->is() && res.isInt32()) { + JitSpew(JitSpew_BaselineIC, " Generating GetProp(Array.length) stub"); + ICGetProp_ArrayLength::Compiler compiler(cx, engine); + ICStub* newStub = compiler.getStub(compiler.getStubSpace(script)); + if (!newStub) + return false; + + *attached = true; + stub->addNewStub(newStub); + return true; + } + + if (obj->is() && res.isInt32()) { + JitSpew(JitSpew_BaselineIC, " Generating GetProp(UnboxedArray.length) stub"); + ICGetProp_UnboxedArrayLength::Compiler compiler(cx, engine); + ICStub* newStub = compiler.getStub(compiler.getStubSpace(script)); + if (!newStub) + return false; + + *attached = true; + stub->addNewStub(newStub); + return true; + } + + if (obj->is() && res.isInt32()) { + JitSpew(JitSpew_BaselineIC, " Generating GetProp(ArgsObj.length %s) stub", + obj->is() ? "Mapped" : "Unmapped"); + ICGetProp_ArgumentsLength::Which which = ICGetProp_ArgumentsLength::Mapped; + if (obj->is()) + which = ICGetProp_ArgumentsLength::Unmapped; + ICGetProp_ArgumentsLength::Compiler compiler(cx, engine, which); + ICStub* newStub = compiler.getStub(compiler.getStubSpace(script)); + if (!newStub) + return false; + + *attached = true; + stub->addNewStub(newStub); + return true; + } + + return true; +} + +static bool +UpdateExistingGenerationalDOMProxyStub(ICGetProp_Fallback* stub, + HandleObject obj) +{ + Value expandoSlot = GetProxyExtra(obj, GetDOMProxyExpandoSlot()); + MOZ_ASSERT(!expandoSlot.isObject() && !expandoSlot.isUndefined()); + ExpandoAndGeneration* expandoAndGeneration = (ExpandoAndGeneration*)expandoSlot.toPrivate(); + for (ICStubConstIterator iter = stub->beginChainConst(); !iter.atEnd(); iter++) { + if (iter->isGetProp_CallDOMProxyWithGenerationNative()) { + ICGetProp_CallDOMProxyWithGenerationNative* updateStub = + iter->toGetProp_CallDOMProxyWithGenerationNative(); + if (updateStub->expandoAndGeneration() == expandoAndGeneration) { + // Update generation + uint32_t generation = expandoAndGeneration->generation; + JitSpew(JitSpew_BaselineIC, + " Updating existing stub with generation, old value: %i, " + "new value: %i", updateStub->generation(), + generation); + updateStub->setGeneration(generation); + return true; + } + } + } + return false; +} + +// Return whether obj is in some PreliminaryObjectArray and has a structure +// that might change in the future. +bool +IsPreliminaryObject(JSObject* obj) +{ + if (obj->isSingleton()) + return false; + + TypeNewScript* newScript = obj->group()->newScript(); + if (newScript && !newScript->analyzed()) + return true; + + if (obj->group()->maybePreliminaryObjects()) + return true; + + return false; +} + +void +StripPreliminaryObjectStubs(JSContext* cx, ICFallbackStub* stub) +{ + // Before the new script properties analysis has been performed on a type, + // all instances of that type have the maximum number of fixed slots. + // Afterwards, the objects (even the preliminary ones) might be changed + // to reduce the number of fixed slots they have. If we generate stubs for + // both the old and new number of fixed slots, the stub will look + // polymorphic to IonBuilder when it is actually monomorphic. To avoid + // this, strip out any stubs for preliminary objects before attaching a new + // stub which isn't on a preliminary object. + + for (ICStubIterator iter = stub->beginChain(); !iter.atEnd(); iter++) { + if (iter->isGetProp_Native() && iter->toGetProp_Native()->hasPreliminaryObject()) + iter.unlink(cx); + else if (iter->isSetProp_Native() && iter->toSetProp_Native()->hasPreliminaryObject()) + iter.unlink(cx); + } +} + +JSObject* +GetDOMProxyProto(JSObject* obj) +{ + MOZ_ASSERT(IsCacheableDOMProxy(obj)); + return obj->getTaggedProto().toObjectOrNull(); +} + +// Look up a property's shape on an object, being careful never to do any effectful +// operations. This procedure not yielding a shape should not be taken as a lack of +// existence of the property on the object. +bool +EffectlesslyLookupProperty(JSContext* cx, HandleObject obj, HandleId id, + MutableHandleObject holder, MutableHandleShape shape, + bool* checkDOMProxy, + DOMProxyShadowsResult* shadowsResult, + bool* domProxyHasGeneration) +{ + shape.set(nullptr); + holder.set(nullptr); + + if (checkDOMProxy) { + *checkDOMProxy = false; + *shadowsResult = ShadowCheckFailed; + } + + // Check for list base if asked to. + RootedObject checkObj(cx, obj); + if (checkDOMProxy && IsCacheableDOMProxy(obj)) { + MOZ_ASSERT(domProxyHasGeneration); + MOZ_ASSERT(shadowsResult); + + *checkDOMProxy = true; + if (obj->hasUncacheableProto()) + return true; + + *shadowsResult = GetDOMProxyShadowsCheck()(cx, obj, id); + if (*shadowsResult == ShadowCheckFailed) + return false; + + if (DOMProxyIsShadowing(*shadowsResult)) { + holder.set(obj); + return true; + } + + *domProxyHasGeneration = (*shadowsResult == DoesntShadowUnique); + + checkObj = GetDOMProxyProto(obj); + if (!checkObj) + return true; + } + + if (LookupPropertyPure(cx, checkObj, id, holder.address(), shape.address())) + return true; + + holder.set(nullptr); + shape.set(nullptr); + return true; +} + +bool +IsCacheableProtoChain(JSObject* obj, JSObject* holder, bool isDOMProxy) +{ + MOZ_ASSERT_IF(isDOMProxy, IsCacheableDOMProxy(obj)); + + if (!isDOMProxy && !obj->isNative()) { + if (obj == holder) + return false; + if (!obj->is() && + !obj->is() && + !obj->is()) + { + return false; + } + } + + // Don't handle objects which require a prototype guard. This should + // be uncommon so handling it is likely not worth the complexity. + if (obj->hasUncacheableProto()) + return false; + + JSObject* cur = obj; + while (cur != holder) { + // We cannot assume that we find the holder object on the prototype + // chain and must check for null proto. The prototype chain can be + // altered during the lookupProperty call. + JSObject* proto; + if (isDOMProxy && cur == obj) + proto = cur->getTaggedProto().toObjectOrNull(); + else + proto = cur->getProto(); + + if (!proto || !proto->isNative()) + return false; + + if (proto->hasUncacheableProto()) + return false; + + cur = proto; + } + return true; +} + +bool +IsCacheableGetPropReadSlot(JSObject* obj, JSObject* holder, Shape* shape, bool isDOMProxy) +{ + if (!shape || !IsCacheableProtoChain(obj, holder, isDOMProxy)) + return false; + + if (!shape->hasSlot() || !shape->hasDefaultGetter()) + return false; + + return true; +} + +void +GetFixedOrDynamicSlotOffset(Shape* shape, bool* isFixed, uint32_t* offset) +{ + MOZ_ASSERT(isFixed); + MOZ_ASSERT(offset); + *isFixed = shape->slot() < shape->numFixedSlots(); + *offset = *isFixed ? NativeObject::getFixedSlotOffset(shape->slot()) + : (shape->slot() - shape->numFixedSlots()) * sizeof(Value); +} + + +static bool +TryAttachNativeGetValuePropStub(JSContext* cx, HandleScript script, jsbytecode* pc, + ICGetProp_Fallback* stub, ICStubCompiler::Engine engine, + HandlePropertyName name, + HandleValue val, HandleShape oldShape, + HandleValue res, bool* attached) +{ + MOZ_ASSERT(!*attached); + + if (!val.isObject()) + return true; + + RootedObject obj(cx, &val.toObject()); + + if (obj->isNative() && oldShape != obj->as().lastProperty()) { + // No point attaching anything, since we know the shape guard will fail + return true; + } + + RootedShape shape(cx); + RootedObject holder(cx); + RootedId id(cx, NameToId(name)); + if (!EffectlesslyLookupProperty(cx, obj, id, &holder, &shape)) + return false; + + ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub(); + if (IsCacheableGetPropReadSlot(obj, holder, shape)) { + bool isFixedSlot; + uint32_t offset; + GetFixedOrDynamicSlotOffset(shape, &isFixedSlot, &offset); + + // Instantiate this property for singleton holders, for use during Ion compilation. + if (IsIonEnabled(cx)) + EnsureTrackPropertyTypes(cx, holder, NameToId(name)); + + ICStub::Kind kind = + (obj == holder) ? ICStub::GetProp_Native : ICStub::GetProp_NativePrototype; + + JitSpew(JitSpew_BaselineIC, " Generating GetProp(Native %s) stub", + (obj == holder) ? "direct" : "prototype"); + ICGetPropNativeCompiler compiler(cx, kind, engine, monitorStub, obj, holder, + name, isFixedSlot, offset); + ICGetPropNativeStub* newStub = compiler.getStub(compiler.getStubSpace(script)); + if (!newStub) + return false; + + if (IsPreliminaryObject(obj)) + newStub->notePreliminaryObject(); + else + StripPreliminaryObjectStubs(cx, stub); + + stub->addNewStub(newStub); + *attached = true; + return true; + } + return true; +} + +bool +IsCacheableGetPropCall(JSContext* cx, JSObject* obj, JSObject* holder, Shape* shape, + bool* isScripted, bool* isTemporarilyUnoptimizable, bool isDOMProxy) +{ + MOZ_ASSERT(isScripted); + + if (!shape || !IsCacheableProtoChain(obj, holder, isDOMProxy)) + return false; + + if (shape->hasSlot() || shape->hasDefaultGetter()) + return false; + + if (!shape->hasGetterValue()) + return false; + + if (!shape->getterValue().isObject() || !shape->getterObject()->is()) + return false; + + JSFunction* func = &shape->getterObject()->as(); + if (func->isNative()) { + *isScripted = false; + return true; + } + + if (!func->hasJITCode()) { + *isTemporarilyUnoptimizable = true; + return false; + } + + *isScripted = true; + return true; +} + +// Try to update all existing GetProp/GetName getter call stubs that match the +// given holder in place with a new shape and getter. fallbackStub can be +// either an ICGetProp_Fallback or an ICGetName_Fallback. +// +// If 'getter' is an own property, holder == receiver must be true. +bool +UpdateExistingGetPropCallStubs(ICFallbackStub* fallbackStub, + ICStub::Kind kind, + HandleNativeObject holder, + HandleObject receiver, + HandleFunction getter) +{ + MOZ_ASSERT(kind == ICStub::GetProp_CallScripted || + kind == ICStub::GetProp_CallNative || + kind == ICStub::GetProp_CallNativeGlobal); + MOZ_ASSERT(fallbackStub->isGetName_Fallback() || + fallbackStub->isGetProp_Fallback()); + MOZ_ASSERT(holder); + MOZ_ASSERT(receiver); + + bool isOwnGetter = (holder == receiver); + bool foundMatchingStub = false; + ReceiverGuard receiverGuard(receiver); + for (ICStubConstIterator iter = fallbackStub->beginChainConst(); !iter.atEnd(); iter++) { + if (iter->kind() == kind) { + ICGetPropCallGetter* getPropStub = static_cast(*iter); + if (getPropStub->holder() == holder && getPropStub->isOwnGetter() == isOwnGetter) { + // If this is an own getter, update the receiver guard as well, + // since that's the shape we'll be guarding on. Furthermore, + // isOwnGetter() relies on holderShape_ and receiverGuard_ being + // the same shape. + if (isOwnGetter) + getPropStub->receiverGuard().update(receiverGuard); + + MOZ_ASSERT(getPropStub->holderShape() != holder->lastProperty() || + !getPropStub->receiverGuard().matches(receiverGuard) || + getPropStub->toGetProp_CallNativeGlobal()->globalShape() != + receiver->as().global().lastProperty(), + "Why didn't we end up using this stub?"); + + // We want to update the holder shape to match the new one no + // matter what, even if the receiver shape is different. + getPropStub->holderShape() = holder->lastProperty(); + + // Make sure to update the getter, since a shape change might + // have changed which getter we want to use. + getPropStub->getter() = getter; + + if (getPropStub->isGetProp_CallNativeGlobal()) { + ICGetProp_CallNativeGlobal* globalStub = + getPropStub->toGetProp_CallNativeGlobal(); + globalStub->globalShape() = + receiver->as().global().lastProperty(); + } + + if (getPropStub->receiverGuard().matches(receiverGuard)) + foundMatchingStub = true; + } + } + } + + return foundMatchingStub; +} + +static bool +TryAttachNativeGetAccessorPropStub(JSContext* cx, HandleScript script, jsbytecode* pc, + ICGetProp_Fallback* stub, ICStubCompiler::Engine engine, + HandlePropertyName name, HandleValue val, HandleValue res, + bool* attached, bool* isTemporarilyUnoptimizable) +{ + MOZ_ASSERT(!*attached); + MOZ_ASSERT(!*isTemporarilyUnoptimizable); + + if (!val.isObject()) + return true; + + RootedObject obj(cx, &val.toObject()); + + bool isDOMProxy; + bool domProxyHasGeneration; + DOMProxyShadowsResult domProxyShadowsResult; + RootedShape shape(cx); + RootedObject holder(cx); + RootedId id(cx, NameToId(name)); + if (!EffectlesslyLookupProperty(cx, obj, id, &holder, &shape, &isDOMProxy, + &domProxyShadowsResult, &domProxyHasGeneration)) + { + return false; + } + + ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub(); + + bool isScripted = false; + bool cacheableCall = IsCacheableGetPropCall(cx, obj, holder, shape, &isScripted, + isTemporarilyUnoptimizable); + + // Try handling scripted getters. + if (cacheableCall && isScripted && !isDOMProxy) { + RootedFunction callee(cx, &shape->getterObject()->as()); + MOZ_ASSERT(callee->hasScript()); + + if (UpdateExistingGetPropCallStubs(stub, ICStub::GetProp_CallScripted, + holder.as(), obj, callee)) { + *attached = true; + return true; + } + + JitSpew(JitSpew_BaselineIC, " Generating GetProp(NativeObj/ScriptedGetter %s:%" PRIuSIZE ") stub", + callee->nonLazyScript()->filename(), callee->nonLazyScript()->lineno()); + + ICGetProp_CallScripted::Compiler compiler(cx, engine, monitorStub, obj, holder, callee, + script->pcToOffset(pc)); + ICStub* newStub = compiler.getStub(compiler.getStubSpace(script)); + if (!newStub) + return false; + + stub->addNewStub(newStub); + *attached = true; + return true; + } + + // If it's a shadowed listbase proxy property, attach stub to call Proxy::get instead. + if (isDOMProxy && DOMProxyIsShadowing(domProxyShadowsResult)) { + MOZ_ASSERT(obj == holder); + + JitSpew(JitSpew_BaselineIC, " Generating GetProp(DOMProxyProxy) stub"); + Rooted proxy(cx, &obj->as()); + ICGetProp_DOMProxyShadowed::Compiler compiler(cx, engine, monitorStub, proxy, name, + script->pcToOffset(pc)); + ICStub* newStub = compiler.getStub(compiler.getStubSpace(script)); + if (!newStub) + return false; + stub->addNewStub(newStub); + *attached = true; + return true; + } + + const Class* outerClass = nullptr; + if (!isDOMProxy && !obj->isNative()) { + outerClass = obj->getClass(); + if (!IsWindowProxy(obj)) + return true; + + // This must be a WindowProxy for the current Window/global. Else it'd + // be a cross-compartment wrapper and IsWindowProxy returns false for + // those. + MOZ_ASSERT(ToWindowIfWindowProxy(obj) == cx->global()); + obj = cx->global(); + + if (!EffectlesslyLookupProperty(cx, obj, id, &holder, &shape, &isDOMProxy, + &domProxyShadowsResult, &domProxyHasGeneration)) + { + return false; + } + cacheableCall = IsCacheableGetPropCall(cx, obj, holder, shape, &isScripted, + isTemporarilyUnoptimizable, isDOMProxy); + } + + // Try handling JSNative getters. + if (!cacheableCall || isScripted) + return true; + + if (!shape || !shape->hasGetterValue() || !shape->getterValue().isObject() || + !shape->getterObject()->is()) + { + return true; + } + + RootedFunction callee(cx, &shape->getterObject()->as()); + MOZ_ASSERT(callee->isNative()); + + if (outerClass && (!callee->jitInfo() || callee->jitInfo()->needsOuterizedThisObject())) + return true; + + JitSpew(JitSpew_BaselineIC, " Generating GetProp(%s%s/NativeGetter %p) stub", + isDOMProxy ? "DOMProxyObj" : "NativeObj", + isDOMProxy && domProxyHasGeneration ? "WithGeneration" : "", + callee->native()); + + ICStub* newStub = nullptr; + if (isDOMProxy) { + MOZ_ASSERT(obj != holder); + ICStub::Kind kind; + if (domProxyHasGeneration) { + if (UpdateExistingGenerationalDOMProxyStub(stub, obj)) { + *attached = true; + return true; + } + kind = ICStub::GetProp_CallDOMProxyWithGenerationNative; + } else { + kind = ICStub::GetProp_CallDOMProxyNative; + } + Rooted proxy(cx, &obj->as()); + ICGetPropCallDOMProxyNativeCompiler compiler(cx, kind, engine, monitorStub, proxy, holder, + callee, script->pcToOffset(pc)); + newStub = compiler.getStub(compiler.getStubSpace(script)); + } else { + if (UpdateExistingGetPropCallStubs(stub, ICStub::GetProp_CallNative, + holder.as(), obj, callee)) + { + *attached = true; + return true; + } + + ICGetPropCallNativeCompiler compiler(cx, ICStub::GetProp_CallNative, engine, + monitorStub, obj, holder, callee, + script->pcToOffset(pc), outerClass); + newStub = compiler.getStub(compiler.getStubSpace(script)); + } + if (!newStub) + return false; + stub->addNewStub(newStub); + *attached = true; + return true; +} + +static bool +TryAttachUnboxedGetPropStub(JSContext* cx, HandleScript script, ICGetProp_Fallback* stub, + ICStubCompiler::Engine engine, HandlePropertyName name, + HandleValue val, bool* attached) +{ + MOZ_ASSERT(!*attached); + + if (!cx->runtime()->jitSupportsFloatingPoint) + return true; + + if (!val.isObject() || !val.toObject().is()) + return true; + Rooted obj(cx, &val.toObject().as()); + + const UnboxedLayout::Property* property = obj->layout().lookup(name); + if (!property) + return true; + + ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub(); + + ICGetProp_Unboxed::Compiler compiler(cx, engine, monitorStub, obj->group(), + property->offset + UnboxedPlainObject::offsetOfData(), + property->type); + ICStub* newStub = compiler.getStub(compiler.getStubSpace(script)); + if (!newStub) + return false; + stub->addNewStub(newStub); + + StripPreliminaryObjectStubs(cx, stub); + + *attached = true; + return true; +} + +static bool +TryAttachUnboxedExpandoGetPropStub(JSContext* cx, HandleScript script, jsbytecode* pc, + ICGetProp_Fallback* stub, ICStubCompiler::Engine engine, + HandlePropertyName name, HandleValue val, + bool* attached) +{ + MOZ_ASSERT(!*attached); + + if (!val.isObject() || !val.toObject().is()) + return true; + Rooted obj(cx, &val.toObject().as()); + + Rooted expando(cx, obj->maybeExpando()); + if (!expando) + return true; + + Shape* shape = expando->lookup(cx, name); + if (!shape || !shape->hasDefaultGetter() || !shape->hasSlot()) + return true; + + bool isFixedSlot; + uint32_t offset; + GetFixedOrDynamicSlotOffset(shape, &isFixedSlot, &offset); + + ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub(); + + ICGetPropNativeCompiler compiler(cx, ICStub::GetProp_Native, engine, monitorStub, obj, obj, + name, isFixedSlot, offset); + ICGetPropNativeStub* newStub = compiler.getStub(compiler.getStubSpace(script)); + if (!newStub) + return false; + + StripPreliminaryObjectStubs(cx, stub); + + stub->addNewStub(newStub); + *attached = true; + return true; +} + +static bool +TryAttachTypedObjectGetPropStub(JSContext* cx, HandleScript script, ICGetProp_Fallback* stub, + ICStubCompiler::Engine engine, HandlePropertyName name, + HandleValue val, bool* attached) +{ + MOZ_ASSERT(!*attached); + + if (!cx->runtime()->jitSupportsFloatingPoint) + return true; + + if (!val.isObject() || !val.toObject().is()) + return true; + Rooted obj(cx, &val.toObject().as()); + + if (!obj->typeDescr().is()) + return true; + Rooted structDescr(cx, &obj->typeDescr().as()); + + size_t fieldIndex; + if (!structDescr->fieldIndex(NameToId(name), &fieldIndex)) + return true; + + Rooted fieldDescr(cx, &structDescr->fieldDescr(fieldIndex)); + if (!fieldDescr->is()) + return true; + + uint32_t fieldOffset = structDescr->fieldOffset(fieldIndex); + ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub(); + + ICGetProp_TypedObject::Compiler compiler(cx, engine, monitorStub, obj->maybeShape(), + fieldOffset, &fieldDescr->as()); + ICStub* newStub = compiler.getStub(compiler.getStubSpace(script)); + if (!newStub) + return false; + stub->addNewStub(newStub); + + *attached = true; + return true; +} + +static bool +TryAttachPrimitiveGetPropStub(JSContext* cx, HandleScript script, jsbytecode* pc, + ICGetProp_Fallback* stub, ICStubCompiler::Engine engine, + HandlePropertyName name, HandleValue val, + HandleValue res, bool* attached) +{ + MOZ_ASSERT(!*attached); + + JSValueType primitiveType; + RootedNativeObject proto(cx); + Rooted global(cx, &script->global()); + if (val.isString()) { + primitiveType = JSVAL_TYPE_STRING; + proto = GlobalObject::getOrCreateStringPrototype(cx, global); + } else if (val.isSymbol()) { + primitiveType = JSVAL_TYPE_SYMBOL; + proto = GlobalObject::getOrCreateSymbolPrototype(cx, global); + } else if (val.isNumber()) { + primitiveType = JSVAL_TYPE_DOUBLE; + proto = GlobalObject::getOrCreateNumberPrototype(cx, global); + } else { + MOZ_ASSERT(val.isBoolean()); + primitiveType = JSVAL_TYPE_BOOLEAN; + proto = GlobalObject::getOrCreateBooleanPrototype(cx, global); + } + if (!proto) + return false; + + // Instantiate this property, for use during Ion compilation. + RootedId id(cx, NameToId(name)); + if (IsIonEnabled(cx)) + EnsureTrackPropertyTypes(cx, proto, id); + + // For now, only look for properties directly set on the prototype. + RootedShape shape(cx, proto->lookup(cx, id)); + if (!shape || !shape->hasSlot() || !shape->hasDefaultGetter()) + return true; + + bool isFixedSlot; + uint32_t offset; + GetFixedOrDynamicSlotOffset(shape, &isFixedSlot, &offset); + + ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub(); + + JitSpew(JitSpew_BaselineIC, " Generating GetProp_Primitive stub"); + ICGetProp_Primitive::Compiler compiler(cx, engine, monitorStub, primitiveType, proto, + isFixedSlot, offset); + ICStub* newStub = compiler.getStub(compiler.getStubSpace(script)); + if (!newStub) + return false; + + stub->addNewStub(newStub); + *attached = true; + return true; +} + +bool +CheckHasNoSuchProperty(JSContext* cx, HandleObject obj, HandlePropertyName name, + MutableHandleObject lastProto, size_t* protoChainDepthOut) +{ + MOZ_ASSERT(protoChainDepthOut != nullptr); + + size_t depth = 0; + RootedObject curObj(cx, obj); + while (curObj) { + if (curObj->isNative()) { + // Don't handle proto chains with resolve hooks. + if (ClassMayResolveId(cx->names(), curObj->getClass(), NameToId(name), curObj)) + return false; + if (curObj->as().contains(cx, NameToId(name))) + return false; + } else if (curObj != obj) { + // Non-native objects are only handled as the original receiver. + return false; + } else if (curObj->is()) { + if (curObj->as().containsUnboxedOrExpandoProperty(cx, NameToId(name))) + return false; + } else if (curObj->is()) { + if (name == cx->names().length) + return false; + } else if (curObj->is()) { + if (curObj->as().typeDescr().hasProperty(cx->names(), NameToId(name))) + return false; + } else { + return false; + } + + JSObject* proto = curObj->getTaggedProto().toObjectOrNull(); + if (!proto) + break; + + curObj = proto; + depth++; + } + + lastProto.set(curObj); + *protoChainDepthOut = depth; + return true; +} + +static bool +TryAttachNativeGetPropDoesNotExistStub(JSContext* cx, HandleScript script, + jsbytecode* pc, ICGetProp_Fallback* stub, + ICStubCompiler::Engine engine, + HandlePropertyName name, HandleValue val, + bool* attached) +{ + MOZ_ASSERT(!*attached); + + if (!val.isObject()) + return true; + + RootedObject obj(cx, &val.toObject()); + + // Don't attach stubs for CALLPROP since those need NoSuchMethod handling. + if (JSOp(*pc) == JSOP_CALLPROP) + return true; + + // Check if does-not-exist can be confirmed on property. + RootedObject lastProto(cx); + size_t protoChainDepth = SIZE_MAX; + if (!CheckHasNoSuchProperty(cx, obj, name, &lastProto, &protoChainDepth)) + return true; + MOZ_ASSERT(protoChainDepth < SIZE_MAX); + + if (protoChainDepth > ICGetProp_NativeDoesNotExist::MAX_PROTO_CHAIN_DEPTH) + return true; + + ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub(); + + // Confirmed no-such-property. Add stub. + JitSpew(JitSpew_BaselineIC, " Generating GetProp_NativeDoesNotExist stub"); + ICGetPropNativeDoesNotExistCompiler compiler(cx, engine, monitorStub, obj, protoChainDepth); + ICStub* newStub = compiler.getStub(compiler.getStubSpace(script)); + if (!newStub) + return false; + + stub->addNewStub(newStub); + *attached = true; + return true; +} + +static bool +ComputeGetPropResult(JSContext* cx, BaselineFrame* frame, JSOp op, HandlePropertyName name, + MutableHandleValue val, MutableHandleValue res) +{ + // Handle arguments.length and arguments.callee on optimized arguments, as + // it is not an object. + if (frame && val.isMagic(JS_OPTIMIZED_ARGUMENTS) && IsOptimizedArguments(frame, val)) { + if (op == JSOP_LENGTH) { + res.setInt32(frame->numActualArgs()); + } else { + MOZ_ASSERT(name == cx->names().callee); + MOZ_ASSERT(frame->script()->hasMappedArgsObj()); + res.setObject(*frame->callee()); + } + } else { + if (op == JSOP_GETXPROP) { + RootedObject obj(cx, &val.toObject()); + RootedId id(cx, NameToId(name)); + if (!GetPropertyForNameLookup(cx, obj, id, res)) + return false; + } else { + MOZ_ASSERT(op == JSOP_GETPROP || op == JSOP_CALLPROP || op == JSOP_LENGTH); + if (!GetProperty(cx, val, name, res)) + return false; + } + } + + return true; +} + +static bool +DoGetPropFallback(JSContext* cx, BaselineFrame* frame, ICGetProp_Fallback* stub_, + MutableHandleValue val, MutableHandleValue res) +{ + ICStubCompiler::Engine engine = SharedStubEngine(frame); + RootedScript script(cx, SharedStubScript(frame, stub_)); + + // This fallback stub may trigger debug mode toggling. + DebugModeOSRVolatileStub stub(engine, frame, stub_); + + jsbytecode* pc = stub->icEntry()->pc(script); + JSOp op = JSOp(*pc); + FallbackICSpew(cx, stub, "GetProp(%s)", js_CodeName[op]); + + MOZ_ASSERT(op == JSOP_GETPROP || op == JSOP_CALLPROP || op == JSOP_LENGTH || op == JSOP_GETXPROP); + + // Grab our old shape before it goes away. + RootedShape oldShape(cx); + if (val.isObject()) + oldShape = val.toObject().maybeShape(); + + bool attached = false; + // There are some reasons we can fail to attach a stub that are temporary. + // We want to avoid calling noteUnoptimizableAccess() if the reason we + // failed to attach a stub is one of those temporary reasons, since we might + // end up attaching a stub for the exact same access later. + bool isTemporarilyUnoptimizable = false; + + RootedPropertyName name(cx, script->getName(pc)); + + // After the Genericstub was added, we should never reach the Fallbackstub again. + MOZ_ASSERT(!stub->hasStub(ICStub::GetProp_Generic)); + + if (stub->numOptimizedStubs() >= ICGetProp_Fallback::MAX_OPTIMIZED_STUBS) { + // Discard all stubs in this IC and replace with generic getprop stub. + for(ICStubIterator iter = stub->beginChain(); !iter.atEnd(); iter++) + iter.unlink(cx); + ICGetProp_Generic::Compiler compiler(cx, engine, + stub->fallbackMonitorStub()->firstMonitorStub()); + ICStub* newStub = compiler.getStub(compiler.getStubSpace(script)); + if (!newStub) + return false; + stub->addNewStub(newStub); + attached = true; + } + + if (!attached && !TryAttachNativeGetAccessorPropStub(cx, script, pc, stub, engine, name, val, + res, &attached, + &isTemporarilyUnoptimizable)) + { + return false; + } + + if (!ComputeGetPropResult(cx, frame, op, name, val, res)) + return false; + + TypeScript::Monitor(cx, script, pc, res); + + // Check if debug mode toggling made the stub invalid. + if (stub.invalid()) + return true; + + // Add a type monitor stub for the resulting value. + if (!stub->addMonitorStubForValue(cx, script, res, engine)) + return false; + + if (attached) + return true; + + if (op == JSOP_LENGTH) { + if (!TryAttachLengthStub(cx, script, stub, engine, val, res, &attached)) + return false; + if (attached) + return true; + } + + if (!TryAttachMagicArgumentsGetPropStub(cx, script, stub, engine, name, val, res, &attached)) + return false; + if (attached) + return true; + + if (!TryAttachNativeGetValuePropStub(cx, script, pc, stub, engine, name, val, oldShape, + res, &attached)) + return false; + if (attached) + return true; + + if (!TryAttachUnboxedGetPropStub(cx, script, stub, engine, name, val, &attached)) + return false; + if (attached) + return true; + + if (!TryAttachUnboxedExpandoGetPropStub(cx, script, pc, stub, engine, name, val, &attached)) + return false; + if (attached) + return true; + + if (!TryAttachTypedObjectGetPropStub(cx, script, stub, engine, name, val, &attached)) + return false; + if (attached) + return true; + + if (val.isString() || val.isNumber() || val.isBoolean()) { + if (!TryAttachPrimitiveGetPropStub(cx, script, pc, stub, engine, name, val, res, &attached)) + return false; + if (attached) + return true; + } + + if (res.isUndefined()) { + // Try attaching property-not-found optimized stub for undefined results. + if (!TryAttachNativeGetPropDoesNotExistStub(cx, script, pc, stub, engine, name, val, + &attached)) + { + return false; + } + if (attached) + return true; + } + + MOZ_ASSERT(!attached); + if (!isTemporarilyUnoptimizable) + stub->noteUnoptimizableAccess(); + + return true; +} + +typedef bool (*DoGetPropFallbackFn)(JSContext*, BaselineFrame*, ICGetProp_Fallback*, + MutableHandleValue, MutableHandleValue); +static const VMFunction DoGetPropFallbackInfo = + FunctionInfo(DoGetPropFallback, TailCall, PopValues(1)); + +bool +ICGetProp_Fallback::Compiler::generateStubCode(MacroAssembler& masm) +{ + MOZ_ASSERT(R0 == JSReturnOperand); + + EmitRestoreTailCallReg(masm); + + // Ensure stack is fully synced for the expression decompiler. + masm.pushValue(R0); + + // Push arguments. + masm.pushValue(R0); + masm.push(ICStubReg); + pushFramePtr(masm, R0.scratchReg()); + + if (!tailCallVM(DoGetPropFallbackInfo, masm)) + return false; + + // Even though the fallback frame doesn't enter a stub frame, the CallScripted + // frame that we are emulating does. Again, we lie. +#ifdef DEBUG + EmitRepushTailCallReg(masm); + enterStubFrame(masm, R0.scratchReg()); +#else + inStubFrame_ = true; +#endif + + // What follows is bailout for inlined scripted getters. + // The return address pointed to by the baseline stack points here. + returnOffset_ = masm.currentOffset(); + + leaveStubFrame(masm, true); + + // When we get here, ICStubReg contains the ICGetProp_Fallback stub, + // which we can't use to enter the TypeMonitor IC, because it's a MonitoredFallbackStub + // instead of a MonitoredStub. So, we cheat. + masm.loadPtr(Address(ICStubReg, ICMonitoredFallbackStub::offsetOfFallbackMonitorStub()), + ICStubReg); + EmitEnterTypeMonitorIC(masm, ICTypeMonitor_Fallback::offsetOfFirstMonitorStub()); + + return true; +} + +void +ICGetProp_Fallback::Compiler::postGenerateStubCode(MacroAssembler& masm, Handle code) +{ + if (engine_ == Engine::Baseline) { + void* address = code->raw() + returnOffset_; + cx->compartment()->jitCompartment()->initBaselineGetPropReturnAddr(address); + } +} + +bool +ICGetProp_ArrayLength::Compiler::generateStubCode(MacroAssembler& masm) +{ + Label failure; + masm.branchTestObject(Assembler::NotEqual, R0, &failure); + + Register scratch = R1.scratchReg(); + + // Unbox R0 and guard it's an array. + Register obj = masm.extractObject(R0, ExtractTemp0); + masm.branchTestObjClass(Assembler::NotEqual, obj, scratch, &ArrayObject::class_, &failure); + + // Load obj->elements->length. + masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratch); + masm.load32(Address(scratch, ObjectElements::offsetOfLength()), scratch); + + // Guard length fits in an int32. + masm.branchTest32(Assembler::Signed, scratch, scratch, &failure); + + masm.tagValue(JSVAL_TYPE_INT32, scratch, R0); + EmitReturnFromIC(masm); + + // Failure case - jump to next stub + masm.bind(&failure); + EmitStubGuardFailure(masm); + return true; +} + +bool +ICGetProp_UnboxedArrayLength::Compiler::generateStubCode(MacroAssembler& masm) +{ + Label failure; + masm.branchTestObject(Assembler::NotEqual, R0, &failure); + + Register scratch = R1.scratchReg(); + + // Unbox R0 and guard it's an unboxed array. + Register obj = masm.extractObject(R0, ExtractTemp0); + masm.branchTestObjClass(Assembler::NotEqual, obj, scratch, &UnboxedArrayObject::class_, &failure); + + // Load obj->length. + masm.load32(Address(obj, UnboxedArrayObject::offsetOfLength()), scratch); + + masm.tagValue(JSVAL_TYPE_INT32, scratch, R0); + EmitReturnFromIC(masm); + + // Failure case - jump to next stub + masm.bind(&failure); + EmitStubGuardFailure(masm); + return true; +} + +bool +ICGetProp_StringLength::Compiler::generateStubCode(MacroAssembler& masm) +{ + Label failure; + masm.branchTestString(Assembler::NotEqual, R0, &failure); + + // Unbox string and load its length. + Register string = masm.extractString(R0, ExtractTemp0); + masm.loadStringLength(string, string); + + masm.tagValue(JSVAL_TYPE_INT32, string, R0); + EmitReturnFromIC(masm); + + // Failure case - jump to next stub + masm.bind(&failure); + EmitStubGuardFailure(masm); + return true; +} + +bool +ICGetProp_Primitive::Compiler::generateStubCode(MacroAssembler& masm) +{ + Label failure; + switch (primitiveType_) { + case JSVAL_TYPE_STRING: + masm.branchTestString(Assembler::NotEqual, R0, &failure); + break; + case JSVAL_TYPE_SYMBOL: + masm.branchTestSymbol(Assembler::NotEqual, R0, &failure); + break; + case JSVAL_TYPE_DOUBLE: // Also used for int32. + masm.branchTestNumber(Assembler::NotEqual, R0, &failure); + break; + case JSVAL_TYPE_BOOLEAN: + masm.branchTestBoolean(Assembler::NotEqual, R0, &failure); + break; + default: + MOZ_CRASH("unexpected type"); + } + + AllocatableGeneralRegisterSet regs(availableGeneralRegs(1)); + Register holderReg = regs.takeAny(); + Register scratchReg = regs.takeAny(); + + // Verify the shape of the prototype. + masm.movePtr(ImmGCPtr(prototype_.get()), holderReg); + + Address shapeAddr(ICStubReg, ICGetProp_Primitive::offsetOfProtoShape()); + masm.loadPtr(Address(holderReg, JSObject::offsetOfShape()), scratchReg); + masm.branchPtr(Assembler::NotEqual, shapeAddr, scratchReg, &failure); + + if (!isFixedSlot_) + masm.loadPtr(Address(holderReg, NativeObject::offsetOfSlots()), holderReg); + + masm.load32(Address(ICStubReg, ICGetProp_Primitive::offsetOfOffset()), scratchReg); + masm.loadValue(BaseIndex(holderReg, scratchReg, TimesOne), R0); + + // Enter type monitor IC to type-check result. + EmitEnterTypeMonitorIC(masm); + + // Failure case - jump to next stub + masm.bind(&failure); + EmitStubGuardFailure(masm); + return true; +} + +ICGetPropNativeStub* +ICGetPropNativeCompiler::getStub(ICStubSpace* space) +{ + ReceiverGuard guard(obj_); + + switch (kind) { + case ICStub::GetProp_Native: { + MOZ_ASSERT(obj_ == holder_); + return newStub(space, getStubCode(), firstMonitorStub_, guard, offset_); + } + + case ICStub::GetProp_NativePrototype: { + MOZ_ASSERT(obj_ != holder_); + Shape* holderShape = holder_->as().lastProperty(); + return newStub(space, getStubCode(), firstMonitorStub_, guard, + offset_, holder_, holderShape); + } + + case ICStub::GetName_Global: { + MOZ_ASSERT(obj_ != holder_); + Shape* holderShape = holder_->as().lastProperty(); + Shape* globalShape = obj_->as().global().lastProperty(); + return newStub(space, getStubCode(), firstMonitorStub_, guard, + offset_, holder_, holderShape, globalShape); + } + + default: + MOZ_CRASH("Bad stub kind"); + } +} + +void +GuardReceiverObject(MacroAssembler& masm, ReceiverGuard guard, + Register object, Register scratch, + size_t receiverGuardOffset, Label* failure) +{ + Address groupAddress(ICStubReg, receiverGuardOffset + HeapReceiverGuard::offsetOfGroup()); + Address shapeAddress(ICStubReg, receiverGuardOffset + HeapReceiverGuard::offsetOfShape()); + Address expandoAddress(object, UnboxedPlainObject::offsetOfExpando()); + + if (guard.group) { + masm.loadPtr(groupAddress, scratch); + masm.branchTestObjGroup(Assembler::NotEqual, object, scratch, failure); + + if (guard.group->clasp() == &UnboxedPlainObject::class_ && !guard.shape) { + // Guard the unboxed object has no expando object. + masm.branchPtr(Assembler::NotEqual, expandoAddress, ImmWord(0), failure); + } + } + + if (guard.shape) { + masm.loadPtr(shapeAddress, scratch); + if (guard.group && guard.group->clasp() == &UnboxedPlainObject::class_) { + // Guard the unboxed object has a matching expando object. + masm.branchPtr(Assembler::Equal, expandoAddress, ImmWord(0), failure); + Label done; + masm.push(object); + masm.loadPtr(expandoAddress, object); + masm.branchTestObjShape(Assembler::Equal, object, scratch, &done); + masm.pop(object); + masm.jump(failure); + masm.bind(&done); + masm.pop(object); + } else { + masm.branchTestObjShape(Assembler::NotEqual, object, scratch, failure); + } + } +} + +static void +GuardGlobalObject(MacroAssembler& masm, HandleObject holder, Register globalLexicalReg, + Register holderReg, Register scratch, size_t globalShapeOffset, Label* failure) +{ + if (holder->is()) + return; + masm.extractObject(Address(globalLexicalReg, ScopeObject::offsetOfEnclosingScope()), + holderReg); + masm.loadPtr(Address(ICStubReg, globalShapeOffset), scratch); + masm.branchTestObjShape(Assembler::NotEqual, holderReg, scratch, failure); +} + +bool +ICGetPropNativeCompiler::generateStubCode(MacroAssembler& masm) +{ + Label failure; + AllocatableGeneralRegisterSet regs(availableGeneralRegs(0)); + Register objReg = InvalidReg; + + if (inputDefinitelyObject_) { + objReg = R0.scratchReg(); + } else { + regs.take(R0); + // Guard input is an object and unbox. + masm.branchTestObject(Assembler::NotEqual, R0, &failure); + objReg = masm.extractObject(R0, ExtractTemp0); + } + regs.takeUnchecked(objReg); + + Register scratch = regs.takeAnyExcluding(ICTailCallReg); + + // Shape/group guard. + GuardReceiverObject(masm, ReceiverGuard(obj_), objReg, scratch, + ICGetPropNativeStub::offsetOfReceiverGuard(), &failure); + + Register holderReg; + if (obj_ == holder_) { + MOZ_ASSERT(kind != ICStub::GetName_Global); + if (obj_->is()) { + // We are loading off the expando object, so use that for the holder. + holderReg = regs.takeAny(); + masm.loadPtr(Address(objReg, UnboxedPlainObject::offsetOfExpando()), holderReg); + } else { + holderReg = objReg; + } + } else { + holderReg = regs.takeAny(); + + // If we are generating a non-lexical GETGNAME stub, we must also + // guard on the shape of the GlobalObject. + if (kind == ICStub::GetName_Global) { + MOZ_ASSERT(obj_->is() && obj_->as().isGlobal()); + GuardGlobalObject(masm, holder_, objReg, holderReg, scratch, + ICGetName_Global::offsetOfGlobalShape(), &failure); + } + + // Shape guard holder. + masm.loadPtr(Address(ICStubReg, ICGetProp_NativePrototype::offsetOfHolder()), + holderReg); + masm.loadPtr(Address(ICStubReg, ICGetProp_NativePrototype::offsetOfHolderShape()), + scratch); + masm.branchTestObjShape(Assembler::NotEqual, holderReg, scratch, &failure); + } + + if (!isFixedSlot_) { + // Don't overwrite actual holderReg if we need to load a dynamic slots object. + // May need to preserve object for noSuchMethod check later. + Register nextHolder = regs.takeAny(); + masm.loadPtr(Address(holderReg, NativeObject::offsetOfSlots()), nextHolder); + holderReg = nextHolder; + } + + masm.load32(Address(ICStubReg, ICGetPropNativeStub::offsetOfOffset()), scratch); + BaseIndex result(holderReg, scratch, TimesOne); + + masm.loadValue(result, R0); + + // Enter type monitor IC to type-check result. + EmitEnterTypeMonitorIC(masm); + + // Failure case - jump to next stub + masm.bind(&failure); + EmitStubGuardFailure(masm); + return true; +} + +bool +GetProtoShapes(JSObject* obj, size_t protoChainDepth, MutableHandle shapes) +{ + JSObject* curProto = obj->getProto(); + for (size_t i = 0; i < protoChainDepth; i++) { + if (!shapes.append(curProto->as().lastProperty())) + return false; + curProto = curProto->getProto(); + } + MOZ_ASSERT(!curProto); + return true; +} + +ICStub* +ICGetPropNativeDoesNotExistCompiler::getStub(ICStubSpace* space) +{ + Rooted shapes(cx, ShapeVector(cx)); + + if (!GetProtoShapes(obj_, protoChainDepth_, &shapes)) + return nullptr; + + JS_STATIC_ASSERT(ICGetProp_NativeDoesNotExist::MAX_PROTO_CHAIN_DEPTH == 8); + + ICStub* stub = nullptr; + switch(protoChainDepth_) { + case 0: stub = getStubSpecific<0>(space, shapes); break; + case 1: stub = getStubSpecific<1>(space, shapes); break; + case 2: stub = getStubSpecific<2>(space, shapes); break; + case 3: stub = getStubSpecific<3>(space, shapes); break; + case 4: stub = getStubSpecific<4>(space, shapes); break; + case 5: stub = getStubSpecific<5>(space, shapes); break; + case 6: stub = getStubSpecific<6>(space, shapes); break; + case 7: stub = getStubSpecific<7>(space, shapes); break; + case 8: stub = getStubSpecific<8>(space, shapes); break; + default: MOZ_CRASH("ProtoChainDepth too high."); + } + if (!stub) + return nullptr; + return stub; +} + +bool +ICGetPropNativeDoesNotExistCompiler::generateStubCode(MacroAssembler& masm) +{ + Label failure; + + AllocatableGeneralRegisterSet regs(availableGeneralRegs(1)); + Register scratch = regs.takeAny(); + +#ifdef DEBUG + // Ensure that protoChainDepth_ matches the protoChainDepth stored on the stub. + { + Label ok; + masm.load16ZeroExtend(Address(ICStubReg, ICStub::offsetOfExtra()), scratch); + masm.branch32(Assembler::Equal, scratch, Imm32(protoChainDepth_), &ok); + masm.assumeUnreachable("Non-matching proto chain depth on stub."); + masm.bind(&ok); + } +#endif // DEBUG + + // Guard input is an object. + masm.branchTestObject(Assembler::NotEqual, R0, &failure); + + // Unbox and guard against old shape/group. + Register objReg = masm.extractObject(R0, ExtractTemp0); + GuardReceiverObject(masm, ReceiverGuard(obj_), objReg, scratch, + ICGetProp_NativeDoesNotExist::offsetOfGuard(), &failure); + + Register protoReg = regs.takeAny(); + // Check the proto chain. + for (size_t i = 0; i < protoChainDepth_; i++) { + masm.loadObjProto(i == 0 ? objReg : protoReg, protoReg); + masm.branchTestPtr(Assembler::Zero, protoReg, protoReg, &failure); + size_t shapeOffset = ICGetProp_NativeDoesNotExistImpl<0>::offsetOfShape(i); + masm.loadPtr(Address(ICStubReg, shapeOffset), scratch); + masm.branchTestObjShape(Assembler::NotEqual, protoReg, scratch, &failure); + } + + // Shape and type checks succeeded, ok to proceed. + masm.moveValue(UndefinedValue(), R0); + + // Normally for this op, the result would have to be monitored by TI. + // However, since this stub ALWAYS returns UndefinedValue(), and we can be sure + // that undefined is already registered with the type-set, this can be avoided. + EmitReturnFromIC(masm); + + masm.bind(&failure); + EmitStubGuardFailure(masm); + return true; +} + +bool +ICGetProp_CallScripted::Compiler::generateStubCode(MacroAssembler& masm) +{ + Label failure; + Label failureLeaveStubFrame; + AllocatableGeneralRegisterSet regs(availableGeneralRegs(1)); + Register scratch = regs.takeAnyExcluding(ICTailCallReg); + + // Guard input is an object. + masm.branchTestObject(Assembler::NotEqual, R0, &failure); + + // Unbox and shape guard. + Register objReg = masm.extractObject(R0, ExtractTemp0); + GuardReceiverObject(masm, ReceiverGuard(receiver_), objReg, scratch, + ICGetProp_CallScripted::offsetOfReceiverGuard(), &failure); + + if (receiver_ != holder_) { + Register holderReg = regs.takeAny(); + masm.loadPtr(Address(ICStubReg, ICGetProp_CallScripted::offsetOfHolder()), holderReg); + masm.loadPtr(Address(ICStubReg, ICGetProp_CallScripted::offsetOfHolderShape()), scratch); + masm.branchTestObjShape(Assembler::NotEqual, holderReg, scratch, &failure); + regs.add(holderReg); + } + + // Push a stub frame so that we can perform a non-tail call. + enterStubFrame(masm, scratch); + + // Load callee function and code. To ensure that |code| doesn't end up being + // ArgumentsRectifierReg, if it's available we assign it to |callee| instead. + Register callee; + if (regs.has(ArgumentsRectifierReg)) { + callee = ArgumentsRectifierReg; + regs.take(callee); + } else { + callee = regs.takeAny(); + } + Register code = regs.takeAny(); + masm.loadPtr(Address(ICStubReg, ICGetProp_CallScripted::offsetOfGetter()), callee); + masm.branchIfFunctionHasNoScript(callee, &failureLeaveStubFrame); + masm.loadPtr(Address(callee, JSFunction::offsetOfNativeOrScript()), code); + masm.loadBaselineOrIonRaw(code, code, &failureLeaveStubFrame); + + // Align the stack such that the JitFrameLayout is aligned on + // JitStackAlignment. + masm.alignJitStackBasedOnNArgs(0); + + // Getter is called with 0 arguments, just |obj| as thisv. + // Note that we use Push, not push, so that callJit will align the stack + // properly on ARM. + masm.Push(R0); + EmitBaselineCreateStubFrameDescriptor(masm, scratch); + masm.Push(Imm32(0)); // ActualArgc is 0 + masm.Push(callee); + masm.Push(scratch); + + // Handle arguments underflow. + Label noUnderflow; + masm.load16ZeroExtend(Address(callee, JSFunction::offsetOfNargs()), scratch); + masm.branch32(Assembler::Equal, scratch, Imm32(0), &noUnderflow); + { + // Call the arguments rectifier. + MOZ_ASSERT(ArgumentsRectifierReg != code); + + JitCode* argumentsRectifier = + cx->runtime()->jitRuntime()->getArgumentsRectifier(); + + masm.movePtr(ImmGCPtr(argumentsRectifier), code); + masm.loadPtr(Address(code, JitCode::offsetOfCode()), code); + masm.movePtr(ImmWord(0), ArgumentsRectifierReg); + } + + masm.bind(&noUnderflow); + masm.callJit(code); + + leaveStubFrame(masm, true); + + // Enter type monitor IC to type-check result. + EmitEnterTypeMonitorIC(masm); + + // Leave stub frame and go to next stub. + masm.bind(&failureLeaveStubFrame); + inStubFrame_ = true; + leaveStubFrame(masm, false); + + // Failure case - jump to next stub + masm.bind(&failure); + EmitStubGuardFailure(masm); + return true; +} + +// +// VM function to help call native getters. +// + +bool +DoCallNativeGetter(JSContext* cx, HandleFunction callee, HandleObject obj, + MutableHandleValue result) +{ + MOZ_ASSERT(callee->isNative()); + JSNative natfun = callee->native(); + + JS::AutoValueArray<2> vp(cx); + vp[0].setObject(*callee.get()); + vp[1].setObject(*obj.get()); + + if (!natfun(cx, 0, vp.begin())) + return false; + + result.set(vp[0]); + return true; +} + +typedef bool (*DoCallNativeGetterFn)(JSContext*, HandleFunction, HandleObject, MutableHandleValue); +static const VMFunction DoCallNativeGetterInfo = + FunctionInfo(DoCallNativeGetter); + +bool +ICGetPropCallNativeCompiler::generateStubCode(MacroAssembler& masm) +{ + Label failure; + + AllocatableGeneralRegisterSet regs(availableGeneralRegs(1)); + Register objReg = InvalidReg; + + MOZ_ASSERT(!(inputDefinitelyObject_ && outerClass_)); + if (inputDefinitelyObject_) { + objReg = R0.scratchReg(); + } else { + // Guard input is an object and unbox. + masm.branchTestObject(Assembler::NotEqual, R0, &failure); + objReg = masm.extractObject(R0, ExtractTemp0); + if (outerClass_) { + Register tmp = regs.takeAny(); + masm.branchTestObjClass(Assembler::NotEqual, objReg, tmp, outerClass_, &failure); + masm.movePtr(ImmGCPtr(cx->global()), objReg); + regs.add(tmp); + } + } + + Register scratch = regs.takeAnyExcluding(ICTailCallReg); + + // Shape guard. + GuardReceiverObject(masm, ReceiverGuard(receiver_), objReg, scratch, + ICGetPropCallGetter::offsetOfReceiverGuard(), &failure); + + if (receiver_ != holder_) { + Register holderReg = regs.takeAny(); + + // If we are generating a non-lexical GETGNAME stub, we must also + // guard on the shape of the GlobalObject. + if (kind == ICStub::GetProp_CallNativeGlobal) { + MOZ_ASSERT(receiver_->is() && + receiver_->as().isGlobal()); + GuardGlobalObject(masm, holder_, objReg, holderReg, scratch, + ICGetProp_CallNativeGlobal::offsetOfGlobalShape(), &failure); + } + + masm.loadPtr(Address(ICStubReg, ICGetPropCallGetter::offsetOfHolder()), holderReg); + masm.loadPtr(Address(ICStubReg, ICGetPropCallGetter::offsetOfHolderShape()), scratch); + masm.branchTestObjShape(Assembler::NotEqual, holderReg, scratch, &failure); + regs.add(holderReg); + } + + // Box and push obj onto baseline frame stack for decompiler + if (engine_ == Engine::Baseline) { + if (inputDefinitelyObject_) + masm.tagValue(JSVAL_TYPE_OBJECT, objReg, R0); + EmitStowICValues(masm, 1); + if (inputDefinitelyObject_) + objReg = masm.extractObject(R0, ExtractTemp0); + } + + // Push a stub frame so that we can perform a non-tail call. + enterStubFrame(masm, scratch); + + // Load callee function. + Register callee = regs.takeAny(); + masm.loadPtr(Address(ICStubReg, ICGetPropCallGetter::offsetOfGetter()), callee); + + // If we're calling a getter on the global, inline the logic for the + // 'this' hook on the global lexical scope and manually push the global. + if (kind == ICStub::GetProp_CallNativeGlobal) + masm.extractObject(Address(objReg, ScopeObject::offsetOfEnclosingScope()), objReg); + + // Push args for vm call. + masm.push(objReg); + masm.push(callee); + + regs.add(R0); + + if (!callVM(DoCallNativeGetterInfo, masm)) + return false; + leaveStubFrame(masm); + + if (engine_ == Engine::Baseline) + EmitUnstowICValues(masm, 1, /* discard = */true); + + // Enter type monitor IC to type-check result. + EmitEnterTypeMonitorIC(masm); + + // Failure case - jump to next stub + masm.bind(&failure); + EmitStubGuardFailure(masm); + return true; +} + +ICStub* +ICGetPropCallNativeCompiler::getStub(ICStubSpace* space) +{ + ReceiverGuard guard(receiver_); + Shape* holderShape = holder_->as().lastProperty(); + + switch (kind) { + case ICStub::GetProp_CallNative: + return newStub(space, getStubCode(), firstMonitorStub_, + guard, holder_, holderShape, + getter_, pcOffset_); + + case ICStub::GetProp_CallNativeGlobal: { + Shape* globalShape = receiver_->as().global().lastProperty(); + return newStub(space, getStubCode(), firstMonitorStub_, + guard, holder_, holderShape, globalShape, + getter_, pcOffset_); + } + + default: + MOZ_CRASH("Bad stub kind"); + } +} + +// Callers are expected to have already guarded on the shape of the +// object, which guarantees the object is a DOM proxy. +void +CheckDOMProxyExpandoDoesNotShadow(JSContext* cx, MacroAssembler& masm, Register object, + const Address& checkExpandoShapeAddr, + Address* expandoAndGenerationAddr, + Address* generationAddr, + Register scratch, + AllocatableGeneralRegisterSet& domProxyRegSet, + Label* checkFailed) +{ + // Guard that the object does not have expando properties, or has an expando + // which is known to not have the desired property. + + // For the remaining code, we need to reserve some registers to load a value. + // This is ugly, but unavoidable. + ValueOperand tempVal = domProxyRegSet.takeAnyValue(); + masm.pushValue(tempVal); + + Label failDOMProxyCheck; + Label domProxyOk; + + masm.loadPtr(Address(object, ProxyObject::offsetOfValues()), scratch); + Address expandoAddr(scratch, ProxyObject::offsetOfExtraSlotInValues(GetDOMProxyExpandoSlot())); + + if (expandoAndGenerationAddr) { + MOZ_ASSERT(generationAddr); + + masm.loadPtr(*expandoAndGenerationAddr, tempVal.scratchReg()); + masm.branchPrivatePtr(Assembler::NotEqual, expandoAddr, tempVal.scratchReg(), + &failDOMProxyCheck); + + masm.load32(*generationAddr, scratch); + masm.branch32(Assembler::NotEqual, + Address(tempVal.scratchReg(), offsetof(ExpandoAndGeneration, generation)), + scratch, &failDOMProxyCheck); + + masm.loadValue(Address(tempVal.scratchReg(), 0), tempVal); + } else { + masm.loadValue(expandoAddr, tempVal); + } + + // If the incoming object does not have an expando object then we're sure we're not + // shadowing. + masm.branchTestUndefined(Assembler::Equal, tempVal, &domProxyOk); + + // The reference object used to generate this check may not have had an + // expando object at all, in which case the presence of a non-undefined + // expando value in the incoming object is automatically a failure. + masm.loadPtr(checkExpandoShapeAddr, scratch); + masm.branchPtr(Assembler::Equal, scratch, ImmPtr(nullptr), &failDOMProxyCheck); + + // Otherwise, ensure that the incoming object has an object for its expando value and that + // the shape matches. + masm.branchTestObject(Assembler::NotEqual, tempVal, &failDOMProxyCheck); + Register objReg = masm.extractObject(tempVal, tempVal.scratchReg()); + masm.branchTestObjShape(Assembler::Equal, objReg, scratch, &domProxyOk); + + // Failure case: restore the tempVal registers and jump to failures. + masm.bind(&failDOMProxyCheck); + masm.popValue(tempVal); + masm.jump(checkFailed); + + // Success case: restore the tempval and proceed. + masm.bind(&domProxyOk); + masm.popValue(tempVal); +} + +bool +ICGetPropCallDOMProxyNativeCompiler::generateStubCode(MacroAssembler& masm, + Address* expandoAndGenerationAddr, + Address* generationAddr) +{ + Label failure; + AllocatableGeneralRegisterSet regs(availableGeneralRegs(1)); + Register scratch = regs.takeAnyExcluding(ICTailCallReg); + + // Guard input is an object. + masm.branchTestObject(Assembler::NotEqual, R0, &failure); + + // Unbox. + Register objReg = masm.extractObject(R0, ExtractTemp0); + + // Shape guard. + static const size_t receiverShapeOffset = + ICGetProp_CallDOMProxyNative::offsetOfReceiverGuard() + + HeapReceiverGuard::offsetOfShape(); + masm.loadPtr(Address(ICStubReg, receiverShapeOffset), scratch); + masm.branchTestObjShape(Assembler::NotEqual, objReg, scratch, &failure); + + // Guard that our expando object hasn't started shadowing this property. + { + AllocatableGeneralRegisterSet domProxyRegSet(GeneralRegisterSet::All()); + domProxyRegSet.take(ICStubReg); + domProxyRegSet.take(objReg); + domProxyRegSet.take(scratch); + Address expandoShapeAddr(ICStubReg, ICGetProp_CallDOMProxyNative::offsetOfExpandoShape()); + CheckDOMProxyExpandoDoesNotShadow( + cx, masm, objReg, + expandoShapeAddr, expandoAndGenerationAddr, generationAddr, + scratch, + domProxyRegSet, + &failure); + } + + Register holderReg = regs.takeAny(); + masm.loadPtr(Address(ICStubReg, ICGetProp_CallDOMProxyNative::offsetOfHolder()), + holderReg); + masm.loadPtr(Address(ICStubReg, ICGetProp_CallDOMProxyNative::offsetOfHolderShape()), + scratch); + masm.branchTestObjShape(Assembler::NotEqual, holderReg, scratch, &failure); + regs.add(holderReg); + + // Push a stub frame so that we can perform a non-tail call. + enterStubFrame(masm, scratch); + + // Load callee function. + Register callee = regs.takeAny(); + masm.loadPtr(Address(ICStubReg, ICGetProp_CallDOMProxyNative::offsetOfGetter()), callee); + + // Push args for vm call. + masm.Push(objReg); + masm.Push(callee); + + // Don't have to preserve R0 anymore. + regs.add(R0); + + if (!callVM(DoCallNativeGetterInfo, masm)) + return false; + leaveStubFrame(masm); + + // Enter type monitor IC to type-check result. + EmitEnterTypeMonitorIC(masm); + + // Failure case - jump to next stub + masm.bind(&failure); + EmitStubGuardFailure(masm); + return true; +} + +bool +ICGetPropCallDOMProxyNativeCompiler::generateStubCode(MacroAssembler& masm) +{ + if (kind == ICStub::GetProp_CallDOMProxyNative) + return generateStubCode(masm, nullptr, nullptr); + + Address internalStructAddress(ICStubReg, + ICGetProp_CallDOMProxyWithGenerationNative::offsetOfInternalStruct()); + Address generationAddress(ICStubReg, + ICGetProp_CallDOMProxyWithGenerationNative::offsetOfGeneration()); + return generateStubCode(masm, &internalStructAddress, &generationAddress); +} + +ICStub* +ICGetPropCallDOMProxyNativeCompiler::getStub(ICStubSpace* space) +{ + RootedShape shape(cx, proxy_->maybeShape()); + RootedShape holderShape(cx, holder_->as().lastProperty()); + + Value expandoSlot = GetProxyExtra(proxy_, GetDOMProxyExpandoSlot()); + RootedShape expandoShape(cx, nullptr); + ExpandoAndGeneration* expandoAndGeneration; + int32_t generation; + Value expandoVal; + if (kind == ICStub::GetProp_CallDOMProxyNative) { + expandoVal = expandoSlot; + expandoAndGeneration = nullptr; // initialize to silence GCC warning + generation = 0; // initialize to silence GCC warning + } else { + MOZ_ASSERT(kind == ICStub::GetProp_CallDOMProxyWithGenerationNative); + MOZ_ASSERT(!expandoSlot.isObject() && !expandoSlot.isUndefined()); + expandoAndGeneration = (ExpandoAndGeneration*)expandoSlot.toPrivate(); + expandoVal = expandoAndGeneration->expando; + generation = expandoAndGeneration->generation; + } + + if (expandoVal.isObject()) + expandoShape = expandoVal.toObject().as().lastProperty(); + + if (kind == ICStub::GetProp_CallDOMProxyNative) { + return newStub( + space, getStubCode(), firstMonitorStub_, shape, + expandoShape, holder_, holderShape, getter_, pcOffset_); + } + + return newStub( + space, getStubCode(), firstMonitorStub_, shape, + expandoAndGeneration, generation, expandoShape, holder_, holderShape, getter_, + pcOffset_); +} + +ICStub* +ICGetProp_DOMProxyShadowed::Compiler::getStub(ICStubSpace* space) +{ + RootedShape shape(cx, proxy_->maybeShape()); + return New(cx, space, getStubCode(), firstMonitorStub_, shape, + proxy_->handler(), name_, pcOffset_); +} + +static bool +ProxyGet(JSContext* cx, HandleObject proxy, HandlePropertyName name, MutableHandleValue vp) +{ + RootedValue receiver(cx, ObjectValue(*proxy)); + RootedId id(cx, NameToId(name)); + return Proxy::get(cx, proxy, receiver, id, vp); +} + +typedef bool (*ProxyGetFn)(JSContext* cx, HandleObject proxy, HandlePropertyName name, + MutableHandleValue vp); +static const VMFunction ProxyGetInfo = FunctionInfo(ProxyGet); + +bool +ICGetProp_DOMProxyShadowed::Compiler::generateStubCode(MacroAssembler& masm) +{ + Label failure; + + AllocatableGeneralRegisterSet regs(availableGeneralRegs(1)); + // Need to reserve a scratch register, but the scratch register should not be + // ICTailCallReg, because it's used for |enterStubFrame| which needs a + // non-ICTailCallReg scratch reg. + Register scratch = regs.takeAnyExcluding(ICTailCallReg); + + // Guard input is an object. + masm.branchTestObject(Assembler::NotEqual, R0, &failure); + + // Unbox. + Register objReg = masm.extractObject(R0, ExtractTemp0); + + // Shape guard. + masm.loadPtr(Address(ICStubReg, ICGetProp_DOMProxyShadowed::offsetOfShape()), scratch); + masm.branchTestObjShape(Assembler::NotEqual, objReg, scratch, &failure); + + // No need to do any more guards; it's safe to call ProxyGet even + // if we've since stopped shadowing. + + // Call ProxyGet(JSContext* cx, HandleObject proxy, HandlePropertyName name, MutableHandleValue vp); + + // Push a stub frame so that we can perform a non-tail call. + enterStubFrame(masm, scratch); + + // Push property name and proxy object. + masm.loadPtr(Address(ICStubReg, ICGetProp_DOMProxyShadowed::offsetOfName()), scratch); + masm.Push(scratch); + masm.Push(objReg); + + // Don't have to preserve R0 anymore. + regs.add(R0); + + if (!callVM(ProxyGetInfo, masm)) + return false; + leaveStubFrame(masm); + + // Enter type monitor IC to type-check result. + EmitEnterTypeMonitorIC(masm); + + // Failure case - jump to next stub + masm.bind(&failure); + EmitStubGuardFailure(masm); + return true; +} + +bool +ICGetProp_ArgumentsLength::Compiler::generateStubCode(MacroAssembler& masm) +{ + Label failure; + if (which_ == ICGetProp_ArgumentsLength::Magic) { + // Ensure that this is lazy arguments. + masm.branchTestMagicValue(Assembler::NotEqual, R0, JS_OPTIMIZED_ARGUMENTS, &failure); + + // Ensure that frame has not loaded different arguments object since. + masm.branchTest32(Assembler::NonZero, + Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfFlags()), + Imm32(BaselineFrame::HAS_ARGS_OBJ), + &failure); + + Address actualArgs(BaselineFrameReg, BaselineFrame::offsetOfNumActualArgs()); + masm.loadPtr(actualArgs, R0.scratchReg()); + masm.tagValue(JSVAL_TYPE_INT32, R0.scratchReg(), R0); + EmitReturnFromIC(masm); + + masm.bind(&failure); + EmitStubGuardFailure(masm); + return true; + } + MOZ_ASSERT(which_ == ICGetProp_ArgumentsLength::Mapped || + which_ == ICGetProp_ArgumentsLength::Unmapped); + + const Class* clasp = (which_ == ICGetProp_ArgumentsLength::Mapped) + ? &MappedArgumentsObject::class_ + : &UnmappedArgumentsObject::class_; + + Register scratchReg = R1.scratchReg(); + + // Guard on input being an arguments object. + masm.branchTestObject(Assembler::NotEqual, R0, &failure); + Register objReg = masm.extractObject(R0, ExtractTemp0); + masm.branchTestObjClass(Assembler::NotEqual, objReg, scratchReg, clasp, &failure); + + // Get initial length value. + masm.unboxInt32(Address(objReg, ArgumentsObject::getInitialLengthSlotOffset()), scratchReg); + + // Test if length has been overridden. + masm.branchTest32(Assembler::NonZero, + scratchReg, + Imm32(ArgumentsObject::LENGTH_OVERRIDDEN_BIT), + &failure); + + // Nope, shift out arguments length and return it. + // No need to type monitor because this stub always returns Int32. + masm.rshiftPtr(Imm32(ArgumentsObject::PACKED_BITS_COUNT), scratchReg); + masm.tagValue(JSVAL_TYPE_INT32, scratchReg, R0); + EmitReturnFromIC(masm); + + masm.bind(&failure); + EmitStubGuardFailure(masm); + return true; +} + +ICGetProp_ArgumentsCallee::ICGetProp_ArgumentsCallee(JitCode* stubCode, ICStub* firstMonitorStub) + : ICMonitoredStub(GetProp_ArgumentsCallee, stubCode, firstMonitorStub) +{ } + +bool +ICGetProp_ArgumentsCallee::Compiler::generateStubCode(MacroAssembler& masm) +{ + Label failure; + + // Ensure that this is lazy arguments. + masm.branchTestMagicValue(Assembler::NotEqual, R0, JS_OPTIMIZED_ARGUMENTS, &failure); + + // Ensure that frame has not loaded different arguments object since. + masm.branchTest32(Assembler::NonZero, + Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfFlags()), + Imm32(BaselineFrame::HAS_ARGS_OBJ), + &failure); + + Address callee(BaselineFrameReg, BaselineFrame::offsetOfCalleeToken()); + masm.loadFunctionFromCalleeToken(callee, R0.scratchReg()); + masm.tagValue(JSVAL_TYPE_OBJECT, R0.scratchReg(), R0); + + EmitEnterTypeMonitorIC(masm); + + masm.bind(&failure); + EmitStubGuardFailure(masm); + return true; +} + +/* static */ ICGetProp_Generic* +ICGetProp_Generic::Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub, + ICGetProp_Generic& other) +{ + return New(cx, space, other.jitCode(), firstMonitorStub); +} + +static bool +DoGetPropGeneric(JSContext* cx, BaselineFrame* frame, ICGetProp_Generic* stub, + MutableHandleValue val, MutableHandleValue res) +{ + ICFallbackStub* fallback = stub->getChainFallback(); + RootedScript script(cx, SharedStubScript(frame, fallback)); + jsbytecode* pc = fallback->icEntry()->pc(script); + JSOp op = JSOp(*pc); + RootedPropertyName name(cx, script->getName(pc)); + return ComputeGetPropResult(cx, frame, op, name, val, res); +} + +typedef bool (*DoGetPropGenericFn)(JSContext*, BaselineFrame*, ICGetProp_Generic*, MutableHandleValue, MutableHandleValue); +static const VMFunction DoGetPropGenericInfo = FunctionInfo(DoGetPropGeneric); + +bool +ICGetProp_Generic::Compiler::generateStubCode(MacroAssembler& masm) +{ + AllocatableGeneralRegisterSet regs(availableGeneralRegs(1)); + + Register scratch = regs.takeAnyExcluding(ICTailCallReg); + + // Sync for the decompiler. + if (engine_ == Engine::Baseline) + EmitStowICValues(masm, 1); + + enterStubFrame(masm, scratch); + + // Push arguments. + masm.Push(R0); + masm.Push(ICStubReg); + PushFramePtr(masm, R0.scratchReg()); + + if (!callVM(DoGetPropGenericInfo, masm)) + return false; + + leaveStubFrame(masm); + + if (engine_ == Engine::Baseline) + EmitUnstowICValues(masm, 1, /* discard = */ true); + + EmitEnterTypeMonitorIC(masm); + return true; +} + +bool +ICGetProp_Unboxed::Compiler::generateStubCode(MacroAssembler& masm) +{ + Label failure; + + AllocatableGeneralRegisterSet regs(availableGeneralRegs(1)); + + Register scratch = regs.takeAnyExcluding(ICTailCallReg); + + // Object and group guard. + masm.branchTestObject(Assembler::NotEqual, R0, &failure); + Register object = masm.extractObject(R0, ExtractTemp0); + masm.loadPtr(Address(ICStubReg, ICGetProp_Unboxed::offsetOfGroup()), scratch); + masm.branchPtr(Assembler::NotEqual, Address(object, JSObject::offsetOfGroup()), scratch, + &failure); + + // Get the address being read from. + masm.load32(Address(ICStubReg, ICGetProp_Unboxed::offsetOfFieldOffset()), scratch); + + masm.loadUnboxedProperty(BaseIndex(object, scratch, TimesOne), fieldType_, TypedOrValueRegister(R0)); + + // Only monitor the result if its type might change. + if (fieldType_ == JSVAL_TYPE_OBJECT) + EmitEnterTypeMonitorIC(masm); + else + EmitReturnFromIC(masm); + + masm.bind(&failure); + EmitStubGuardFailure(masm); + + return true; +} + +void +CheckForNeuteredTypedObject(JSContext* cx, MacroAssembler& masm, Label* failure) +{ + // All stubs which manipulate typed objects need to check the compartment + // wide flag indicating whether the objects are neutered, and bail out in + // this case. + int32_t* address = &cx->compartment()->neuteredTypedObjects; + masm.branch32(Assembler::NotEqual, AbsoluteAddress(address), Imm32(0), failure); +} + +void +LoadTypedThingData(MacroAssembler& masm, TypedThingLayout layout, Register obj, Register result) +{ + switch (layout) { + case Layout_TypedArray: + masm.loadPtr(Address(obj, TypedArrayLayout::dataOffset()), result); + break; + case Layout_OutlineTypedObject: + masm.loadPtr(Address(obj, OutlineTypedObject::offsetOfData()), result); + break; + case Layout_InlineTypedObject: + masm.computeEffectiveAddress(Address(obj, InlineTypedObject::offsetOfDataStart()), result); + break; + default: + MOZ_CRASH(); + } +} + +bool +ICGetProp_TypedObject::Compiler::generateStubCode(MacroAssembler& masm) +{ + Label failure; + + CheckForNeuteredTypedObject(cx, masm, &failure); + + AllocatableGeneralRegisterSet regs(availableGeneralRegs(1)); + + Register scratch1 = regs.takeAnyExcluding(ICTailCallReg); + Register scratch2 = regs.takeAnyExcluding(ICTailCallReg); + + // Object and shape guard. + masm.branchTestObject(Assembler::NotEqual, R0, &failure); + Register object = masm.extractObject(R0, ExtractTemp0); + masm.loadPtr(Address(ICStubReg, ICGetProp_TypedObject::offsetOfShape()), scratch1); + masm.branchTestObjShape(Assembler::NotEqual, object, scratch1, &failure); + + // Get the object's data pointer. + LoadTypedThingData(masm, layout_, object, scratch1); + + // Get the address being written to. + masm.load32(Address(ICStubReg, ICGetProp_TypedObject::offsetOfFieldOffset()), scratch2); + masm.addPtr(scratch2, scratch1); + + // Only monitor the result if the type produced by this stub might vary. + bool monitorLoad; + + if (fieldDescr_->is()) { + Scalar::Type type = fieldDescr_->as().type(); + monitorLoad = type == Scalar::Uint32; + + masm.loadFromTypedArray(type, Address(scratch1, 0), R0, /* allowDouble = */ true, + scratch2, nullptr); + } else { + ReferenceTypeDescr::Type type = fieldDescr_->as().type(); + monitorLoad = type != ReferenceTypeDescr::TYPE_STRING; + + switch (type) { + case ReferenceTypeDescr::TYPE_ANY: + masm.loadValue(Address(scratch1, 0), R0); + break; + + case ReferenceTypeDescr::TYPE_OBJECT: { + Label notNull, done; + masm.loadPtr(Address(scratch1, 0), scratch1); + masm.branchTestPtr(Assembler::NonZero, scratch1, scratch1, ¬Null); + masm.moveValue(NullValue(), R0); + masm.jump(&done); + masm.bind(¬Null); + masm.tagValue(JSVAL_TYPE_OBJECT, scratch1, R0); + masm.bind(&done); + break; + } + + case ReferenceTypeDescr::TYPE_STRING: + masm.loadPtr(Address(scratch1, 0), scratch1); + masm.tagValue(JSVAL_TYPE_STRING, scratch1, R0); + break; + + default: + MOZ_CRASH(); + } + } + + if (monitorLoad) + EmitEnterTypeMonitorIC(masm); + else + EmitReturnFromIC(masm); + + masm.bind(&failure); + EmitStubGuardFailure(masm); + + return true; +} + +void +BaselineScript::noteAccessedGetter(uint32_t pcOffset) +{ + ICEntry& entry = icEntryFromPCOffset(pcOffset); + ICFallbackStub* stub = entry.fallbackStub(); + + if (stub->isGetProp_Fallback()) + stub->toGetProp_Fallback()->noteAccessedGetter(); +} + +ICGetProp_Primitive::ICGetProp_Primitive(JitCode* stubCode, ICStub* firstMonitorStub, + JSValueType primitiveType, Shape* protoShape, + uint32_t offset) + : ICMonitoredStub(GetProp_Primitive, stubCode, firstMonitorStub), + protoShape_(protoShape), + offset_(offset) +{ + extra_ = uint16_t(primitiveType); + MOZ_ASSERT(JSValueType(extra_) == primitiveType); +} + +ICGetPropNativeStub::ICGetPropNativeStub(ICStub::Kind kind, JitCode* stubCode, + ICStub* firstMonitorStub, + ReceiverGuard guard, uint32_t offset) + : ICMonitoredStub(kind, stubCode, firstMonitorStub), + receiverGuard_(guard), + offset_(offset) +{ } + +/* static */ ICGetProp_Native* +ICGetProp_Native::Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub, + ICGetProp_Native& other) +{ + return New(cx, space, other.jitCode(), firstMonitorStub, + other.receiverGuard(), other.offset()); +} + +ICGetPropNativePrototypeStub::ICGetPropNativePrototypeStub(ICStub::Kind kind, JitCode* stubCode, + ICStub* firstMonitorStub, + ReceiverGuard guard, uint32_t offset, + JSObject* holder, Shape* holderShape) + : ICGetPropNativeStub(kind, stubCode, firstMonitorStub, guard, offset), + holder_(holder), + holderShape_(holderShape) +{ } + +/* static */ ICGetProp_NativePrototype* +ICGetProp_NativePrototype::Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub, + ICGetProp_NativePrototype& other) +{ + return New(cx, space, other.jitCode(), firstMonitorStub, + other.receiverGuard(), other.offset(), + other.holder(), other.holderShape()); +} + +ICGetProp_NativeDoesNotExist::ICGetProp_NativeDoesNotExist( + JitCode* stubCode, ICStub* firstMonitorStub, ReceiverGuard guard, + size_t protoChainDepth) + : ICMonitoredStub(GetProp_NativeDoesNotExist, stubCode, firstMonitorStub), + guard_(guard) +{ + MOZ_ASSERT(protoChainDepth <= MAX_PROTO_CHAIN_DEPTH); + extra_ = protoChainDepth; +} + +/* static */ size_t +ICGetProp_NativeDoesNotExist::offsetOfShape(size_t idx) +{ + MOZ_ASSERT(ICGetProp_NativeDoesNotExistImpl<0>::offsetOfShape(idx) == + ICGetProp_NativeDoesNotExistImpl< + ICGetProp_NativeDoesNotExist::MAX_PROTO_CHAIN_DEPTH>::offsetOfShape(idx)); + return ICGetProp_NativeDoesNotExistImpl<0>::offsetOfShape(idx); +} + +template +ICGetProp_NativeDoesNotExistImpl::ICGetProp_NativeDoesNotExistImpl( + JitCode* stubCode, ICStub* firstMonitorStub, ReceiverGuard guard, + Handle shapes) + : ICGetProp_NativeDoesNotExist(stubCode, firstMonitorStub, guard, ProtoChainDepth) +{ + MOZ_ASSERT(shapes.length() == NumShapes); + + // Note: using int32_t here to avoid gcc warning. + for (int32_t i = 0; i < int32_t(NumShapes); i++) + shapes_[i].init(shapes[i]); +} + +ICGetPropNativeDoesNotExistCompiler::ICGetPropNativeDoesNotExistCompiler( + JSContext* cx, ICStubCompiler::Engine engine, ICStub* firstMonitorStub, + HandleObject obj, size_t protoChainDepth) + : ICStubCompiler(cx, ICStub::GetProp_NativeDoesNotExist, engine), + firstMonitorStub_(firstMonitorStub), + obj_(cx, obj), + protoChainDepth_(protoChainDepth) +{ + MOZ_ASSERT(protoChainDepth_ <= ICGetProp_NativeDoesNotExist::MAX_PROTO_CHAIN_DEPTH); +} + +ICGetPropCallGetter::ICGetPropCallGetter(Kind kind, JitCode* stubCode, ICStub* firstMonitorStub, + ReceiverGuard receiverGuard, JSObject* holder, + Shape* holderShape, JSFunction* getter, + uint32_t pcOffset) + : ICMonitoredStub(kind, stubCode, firstMonitorStub), + receiverGuard_(receiverGuard), + holder_(holder), + holderShape_(holderShape), + getter_(getter), + pcOffset_(pcOffset) +{ + MOZ_ASSERT(kind == ICStub::GetProp_CallScripted || + kind == ICStub::GetProp_CallNative || + kind == ICStub::GetProp_CallNativeGlobal || + kind == ICStub::GetProp_CallDOMProxyNative || + kind == ICStub::GetProp_CallDOMProxyWithGenerationNative); +} + +/* static */ ICGetProp_CallScripted* +ICGetProp_CallScripted::Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub, + ICGetProp_CallScripted& other) +{ + return New(cx, space, other.jitCode(), firstMonitorStub, + other.receiverGuard(), + other.holder_, other.holderShape_, + other.getter_, other.pcOffset_); +} + +/* static */ ICGetProp_CallNative* +ICGetProp_CallNative::Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub, + ICGetProp_CallNative& other) +{ + return New(cx, space, other.jitCode(), firstMonitorStub, + other.receiverGuard(), other.holder_, + other.holderShape_, other.getter_, other.pcOffset_); +} + +/* static */ ICGetProp_CallNativeGlobal* +ICGetProp_CallNativeGlobal::Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub, + ICGetProp_CallNativeGlobal& other) +{ + return New(cx, space, other.jitCode(), firstMonitorStub, + other.receiverGuard(), other.holder_, + other.holderShape_, other.globalShape_, + other.getter_, other.pcOffset_); +} + +ICGetPropCallDOMProxyNativeStub::ICGetPropCallDOMProxyNativeStub(Kind kind, JitCode* stubCode, + ICStub* firstMonitorStub, + Shape* shape, + Shape* expandoShape, + JSObject* holder, + Shape* holderShape, + JSFunction* getter, + uint32_t pcOffset) + : ICGetPropCallGetter(kind, stubCode, firstMonitorStub, ReceiverGuard(nullptr, shape), + holder, holderShape, getter, pcOffset), + expandoShape_(expandoShape) +{ } + +ICGetPropCallDOMProxyNativeCompiler::ICGetPropCallDOMProxyNativeCompiler(JSContext* cx, + ICStub::Kind kind, + ICStubCompiler::Engine engine, + ICStub* firstMonitorStub, + Handle proxy, + HandleObject holder, + HandleFunction getter, + uint32_t pcOffset) + : ICStubCompiler(cx, kind, engine), + firstMonitorStub_(firstMonitorStub), + proxy_(cx, proxy), + holder_(cx, holder), + getter_(cx, getter), + pcOffset_(pcOffset) +{ + MOZ_ASSERT(kind == ICStub::GetProp_CallDOMProxyNative || + kind == ICStub::GetProp_CallDOMProxyWithGenerationNative); + MOZ_ASSERT(proxy_->handler()->family() == GetDOMProxyHandlerFamily()); +} + +/* static */ ICGetProp_CallDOMProxyNative* +ICGetProp_CallDOMProxyNative::Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub, + ICGetProp_CallDOMProxyNative& other) +{ + return New(cx, space, other.jitCode(), firstMonitorStub, + other.receiverGuard_.shape(), other.expandoShape_, + other.holder_, other.holderShape_, other.getter_, + other.pcOffset_); +} + +/* static */ ICGetProp_CallDOMProxyWithGenerationNative* +ICGetProp_CallDOMProxyWithGenerationNative::Clone(JSContext* cx, + ICStubSpace* space, + ICStub* firstMonitorStub, + ICGetProp_CallDOMProxyWithGenerationNative& other) +{ + return New(cx, space, other.jitCode(), + firstMonitorStub, + other.receiverGuard_.shape(), + other.expandoAndGeneration_, + other.generation_, + other.expandoShape_, other.holder_, + other.holderShape_, other.getter_, + other.pcOffset_); +} + +ICGetProp_DOMProxyShadowed::ICGetProp_DOMProxyShadowed(JitCode* stubCode, + ICStub* firstMonitorStub, + Shape* shape, + const BaseProxyHandler* proxyHandler, + PropertyName* name, + uint32_t pcOffset) + : ICMonitoredStub(ICStub::GetProp_DOMProxyShadowed, stubCode, firstMonitorStub), + shape_(shape), + proxyHandler_(proxyHandler), + name_(name), + pcOffset_(pcOffset) +{ } + +/* static */ ICGetProp_DOMProxyShadowed* +ICGetProp_DOMProxyShadowed::Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub, + ICGetProp_DOMProxyShadowed& other) +{ + return New(cx, space, other.jitCode(), firstMonitorStub, + other.shape_, other.proxyHandler_, other.name_, + other.pcOffset_); +} + +// +// TypeMonitor_Fallback +// + +bool +ICTypeMonitor_Fallback::addMonitorStubForValue(JSContext* cx, JSScript* script, HandleValue val, ICStubCompiler::Engine engine) +{ + bool wasDetachedMonitorChain = lastMonitorStubPtrAddr_ == nullptr; + MOZ_ASSERT_IF(wasDetachedMonitorChain, numOptimizedMonitorStubs_ == 0); + + if (numOptimizedMonitorStubs_ >= MAX_OPTIMIZED_STUBS) { + // TODO: if the TypeSet becomes unknown or has the AnyObject type, + // replace stubs with a single stub to handle these. + return true; + } + + if (val.isPrimitive()) { + if (val.isMagic(JS_UNINITIALIZED_LEXICAL)) + return true; + MOZ_ASSERT(!val.isMagic()); + JSValueType type = val.isDouble() ? JSVAL_TYPE_DOUBLE : val.extractNonDoubleType(); + + // Check for existing TypeMonitor stub. + ICTypeMonitor_PrimitiveSet* existingStub = nullptr; + for (ICStubConstIterator iter(firstMonitorStub()); !iter.atEnd(); iter++) { + if (iter->isTypeMonitor_PrimitiveSet()) { + existingStub = iter->toTypeMonitor_PrimitiveSet(); + if (existingStub->containsType(type)) + return true; + } + } + + ICTypeMonitor_PrimitiveSet::Compiler compiler(cx, engine, existingStub, type); + ICStub* stub = existingStub ? compiler.updateStub() + : compiler.getStub(compiler.getStubSpace(script)); + if (!stub) { + ReportOutOfMemory(cx); + return false; + } + + JitSpew(JitSpew_BaselineIC, " %s TypeMonitor stub %p for primitive type %d", + existingStub ? "Modified existing" : "Created new", stub, type); + + if (!existingStub) { + MOZ_ASSERT(!hasStub(TypeMonitor_PrimitiveSet)); + addOptimizedMonitorStub(stub); + } + + } else if (val.toObject().isSingleton()) { + RootedObject obj(cx, &val.toObject()); + + // Check for existing TypeMonitor stub. + for (ICStubConstIterator iter(firstMonitorStub()); !iter.atEnd(); iter++) { + if (iter->isTypeMonitor_SingleObject() && + iter->toTypeMonitor_SingleObject()->object() == obj) + { + return true; + } + } + + ICTypeMonitor_SingleObject::Compiler compiler(cx, obj); + ICStub* stub = compiler.getStub(compiler.getStubSpace(script)); + if (!stub) { + ReportOutOfMemory(cx); + return false; + } + + JitSpew(JitSpew_BaselineIC, " Added TypeMonitor stub %p for singleton %p", + stub, obj.get()); + + addOptimizedMonitorStub(stub); + + } else { + RootedObjectGroup group(cx, val.toObject().group()); + + // Check for existing TypeMonitor stub. + for (ICStubConstIterator iter(firstMonitorStub()); !iter.atEnd(); iter++) { + if (iter->isTypeMonitor_ObjectGroup() && + iter->toTypeMonitor_ObjectGroup()->group() == group) + { + return true; + } + } + + ICTypeMonitor_ObjectGroup::Compiler compiler(cx, group); + ICStub* stub = compiler.getStub(compiler.getStubSpace(script)); + if (!stub) { + ReportOutOfMemory(cx); + return false; + } + + JitSpew(JitSpew_BaselineIC, " Added TypeMonitor stub %p for ObjectGroup %p", + stub, group.get()); + + addOptimizedMonitorStub(stub); + } + + bool firstMonitorStubAdded = wasDetachedMonitorChain && (numOptimizedMonitorStubs_ > 0); + + if (firstMonitorStubAdded) { + // Was an empty monitor chain before, but a new stub was added. This is the + // only time that any main stubs' firstMonitorStub fields need to be updated to + // refer to the newly added monitor stub. + ICStub* firstStub = mainFallbackStub_->icEntry()->firstStub(); + for (ICStubConstIterator iter(firstStub); !iter.atEnd(); iter++) { + // Non-monitored stubs are used if the result has always the same type, + // e.g. a StringLength stub will always return int32. + if (!iter->isMonitored()) + continue; + + // Since we just added the first optimized monitoring stub, any + // existing main stub's |firstMonitorStub| MUST be pointing to the fallback + // monitor stub (i.e. this stub). + MOZ_ASSERT(iter->toMonitoredStub()->firstMonitorStub() == this); + iter->toMonitoredStub()->updateFirstMonitorStub(firstMonitorStub_); + } + } + + return true; +} + +static bool +DoTypeMonitorFallback(JSContext* cx, BaselineFrame* frame, ICTypeMonitor_Fallback* stub, + HandleValue value, MutableHandleValue res) +{ + // It's possible that we arrived here from bailing out of Ion, and that + // Ion proved that the value is dead and optimized out. In such cases, do + // nothing. However, it's also possible that we have an uninitialized this, + // in which case we should not look for other magic values. + if (stub->monitorsThis()) { + MOZ_ASSERT_IF(value.isMagic(), value.isMagic(JS_UNINITIALIZED_LEXICAL)); + } else { + if (value.isMagic(JS_OPTIMIZED_OUT)) { + res.set(value); + return true; + } + } + + ICStubCompiler::Engine engine = SharedStubEngine(frame); + RootedScript script(cx, SharedStubScript(frame, stub)); + jsbytecode* pc = stub->icEntry()->pc(script); + TypeFallbackICSpew(cx, stub, "TypeMonitor"); + + uint32_t argument; + if (stub->monitorsThis()) { + MOZ_ASSERT(pc == script->code()); + if (value.isMagic(JS_UNINITIALIZED_LEXICAL)) + TypeScript::SetThis(cx, script, TypeSet::UnknownType()); + else + TypeScript::SetThis(cx, script, value); + } else if (stub->monitorsArgument(&argument)) { + MOZ_ASSERT(pc == script->code()); + TypeScript::SetArgument(cx, script, argument, value); + } else { + TypeScript::Monitor(cx, script, pc, value); + } + + if (!stub->addMonitorStubForValue(cx, script, value, engine)) + return false; + + // Copy input value to res. + res.set(value); + return true; +} + +typedef bool (*DoTypeMonitorFallbackFn)(JSContext*, BaselineFrame*, ICTypeMonitor_Fallback*, + HandleValue, MutableHandleValue); +static const VMFunction DoTypeMonitorFallbackInfo = + FunctionInfo(DoTypeMonitorFallback, TailCall); + +bool +ICTypeMonitor_Fallback::Compiler::generateStubCode(MacroAssembler& masm) +{ + MOZ_ASSERT(R0 == JSReturnOperand); + + // Restore the tail call register. + EmitRestoreTailCallReg(masm); + + masm.pushValue(R0); + masm.push(ICStubReg); + pushFramePtr(masm, R0.scratchReg()); + + return tailCallVM(DoTypeMonitorFallbackInfo, masm); +} + +bool +ICTypeMonitor_PrimitiveSet::Compiler::generateStubCode(MacroAssembler& masm) +{ + Label success; + if ((flags_ & TypeToFlag(JSVAL_TYPE_INT32)) && !(flags_ & TypeToFlag(JSVAL_TYPE_DOUBLE))) + masm.branchTestInt32(Assembler::Equal, R0, &success); + + if (flags_ & TypeToFlag(JSVAL_TYPE_DOUBLE)) + masm.branchTestNumber(Assembler::Equal, R0, &success); + + if (flags_ & TypeToFlag(JSVAL_TYPE_UNDEFINED)) + masm.branchTestUndefined(Assembler::Equal, R0, &success); + + if (flags_ & TypeToFlag(JSVAL_TYPE_BOOLEAN)) + masm.branchTestBoolean(Assembler::Equal, R0, &success); + + if (flags_ & TypeToFlag(JSVAL_TYPE_STRING)) + masm.branchTestString(Assembler::Equal, R0, &success); + + if (flags_ & TypeToFlag(JSVAL_TYPE_SYMBOL)) + masm.branchTestSymbol(Assembler::Equal, R0, &success); + + // Currently, we will never generate primitive stub checks for object. However, + // when we do get to the point where we want to collapse our monitor chains of + // objects and singletons down (when they get too long) to a generic "any object" + // in coordination with the typeset doing the same thing, this will need to + // be re-enabled. + /* + if (flags_ & TypeToFlag(JSVAL_TYPE_OBJECT)) + masm.branchTestObject(Assembler::Equal, R0, &success); + */ + MOZ_ASSERT(!(flags_ & TypeToFlag(JSVAL_TYPE_OBJECT))); + + if (flags_ & TypeToFlag(JSVAL_TYPE_NULL)) + masm.branchTestNull(Assembler::Equal, R0, &success); + + EmitStubGuardFailure(masm); + + masm.bind(&success); + EmitReturnFromIC(masm); + return true; +} + +bool +ICTypeMonitor_SingleObject::Compiler::generateStubCode(MacroAssembler& masm) +{ + Label failure; + masm.branchTestObject(Assembler::NotEqual, R0, &failure); + + // Guard on the object's identity. + Register obj = masm.extractObject(R0, ExtractTemp0); + Address expectedObject(ICStubReg, ICTypeMonitor_SingleObject::offsetOfObject()); + masm.branchPtr(Assembler::NotEqual, expectedObject, obj, &failure); + + EmitReturnFromIC(masm); + + masm.bind(&failure); + EmitStubGuardFailure(masm); + return true; +} + +bool +ICTypeMonitor_ObjectGroup::Compiler::generateStubCode(MacroAssembler& masm) +{ + Label failure; + masm.branchTestObject(Assembler::NotEqual, R0, &failure); + + // Guard on the object's ObjectGroup. + Register obj = masm.extractObject(R0, ExtractTemp0); + masm.loadPtr(Address(obj, JSObject::offsetOfGroup()), R1.scratchReg()); + + Address expectedGroup(ICStubReg, ICTypeMonitor_ObjectGroup::offsetOfGroup()); + masm.branchPtr(Assembler::NotEqual, expectedGroup, R1.scratchReg(), &failure); + + EmitReturnFromIC(masm); + + masm.bind(&failure); + EmitStubGuardFailure(masm); + return true; +} + +bool +ICUpdatedStub::addUpdateStubForValue(JSContext* cx, HandleScript script, HandleObject obj, + HandleId id, HandleValue val) +{ + if (numOptimizedStubs_ >= MAX_OPTIMIZED_STUBS) { + // TODO: if the TypeSet becomes unknown or has the AnyObject type, + // replace stubs with a single stub to handle these. + return true; + } + + EnsureTrackPropertyTypes(cx, obj, id); + + // Make sure that undefined values are explicitly included in the property + // types for an object if generating a stub to write an undefined value. + if (val.isUndefined() && CanHaveEmptyPropertyTypesForOwnProperty(obj)) + AddTypePropertyId(cx, obj, id, val); + + if (val.isPrimitive()) { + JSValueType type = val.isDouble() ? JSVAL_TYPE_DOUBLE : val.extractNonDoubleType(); + + // Check for existing TypeUpdate stub. + ICTypeUpdate_PrimitiveSet* existingStub = nullptr; + for (ICStubConstIterator iter(firstUpdateStub_); !iter.atEnd(); iter++) { + if (iter->isTypeUpdate_PrimitiveSet()) { + existingStub = iter->toTypeUpdate_PrimitiveSet(); + if (existingStub->containsType(type)) + return true; + } + } + + ICTypeUpdate_PrimitiveSet::Compiler compiler(cx, existingStub, type); + ICStub* stub = existingStub ? compiler.updateStub() + : compiler.getStub(compiler.getStubSpace(script)); + if (!stub) + return false; + if (!existingStub) { + MOZ_ASSERT(!hasTypeUpdateStub(TypeUpdate_PrimitiveSet)); + addOptimizedUpdateStub(stub); + } + + JitSpew(JitSpew_BaselineIC, " %s TypeUpdate stub %p for primitive type %d", + existingStub ? "Modified existing" : "Created new", stub, type); + + } else if (val.toObject().isSingleton()) { + RootedObject obj(cx, &val.toObject()); + + // Check for existing TypeUpdate stub. + for (ICStubConstIterator iter(firstUpdateStub_); !iter.atEnd(); iter++) { + if (iter->isTypeUpdate_SingleObject() && + iter->toTypeUpdate_SingleObject()->object() == obj) + { + return true; + } + } + + ICTypeUpdate_SingleObject::Compiler compiler(cx, obj); + ICStub* stub = compiler.getStub(compiler.getStubSpace(script)); + if (!stub) + return false; + + JitSpew(JitSpew_BaselineIC, " Added TypeUpdate stub %p for singleton %p", stub, obj.get()); + + addOptimizedUpdateStub(stub); + + } else { + RootedObjectGroup group(cx, val.toObject().group()); + + // Check for existing TypeUpdate stub. + for (ICStubConstIterator iter(firstUpdateStub_); !iter.atEnd(); iter++) { + if (iter->isTypeUpdate_ObjectGroup() && + iter->toTypeUpdate_ObjectGroup()->group() == group) + { + return true; + } + } + + ICTypeUpdate_ObjectGroup::Compiler compiler(cx, group); + ICStub* stub = compiler.getStub(compiler.getStubSpace(script)); + if (!stub) + return false; + + JitSpew(JitSpew_BaselineIC, " Added TypeUpdate stub %p for ObjectGroup %p", + stub, group.get()); + + addOptimizedUpdateStub(stub); + } + + return true; +} + } // namespace jit } // namespace js diff --git a/js/src/jit/SharedIC.h b/js/src/jit/SharedIC.h index 3cad42885b98..febe5431469c 100644 --- a/js/src/jit/SharedIC.h +++ b/js/src/jit/SharedIC.h @@ -16,10 +16,14 @@ #include "jit/MacroAssembler.h" #include "jit/SharedICList.h" #include "jit/SharedICRegisters.h" +#include "vm/ReceiverGuard.h" +#include "vm/TypedArrayCommon.h" namespace js { namespace jit { +class AutoShapeVector; + // // Baseline Inline Caches are polymorphic caches that aggressively // share their stub code. @@ -867,30 +871,6 @@ class ICMonitoredStub : public ICStub } }; -// Monitored fallback stubs - as the name implies. -class ICMonitoredFallbackStub : public ICFallbackStub -{ - protected: - // Pointer to the fallback monitor stub. - ICTypeMonitor_Fallback* fallbackMonitorStub_; - - ICMonitoredFallbackStub(Kind kind, JitCode* stubCode) - : ICFallbackStub(kind, ICStub::MonitoredFallback, stubCode), - fallbackMonitorStub_(nullptr) {} - - public: - bool initMonitoringChain(JSContext* cx, ICStubSpace* space); - bool addMonitorStubForValue(JSContext* cx, JSScript* script, HandleValue val); - - inline ICTypeMonitor_Fallback* fallbackMonitorStub() const { - return fallbackMonitorStub_; - } - - static inline size_t offsetOfFallbackMonitorStub() { - return offsetof(ICMonitoredFallbackStub, fallbackMonitorStub_); - } -}; - // Updated stubs are IC stubs that use a TypeUpdate IC to track // the status of heap typesets that need to be updated. class ICUpdatedStub : public ICStub @@ -976,6 +956,7 @@ class ICStubCompiler #ifdef DEBUG bool entersStubFrame_; + uint32_t framePushedAtEnterStubFrame_; #endif // By default the stubcode key is just the kind. @@ -992,11 +973,12 @@ class ICStubCompiler ICStubCompiler(JSContext* cx, ICStub::Kind kind, Engine engine) : suppressGC(cx), cx(cx), kind(kind), engine_(engine), inStubFrame_(false) #ifdef DEBUG - , entersStubFrame_(false) + , entersStubFrame_(false), framePushedAtEnterStubFrame_(0) #endif {} // Pushes the frame ptr. + void PushFramePtr(MacroAssembler& masm, Register scratch); void pushFramePtr(MacroAssembler& masm, Register scratch); // Emits a tail call to a VMFunction wrapper. @@ -1083,6 +1065,32 @@ class ICStubCompiler } }; +// Monitored fallback stubs - as the name implies. +class ICMonitoredFallbackStub : public ICFallbackStub +{ + protected: + // Pointer to the fallback monitor stub. + ICTypeMonitor_Fallback* fallbackMonitorStub_; + + ICMonitoredFallbackStub(Kind kind, JitCode* stubCode) + : ICFallbackStub(kind, ICStub::MonitoredFallback, stubCode), + fallbackMonitorStub_(nullptr) {} + + public: + bool initMonitoringChain(JSContext* cx, ICStubSpace* space, ICStubCompiler::Engine engine); + bool addMonitorStubForValue(JSContext* cx, JSScript* script, HandleValue val, + ICStubCompiler::Engine engine); + + inline ICTypeMonitor_Fallback* fallbackMonitorStub() const { + return fallbackMonitorStub_; + } + + static inline size_t offsetOfFallbackMonitorStub() { + return offsetof(ICMonitoredFallbackStub, fallbackMonitorStub_); + } +}; + + // Base class for stub compilers that can generate multiple stubcodes. // These compilers need access to the JSOp they are compiling for. class ICMultiStubCompiler : public ICStubCompiler @@ -1102,6 +1110,360 @@ class ICMultiStubCompiler : public ICStubCompiler : ICStubCompiler(cx, kind, engine), op(op) {} }; +// TypeCheckPrimitiveSetStub +// Base class for IC stubs (TypeUpdate or TypeMonitor) that check that a given +// value's type falls within a set of primitive types. + +class TypeCheckPrimitiveSetStub : public ICStub +{ + friend class ICStubSpace; + protected: + inline static uint16_t TypeToFlag(JSValueType type) { + return 1u << static_cast(type); + } + + inline static uint16_t ValidFlags() { + return ((TypeToFlag(JSVAL_TYPE_OBJECT) << 1) - 1) & ~TypeToFlag(JSVAL_TYPE_MAGIC); + } + + TypeCheckPrimitiveSetStub(Kind kind, JitCode* stubCode, uint16_t flags) + : ICStub(kind, stubCode) + { + MOZ_ASSERT(kind == TypeMonitor_PrimitiveSet || kind == TypeUpdate_PrimitiveSet); + MOZ_ASSERT(flags && !(flags & ~ValidFlags())); + extra_ = flags; + } + + TypeCheckPrimitiveSetStub* updateTypesAndCode(uint16_t flags, JitCode* code) { + MOZ_ASSERT(flags && !(flags & ~ValidFlags())); + if (!code) + return nullptr; + extra_ = flags; + updateCode(code); + return this; + } + + public: + uint16_t typeFlags() const { + return extra_; + } + + bool containsType(JSValueType type) const { + MOZ_ASSERT(type <= JSVAL_TYPE_OBJECT); + MOZ_ASSERT(type != JSVAL_TYPE_MAGIC); + return extra_ & TypeToFlag(type); + } + + ICTypeMonitor_PrimitiveSet* toMonitorStub() { + return toTypeMonitor_PrimitiveSet(); + } + + ICTypeUpdate_PrimitiveSet* toUpdateStub() { + return toTypeUpdate_PrimitiveSet(); + } + + class Compiler : public ICStubCompiler { + protected: + TypeCheckPrimitiveSetStub* existingStub_; + uint16_t flags_; + + virtual int32_t getKey() const { + return static_cast(engine_) | + (static_cast(kind) << 1) | + (static_cast(flags_) << 17); + } + + public: + Compiler(JSContext* cx, Kind kind, Engine engine_, TypeCheckPrimitiveSetStub* existingStub, + JSValueType type) + : ICStubCompiler(cx, kind, engine_), + existingStub_(existingStub), + flags_((existingStub ? existingStub->typeFlags() : 0) | TypeToFlag(type)) + { + MOZ_ASSERT_IF(existingStub_, flags_ != existingStub_->typeFlags()); + } + + TypeCheckPrimitiveSetStub* updateStub() { + MOZ_ASSERT(existingStub_); + return existingStub_->updateTypesAndCode(flags_, getStubCode()); + } + }; +}; + +// TypeMonitor + +// The TypeMonitor fallback stub is not always a regular fallback stub. When +// used for monitoring the values pushed by a bytecode it doesn't hold a +// pointer to the IC entry, but rather back to the main fallback stub for the +// IC (from which a pointer to the IC entry can be retrieved). When monitoring +// the types of 'this', arguments or other values with no associated IC, there +// is no main fallback stub, and the IC entry is referenced directly. +class ICTypeMonitor_Fallback : public ICStub +{ + friend class ICStubSpace; + + static const uint32_t MAX_OPTIMIZED_STUBS = 8; + + // Pointer to the main fallback stub for the IC or to the main IC entry, + // depending on hasFallbackStub. + union { + ICMonitoredFallbackStub* mainFallbackStub_; + ICEntry* icEntry_; + }; + + // Pointer to the first monitor stub. + ICStub* firstMonitorStub_; + + // Address of the last monitor stub's field pointing to this + // fallback monitor stub. This will get updated when new + // monitor stubs are created and added. + ICStub** lastMonitorStubPtrAddr_; + + // Count of optimized type monitor stubs in this chain. + uint32_t numOptimizedMonitorStubs_ : 8; + + // Whether this has a fallback stub referring to the IC entry. + bool hasFallbackStub_ : 1; + + // Index of 'this' or argument which is being monitored, or BYTECODE_INDEX + // if this is monitoring the types of values pushed at some bytecode. + uint32_t argumentIndex_ : 23; + + static const uint32_t BYTECODE_INDEX = (1 << 23) - 1; + + ICTypeMonitor_Fallback(JitCode* stubCode, ICMonitoredFallbackStub* mainFallbackStub, + uint32_t argumentIndex) + : ICStub(ICStub::TypeMonitor_Fallback, stubCode), + mainFallbackStub_(mainFallbackStub), + firstMonitorStub_(thisFromCtor()), + lastMonitorStubPtrAddr_(nullptr), + numOptimizedMonitorStubs_(0), + hasFallbackStub_(mainFallbackStub != nullptr), + argumentIndex_(argumentIndex) + { } + + ICTypeMonitor_Fallback* thisFromCtor() { + return this; + } + + void addOptimizedMonitorStub(ICStub* stub) { + stub->setNext(this); + + MOZ_ASSERT((lastMonitorStubPtrAddr_ != nullptr) == + (numOptimizedMonitorStubs_ || !hasFallbackStub_)); + + if (lastMonitorStubPtrAddr_) + *lastMonitorStubPtrAddr_ = stub; + + if (numOptimizedMonitorStubs_ == 0) { + MOZ_ASSERT(firstMonitorStub_ == this); + firstMonitorStub_ = stub; + } else { + MOZ_ASSERT(firstMonitorStub_ != nullptr); + } + + lastMonitorStubPtrAddr_ = stub->addressOfNext(); + numOptimizedMonitorStubs_++; + } + + public: + bool hasStub(ICStub::Kind kind) { + ICStub* stub = firstMonitorStub_; + do { + if (stub->kind() == kind) + return true; + + stub = stub->next(); + } while (stub); + + return false; + } + + inline ICFallbackStub* mainFallbackStub() const { + MOZ_ASSERT(hasFallbackStub_); + return mainFallbackStub_; + } + + inline ICEntry* icEntry() const { + return hasFallbackStub_ ? mainFallbackStub()->icEntry() : icEntry_; + } + + inline ICStub* firstMonitorStub() const { + return firstMonitorStub_; + } + + static inline size_t offsetOfFirstMonitorStub() { + return offsetof(ICTypeMonitor_Fallback, firstMonitorStub_); + } + + inline uint32_t numOptimizedMonitorStubs() const { + return numOptimizedMonitorStubs_; + } + + inline bool monitorsThis() const { + return argumentIndex_ == 0; + } + + inline bool monitorsArgument(uint32_t* pargument) const { + if (argumentIndex_ > 0 && argumentIndex_ < BYTECODE_INDEX) { + *pargument = argumentIndex_ - 1; + return true; + } + return false; + } + + inline bool monitorsBytecode() const { + return argumentIndex_ == BYTECODE_INDEX; + } + + // Fixup the IC entry as for a normal fallback stub, for this/arguments. + void fixupICEntry(ICEntry* icEntry) { + MOZ_ASSERT(!hasFallbackStub_); + MOZ_ASSERT(icEntry_ == nullptr); + MOZ_ASSERT(lastMonitorStubPtrAddr_ == nullptr); + icEntry_ = icEntry; + lastMonitorStubPtrAddr_ = icEntry_->addressOfFirstStub(); + } + + // Create a new monitor stub for the type of the given value, and + // add it to this chain. + bool addMonitorStubForValue(JSContext* cx, JSScript* script, HandleValue val, + ICStubCompiler::Engine engine); + + void resetMonitorStubChain(Zone* zone); + + // Compiler for this stub kind. + class Compiler : public ICStubCompiler { + ICMonitoredFallbackStub* mainFallbackStub_; + uint32_t argumentIndex_; + + protected: + bool generateStubCode(MacroAssembler& masm); + + public: + Compiler(JSContext* cx, Engine engine, ICMonitoredFallbackStub* mainFallbackStub) + : ICStubCompiler(cx, ICStub::TypeMonitor_Fallback, engine), + mainFallbackStub_(mainFallbackStub), + argumentIndex_(BYTECODE_INDEX) + { } + + Compiler(JSContext* cx, Engine engine, uint32_t argumentIndex) + : ICStubCompiler(cx, ICStub::TypeMonitor_Fallback, engine), + mainFallbackStub_(nullptr), + argumentIndex_(argumentIndex) + { } + + ICTypeMonitor_Fallback* getStub(ICStubSpace* space) { + return newStub(space, getStubCode(), mainFallbackStub_, + argumentIndex_); + } + }; +}; + +class ICTypeMonitor_PrimitiveSet : public TypeCheckPrimitiveSetStub +{ + friend class ICStubSpace; + + ICTypeMonitor_PrimitiveSet(JitCode* stubCode, uint16_t flags) + : TypeCheckPrimitiveSetStub(TypeMonitor_PrimitiveSet, stubCode, flags) + {} + + public: + class Compiler : public TypeCheckPrimitiveSetStub::Compiler { + protected: + bool generateStubCode(MacroAssembler& masm); + + public: + Compiler(JSContext* cx, Engine engine, ICTypeMonitor_PrimitiveSet* existingStub, + JSValueType type) + : TypeCheckPrimitiveSetStub::Compiler(cx, TypeMonitor_PrimitiveSet, engine, existingStub, + type) + {} + + ICTypeMonitor_PrimitiveSet* updateStub() { + TypeCheckPrimitiveSetStub* stub = + this->TypeCheckPrimitiveSetStub::Compiler::updateStub(); + if (!stub) + return nullptr; + return stub->toMonitorStub(); + } + + ICTypeMonitor_PrimitiveSet* getStub(ICStubSpace* space) { + MOZ_ASSERT(!existingStub_); + return newStub(space, getStubCode(), flags_); + } + }; +}; + +class ICTypeMonitor_SingleObject : public ICStub +{ + friend class ICStubSpace; + + HeapPtrObject obj_; + + ICTypeMonitor_SingleObject(JitCode* stubCode, JSObject* obj); + + public: + HeapPtrObject& object() { + return obj_; + } + + static size_t offsetOfObject() { + return offsetof(ICTypeMonitor_SingleObject, obj_); + } + + class Compiler : public ICStubCompiler { + protected: + HandleObject obj_; + bool generateStubCode(MacroAssembler& masm); + + public: + Compiler(JSContext* cx, HandleObject obj) + : ICStubCompiler(cx, TypeMonitor_SingleObject, Engine::Baseline), + obj_(obj) + { } + + ICTypeMonitor_SingleObject* getStub(ICStubSpace* space) { + return newStub(space, getStubCode(), obj_); + } + }; +}; + +class ICTypeMonitor_ObjectGroup : public ICStub +{ + friend class ICStubSpace; + + HeapPtrObjectGroup group_; + + ICTypeMonitor_ObjectGroup(JitCode* stubCode, ObjectGroup* group); + + public: + HeapPtrObjectGroup& group() { + return group_; + } + + static size_t offsetOfGroup() { + return offsetof(ICTypeMonitor_ObjectGroup, group_); + } + + class Compiler : public ICStubCompiler { + protected: + HandleObjectGroup group_; + bool generateStubCode(MacroAssembler& masm); + + public: + Compiler(JSContext* cx, HandleObjectGroup group) + : ICStubCompiler(cx, TypeMonitor_ObjectGroup, Engine::Baseline), + group_(group) + { } + + ICTypeMonitor_ObjectGroup* getStub(ICStubSpace* space) { + return newStub(space, getStubCode(), group_); + } + }; +}; + + // BinaryArith // JSOP_ADD, JSOP_SUB, JSOP_MUL, JOP_DIV, JSOP_MOD // JSOP_BITAND, JSOP_BITXOR, JSOP_BITOR @@ -1746,6 +2108,1149 @@ class ICCompare_Int32WithBoolean : public ICStub }; }; +// Enum for stubs handling a combination of typed arrays and typed objects. +enum TypedThingLayout { + Layout_TypedArray, + Layout_OutlineTypedObject, + Layout_InlineTypedObject +}; + +static inline TypedThingLayout +GetTypedThingLayout(const Class* clasp) +{ + if (IsAnyTypedArrayClass(clasp)) + return Layout_TypedArray; + if (IsOutlineTypedObjectClass(clasp)) + return Layout_OutlineTypedObject; + if (IsInlineTypedObjectClass(clasp)) + return Layout_InlineTypedObject; + MOZ_CRASH("Bad object class"); +} + +bool +IsPreliminaryObject(JSObject* obj); + +void +StripPreliminaryObjectStubs(JSContext* cx, ICFallbackStub* stub); + +bool +EffectlesslyLookupProperty(JSContext* cx, HandleObject obj, HandleId name, + MutableHandleObject holder, MutableHandleShape shape, + bool* checkDOMProxy=nullptr, + DOMProxyShadowsResult* shadowsResult=nullptr, + bool* domProxyHasGeneration=nullptr); + +JSObject* +GetDOMProxyProto(JSObject* obj); + +bool +IsCacheableProtoChain(JSObject* obj, JSObject* holder, bool isDOMProxy=false); + +bool +IsCacheableGetPropReadSlot(JSObject* obj, JSObject* holder, Shape* shape, bool isDOMProxy=false); + +void +GetFixedOrDynamicSlotOffset(Shape* shape, bool* isFixed, uint32_t* offset); + +bool +IsCacheableGetPropCall(JSContext* cx, JSObject* obj, JSObject* holder, Shape* shape, + bool* isScripted, bool* isTemporarilyUnoptimizable, bool isDOMProxy=false); + +bool +UpdateExistingGetPropCallStubs(ICFallbackStub* fallbackStub, + ICStub::Kind kind, + HandleNativeObject holder, + HandleObject receiver, + HandleFunction getter); +bool +CheckHasNoSuchProperty(JSContext* cx, HandleObject obj, HandlePropertyName name, + MutableHandleObject lastProto, size_t* protoChainDepthOut); + +void +GuardReceiverObject(MacroAssembler& masm, ReceiverGuard guard, + Register object, Register scratch, + size_t receiverGuardOffset, Label* failure); + +bool +GetProtoShapes(JSObject* obj, size_t protoChainDepth, MutableHandle shapes); + +void +CheckDOMProxyExpandoDoesNotShadow(JSContext* cx, MacroAssembler& masm, Register object, + const Address& checkExpandoShapeAddr, + Address* expandoAndGenerationAddr, + Address* generationAddr, + Register scratch, + AllocatableGeneralRegisterSet& domProxyRegSet, + Label* checkFailed); + +void +CheckForNeuteredTypedObject(JSContext* cx, MacroAssembler& masm, Label* failure); + +bool +DoCallNativeGetter(JSContext* cx, HandleFunction callee, HandleObject obj, + MutableHandleValue result); + +void +LoadTypedThingData(MacroAssembler& masm, TypedThingLayout layout, Register obj, Register result); + +class ICGetProp_Fallback : public ICMonitoredFallbackStub +{ + friend class ICStubSpace; + + explicit ICGetProp_Fallback(JitCode* stubCode) + : ICMonitoredFallbackStub(ICStub::GetProp_Fallback, stubCode) + { } + + public: + static const uint32_t MAX_OPTIMIZED_STUBS = 16; + static const size_t UNOPTIMIZABLE_ACCESS_BIT = 0; + static const size_t ACCESSED_GETTER_BIT = 1; + + void noteUnoptimizableAccess() { + extra_ |= (1u << UNOPTIMIZABLE_ACCESS_BIT); + } + bool hadUnoptimizableAccess() const { + return extra_ & (1u << UNOPTIMIZABLE_ACCESS_BIT); + } + + void noteAccessedGetter() { + extra_ |= (1u << ACCESSED_GETTER_BIT); + } + bool hasAccessedGetter() const { + return extra_ & (1u << ACCESSED_GETTER_BIT); + } + + class Compiler : public ICStubCompiler { + public: + static const int32_t BASELINE_KEY = + (static_cast(Engine::Baseline)) | + (static_cast(ICStub::GetProp_Fallback) << 1); + + protected: + uint32_t returnOffset_; + bool generateStubCode(MacroAssembler& masm); + void postGenerateStubCode(MacroAssembler& masm, Handle code); + + public: + explicit Compiler(JSContext* cx, Engine engine) + : ICStubCompiler(cx, ICStub::GetProp_Fallback, engine) + { } + + ICStub* getStub(ICStubSpace* space) { + ICGetProp_Fallback* stub = newStub(space, getStubCode()); + if (!stub || !stub->initMonitoringChain(cx, space, engine_)) + return nullptr; + return stub; + } + }; +}; + +// Stub for sites, which are too polymorphic (i.e. MAX_OPTIMIZED_STUBS was reached) +class ICGetProp_Generic : public ICMonitoredStub +{ + friend class ICStubSpace; + + protected: + explicit ICGetProp_Generic(JitCode* stubCode, ICStub* firstMonitorStub) + : ICMonitoredStub(ICStub::GetProp_Generic, stubCode, firstMonitorStub) {} + + public: + static ICGetProp_Generic* Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub, + ICGetProp_Generic& other); + + class Compiler : public ICStubCompiler { + protected: + bool generateStubCode(MacroAssembler& masm); + ICStub* firstMonitorStub_; + public: + explicit Compiler(JSContext* cx, Engine engine, ICStub* firstMonitorStub) + : ICStubCompiler(cx, ICStub::GetProp_Generic, engine), + firstMonitorStub_(firstMonitorStub) + {} + + ICStub* getStub(ICStubSpace* space) { + return newStub(space, getStubCode(), firstMonitorStub_); + } + }; +}; + +// Stub for accessing a dense array's length. +class ICGetProp_ArrayLength : public ICStub +{ + friend class ICStubSpace; + + explicit ICGetProp_ArrayLength(JitCode* stubCode) + : ICStub(GetProp_ArrayLength, stubCode) + {} + + public: + class Compiler : public ICStubCompiler { + bool generateStubCode(MacroAssembler& masm); + + public: + explicit Compiler(JSContext* cx, Engine engine) + : ICStubCompiler(cx, ICStub::GetProp_ArrayLength, engine) + {} + + ICStub* getStub(ICStubSpace* space) { + return newStub(space, getStubCode()); + } + }; +}; + +// Stub for accessing an unboxed array's length. +class ICGetProp_UnboxedArrayLength : public ICStub +{ + friend class ICStubSpace; + + explicit ICGetProp_UnboxedArrayLength(JitCode* stubCode) + : ICStub(GetProp_UnboxedArrayLength, stubCode) + {} + + public: + class Compiler : public ICStubCompiler { + bool generateStubCode(MacroAssembler& masm); + + public: + explicit Compiler(JSContext* cx, Engine engine) + : ICStubCompiler(cx, ICStub::GetProp_UnboxedArrayLength, engine) + {} + + ICStub* getStub(ICStubSpace* space) { + return newStub(space, getStubCode()); + } + }; +}; + +// Stub for accessing a property on a primitive's prototype. +class ICGetProp_Primitive : public ICMonitoredStub +{ + friend class ICStubSpace; + + protected: // Protected to silence Clang warning. + // Shape of String.prototype/Number.prototype to check for. + HeapPtrShape protoShape_; + + // Fixed or dynamic slot offset. + uint32_t offset_; + + ICGetProp_Primitive(JitCode* stubCode, ICStub* firstMonitorStub, JSValueType primitiveType, + Shape* protoShape, uint32_t offset); + + public: + HeapPtrShape& protoShape() { + return protoShape_; + } + JSValueType primitiveType() const { + return JSValueType(extra_); + } + + static size_t offsetOfProtoShape() { + return offsetof(ICGetProp_Primitive, protoShape_); + } + + static size_t offsetOfOffset() { + return offsetof(ICGetProp_Primitive, offset_); + } + + class Compiler : public ICStubCompiler { + ICStub* firstMonitorStub_; + JSValueType primitiveType_; + RootedObject prototype_; + bool isFixedSlot_; + uint32_t offset_; + + bool generateStubCode(MacroAssembler& masm); + + protected: + virtual int32_t getKey() const { + static_assert(sizeof(JSValueType) == 1, "JSValueType should fit in one byte"); + return static_cast(engine_) | + (static_cast(kind) << 1) | + (static_cast(isFixedSlot_) << 17) | + (static_cast(primitiveType_) << 25); + } + + public: + Compiler(JSContext* cx, Engine engine, ICStub* firstMonitorStub, JSValueType primitiveType, + HandleObject prototype, bool isFixedSlot, uint32_t offset) + : ICStubCompiler(cx, ICStub::GetProp_Primitive, engine), + firstMonitorStub_(firstMonitorStub), + primitiveType_(primitiveType), + prototype_(cx, prototype), + isFixedSlot_(isFixedSlot), + offset_(offset) + {} + + ICStub* getStub(ICStubSpace* space) { + RootedShape protoShape(cx, prototype_->as().lastProperty()); + return newStub(space, getStubCode(), firstMonitorStub_, + primitiveType_, protoShape, offset_); + } + }; +}; + +// Stub for accessing a string's length. +class ICGetProp_StringLength : public ICStub +{ + friend class ICStubSpace; + + explicit ICGetProp_StringLength(JitCode* stubCode) + : ICStub(GetProp_StringLength, stubCode) + {} + + public: + class Compiler : public ICStubCompiler { + bool generateStubCode(MacroAssembler& masm); + + public: + explicit Compiler(JSContext* cx, Engine engine) + : ICStubCompiler(cx, ICStub::GetProp_StringLength, engine) + {} + + ICStub* getStub(ICStubSpace* space) { + return newStub(space, getStubCode()); + } + }; +}; + +// Base class for native GetProp stubs. +class ICGetPropNativeStub : public ICMonitoredStub +{ + // Object shape/group. + HeapReceiverGuard receiverGuard_; + + // Fixed or dynamic slot offset. + uint32_t offset_; + + protected: + ICGetPropNativeStub(ICStub::Kind kind, JitCode* stubCode, ICStub* firstMonitorStub, + ReceiverGuard guard, uint32_t offset); + + public: + HeapReceiverGuard& receiverGuard() { + return receiverGuard_; + } + uint32_t offset() const { + return offset_; + } + + void notePreliminaryObject() { + extra_ = 1; + } + bool hasPreliminaryObject() const { + return extra_; + } + + static size_t offsetOfReceiverGuard() { + return offsetof(ICGetPropNativeStub, receiverGuard_); + } + static size_t offsetOfOffset() { + return offsetof(ICGetPropNativeStub, offset_); + } +}; + +// Stub for accessing an own property on a native object. +class ICGetProp_Native : public ICGetPropNativeStub +{ + friend class ICStubSpace; + + ICGetProp_Native(JitCode* stubCode, ICStub* firstMonitorStub, ReceiverGuard guard, + uint32_t offset) + : ICGetPropNativeStub(GetProp_Native, stubCode, firstMonitorStub, guard, offset) + {} + + public: + static ICGetProp_Native* Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub, + ICGetProp_Native& other); +}; + +class ICGetPropNativePrototypeStub : public ICGetPropNativeStub +{ + // Holder and its shape. + HeapPtrObject holder_; + HeapPtrShape holderShape_; + + protected: + ICGetPropNativePrototypeStub(ICStub::Kind kind, JitCode* stubCode, ICStub* firstMonitorStub, + ReceiverGuard guard, uint32_t offset, JSObject* holder, + Shape* holderShape); + + public: + HeapPtrObject& holder() { + return holder_; + } + HeapPtrShape& holderShape() { + return holderShape_; + } + static size_t offsetOfHolder() { + return offsetof(ICGetPropNativePrototypeStub, holder_); + } + static size_t offsetOfHolderShape() { + return offsetof(ICGetPropNativePrototypeStub, holderShape_); + } +}; + +// Stub for accessing a property on the native prototype of a native or unboxed +// object. Note that due to the shape teleporting optimization, we only have to +// guard on the object's shape/group and the holder's shape. +class ICGetProp_NativePrototype : public ICGetPropNativePrototypeStub +{ + friend class ICStubSpace; + + protected: + ICGetProp_NativePrototype(JitCode* stubCode, ICStub* firstMonitorStub, ReceiverGuard guard, + uint32_t offset, JSObject* holder, Shape* holderShape) + : ICGetPropNativePrototypeStub(GetProp_NativePrototype, stubCode, firstMonitorStub, guard, + offset, holder, holderShape) + { } + + public: + static ICGetProp_NativePrototype* Clone(JSContext* cx, + ICStubSpace* space, + ICStub* firstMonitorStub, + ICGetProp_NativePrototype& other); +}; + +// Stub for accessing a non-lexical global name. Semantically, it is really a +// getprop: the name is either on the GlobalObject or its prototype chain. We +// teleport to the object that has the name, but we also need to guard on the +// shape of the global object. +// +// The receiver object is the global lexical scope. +class ICGetName_Global : public ICGetPropNativePrototypeStub +{ + friend class ICStubSpace; + + protected: + HeapPtrShape globalShape_; + + ICGetName_Global(JitCode* stubCode, ICStub* firstMonitorStub, ReceiverGuard guard, + uint32_t slot, JSObject* holder, Shape* holderShape, Shape* globalShape); + + public: + static ICGetName_Global* Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub, + ICGetName_Global& other); + + HeapPtrShape& globalShape() { + return globalShape_; + } + static size_t offsetOfGlobalShape() { + return offsetof(ICGetName_Global, globalShape_); + } +}; + +// Compiler for native GetProp stubs. +class ICGetPropNativeCompiler : public ICStubCompiler +{ + ICStub* firstMonitorStub_; + HandleObject obj_; + HandleObject holder_; + HandlePropertyName propName_; + bool isFixedSlot_; + uint32_t offset_; + bool inputDefinitelyObject_; + + bool generateStubCode(MacroAssembler& masm); + + protected: + virtual int32_t getKey() const { + return static_cast(engine_) | + (static_cast(kind) << 1) | + (static_cast(isFixedSlot_) << 17) | + (static_cast(inputDefinitelyObject_) << 18) | + (HeapReceiverGuard::keyBits(obj_) << 19); + } + + public: + ICGetPropNativeCompiler(JSContext* cx, ICStub::Kind kind, ICStubCompiler::Engine engine, + ICStub* firstMonitorStub, HandleObject obj, HandleObject holder, + HandlePropertyName propName, bool isFixedSlot, uint32_t offset, + bool inputDefinitelyObject = false) + : ICStubCompiler(cx, kind, engine), + firstMonitorStub_(firstMonitorStub), + obj_(obj), + holder_(holder), + propName_(propName), + isFixedSlot_(isFixedSlot), + offset_(offset), + inputDefinitelyObject_(inputDefinitelyObject) + {} + + ICGetPropNativeStub* getStub(ICStubSpace* space); +}; + +template class ICGetProp_NativeDoesNotExistImpl; + +class ICGetProp_NativeDoesNotExist : public ICMonitoredStub +{ + friend class ICStubSpace; + public: + HeapReceiverGuard guard_; + + static const size_t MAX_PROTO_CHAIN_DEPTH = 8; + + protected: + ICGetProp_NativeDoesNotExist(JitCode* stubCode, ICStub* firstMonitorStub, + ReceiverGuard guard, + size_t protoChainDepth); + + public: + size_t protoChainDepth() const { + MOZ_ASSERT(extra_ <= MAX_PROTO_CHAIN_DEPTH); + return extra_; + } + + template + ICGetProp_NativeDoesNotExistImpl* toImpl() { + MOZ_ASSERT(ProtoChainDepth == protoChainDepth()); + return static_cast*>(this); + } + + HeapReceiverGuard& guard() { + return guard_; + } + + static size_t offsetOfGuard() { + return offsetof(ICGetProp_NativeDoesNotExist, guard_); + } + + static size_t offsetOfShape(size_t idx); +}; + +template +class ICGetProp_NativeDoesNotExistImpl : public ICGetProp_NativeDoesNotExist +{ + friend class ICStubSpace; + public: + static const size_t MAX_PROTO_CHAIN_DEPTH = 8; + static const size_t NumShapes = ProtoChainDepth; + + private: + mozilla::Array shapes_; + + ICGetProp_NativeDoesNotExistImpl(JitCode* stubCode, ICStub* firstMonitorStub, + ReceiverGuard guard, + Handle shapes); + + public: + void traceShapes(JSTracer* trc) { + // Note: using int32_t here to avoid gcc warning. + for (int32_t i = 0; i < int32_t(NumShapes); i++) + TraceEdge(trc, &shapes_[i], "baseline-getpropnativedoesnotexist-stub-shape"); + } + + static size_t offsetOfShape(size_t idx) { + return offsetof(ICGetProp_NativeDoesNotExistImpl, shapes_) + (idx * sizeof(HeapPtrShape)); + } +}; + +class ICGetPropNativeDoesNotExistCompiler : public ICStubCompiler +{ + ICStub* firstMonitorStub_; + RootedObject obj_; + size_t protoChainDepth_; + + protected: + virtual int32_t getKey() const { + return static_cast(engine_) | + (static_cast(kind) << 1) | + (HeapReceiverGuard::keyBits(obj_) << 17) | + (static_cast(protoChainDepth_) << 19); + } + + bool generateStubCode(MacroAssembler& masm); + + public: + ICGetPropNativeDoesNotExistCompiler(JSContext* cx, ICStubCompiler::Engine engine, + ICStub* firstMonitorStub, HandleObject obj, + size_t protoChainDepth); + + template + ICStub* getStubSpecific(ICStubSpace* space, Handle shapes) { + ReceiverGuard guard(obj_); + return newStub> + (space, getStubCode(), firstMonitorStub_, guard, shapes); + } + + ICStub* getStub(ICStubSpace* space); +}; + +class ICGetProp_Unboxed : public ICMonitoredStub +{ + friend class ICStubSpace; + + HeapPtrObjectGroup group_; + uint32_t fieldOffset_; + + ICGetProp_Unboxed(JitCode* stubCode, ICStub* firstMonitorStub, ObjectGroup* group, + uint32_t fieldOffset) + : ICMonitoredStub(ICStub::GetProp_Unboxed, stubCode, firstMonitorStub), + group_(group), fieldOffset_(fieldOffset) + { + (void) fieldOffset_; // Silence clang warning + } + + public: + HeapPtrObjectGroup& group() { + return group_; + } + + static size_t offsetOfGroup() { + return offsetof(ICGetProp_Unboxed, group_); + } + static size_t offsetOfFieldOffset() { + return offsetof(ICGetProp_Unboxed, fieldOffset_); + } + + class Compiler : public ICStubCompiler { + protected: + ICStub* firstMonitorStub_; + RootedObjectGroup group_; + uint32_t fieldOffset_; + JSValueType fieldType_; + + bool generateStubCode(MacroAssembler& masm); + + virtual int32_t getKey() const { + return static_cast(engine_) | + (static_cast(kind) << 1) | + (static_cast(fieldType_) << 17); + } + + public: + Compiler(JSContext* cx, Engine engine, ICStub* firstMonitorStub, + ObjectGroup* group, uint32_t fieldOffset, JSValueType fieldType) + : ICStubCompiler(cx, ICStub::GetProp_Unboxed, engine), + firstMonitorStub_(firstMonitorStub), + group_(cx, group), + fieldOffset_(fieldOffset), + fieldType_(fieldType) + {} + + ICStub* getStub(ICStubSpace* space) { + return newStub(space, getStubCode(), firstMonitorStub_, group_, + fieldOffset_); + } + }; +}; + +static uint32_t +SimpleTypeDescrKey(SimpleTypeDescr* descr) +{ + if (descr->is()) + return uint32_t(descr->as().type()) << 1; + return (uint32_t(descr->as().type()) << 1) | 1; +} + +class ICGetProp_TypedObject : public ICMonitoredStub +{ + friend class ICStubSpace; + + HeapPtrShape shape_; + uint32_t fieldOffset_; + + ICGetProp_TypedObject(JitCode* stubCode, ICStub* firstMonitorStub, Shape* shape, + uint32_t fieldOffset) + : ICMonitoredStub(ICStub::GetProp_TypedObject, stubCode, firstMonitorStub), + shape_(shape), fieldOffset_(fieldOffset) + { + (void) fieldOffset_; // Silence clang warning + } + + public: + HeapPtrShape& shape() { + return shape_; + } + + static size_t offsetOfShape() { + return offsetof(ICGetProp_TypedObject, shape_); + } + static size_t offsetOfFieldOffset() { + return offsetof(ICGetProp_TypedObject, fieldOffset_); + } + + class Compiler : public ICStubCompiler { + protected: + ICStub* firstMonitorStub_; + RootedShape shape_; + uint32_t fieldOffset_; + TypedThingLayout layout_; + Rooted fieldDescr_; + + bool generateStubCode(MacroAssembler& masm); + + virtual int32_t getKey() const { + return static_cast(engine_) | + (static_cast(kind) << 1) | + (static_cast(SimpleTypeDescrKey(fieldDescr_)) << 17) | + (static_cast(layout_) << 25); + } + + public: + Compiler(JSContext* cx, Engine engine, ICStub* firstMonitorStub, + Shape* shape, uint32_t fieldOffset, SimpleTypeDescr* fieldDescr) + : ICStubCompiler(cx, ICStub::GetProp_TypedObject, engine), + firstMonitorStub_(firstMonitorStub), + shape_(cx, shape), + fieldOffset_(fieldOffset), + layout_(GetTypedThingLayout(shape->getObjectClass())), + fieldDescr_(cx, fieldDescr) + {} + + ICStub* getStub(ICStubSpace* space) { + return newStub(space, getStubCode(), firstMonitorStub_, shape_, + fieldOffset_); + } + }; +}; + +class ICGetPropCallGetter : public ICMonitoredStub +{ + friend class ICStubSpace; + + protected: + // Shape/group of receiver object. Used for both own and proto getters. + // In the GetPropCallDOMProxyNative case, the receiver guard enforces + // the proxy handler, because Shape implies Class. + HeapReceiverGuard receiverGuard_; + + // Holder and holder shape. For own getters, guarding on receiverGuard_ is + // sufficient, although Ion may use holder_ and holderShape_ even for own + // getters. In this case holderShape_ == receiverGuard_.shape_ (isOwnGetter + // below relies on this). + HeapPtrObject holder_; + + HeapPtrShape holderShape_; + + // Function to call. + HeapPtrFunction getter_; + + // PC offset of call + uint32_t pcOffset_; + + ICGetPropCallGetter(Kind kind, JitCode* stubCode, ICStub* firstMonitorStub, + ReceiverGuard receiverGuard, JSObject* holder, + Shape* holderShape, JSFunction* getter, uint32_t pcOffset); + + public: + HeapPtrObject& holder() { + return holder_; + } + HeapPtrShape& holderShape() { + return holderShape_; + } + HeapPtrFunction& getter() { + return getter_; + } + HeapReceiverGuard& receiverGuard() { + return receiverGuard_; + } + + bool isOwnGetter() const { + MOZ_ASSERT(holder_->isNative()); + MOZ_ASSERT(holderShape_); + return receiverGuard_.shape() == holderShape_; + } + + static size_t offsetOfHolder() { + return offsetof(ICGetPropCallGetter, holder_); + } + static size_t offsetOfHolderShape() { + return offsetof(ICGetPropCallGetter, holderShape_); + } + static size_t offsetOfGetter() { + return offsetof(ICGetPropCallGetter, getter_); + } + static size_t offsetOfPCOffset() { + return offsetof(ICGetPropCallGetter, pcOffset_); + } + static size_t offsetOfReceiverGuard() { + return offsetof(ICGetPropCallGetter, receiverGuard_); + } + + class Compiler : public ICStubCompiler { + protected: + ICStub* firstMonitorStub_; + RootedObject receiver_; + RootedObject holder_; + RootedFunction getter_; + uint32_t pcOffset_; + const Class* outerClass_; + + virtual int32_t getKey() const { + // ICGetPropCallNativeCompiler::getKey adds more bits to our + // return value, so be careful when making changes here. + return static_cast(engine_) | + (static_cast(kind) << 1) | + (HeapReceiverGuard::keyBits(receiver_) << 17) | + (static_cast(!!outerClass_) << 19) | + (static_cast(receiver_ != holder_) << 20); + } + + public: + Compiler(JSContext* cx, ICStub::Kind kind, Engine engine, ICStub* firstMonitorStub, + HandleObject receiver, HandleObject holder, HandleFunction getter, + uint32_t pcOffset, const Class* outerClass) + : ICStubCompiler(cx, kind, engine), + firstMonitorStub_(firstMonitorStub), + receiver_(cx, receiver), + holder_(cx, holder), + getter_(cx, getter), + pcOffset_(pcOffset), + outerClass_(outerClass) + { + MOZ_ASSERT(kind == ICStub::GetProp_CallScripted || + kind == ICStub::GetProp_CallNative || + kind == ICStub::GetProp_CallNativeGlobal); + } + }; +}; + +// Stub for calling a scripted getter on a native object when the getter is kept on the +// proto-chain. +class ICGetProp_CallScripted : public ICGetPropCallGetter +{ + friend class ICStubSpace; + + protected: + ICGetProp_CallScripted(JitCode* stubCode, ICStub* firstMonitorStub, + ReceiverGuard receiverGuard, + JSObject* holder, Shape* holderShape, + JSFunction* getter, uint32_t pcOffset) + : ICGetPropCallGetter(GetProp_CallScripted, stubCode, firstMonitorStub, + receiverGuard, holder, holderShape, getter, pcOffset) + {} + + public: + static ICGetProp_CallScripted* Clone(JSContext* cx, ICStubSpace* space, + ICStub* firstMonitorStub, ICGetProp_CallScripted& other); + + class Compiler : public ICGetPropCallGetter::Compiler { + protected: + bool generateStubCode(MacroAssembler& masm); + + public: + Compiler(JSContext* cx, Engine engine, ICStub* firstMonitorStub, HandleObject obj, + HandleObject holder, HandleFunction getter, uint32_t pcOffset) + : ICGetPropCallGetter::Compiler(cx, ICStub::GetProp_CallScripted, engine, + firstMonitorStub, obj, holder, + getter, pcOffset, /* outerClass = */ nullptr) + {} + + ICStub* getStub(ICStubSpace* space) { + ReceiverGuard guard(receiver_); + Shape* holderShape = holder_->as().lastProperty(); + return newStub(space, getStubCode(), firstMonitorStub_, + guard, holder_, holderShape, getter_, + pcOffset_); + } + }; +}; + +// Stub for calling a native getter on a native object. +class ICGetProp_CallNative : public ICGetPropCallGetter +{ + friend class ICStubSpace; + + protected: + + ICGetProp_CallNative(JitCode* stubCode, ICStub* firstMonitorStub, + ReceiverGuard receiverGuard, + JSObject* holder, Shape* holderShape, + JSFunction* getter, uint32_t pcOffset) + : ICGetPropCallGetter(GetProp_CallNative, stubCode, firstMonitorStub, + receiverGuard, holder, holderShape, getter, pcOffset) + {} + + public: + static ICGetProp_CallNative* Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub, + ICGetProp_CallNative& other); + +}; + +// Stub for calling a native getter on the GlobalObject. +class ICGetProp_CallNativeGlobal : public ICGetPropCallGetter +{ + friend class ICStubSpace; + + protected: + HeapPtrShape globalShape_; + + ICGetProp_CallNativeGlobal(JitCode* stubCode, ICStub* firstMonitorStub, + ReceiverGuard receiverGuard, + JSObject* holder, Shape* holderShape, Shape* globalShape, + JSFunction* getter, uint32_t pcOffset) + : ICGetPropCallGetter(GetProp_CallNativeGlobal, stubCode, firstMonitorStub, + receiverGuard, holder, holderShape, getter, pcOffset), + globalShape_(globalShape) + { } + + public: + static ICGetProp_CallNativeGlobal* Clone(JSContext* cx, ICStubSpace* space, + ICStub* firstMonitorStub, + ICGetProp_CallNativeGlobal& other); + + HeapPtrShape& globalShape() { + return globalShape_; + } + static size_t offsetOfGlobalShape() { + return offsetof(ICGetProp_CallNativeGlobal, globalShape_); + } +}; + +class ICGetPropCallNativeCompiler : public ICGetPropCallGetter::Compiler +{ + bool inputDefinitelyObject_; + protected: + bool generateStubCode(MacroAssembler& masm); + + virtual int32_t getKey() const { + int32_t baseKey = ICGetPropCallGetter::Compiler::getKey(); + MOZ_ASSERT((baseKey >> 21) == 0); + return baseKey | (static_cast(inputDefinitelyObject_) << 21); + } + + public: + ICGetPropCallNativeCompiler(JSContext* cx, ICStub::Kind kind, ICStubCompiler::Engine engine, + ICStub* firstMonitorStub, HandleObject receiver, + HandleObject holder, HandleFunction getter, uint32_t pcOffset, + const Class* outerClass, bool inputDefinitelyObject = false) + : ICGetPropCallGetter::Compiler(cx, kind, engine, firstMonitorStub, receiver, holder, + getter, pcOffset, outerClass), + inputDefinitelyObject_(inputDefinitelyObject) + {} + + ICStub* getStub(ICStubSpace* space); +}; + +class ICGetPropCallDOMProxyNativeStub : public ICGetPropCallGetter +{ + friend class ICStubSpace; + protected: + // Object shape of expected expando object. (nullptr if no expando object should be there) + HeapPtrShape expandoShape_; + + ICGetPropCallDOMProxyNativeStub(ICStub::Kind kind, JitCode* stubCode, + ICStub* firstMonitorStub, Shape* shape, + Shape* expandoShape, + JSObject* holder, Shape* holderShape, + JSFunction* getter, uint32_t pcOffset); + + public: + HeapPtrShape& expandoShape() { + return expandoShape_; + } + static size_t offsetOfExpandoShape() { + return offsetof(ICGetPropCallDOMProxyNativeStub, expandoShape_); + } +}; + +class ICGetProp_CallDOMProxyNative : public ICGetPropCallDOMProxyNativeStub +{ + friend class ICStubSpace; + ICGetProp_CallDOMProxyNative(JitCode* stubCode, ICStub* firstMonitorStub, Shape* shape, + Shape* expandoShape, + JSObject* holder, Shape* holderShape, + JSFunction* getter, uint32_t pcOffset) + : ICGetPropCallDOMProxyNativeStub(ICStub::GetProp_CallDOMProxyNative, stubCode, + firstMonitorStub, shape, expandoShape, + holder, holderShape, getter, pcOffset) + {} + + public: + static ICGetProp_CallDOMProxyNative* Clone(JSContext* cx, + ICStubSpace* space, + ICStub* firstMonitorStub, + ICGetProp_CallDOMProxyNative& other); +}; + +class ICGetProp_CallDOMProxyWithGenerationNative : public ICGetPropCallDOMProxyNativeStub +{ + protected: + ExpandoAndGeneration* expandoAndGeneration_; + uint32_t generation_; + + public: + ICGetProp_CallDOMProxyWithGenerationNative(JitCode* stubCode, ICStub* firstMonitorStub, + Shape* shape, + ExpandoAndGeneration* expandoAndGeneration, + uint32_t generation, Shape* expandoShape, + JSObject* holder, Shape* holderShape, + JSFunction* getter, uint32_t pcOffset) + : ICGetPropCallDOMProxyNativeStub(ICStub::GetProp_CallDOMProxyWithGenerationNative, + stubCode, firstMonitorStub, shape, + expandoShape, holder, holderShape, getter, pcOffset), + expandoAndGeneration_(expandoAndGeneration), + generation_(generation) + { + } + + static ICGetProp_CallDOMProxyWithGenerationNative* + Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub, + ICGetProp_CallDOMProxyWithGenerationNative& other); + + void* expandoAndGeneration() const { + return expandoAndGeneration_; + } + uint32_t generation() const { + return generation_; + } + + void setGeneration(uint32_t value) { + generation_ = value; + } + + static size_t offsetOfInternalStruct() { + return offsetof(ICGetProp_CallDOMProxyWithGenerationNative, expandoAndGeneration_); + } + static size_t offsetOfGeneration() { + return offsetof(ICGetProp_CallDOMProxyWithGenerationNative, generation_); + } +}; + +class ICGetPropCallDOMProxyNativeCompiler : public ICStubCompiler { + ICStub* firstMonitorStub_; + Rooted proxy_; + RootedObject holder_; + RootedFunction getter_; + uint32_t pcOffset_; + + bool generateStubCode(MacroAssembler& masm, Address* internalStructAddr, + Address* generationAddr); + bool generateStubCode(MacroAssembler& masm); + + public: + ICGetPropCallDOMProxyNativeCompiler(JSContext* cx, ICStub::Kind kind, + ICStubCompiler::Engine engine, + ICStub* firstMonitorStub, Handle proxy, + HandleObject holder, HandleFunction getter, + uint32_t pcOffset); + + ICStub* getStub(ICStubSpace* space); +}; + +class ICGetProp_DOMProxyShadowed : public ICMonitoredStub +{ + friend class ICStubSpace; + protected: + HeapPtrShape shape_; + const BaseProxyHandler* proxyHandler_; + HeapPtrPropertyName name_; + uint32_t pcOffset_; + + ICGetProp_DOMProxyShadowed(JitCode* stubCode, ICStub* firstMonitorStub, Shape* shape, + const BaseProxyHandler* proxyHandler, PropertyName* name, + uint32_t pcOffset); + + public: + static ICGetProp_DOMProxyShadowed* Clone(JSContext* cx, ICStubSpace* space, + ICStub* firstMonitorStub, + ICGetProp_DOMProxyShadowed& other); + + HeapPtrShape& shape() { + return shape_; + } + HeapPtrPropertyName& name() { + return name_; + } + + static size_t offsetOfShape() { + return offsetof(ICGetProp_DOMProxyShadowed, shape_); + } + static size_t offsetOfProxyHandler() { + return offsetof(ICGetProp_DOMProxyShadowed, proxyHandler_); + } + static size_t offsetOfName() { + return offsetof(ICGetProp_DOMProxyShadowed, name_); + } + static size_t offsetOfPCOffset() { + return offsetof(ICGetProp_DOMProxyShadowed, pcOffset_); + } + + class Compiler : public ICStubCompiler { + ICStub* firstMonitorStub_; + Rooted proxy_; + RootedPropertyName name_; + uint32_t pcOffset_; + + bool generateStubCode(MacroAssembler& masm); + + public: + Compiler(JSContext* cx, Engine engine, ICStub* firstMonitorStub, Handle proxy, + HandlePropertyName name, uint32_t pcOffset) + : ICStubCompiler(cx, ICStub::GetProp_CallNative, engine), + firstMonitorStub_(firstMonitorStub), + proxy_(cx, proxy), + name_(cx, name), + pcOffset_(pcOffset) + {} + + ICStub* getStub(ICStubSpace* space); + }; +}; + +class ICGetProp_ArgumentsLength : public ICStub +{ + friend class ICStubSpace; + public: + enum Which { Mapped, Unmapped, Magic }; + + protected: + explicit ICGetProp_ArgumentsLength(JitCode* stubCode) + : ICStub(ICStub::GetProp_ArgumentsLength, stubCode) + { } + + public: + class Compiler : public ICStubCompiler { + protected: + Which which_; + + bool generateStubCode(MacroAssembler& masm); + + virtual int32_t getKey() const { + return static_cast(engine_) | + (static_cast(kind) << 1) | + (static_cast(which_) << 17); + } + + public: + Compiler(JSContext* cx, Engine engine, Which which) + : ICStubCompiler(cx, ICStub::GetProp_ArgumentsLength, engine), + which_(which) + {} + + ICStub* getStub(ICStubSpace* space) { + return newStub(space, getStubCode()); + } + }; +}; + +class ICGetProp_ArgumentsCallee : public ICMonitoredStub +{ + friend class ICStubSpace; + + protected: + ICGetProp_ArgumentsCallee(JitCode* stubCode, ICStub* firstMonitorStub); + + public: + class Compiler : public ICStubCompiler { + protected: + ICStub* firstMonitorStub_; + bool generateStubCode(MacroAssembler& masm); + + public: + Compiler(JSContext* cx, Engine engine, ICStub* firstMonitorStub) + : ICStubCompiler(cx, ICStub::GetProp_ArgumentsCallee, engine), + firstMonitorStub_(firstMonitorStub) + {} + + ICStub* getStub(ICStubSpace* space) { + return newStub(space, getStubCode(), firstMonitorStub_); + } + }; +}; + } // namespace jit } // namespace js diff --git a/js/src/jit/SharedICList.h b/js/src/jit/SharedICList.h index 491d7e2840bc..da6fa312b1c5 100644 --- a/js/src/jit/SharedICList.h +++ b/js/src/jit/SharedICList.h @@ -34,6 +34,26 @@ namespace jit { _(Compare_ObjectWithUndefined) \ _(Compare_Int32WithBoolean) \ \ + _(GetProp_Fallback) \ + _(GetProp_ArrayLength) \ + _(GetProp_UnboxedArrayLength) \ + _(GetProp_Primitive) \ + _(GetProp_StringLength) \ + _(GetProp_Native) \ + _(GetProp_NativeDoesNotExist) \ + _(GetProp_NativePrototype) \ + _(GetProp_Unboxed) \ + _(GetProp_TypedObject) \ + _(GetProp_CallScripted) \ + _(GetProp_CallNative) \ + _(GetProp_CallNativeGlobal) \ + _(GetProp_CallDOMProxyNative) \ + _(GetProp_CallDOMProxyWithGenerationNative) \ + _(GetProp_DOMProxyShadowed) \ + _(GetProp_ArgumentsLength) \ + _(GetProp_ArgumentsCallee) \ + _(GetProp_Generic) \ + \ } // namespace jit } // namespace js diff --git a/js/src/jit/arm/SharedICHelpers-arm.h b/js/src/jit/arm/SharedICHelpers-arm.h index cd56a19e2949..102917ec0738 100644 --- a/js/src/jit/arm/SharedICHelpers-arm.h +++ b/js/src/jit/arm/SharedICHelpers-arm.h @@ -183,12 +183,12 @@ EmitBaselineEnterStubFrame(MacroAssembler& masm, Register scratch) // Push frame descriptor and return address. masm.makeFrameDescriptor(scratch, JitFrame_BaselineJS); - masm.push(scratch); - masm.push(ICTailCallReg); + masm.Push(scratch); + masm.Push(ICTailCallReg); // Save old frame pointer, stack pointer and stub reg. - masm.push(ICStubReg); - masm.push(BaselineFrameReg); + masm.Push(ICStubReg); + masm.Push(BaselineFrameReg); masm.mov(BaselineStackReg, BaselineFrameReg); // We pushed 4 words, so the stack is still aligned to 8 bytes. @@ -199,8 +199,8 @@ inline void EmitIonEnterStubFrame(MacroAssembler& masm, Register scratch) { MOZ_ASSERT(ICTailCallReg == lr); - masm.push(ICTailCallReg); - masm.push(ICStubReg); + masm.Push(ICTailCallReg); + masm.Push(ICStubReg); } inline void @@ -213,28 +213,28 @@ EmitBaselineLeaveStubFrame(MacroAssembler& masm, bool calledIntoIon = false) // we performed a VM call, the descriptor has been popped already so in that // case we use the frame pointer. if (calledIntoIon) { - masm.pop(scratch); + masm.Pop(scratch); masm.rshiftPtr(Imm32(FRAMESIZE_SHIFT), scratch); masm.add32(scratch, BaselineStackReg); } else { masm.mov(BaselineFrameReg, BaselineStackReg); } - masm.pop(BaselineFrameReg); - masm.pop(ICStubReg); + masm.Pop(BaselineFrameReg); + masm.Pop(ICStubReg); // Load the return address. - masm.pop(ICTailCallReg); + masm.Pop(ICTailCallReg); // Discard the frame descriptor. - masm.pop(scratch); + masm.Pop(scratch); } inline void EmitIonLeaveStubFrame(MacroAssembler& masm) { - masm.pop(ICStubReg); - masm.pop(ICTailCallReg); + masm.Pop(ICStubReg); + masm.Pop(ICTailCallReg); } inline void @@ -244,12 +244,12 @@ EmitStowICValues(MacroAssembler& masm, int values) switch(values) { case 1: // Stow R0. - masm.pushValue(R0); + masm.Push(R0); break; case 2: // Stow R0 and R1. - masm.pushValue(R0); - masm.pushValue(R1); + masm.Push(R0); + masm.Push(R1); break; } } @@ -276,6 +276,7 @@ EmitUnstowICValues(MacroAssembler& masm, int values, bool discard = false) } break; } + masm.adjustFrame(-values * sizeof(Value)); } inline void @@ -320,9 +321,9 @@ EmitCallTypeUpdateIC(MacroAssembler& masm, JitCode* code, uint32_t objectOffset) masm.loadValue(Address(BaselineStackReg, STUB_FRAME_SIZE + objectOffset), R1); - masm.pushValue(R0); - masm.pushValue(R1); - masm.push(ICStubReg); + masm.Push(R0); + masm.Push(R1); + masm.Push(ICStubReg); // Load previous frame pointer, push BaselineFrame*. masm.loadPtr(Address(BaselineFrameReg, 0), R0.scratchReg()); diff --git a/js/src/jit/arm64/MacroAssembler-arm64.cpp b/js/src/jit/arm64/MacroAssembler-arm64.cpp index f9b8f419a3cc..b7eb89872e96 100644 --- a/js/src/jit/arm64/MacroAssembler-arm64.cpp +++ b/js/src/jit/arm64/MacroAssembler-arm64.cpp @@ -441,6 +441,13 @@ MacroAssembler::Push(Register reg) adjustFrame(sizeof(intptr_t)); } +void +MacroAssembler::Push(Register reg1, Register reg2, Register reg3, Register reg4) +{ + push(reg1, reg2, reg3, reg4); + adjustFrame(4 * sizeof(intptr_t)); +} + void MacroAssembler::Push(const Imm32 imm) { diff --git a/js/src/jit/arm64/SharedICHelpers-arm64.h b/js/src/jit/arm64/SharedICHelpers-arm64.h index c563d1e21e19..4766a1cad427 100644 --- a/js/src/jit/arm64/SharedICHelpers-arm64.h +++ b/js/src/jit/arm64/SharedICHelpers-arm64.h @@ -163,7 +163,7 @@ EmitBaselineEnterStubFrame(MacroAssembler& masm, Register scratch) // Push frame descriptor and return address. // Save old frame pointer, stack pointer, and stub reg. masm.makeFrameDescriptor(scratch, JitFrame_BaselineJS); - masm.push(scratch, ICTailCallReg, ICStubReg, BaselineFrameReg); + masm.Push(scratch, ICTailCallReg, ICStubReg, BaselineFrameReg); // Update the frame register. masm.Mov(BaselineFrameReg64, masm.GetStackPointer64()); @@ -215,11 +215,12 @@ EmitStowICValues(MacroAssembler& masm, int values) switch (values) { case 1: // Stow R0. - masm.pushValue(R0); + masm.Push(R0); break; case 2: // Stow R0 and R1. - masm.push(R0.valueReg(), R1.valueReg()); + masm.Push(R0.valueReg()); + masm.Push(R1.valueReg()); break; default: MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Expected 1 or 2 values"); @@ -248,6 +249,7 @@ EmitUnstowICValues(MacroAssembler& masm, int values, bool discard = false) default: MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Expected 1 or 2 values"); } + masm.adjustFrame(-values * sizeof(Value)); } inline void @@ -286,7 +288,9 @@ EmitCallTypeUpdateIC(MacroAssembler& masm, JitCode* code, uint32_t objectOffset) EmitBaselineEnterStubFrame(masm, R1.scratchReg()); masm.loadValue(Address(masm.getStackPointer(), STUB_FRAME_SIZE + objectOffset), R1); - masm.push(R0.valueReg(), R1.valueReg(), ICStubReg); + masm.Push(R0.valueReg()); + masm.Push(R1.valueReg()); + masm.Push(ICStubReg); // Load previous frame pointer, push BaselineFrame*. masm.loadPtr(Address(BaselineFrameReg, 0), R0.scratchReg()); diff --git a/js/src/jit/arm64/Trampoline-arm64.cpp b/js/src/jit/arm64/Trampoline-arm64.cpp index 7b18be7b8077..89b126e23c9a 100644 --- a/js/src/jit/arm64/Trampoline-arm64.cpp +++ b/js/src/jit/arm64/Trampoline-arm64.cpp @@ -780,6 +780,11 @@ JitCode* JitRuntime::generateDebugTrapHandler(JSContext* cx) { MacroAssembler masm(cx); +#ifndef JS_USE_LINK_REGISTER + // The first value contains the return addres, + // which we pull into ICTailCallReg for tail calls. + masm.setFramePushed(sizeof(intptr_t)); +#endif Register scratch1 = r0; Register scratch2 = r1; diff --git a/js/src/jit/mips32/SharedICHelpers-mips32.h b/js/src/jit/mips32/SharedICHelpers-mips32.h index 52557562e984..dd4e8d27fb93 100644 --- a/js/src/jit/mips32/SharedICHelpers-mips32.h +++ b/js/src/jit/mips32/SharedICHelpers-mips32.h @@ -229,13 +229,12 @@ EmitStowICValues(MacroAssembler& masm, int values) switch(values) { case 1: // Stow R0 - masm.pushValue(R0); + masm.Push(R0); break; case 2: // Stow R0 and R1 - masm.pushValue(R0); - masm.pushValue(R1); - break; + masm.Push(R0); + masm.Push(R1); } } @@ -261,6 +260,7 @@ EmitUnstowICValues(MacroAssembler& masm, int values, bool discard = false) } break; } + masm.adjustFrame(-values * sizeof(Value)); } inline void @@ -302,9 +302,9 @@ EmitCallTypeUpdateIC(MacroAssembler& masm, JitCode* code, uint32_t objectOffset) masm.loadValue(Address(BaselineStackReg, STUB_FRAME_SIZE + objectOffset), R1); - masm.pushValue(R0); - masm.pushValue(R1); - masm.push(ICStubReg); + masm.Push(R0); + masm.Push(R1); + masm.Push(ICStubReg); // Load previous frame pointer, push BaselineFrame*. masm.loadPtr(Address(BaselineFrameReg, 0), R0.scratchReg()); diff --git a/js/src/jit/x64/SharedICHelpers-x64.h b/js/src/jit/x64/SharedICHelpers-x64.h index 518772a7f334..215332f76cf3 100644 --- a/js/src/jit/x64/SharedICHelpers-x64.h +++ b/js/src/jit/x64/SharedICHelpers-x64.h @@ -21,13 +21,13 @@ static const size_t ICStackValueOffset = sizeof(void*); inline void EmitRestoreTailCallReg(MacroAssembler& masm) { - masm.pop(ICTailCallReg); + masm.Pop(ICTailCallReg); } inline void EmitRepushTailCallReg(MacroAssembler& masm) { - masm.push(ICTailCallReg); + masm.Push(ICTailCallReg); } inline void @@ -136,7 +136,13 @@ EmitBaselineCallVM(JitCode* target, MacroAssembler& masm) inline void EmitIonCallVM(JitCode* target, size_t stackSlots, MacroAssembler& masm) { - uint32_t descriptor = MakeFrameDescriptor(masm.framePushed(), JitFrame_IonStub); + // Stubs often use the return address. Which is actually accounted by the + // caller of the stub. Though in the stubcode we fake that is part of the + // stub. In order to make it possible to pop it. As a result we have to + // fix it here, by subtracting it. Else it would be counted twice. + uint32_t framePushed = masm.framePushed() - sizeof(void*); + + uint32_t descriptor = MakeFrameDescriptor(framePushed, JitFrame_IonStub); masm.Push(Imm32(descriptor)); masm.call(target); @@ -171,21 +177,21 @@ EmitBaselineEnterStubFrame(MacroAssembler& masm, Register) // Push frame descriptor and return address. masm.makeFrameDescriptor(scratch, JitFrame_BaselineJS); - masm.push(scratch); - masm.push(ICTailCallReg); + masm.Push(scratch); + masm.Push(ICTailCallReg); // Save old frame pointer, stack pointer and stub reg. - masm.push(ICStubReg); - masm.push(BaselineFrameReg); + masm.Push(ICStubReg); + masm.Push(BaselineFrameReg); masm.mov(BaselineStackReg, BaselineFrameReg); } inline void EmitIonEnterStubFrame(MacroAssembler& masm, Register) { - masm.pop(ICTailCallReg); - masm.push(ICTailCallReg); - masm.push(ICStubReg); + masm.loadPtr(Address(masm.getStackPointer(), 0), ICTailCallReg); + masm.Push(ICTailCallReg); + masm.Push(ICStubReg); } inline void @@ -197,18 +203,18 @@ EmitBaselineLeaveStubFrame(MacroAssembler& masm, bool calledIntoIon = false) // in that case we use the frame pointer. if (calledIntoIon) { ScratchRegisterScope scratch(masm); - masm.pop(scratch); + masm.Pop(scratch); masm.shrq(Imm32(FRAMESIZE_SHIFT), scratch); masm.addq(scratch, BaselineStackReg); } else { masm.mov(BaselineFrameReg, BaselineStackReg); } - masm.pop(BaselineFrameReg); - masm.pop(ICStubReg); + masm.Pop(BaselineFrameReg); + masm.Pop(ICStubReg); // Pop return address. - masm.pop(ICTailCallReg); + masm.Pop(ICTailCallReg); // Overwrite frame descriptor with return address, so that the stack matches // the state before entering the stub frame. @@ -218,8 +224,8 @@ EmitBaselineLeaveStubFrame(MacroAssembler& masm, bool calledIntoIon = false) inline void EmitIonLeaveStubFrame(MacroAssembler& masm) { - masm.pop(ICStubReg); - masm.pop(ICTailCallReg); + masm.Pop(ICStubReg); + masm.Pop(ICTailCallReg); } inline void @@ -230,14 +236,14 @@ EmitStowICValues(MacroAssembler& masm, int values) case 1: // Stow R0 masm.pop(ICTailCallReg); - masm.pushValue(R0); + masm.Push(R0); masm.push(ICTailCallReg); break; case 2: // Stow R0 and R1 masm.pop(ICTailCallReg); - masm.pushValue(R0); - masm.pushValue(R1); + masm.Push(R0); + masm.Push(R1); masm.push(ICTailCallReg); break; } @@ -269,6 +275,7 @@ EmitUnstowICValues(MacroAssembler& masm, int values, bool discard = false) masm.push(ICTailCallReg); break; } + masm.adjustFrame(-values * sizeof(Value)); } inline void @@ -303,9 +310,9 @@ EmitCallTypeUpdateIC(MacroAssembler& masm, JitCode* code, uint32_t objectOffset) masm.loadValue(Address(BaselineStackReg, STUB_FRAME_SIZE + objectOffset), R1); - masm.pushValue(R0); - masm.pushValue(R1); - masm.push(ICStubReg); + masm.Push(R0); + masm.Push(R1); + masm.Push(ICStubReg); // Load previous frame pointer, push BaselineFrame*. masm.loadPtr(Address(BaselineFrameReg, 0), R0.scratchReg()); diff --git a/js/src/jit/x64/Trampoline-x64.cpp b/js/src/jit/x64/Trampoline-x64.cpp index 9d04f663a0f1..47ee313485dd 100644 --- a/js/src/jit/x64/Trampoline-x64.cpp +++ b/js/src/jit/x64/Trampoline-x64.cpp @@ -853,6 +853,11 @@ JitCode* JitRuntime::generateDebugTrapHandler(JSContext* cx) { MacroAssembler masm; +#ifndef JS_USE_LINK_REGISTER + // The first value contains the return addres, + // which we pull into ICTailCallReg for tail calls. + masm.setFramePushed(sizeof(intptr_t)); +#endif Register scratch1 = rax; Register scratch2 = rcx; diff --git a/js/src/jit/x86-shared/MacroAssembler-x86-shared.cpp b/js/src/jit/x86-shared/MacroAssembler-x86-shared.cpp index 417f980138b5..b5e32eee42d9 100644 --- a/js/src/jit/x86-shared/MacroAssembler-x86-shared.cpp +++ b/js/src/jit/x86-shared/MacroAssembler-x86-shared.cpp @@ -410,28 +410,28 @@ void MacroAssembler::Push(const Operand op) { push(op); - framePushed_ += sizeof(intptr_t); + adjustFrame(sizeof(intptr_t)); } void MacroAssembler::Push(Register reg) { push(reg); - framePushed_ += sizeof(intptr_t); + adjustFrame(sizeof(intptr_t)); } void MacroAssembler::Push(const Imm32 imm) { push(imm); - framePushed_ += sizeof(intptr_t); + adjustFrame(sizeof(intptr_t)); } void MacroAssembler::Push(const ImmWord imm) { push(imm); - framePushed_ += sizeof(intptr_t); + adjustFrame(sizeof(intptr_t)); } void @@ -444,42 +444,42 @@ void MacroAssembler::Push(const ImmGCPtr ptr) { push(ptr); - framePushed_ += sizeof(intptr_t); + adjustFrame(sizeof(intptr_t)); } void MacroAssembler::Push(FloatRegister t) { push(t); - framePushed_ += sizeof(double); + adjustFrame(sizeof(double)); } void MacroAssembler::Pop(const Operand op) { pop(op); - framePushed_ -= sizeof(intptr_t); + implicitPop(sizeof(intptr_t)); } void MacroAssembler::Pop(Register reg) { pop(reg); - framePushed_ -= sizeof(intptr_t); + implicitPop(sizeof(intptr_t)); } void MacroAssembler::Pop(FloatRegister reg) { pop(reg); - framePushed_ -= sizeof(double); + implicitPop(sizeof(double)); } void MacroAssembler::Pop(const ValueOperand& val) { popValue(val); - framePushed_ -= sizeof(Value); + implicitPop(sizeof(Value)); } // =============================================================== diff --git a/js/src/jit/x86/SharedICHelpers-x86.h b/js/src/jit/x86/SharedICHelpers-x86.h index b55643edf210..cc131532a853 100644 --- a/js/src/jit/x86/SharedICHelpers-x86.h +++ b/js/src/jit/x86/SharedICHelpers-x86.h @@ -21,13 +21,13 @@ static const size_t ICStackValueOffset = sizeof(void*); inline void EmitRestoreTailCallReg(MacroAssembler& masm) { - masm.pop(ICTailCallReg); + masm.Pop(ICTailCallReg); } inline void EmitRepushTailCallReg(MacroAssembler& masm) { - masm.push(ICTailCallReg); + masm.Push(ICTailCallReg); } inline void @@ -134,7 +134,13 @@ EmitBaselineCallVM(JitCode* target, MacroAssembler& masm) inline void EmitIonCallVM(JitCode* target, size_t stackSlots, MacroAssembler& masm) { - uint32_t descriptor = MakeFrameDescriptor(masm.framePushed(), JitFrame_IonStub); + // Stubs often use the return address. Which is actually accounted by the + // caller of the stub. Though in the stubcode we fake that is part of the + // stub. In order to make it possible to pop it. As a result we have to + // fix it here, by subtracting it. Else it would be counted twice. + uint32_t framePushed = masm.framePushed() - sizeof(void*); + + uint32_t descriptor = MakeFrameDescriptor(framePushed, JitFrame_IonStub); masm.Push(Imm32(descriptor)); masm.call(target); @@ -169,12 +175,12 @@ EmitBaselineEnterStubFrame(MacroAssembler& masm, Register scratch) // Push frame descriptor and return address. masm.makeFrameDescriptor(scratch, JitFrame_BaselineJS); - masm.push(scratch); - masm.push(ICTailCallReg); + masm.Push(scratch); + masm.Push(ICTailCallReg); // Save old frame pointer, stack pointer and stub reg. - masm.push(ICStubReg); - masm.push(BaselineFrameReg); + masm.Push(ICStubReg); + masm.Push(BaselineFrameReg); masm.mov(BaselineStackReg, BaselineFrameReg); } @@ -183,9 +189,9 @@ EmitIonEnterStubFrame(MacroAssembler& masm, Register scratch) { MOZ_ASSERT(scratch != ICTailCallReg); - masm.pop(ICTailCallReg); - masm.push(ICTailCallReg); - masm.push(ICStubReg); + masm.loadPtr(Address(masm.getStackPointer(), 0), ICTailCallReg); + masm.Push(ICTailCallReg); + masm.Push(ICStubReg); } inline void @@ -197,18 +203,18 @@ EmitBaselineLeaveStubFrame(MacroAssembler& masm, bool calledIntoIon = false) // in that case we use the frame pointer. if (calledIntoIon) { Register scratch = ICTailCallReg; - masm.pop(scratch); + masm.Pop(scratch); masm.shrl(Imm32(FRAMESIZE_SHIFT), scratch); masm.addl(scratch, BaselineStackReg); } else { masm.mov(BaselineFrameReg, BaselineStackReg); } - masm.pop(BaselineFrameReg); - masm.pop(ICStubReg); + masm.Pop(BaselineFrameReg); + masm.Pop(ICStubReg); // Pop return address. - masm.pop(ICTailCallReg); + masm.Pop(ICTailCallReg); // Overwrite frame descriptor with return address, so that the stack matches // the state before entering the stub frame. @@ -218,8 +224,8 @@ EmitBaselineLeaveStubFrame(MacroAssembler& masm, bool calledIntoIon = false) inline void EmitIonLeaveStubFrame(MacroAssembler& masm) { - masm.pop(ICStubReg); - masm.pop(ICTailCallReg); + masm.Pop(ICStubReg); + masm.Pop(ICTailCallReg); } inline void @@ -230,14 +236,14 @@ EmitStowICValues(MacroAssembler& masm, int values) case 1: // Stow R0 masm.pop(ICTailCallReg); - masm.pushValue(R0); + masm.Push(R0); masm.push(ICTailCallReg); break; case 2: // Stow R0 and R1 masm.pop(ICTailCallReg); - masm.pushValue(R0); - masm.pushValue(R1); + masm.Push(R0); + masm.Push(R1); masm.push(ICTailCallReg); break; } @@ -269,6 +275,7 @@ EmitUnstowICValues(MacroAssembler& masm, int values, bool discard = false) masm.push(ICTailCallReg); break; } + masm.adjustFrame(-values * sizeof(Value)); } inline void @@ -303,9 +310,9 @@ EmitCallTypeUpdateIC(MacroAssembler& masm, JitCode* code, uint32_t objectOffset) masm.loadValue(Address(BaselineStackReg, STUB_FRAME_SIZE + objectOffset), R1); - masm.pushValue(R0); - masm.pushValue(R1); - masm.push(ICStubReg); + masm.Push(R0); + masm.Push(R1); + masm.Push(ICStubReg); // Load previous frame pointer, push BaselineFrame*. masm.loadPtr(Address(BaselineFrameReg, 0), R0.scratchReg()); diff --git a/js/src/jit/x86/Trampoline-x86.cpp b/js/src/jit/x86/Trampoline-x86.cpp index e74043c513bb..a0600cb1739e 100644 --- a/js/src/jit/x86/Trampoline-x86.cpp +++ b/js/src/jit/x86/Trampoline-x86.cpp @@ -881,6 +881,11 @@ JitCode* JitRuntime::generateDebugTrapHandler(JSContext* cx) { MacroAssembler masm; +#ifndef JS_USE_LINK_REGISTER + // The first value contains the return addres, + // which we pull into ICTailCallReg for tail calls. + masm.setFramePushed(sizeof(intptr_t)); +#endif Register scratch1 = eax; Register scratch2 = ecx; diff --git a/js/src/jsapi-tests/testUbiNode.cpp b/js/src/jsapi-tests/testUbiNode.cpp index 586bd94a6756..40e26a94e5e6 100644 --- a/js/src/jsapi-tests/testUbiNode.cpp +++ b/js/src/jsapi-tests/testUbiNode.cpp @@ -4,6 +4,7 @@ #include "builtin/TestingFunctions.h" #include "js/UbiNode.h" +#include "js/UbiNodeDominatorTree.h" #include "js/UbiNodePostOrder.h" #include "jsapi-tests/tests.h" #include "vm/SavedFrame.h" @@ -266,6 +267,35 @@ BEGIN_TEST(test_ubiCoarseType) } END_TEST(test_ubiCoarseType) +struct ExpectedEdge +{ + char from; + char to; + + ExpectedEdge(FakeNode& fromNode, FakeNode& toNode) + : from(fromNode.name) + , to(toNode.name) + { } +}; + +namespace js { + +template <> +struct DefaultHasher +{ + using Lookup = ExpectedEdge; + + static HashNumber hash(const Lookup& l) { + return mozilla::AddToHash(l.from, l.to); + } + + static bool match(const ExpectedEdge& k, const Lookup& l) { + return k.from == l.from && k.to == l.to; + } +}; + +} // namespace js + BEGIN_TEST(test_ubiPostOrder) { // Construct the following graph: @@ -304,29 +334,56 @@ BEGIN_TEST(test_ubiPostOrder) FakeNode f('f'); FakeNode g('g'); - r.addEdgeTo(a); - r.addEdgeTo(e); - a.addEdgeTo(b); - a.addEdgeTo(c); - b.addEdgeTo(d); - c.addEdgeTo(a); - c.addEdgeTo(d); - c.addEdgeTo(f); - e.addEdgeTo(f); - e.addEdgeTo(g); - f.addEdgeTo(g); + js::HashSet expectedEdges(cx); + CHECK(expectedEdges.init()); + + auto declareEdge = [&](FakeNode& from, FakeNode& to) { + return from.addEdgeTo(to) && expectedEdges.putNew(ExpectedEdge(from, to)); + }; + + CHECK(declareEdge(r, a)); + CHECK(declareEdge(r, e)); + CHECK(declareEdge(a, b)); + CHECK(declareEdge(a, c)); + CHECK(declareEdge(b, d)); + CHECK(declareEdge(c, a)); + CHECK(declareEdge(c, d)); + CHECK(declareEdge(c, f)); + CHECK(declareEdge(e, f)); + CHECK(declareEdge(e, g)); + CHECK(declareEdge(f, g)); js::Vector visited; { // Do a PostOrder traversal, starting from r. Accumulate the names of - // the nodes we visit in `visited`. + // the nodes we visit in `visited`. Remove edges we traverse from + // `expectedEdges` as we find them to ensure that we only find each edge + // once. + JS::AutoCheckCannotGC nogc(rt); JS::ubi::PostOrder traversal(rt, nogc); CHECK(traversal.init()); CHECK(traversal.addStart(&r)); - CHECK(traversal.traverse([&](const JS::ubi::Node& node) { + + auto onNode = [&](const JS::ubi::Node& node) { return visited.append(node.as()->name); - })); + }; + + auto onEdge = [&](const JS::ubi::Node& origin, const JS::ubi::Edge& edge) { + ExpectedEdge e(*origin.as(), *edge.referent.as()); + if (!expectedEdges.has(e)) { + fprintf(stderr, + "Error: Unexpected edge from %c to %c!\n", + origin.as()->name, + edge.referent.as()->name); + return false; + } + + expectedEdges.remove(e); + return true; + }; + + CHECK(traversal.traverse(onNode, onEdge)); } fprintf(stderr, "visited.length() = %lu\n", (unsigned long) visited.length()); @@ -343,10 +400,182 @@ BEGIN_TEST(test_ubiPostOrder) CHECK(visited[6] == 'a'); CHECK(visited[7] == 'r'); + // We found all the edges we expected. + CHECK(expectedEdges.count() == 0); + return true; } END_TEST(test_ubiPostOrder) +BEGIN_TEST(test_JS_ubi_DominatorTree) +{ + // Construct the following graph: + // + // .-----. + // | <--------------------------------. + // .--------+--------------| r |--------------. | + // | | | | | | + // | | '-----' | | + // | .--V--. .--V--. | + // | | | | | | + // | | b | | c |--------. | + // | | | | | | | + // | '-----' '-----' | | + // .--V--. | | .--V--. | + // | | | | | | | + // | a <-----+ | .----| g | | + // | | | .----' | | | | + // '-----' | | | '-----' | + // | | | | | | + // .--V--. | .-----. .--V--. | | | + // | | | | | | | | | | + // | d <-----+----> e <----. | f | | | | + // | | | | | | | | | | + // '-----' '-----' | '-----' | | | + // | .-----. | | | | .--V--. | + // | | | | | | .-' | | | + // '-----> l | | | | | | j | | + // | | '--. | | | | | | + // '-----' | | | | '-----' | + // | .--V--. | | .--V--. | | + // | | | | | | | | | + // '-------> h |-' '---> i <------' | + // | | .---------> | | + // '-----' | '-----' | + // | .-----. | | + // | | | | | + // '----------> k <---------' | + // | | | + // '-----' | + // | | + // '----------------------------' + // + // This graph has the following dominator tree: + // + // r + // |-- a + // |-- b + // |-- c + // | |-- f + // | `-- g + // | `-- j + // |-- d + // | `-- l + // |-- e + // |-- i + // |-- k + // `-- h + // + // This graph and dominator tree are taken from figures 1 and 2 of "A Fast + // Algorithm for Finding Dominators in a Flowgraph" by Lengauer et al: + // http://www.cs.princeton.edu/courses/archive/spr03/cs423/download/dominators.pdf. + + FakeNode r('r'); + FakeNode a('a'); + FakeNode b('b'); + FakeNode c('c'); + FakeNode d('d'); + FakeNode e('e'); + FakeNode f('f'); + FakeNode g('g'); + FakeNode h('h'); + FakeNode i('i'); + FakeNode j('j'); + FakeNode k('k'); + FakeNode l('l'); + + CHECK(r.addEdgeTo(a)); + CHECK(r.addEdgeTo(b)); + CHECK(r.addEdgeTo(c)); + CHECK(a.addEdgeTo(d)); + CHECK(b.addEdgeTo(a)); + CHECK(b.addEdgeTo(d)); + CHECK(b.addEdgeTo(e)); + CHECK(c.addEdgeTo(f)); + CHECK(c.addEdgeTo(g)); + CHECK(d.addEdgeTo(l)); + CHECK(e.addEdgeTo(h)); + CHECK(f.addEdgeTo(i)); + CHECK(g.addEdgeTo(i)); + CHECK(g.addEdgeTo(j)); + CHECK(h.addEdgeTo(e)); + CHECK(h.addEdgeTo(k)); + CHECK(i.addEdgeTo(k)); + CHECK(j.addEdgeTo(i)); + CHECK(k.addEdgeTo(r)); + CHECK(k.addEdgeTo(i)); + CHECK(l.addEdgeTo(h)); + + mozilla::Maybe maybeTree; + { + JS::AutoCheckCannotGC noGC(rt); + maybeTree = JS::ubi::DominatorTree::Create(rt, noGC, &r); + } + + CHECK(maybeTree.isSome()); + auto& tree = *maybeTree; + + // We return the null JS::ubi::Node for nodes that were not reachable in the + // graph when computing the dominator tree. + FakeNode m('m'); + CHECK(tree.getImmediateDominator(&m) == JS::ubi::Node()); + + fprintf(stderr, "r's immediate dominator is %c\n", + tree.getImmediateDominator(&r).as()->name); + CHECK(tree.getImmediateDominator(&r) == JS::ubi::Node(&r)); + + fprintf(stderr, "a's immediate dominator is %c\n", + tree.getImmediateDominator(&a).as()->name); + CHECK(tree.getImmediateDominator(&a) == JS::ubi::Node(&r)); + + fprintf(stderr, "b's immediate dominator is %c\n", + tree.getImmediateDominator(&b).as()->name); + CHECK(tree.getImmediateDominator(&b) == JS::ubi::Node(&r)); + + fprintf(stderr, "c's immediate dominator is %c\n", + tree.getImmediateDominator(&c).as()->name); + CHECK(tree.getImmediateDominator(&c) == JS::ubi::Node(&r)); + + fprintf(stderr, "d's immediate dominator is %c\n", + tree.getImmediateDominator(&d).as()->name); + CHECK(tree.getImmediateDominator(&d) == JS::ubi::Node(&r)); + + fprintf(stderr, "e's immediate dominator is %c\n", + tree.getImmediateDominator(&e).as()->name); + CHECK(tree.getImmediateDominator(&e) == JS::ubi::Node(&r)); + + fprintf(stderr, "f's immediate dominator is %c\n", + tree.getImmediateDominator(&f).as()->name); + CHECK(tree.getImmediateDominator(&f) == JS::ubi::Node(&c)); + + fprintf(stderr, "g's immediate dominator is %c\n", + tree.getImmediateDominator(&g).as()->name); + CHECK(tree.getImmediateDominator(&g) == JS::ubi::Node(&c)); + + fprintf(stderr, "h's immediate dominator is %c\n", + tree.getImmediateDominator(&h).as()->name); + CHECK(tree.getImmediateDominator(&h) == JS::ubi::Node(&r)); + + fprintf(stderr, "i's immediate dominator is %c\n", + tree.getImmediateDominator(&i).as()->name); + CHECK(tree.getImmediateDominator(&i) == JS::ubi::Node(&r)); + + fprintf(stderr, "j's immediate dominator is %c\n", + tree.getImmediateDominator(&j).as()->name); + CHECK(tree.getImmediateDominator(&j) == JS::ubi::Node(&g)); + + fprintf(stderr, "k's immediate dominator is %c\n", + tree.getImmediateDominator(&k).as()->name); + CHECK(tree.getImmediateDominator(&k) == JS::ubi::Node(&r)); + + fprintf(stderr, "l's immediate dominator is %c\n", + tree.getImmediateDominator(&l).as()->name); + CHECK(tree.getImmediateDominator(&l) == JS::ubi::Node(&d)); + + return true; +} +END_TEST(test_JS_ubi_DominatorTree) + BEGIN_TEST(test_JS_ubi_Node_scriptFilename) { JS::RootedValue val(cx); diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index 0580b74dcc0a..8e24b1fb348f 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -2915,9 +2915,9 @@ JS_FRIEND_API(bool) IsWindowProxy(JSObject* obj); /** - * If `obj` is a Window, get its associated WindowProxy (or a CCW if the - * page was navigated away from), else return `obj`. This function is - * infallible and never returns nullptr. + * If `obj` is a Window, get its associated WindowProxy (or a CCW or dead + * wrapper if the page was navigated away from), else return `obj`. This + * function is infallible and never returns nullptr. */ extern JS_FRIEND_API(JSObject*) ToWindowProxyIfWindow(JSObject* obj); diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index ca3be7dd321a..8b7fd042f7ed 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -6862,7 +6862,7 @@ gc::MergeCompartments(JSCompartment* source, JSCompartment* target) target->zone()->types.typeLifoAlloc.transferFrom(&source->zone()->types.typeLifoAlloc); // Ensure that we did not create any UIDs when running off-thread. - source->zone()->assertNoUniqueIdsInZone(); + target->zone()->adoptUniqueIds(source->zone()); } void diff --git a/js/src/moz.build b/js/src/moz.build index ddcfae632cff..04598a9b2008 100644 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -134,6 +134,7 @@ EXPORTS.js += [ '../public/UbiNode.h', '../public/UbiNodeBreadthFirst.h', '../public/UbiNodeCensus.h', + '../public/UbiNodeDominatorTree.h', '../public/UbiNodePostOrder.h', '../public/Utility.h', '../public/Value.h', diff --git a/js/src/vm/ObjectGroup.cpp b/js/src/vm/ObjectGroup.cpp index 36de84d365f3..c70baabce9fc 100644 --- a/js/src/vm/ObjectGroup.cpp +++ b/js/src/vm/ObjectGroup.cpp @@ -366,9 +366,7 @@ JSObject::setNewGroupUnknown(JSContext* cx, const js::Class* clasp, JS::HandleOb struct ObjectGroupCompartment::NewEntry { ReadBarrieredObjectGroup group; - - // Note: This pointer is only used for equality and does not need a read barrier. - JSObject* associated; + ReadBarrieredObject associated; NewEntry(ObjectGroup* group, JSObject* associated) : group(group), associated(associated) @@ -376,90 +374,32 @@ struct ObjectGroupCompartment::NewEntry struct Lookup { const Class* clasp; - TaggedProto hashProto; - TaggedProto matchProto; + TaggedProto proto; JSObject* associated; Lookup(const Class* clasp, TaggedProto proto, JSObject* associated) - : clasp(clasp), hashProto(proto), matchProto(proto), associated(associated) - {} - - /* - * For use by generational post barriers only. Look up an entry whose - * proto has been moved, but was hashed with the original value. - */ - Lookup(const Class* clasp, TaggedProto hashProto, TaggedProto matchProto, JSObject* associated) - : clasp(clasp), hashProto(hashProto), matchProto(matchProto), associated(associated) + : clasp(clasp), proto(proto), associated(associated) {} }; static inline HashNumber hash(const Lookup& lookup) { - return PointerHasher::hash(lookup.hashProto.raw()) ^ + return lookup.proto.hashCode() ^ PointerHasher::hash(lookup.clasp) ^ - PointerHasher::hash(lookup.associated); + MovableCellHasher::hash(lookup.associated); } static inline bool match(const NewEntry& key, const Lookup& lookup) { - return key.group.unbarrieredGet()->proto() == lookup.matchProto && - (!lookup.clasp || key.group.unbarrieredGet()->clasp() == lookup.clasp) && - key.associated == lookup.associated; + TaggedProto keyProto = key.group.unbarrieredGet()->proto(); + const Class* keyClasp = key.group.unbarrieredGet()->clasp(); + JSObject* keyAssociated = key.associated.unbarrieredGet(); + return keyProto.uniqueId() == lookup.proto.uniqueId() && + (!lookup.clasp || PointerHasher::match(keyClasp, lookup.clasp)) && + MovableCellHasher::match(keyAssociated, lookup.associated); } static void rekey(NewEntry& k, const NewEntry& newKey) { k = newKey; } }; -// This class is used to add a post barrier on a NewTable entry, as the key is -// calculated from a prototype object which may be moved by generational GC. -class ObjectGroupCompartment::NewTableRef : public gc::BufferableRef -{ - NewTable* table; - const Class* clasp; - JSObject* proto; - JSObject* associated; - - public: - NewTableRef(NewTable* table, const Class* clasp, JSObject* proto, JSObject* associated) - : table(table), clasp(clasp), proto(proto), associated(associated) - {} - - void trace(JSTracer* trc) override { - JSObject* prior = proto; - TraceManuallyBarrieredEdge(trc, &proto, "newObjectGroups set prototype"); - if (prior == proto) - return; - - NewTable::Ptr p = table->lookup(NewTable::Lookup(clasp, TaggedProto(prior), - TaggedProto(proto), - associated)); - if (!p) - return; - - table->rekeyAs(NewTable::Lookup(clasp, TaggedProto(prior), TaggedProto(proto), associated), - NewTable::Lookup(clasp, TaggedProto(proto), associated), *p); - } -}; - -/* static */ void -ObjectGroupCompartment::newTablePostBarrier(ExclusiveContext* cx, NewTable* table, - const Class* clasp, TaggedProto proto, - JSObject* associated) -{ - MOZ_ASSERT_IF(associated, !IsInsideNursery(associated)); - - if (!proto.isObject()) - return; - - if (!cx->isJSContext()) { - MOZ_ASSERT(!IsInsideNursery(proto.toObject())); - return; - } - - if (IsInsideNursery(proto.toObject())) { - gc::StoreBuffer& sb = cx->asJSContext()->runtime()->gc.storeBuffer; - sb.putGeneric(NewTableRef(table, clasp, proto.toObject(), associated)); - } -} - /* static */ ObjectGroup* ObjectGroup::defaultNewGroup(ExclusiveContext* cx, const Class* clasp, TaggedProto proto, JSObject* associated) @@ -553,8 +493,6 @@ ObjectGroup::defaultNewGroup(ExclusiveContext* cx, const Class* clasp, return nullptr; } - ObjectGroupCompartment::newTablePostBarrier(cx, table, clasp, proto, associated); - if (proto.isObject()) { RootedObject obj(cx, proto.toObject()); @@ -639,8 +577,6 @@ ObjectGroup::lazySingletonGroup(ExclusiveContext* cx, const Class* clasp, Tagged return nullptr; } - ObjectGroupCompartment::newTablePostBarrier(cx, table, clasp, proto, nullptr); - return group; } @@ -1782,18 +1718,15 @@ ObjectGroupCompartment::sweep(FreeOp* fop) void ObjectGroupCompartment::sweepNewTable(NewTable* table) { - if (table && table->initialized()) { - for (NewTable::Enum e(*table); !e.empty(); e.popFront()) { - NewEntry entry = e.front(); - if (IsAboutToBeFinalized(&entry.group) || - (entry.associated && IsAboutToBeFinalizedUnbarriered(&entry.associated))) - { - e.removeFront(); - } else { - /* Any rekeying necessary is handled by fixupNewObjectGroupTable() below. */ - MOZ_ASSERT(entry.group.unbarrieredGet() == e.front().group.unbarrieredGet()); - MOZ_ASSERT(entry.associated == e.front().associated); - } + if (!table || !table->initialized()) + return; + + for (NewTable::Enum e(*table); !e.empty(); e.popFront()) { + NewEntry entry = e.front(); + if (IsAboutToBeFinalized(&entry.group) || + (entry.associated && IsAboutToBeFinalized(&entry.associated))) + { + e.removeFront(); } } } @@ -1805,31 +1738,14 @@ ObjectGroupCompartment::fixupNewTableAfterMovingGC(NewTable* table) * Each entry's hash depends on the object's prototype and we can't tell * whether that has been moved or not in sweepNewObjectGroupTable(). */ - if (table && table->initialized()) { - for (NewTable::Enum e(*table); !e.empty(); e.popFront()) { - NewEntry entry = e.front(); - bool needRekey = false; - if (IsForwarded(entry.group.unbarrieredGet())) { - entry.group.set(Forwarded(entry.group.unbarrieredGet())); - needRekey = true; - } - TaggedProto proto = entry.group.unbarrieredGet()->proto(); - if (proto.isObject() && IsForwarded(proto.toObject())) { - proto = TaggedProto(Forwarded(proto.toObject())); - needRekey = true; - } - if (entry.associated && IsForwarded(entry.associated)) { - entry.associated = Forwarded(entry.associated); - needRekey = true; - } - if (needRekey) { - const Class* clasp = entry.group.unbarrieredGet()->clasp(); - if (entry.associated && entry.associated->is()) - clasp = nullptr; - NewEntry::Lookup lookup(clasp, proto, entry.associated); - e.rekeyFront(lookup, entry); - } - } + if (!table || !table->initialized()) + return; + + for (NewTable::Enum e(*table); !e.empty(); e.popFront()) { + if (IsForwarded(e.front().group.unbarrieredGet())) + e.mutableFront().group.set(Forwarded(e.front().group.unbarrieredGet())); + if (e.front().associated && IsForwarded(e.front().associated.unbarrieredGet())) + e.mutableFront().associated.set(Forwarded(e.front().associated.unbarrieredGet())); } } diff --git a/js/src/vm/ObjectGroup.h b/js/src/vm/ObjectGroup.h index a83fe0071ed0..ec862f955b97 100644 --- a/js/src/vm/ObjectGroup.h +++ b/js/src/vm/ObjectGroup.h @@ -539,7 +539,6 @@ class ObjectGroupCompartment struct NewEntry; typedef HashSet NewTable; - class NewTableRef; // Set of default 'new' or lazy groups in the compartment. NewTable* defaultNewTable; @@ -623,9 +622,6 @@ class ObjectGroupCompartment void sweepNewTable(NewTable* table); void fixupNewTableAfterMovingGC(NewTable* table); - - static void newTablePostBarrier(ExclusiveContext* cx, NewTable* table, - const Class* clasp, TaggedProto proto, JSObject* associated); }; PlainObject* diff --git a/js/src/vm/Shape.cpp b/js/src/vm/Shape.cpp index a05bbccb461c..a83d09b7b4c5 100644 --- a/js/src/vm/Shape.cpp +++ b/js/src/vm/Shape.cpp @@ -1350,12 +1350,13 @@ BaseShape::finalize(FreeOp* fop) } inline -InitialShapeEntry::InitialShapeEntry() : shape(nullptr), proto(nullptr) +InitialShapeEntry::InitialShapeEntry() : shape(nullptr), proto(TaggedProto(nullptr)) { } inline -InitialShapeEntry::InitialShapeEntry(const ReadBarrieredShape& shape, TaggedProto proto) +InitialShapeEntry::InitialShapeEntry(const ReadBarriered& shape, + const ReadBarriered& proto) : shape(shape), proto(proto) { } @@ -1369,73 +1370,19 @@ InitialShapeEntry::getLookup() const /* static */ inline HashNumber InitialShapeEntry::hash(const Lookup& lookup) { - HashNumber hash = uintptr_t(lookup.clasp) >> 3; - hash = RotateLeft(hash, 4) ^ - (uintptr_t(lookup.hashProto.toWord()) >> 3); - return hash + lookup.nfixed; + return (RotateLeft(uintptr_t(lookup.clasp) >> 3, 4) ^ lookup.proto.hashCode()) + lookup.nfixed; } /* static */ inline bool InitialShapeEntry::match(const InitialShapeEntry& key, const Lookup& lookup) { - const Shape* shape = *key.shape.unsafeGet(); + const Shape* shape = key.shape.unbarrieredGet(); return lookup.clasp == shape->getObjectClass() - && lookup.matchProto.toWord() == key.proto.toWord() && lookup.nfixed == shape->numFixedSlots() - && lookup.baseFlags == shape->getObjectFlags(); + && lookup.baseFlags == shape->getObjectFlags() + && lookup.proto.uniqueId() == key.proto.unbarrieredGet().uniqueId(); } -/* - * This class is used to add a post barrier on the initialShapes set, as the key - * is calculated based on objects which may be moved by generational GC. - */ -class InitialShapeSetRef : public BufferableRef -{ - InitialShapeSet* set; - const Class* clasp; - TaggedProto proto; - size_t nfixed; - uint32_t objectFlags; - - public: - InitialShapeSetRef(InitialShapeSet* set, - const Class* clasp, - TaggedProto proto, - size_t nfixed, - uint32_t objectFlags) - : set(set), - clasp(clasp), - proto(proto), - nfixed(nfixed), - objectFlags(objectFlags) - {} - - void trace(JSTracer* trc) override { - TaggedProto priorProto = proto; - if (proto.isObject()) { - TraceManuallyBarrieredEdge(trc, reinterpret_cast(&proto), - "initialShapes set proto"); - } - if (proto == priorProto) - return; - - /* Find the original entry, which must still be present. */ - InitialShapeEntry::Lookup lookup(clasp, priorProto, nfixed, objectFlags); - InitialShapeSet::Ptr p = set->lookup(lookup); - MOZ_ASSERT(p); - - /* Update the entry's possibly-moved proto, and ensure lookup will still match. */ - InitialShapeEntry& entry = const_cast(*p); - entry.proto = proto; - lookup.matchProto = proto; - - /* Rekey the entry. */ - set->rekeyAs(lookup, - InitialShapeEntry::Lookup(clasp, proto, nfixed, objectFlags), - *p); - } -}; - #ifdef JSGC_HASH_TABLE_CHECKS void @@ -1451,7 +1398,7 @@ JSCompartment::checkInitialShapesTableAfterMovingGC() */ for (InitialShapeSet::Enum e(initialShapes); !e.empty(); e.popFront()) { InitialShapeEntry entry = e.front(); - TaggedProto proto = entry.proto; + TaggedProto proto = entry.proto.unbarrieredGet(); Shape* shape = entry.shape.unbarrieredGet(); if (proto.isObject()) @@ -1512,17 +1459,9 @@ EmptyShape::getInitialShape(ExclusiveContext* cx, const Class* clasp, TaggedProt return nullptr; Lookup lookup(clasp, protoRoot, nfixed, objectFlags); - if (!p.add(cx, table, lookup, InitialShapeEntry(ReadBarrieredShape(shape), protoRoot))) + if (!p.add(cx, table, lookup, InitialShapeEntry(shape, protoRoot.get()))) return nullptr; - // Post-barrier for the initial shape table update. - if (cx->isJSContext()) { - if (protoRoot.isObject() && IsInsideNursery(protoRoot.toObject())) { - InitialShapeSetRef ref(&table, clasp, protoRoot, nfixed, objectFlags); - cx->asJSContext()->runtime()->gc.storeBuffer.putGeneric(ref); - } - } - return shape; } @@ -1603,22 +1542,14 @@ EmptyShape::insertInitialShape(ExclusiveContext* cx, HandleShape shape, HandleOb void JSCompartment::sweepInitialShapeTable() { - if (initialShapes.initialized()) { - for (InitialShapeSet::Enum e(initialShapes); !e.empty(); e.popFront()) { - const InitialShapeEntry& entry = e.front(); - Shape* shape = entry.shape.unbarrieredGet(); - JSObject* proto = entry.proto.raw(); - if (IsAboutToBeFinalizedUnbarriered(&shape) || - (entry.proto.isObject() && IsAboutToBeFinalizedUnbarriered(&proto))) - { - e.removeFront(); - } else { - if (shape != entry.shape.unbarrieredGet() || proto != entry.proto.raw()) { - ReadBarrieredShape readBarrieredShape(shape); - InitialShapeEntry newKey(readBarrieredShape, TaggedProto(proto)); - e.rekeyFront(newKey.getLookup(), newKey); - } - } + if (!initialShapes.initialized()) + return; + + for (InitialShapeSet::Enum e(initialShapes); !e.empty(); e.popFront()) { + if (IsAboutToBeFinalized(&e.mutableFront().shape) || + IsAboutToBeFinalized(&e.mutableFront().proto)) + { + e.removeFront(); } } } @@ -1630,23 +1561,10 @@ JSCompartment::fixupInitialShapeTable() return; for (InitialShapeSet::Enum e(initialShapes); !e.empty(); e.popFront()) { - InitialShapeEntry entry = e.front(); - bool needRekey = false; - if (IsForwarded(entry.shape.unbarrieredGet())) { - entry.shape.set(Forwarded(entry.shape.unbarrieredGet())); - needRekey = true; - } - if (entry.proto.isObject() && IsForwarded(entry.proto.toObject())) { - entry.proto = TaggedProto(Forwarded(entry.proto.toObject())); - needRekey = true; - } - if (needRekey) { - InitialShapeEntry::Lookup relookup(entry.shape.unbarrieredGet()->getObjectClass(), - entry.proto, - entry.shape.unbarrieredGet()->numFixedSlots(), - entry.shape.unbarrieredGet()->getObjectFlags()); - e.rekeyFront(relookup, entry); - } + if (IsForwarded(e.front().shape.unbarrieredGet())) + e.mutableFront().shape.set(Forwarded(e.front().shape.unbarrieredGet())); + if (e.front().proto.isObject() && IsForwarded(e.front().proto.toObject())) + e.mutableFront().proto = TaggedProto(Forwarded(e.front().proto.toObject())); } } diff --git a/js/src/vm/Shape.h b/js/src/vm/Shape.h index 51185898d800..f9bb9ca85fb2 100644 --- a/js/src/vm/Shape.h +++ b/js/src/vm/Shape.h @@ -1064,31 +1064,29 @@ struct InitialShapeEntry * certain classes (e.g. String, RegExp) which may add certain baked-in * properties. */ - ReadBarrieredShape shape; + ReadBarriered shape; /* * Matching prototype for the entry. The shape of an object determines its * prototype, but the prototype cannot be determined from the shape itself. */ - TaggedProto proto; + ReadBarriered proto; /* State used to determine a match on an initial shape. */ struct Lookup { const Class* clasp; - TaggedProto hashProto; - TaggedProto matchProto; + TaggedProto proto; uint32_t nfixed; uint32_t baseFlags; Lookup(const Class* clasp, TaggedProto proto, uint32_t nfixed, uint32_t baseFlags) - : clasp(clasp), - hashProto(proto), matchProto(proto), - nfixed(nfixed), baseFlags(baseFlags) + : clasp(clasp), proto(proto), nfixed(nfixed), baseFlags(baseFlags) {} }; inline InitialShapeEntry(); - inline InitialShapeEntry(const ReadBarrieredShape& shape, TaggedProto proto); + inline InitialShapeEntry(const ReadBarriered& shape, + const ReadBarriered& proto); inline Lookup getLookup() const; diff --git a/js/src/vm/TaggedProto.cpp b/js/src/vm/TaggedProto.cpp index f00f07d31c93..ba01c3670b1a 100644 --- a/js/src/vm/TaggedProto.cpp +++ b/js/src/vm/TaggedProto.cpp @@ -10,6 +10,8 @@ #include "jsobj.h" #include "gc/Barrier.h" +#include "gc/Zone.h" +#include "js/HashTable.h" namespace js { @@ -28,4 +30,32 @@ InternalGCMethods::postBarrier(TaggedProto* vp, TaggedProto prev, T nextObj); } +/* static */ void +InternalGCMethods::readBarrier(const TaggedProto& proto) +{ + InternalGCMethods::readBarrier(proto.toObjectOrNull()); +} + } // namespace js + +js::HashNumber +js::TaggedProto::hashCode() const +{ + uint64_t uid = uniqueId(); + return js::HashNumber(uid >> 32) ^ js::HashNumber(uid & 0xFFFFFFFF); +} + +uint64_t +js::TaggedProto::uniqueId() const +{ + if (isLazy()) + return uint64_t(1); + JSObject* obj = toObjectOrNull(); + if (!obj) + return uint64_t(0); + AutoEnterOOMUnsafeRegion oomUnsafe; + uint64_t uid; + if (!obj->zone()->getUniqueId(obj, &uid)) + oomUnsafe.crash("failed to get unique id for TaggedProto"); + return uid; +} diff --git a/js/src/vm/TaggedProto.h b/js/src/vm/TaggedProto.h index 72538802535e..929d0abbdcff 100644 --- a/js/src/vm/TaggedProto.h +++ b/js/src/vm/TaggedProto.h @@ -44,6 +44,9 @@ class TaggedProto : public JS::Traceable bool operator ==(const TaggedProto& other) const { return proto == other.proto; } bool operator !=(const TaggedProto& other) const { return proto != other.proto; } + HashNumber hashCode() const; + uint64_t uniqueId() const; + static void trace(TaggedProto* protop, JSTracer* trc) { TraceManuallyBarrieredEdge(trc, protop, "TaggedProto"); } @@ -63,6 +66,8 @@ template <> struct InternalGCMethods static void postBarrier(TaggedProto* vp, TaggedProto prev, TaggedProto next); + static void readBarrier(const TaggedProto& proto); + static bool isMarkableTaggedPointer(TaggedProto proto) { return proto.isObject(); } diff --git a/js/src/vm/WeakMapPtr.cpp b/js/src/vm/WeakMapPtr.cpp index c998d996100a..0788a4a75d1d 100644 --- a/js/src/vm/WeakMapPtr.cpp +++ b/js/src/vm/WeakMapPtr.cpp @@ -25,23 +25,25 @@ struct DataType template<> struct DataType { - typedef PreBarrieredObject PreBarriered; + using BarrieredType = RelocatablePtrObject; + using HasherType = MovableCellHasher; static JSObject* NullValue() { return nullptr; } }; template<> struct DataType { - typedef PreBarrieredValue PreBarriered; + using BarrieredType = RelocatablePtr; static JS::Value NullValue() { return JS::UndefinedValue(); } }; template struct Utils { - typedef typename DataType::PreBarriered KeyType; - typedef typename DataType::PreBarriered ValueType; - typedef WeakMap Type; + typedef typename DataType::BarrieredType KeyType; + typedef typename DataType::HasherType HasherType; + typedef typename DataType::BarrieredType ValueType; + typedef WeakMap Type; typedef Type* PtrType; static PtrType cast(void* ptr) { return static_cast(ptr); } }; @@ -88,27 +90,12 @@ JS::WeakMapPtr::lookup(const K& key) return result->value(); } -template -/* static */ void -JS::WeakMapPtr::keyMarkCallback(JSTracer* trc, K key, void* data) -{ - auto map = static_cast< JS::WeakMapPtr* >(data); - K prior = key; - JS_CallUnbarrieredObjectTracer(trc, &key, "WeakMapPtr key"); - return Utils::cast(map->ptr)->rekeyIfMoved(prior, key); -} - template bool JS::WeakMapPtr::put(JSContext* cx, const K& key, const V& value) { MOZ_ASSERT(initialized()); - if (!Utils::cast(ptr)->put(key, value)) - return false; - JS_StoreObjectPostBarrierCallback(cx, keyMarkCallback, key, this); - // Values do not need to be barriered because only put() is supported, - // which is always an initializing write. - return true; + return Utils::cast(ptr)->put(key, value); } // diff --git a/js/xpconnect/tests/chrome/chrome.ini b/js/xpconnect/tests/chrome/chrome.ini index 8d6e167c88e3..7e6b9439bca5 100644 --- a/js/xpconnect/tests/chrome/chrome.ini +++ b/js/xpconnect/tests/chrome/chrome.ini @@ -109,3 +109,4 @@ skip-if = buildapp == 'mulet' skip-if = true [test_watchpoints.xul] [test_nsScriptErrorWithStack.html] +[test_windowProxyDeadWrapper.html] diff --git a/js/xpconnect/tests/chrome/test_windowProxyDeadWrapper.html b/js/xpconnect/tests/chrome/test_windowProxyDeadWrapper.html new file mode 100644 index 000000000000..91f4037f70bd --- /dev/null +++ b/js/xpconnect/tests/chrome/test_windowProxyDeadWrapper.html @@ -0,0 +1,72 @@ + + + + + + Test for Bug 1223372 + + + + + + +Mozilla Bug 1223372 + + + + + diff --git a/js/xpconnect/wrappers/WrapperFactory.cpp b/js/xpconnect/wrappers/WrapperFactory.cpp index b6d37c26afee..15cc149b9b5a 100644 --- a/js/xpconnect/wrappers/WrapperFactory.cpp +++ b/js/xpconnect/wrappers/WrapperFactory.cpp @@ -162,6 +162,10 @@ WrapperFactory::PrepareForWrapping(JSContext* cx, HandleObject scope, // ToWindowProxyIfWindow can return a CCW if |obj| was a // navigated-away-from Window. Strip any CCWs. obj = js::UncheckedUnwrap(obj); + if (JS_IsDeadWrapper(obj)) { + JS_ReportError(cx, "Can't wrap dead object"); + return nullptr; + } MOZ_ASSERT(js::IsWindowProxy(obj)); } diff --git a/layout/base/AccessibleCaret.h b/layout/base/AccessibleCaret.h index 69030b2bfe2f..8ff1717e9d57 100644 --- a/layout/base/AccessibleCaret.h +++ b/layout/base/AccessibleCaret.h @@ -37,6 +37,8 @@ namespace mozilla { // that SetPosition() works correctly, the caller must make sure the layout is // up to date. // +// Please see the wiki page for more information. +// https://wiki.mozilla.org/Copy_n_Paste class AccessibleCaret { public: diff --git a/layout/base/AccessibleCaretEventHub.h b/layout/base/AccessibleCaretEventHub.h index 5285d08d0ec3..c8a5603bb2ae 100644 --- a/layout/base/AccessibleCaretEventHub.h +++ b/layout/base/AccessibleCaretEventHub.h @@ -47,14 +47,19 @@ class WidgetTouchEvent; // transitions by giving events, callbacks, and the return values by mocked // methods of AccessibleCaretEventHub. See TestAccessibleCaretEventHub.cpp. // -// Besides dealing with real events, AccessibleCaretEventHub also synthesizes -// fake events such as scroll-end or long-tap providing APZ is not in use. +// Besides dealing with real events, AccessibleCaretEventHub could also +// synthesize fake long-tap events and inject those events to itself on the +// platform lacks eMouseLongTap. Turn on this preference +// "layout.accessiblecaret.use_long_tap_injector" for the fake long-tap events. // // State transition diagram: // http://hg.mozilla.org/mozilla-central/raw-file/default/layout/base/doc/AccessibleCaretEventHubStates.png // Source code of the diagram: // http://hg.mozilla.org/mozilla-central/file/default/layout/base/doc/AccessibleCaretEventHubStates.dot // +// Please see the wiki page for more information. +// https://wiki.mozilla.org/Copy_n_Paste +// class AccessibleCaretEventHub : public nsIReflowObserver, public nsIScrollObserver, public nsISelectionListener, diff --git a/layout/base/AccessibleCaretManager.h b/layout/base/AccessibleCaretManager.h index a753c8ad16a5..c1a78206b998 100644 --- a/layout/base/AccessibleCaretManager.h +++ b/layout/base/AccessibleCaretManager.h @@ -41,6 +41,9 @@ class Selection; // prior to performing its task. The caller must ensure the layout is up to // date. // +// Please see the wiki page for more information. +// https://wiki.mozilla.org/Copy_n_Paste +// class AccessibleCaretManager { public: diff --git a/layout/base/SelectionCarets.h b/layout/base/SelectionCarets.h index 04b7285e35e6..a5448a500e09 100644 --- a/layout/base/SelectionCarets.h +++ b/layout/base/SelectionCarets.h @@ -31,6 +31,10 @@ class Selection; } // namespace dom /** + * NOTE: SelectionCarets was obsoleted by AccessibleCaret, and is no longer used + * on B2G. This file is going to be removed in bug 1221459. Please see the wiki + * page for more information. https://wiki.mozilla.org/Copy_n_Paste + * * The SelectionCarets draw a pair of carets when the selection is not * collapsed, one at each end of the selection. * SelectionCarets also handle visibility, dragging caret and selecting word diff --git a/layout/base/TouchCaret.h b/layout/base/TouchCaret.h index c9b928d56bfe..7c682de2cd5d 100644 --- a/layout/base/TouchCaret.h +++ b/layout/base/TouchCaret.h @@ -22,6 +22,10 @@ class nsIPresShell; namespace mozilla { /** + * NOTE: TouchCaret was obsoleted by AccessibleCaret, and is no longer used on + * B2G. This file is going to be removed in bug 1221459. Please see the wiki + * page for more information. https://wiki.mozilla.org/Copy_n_Paste + * * The TouchCaret places a touch caret according to caret position when the * caret is shown. * TouchCaret is also responsible for touch caret visibility. Touch caret diff --git a/layout/tables/nsTableFrame.cpp b/layout/tables/nsTableFrame.cpp index 473f5ee851d4..93c89523e3ea 100644 --- a/layout/tables/nsTableFrame.cpp +++ b/layout/tables/nsTableFrame.cpp @@ -622,7 +622,24 @@ nsTableFrame::RemoveCol(nsTableColGroupFrame* aColGroupFrame, if (aRemoveFromCellMap) { nsTableCellMap* cellMap = GetCellMap(); if (cellMap) { - AppendAnonymousColFrames(1); + // If we have some anonymous cols at the end already, we just + // add a new anonymous col. + if (!mColFrames.IsEmpty() && + mColFrames.LastElement() && // XXXbz is this ever null? + mColFrames.LastElement()->GetColType() == eColAnonymousCell) { + AppendAnonymousColFrames(1); + } else { + // All of our colframes correspond to actual tags. It's possible + // that we still have at least as many tags as we have logical + // columns from cells, but we might have one less. Handle the latter + // case as follows: First ask the cellmap to drop its last col if it + // doesn't have any actual cells in it. Then call + // MatchCellMapToColCache to append an anonymous column if it's needed; + // this needs to be after RemoveColsAtEnd, since it will determine the + // need for a new column frame based on the width of the cell map. + cellMap->RemoveColsAtEnd(); + MatchCellMapToColCache(cellMap); + } } } // for now, just bail and recalc all of the collapsing borders @@ -2534,10 +2551,30 @@ nsTableFrame::DoRemoveFrame(ChildListID aListID, } } - int32_t numAnonymousColsToAdd = GetColCount() - mColFrames.Length(); - if (numAnonymousColsToAdd > 0) { - // this sets the child list, updates the col cache and cell map - AppendAnonymousColFrames(numAnonymousColsToAdd); + // If we have some anonymous cols at the end already, we just + // add more of them. + if (!mColFrames.IsEmpty() && + mColFrames.LastElement() && // XXXbz is this ever null? + mColFrames.LastElement()->GetColType() == eColAnonymousCell) { + int32_t numAnonymousColsToAdd = GetColCount() - mColFrames.Length(); + if (numAnonymousColsToAdd > 0) { + // this sets the child list, updates the col cache and cell map + AppendAnonymousColFrames(numAnonymousColsToAdd); + } + } else { + // All of our colframes correspond to actual tags. It's possible + // that we still have at least as many tags as we have logical + // columns from cells, but we might have one less. Handle the latter case + // as follows: First ask the cellmap to drop its last col if it doesn't + // have any actual cells in it. Then call MatchCellMapToColCache to + // append an anonymous column if it's needed; this needs to be after + // RemoveColsAtEnd, since it will determine the need for a new column + // frame based on the width of the cell map. + nsTableCellMap* cellMap = GetCellMap(); + if (cellMap) { // XXXbz is this ever null? + cellMap->RemoveColsAtEnd(); + MatchCellMapToColCache(cellMap); + } } } else { diff --git a/layout/tables/reftests/1220621-1-ref.html b/layout/tables/reftests/1220621-1-ref.html new file mode 100644 index 000000000000..bc9e9006cb6e --- /dev/null +++ b/layout/tables/reftests/1220621-1-ref.html @@ -0,0 +1,17 @@ + + + + + + +
OneTwoThree
diff --git a/layout/tables/reftests/1220621-1a.html b/layout/tables/reftests/1220621-1a.html new file mode 100644 index 000000000000..70026618a143 --- /dev/null +++ b/layout/tables/reftests/1220621-1a.html @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + +
OneTwoThree
+ diff --git a/layout/tables/reftests/1220621-1b.html b/layout/tables/reftests/1220621-1b.html new file mode 100644 index 000000000000..82ab75544f86 --- /dev/null +++ b/layout/tables/reftests/1220621-1b.html @@ -0,0 +1,31 @@ + + + + + + + + + + + + + +
OneTwoThree
+ diff --git a/layout/tables/reftests/1220621-1c.html b/layout/tables/reftests/1220621-1c.html new file mode 100644 index 000000000000..3d0949abc953 --- /dev/null +++ b/layout/tables/reftests/1220621-1c.html @@ -0,0 +1,30 @@ + + + + + + + + + + + + +
OneTwoThree
+ diff --git a/layout/tables/reftests/1220621-1d.html b/layout/tables/reftests/1220621-1d.html new file mode 100644 index 000000000000..cf6291d87b37 --- /dev/null +++ b/layout/tables/reftests/1220621-1d.html @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + +
OneTwoThree
+ diff --git a/layout/tables/reftests/1220621-1e.html b/layout/tables/reftests/1220621-1e.html new file mode 100644 index 000000000000..44e8b94e284e --- /dev/null +++ b/layout/tables/reftests/1220621-1e.html @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + +
OneTwoThree
+ diff --git a/layout/tables/reftests/1220621-1f.html b/layout/tables/reftests/1220621-1f.html new file mode 100644 index 000000000000..0b5f9a84e2ce --- /dev/null +++ b/layout/tables/reftests/1220621-1f.html @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + +
OneTwoThree
+ diff --git a/layout/tables/reftests/1220621-2-ref.html b/layout/tables/reftests/1220621-2-ref.html new file mode 100644 index 000000000000..b6a02820b25b --- /dev/null +++ b/layout/tables/reftests/1220621-2-ref.html @@ -0,0 +1,21 @@ + + + + + + + + + + +
One
diff --git a/layout/tables/reftests/1220621-2a.html b/layout/tables/reftests/1220621-2a.html new file mode 100644 index 000000000000..a66768e0fc5c --- /dev/null +++ b/layout/tables/reftests/1220621-2a.html @@ -0,0 +1,29 @@ + + + + + + + + + + + +
One
+ diff --git a/layout/tables/reftests/1220621-2b.html b/layout/tables/reftests/1220621-2b.html new file mode 100644 index 000000000000..379857235b92 --- /dev/null +++ b/layout/tables/reftests/1220621-2b.html @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + +
One
+ diff --git a/layout/tables/reftests/reftest.list b/layout/tables/reftests/reftest.list index 96ceeafe8fd0..f6f7d5bcef46 100644 --- a/layout/tables/reftests/reftest.list +++ b/layout/tables/reftests/reftest.list @@ -1 +1,9 @@ == 1031934.html 1031934-ref.html +== 1220621-1a.html 1220621-1-ref.html +== 1220621-1b.html 1220621-1-ref.html +== 1220621-1c.html 1220621-1-ref.html +== 1220621-1d.html 1220621-1-ref.html +== 1220621-1e.html 1220621-1-ref.html +== 1220621-1f.html 1220621-1-ref.html +== 1220621-2a.html 1220621-2-ref.html +== 1220621-2b.html 1220621-2-ref.html diff --git a/media/libvpx/update.py b/media/libvpx/update.py index d6c7a08e6dc6..e2c19f1fdefd 100755 --- a/media/libvpx/update.py +++ b/media/libvpx/update.py @@ -398,7 +398,7 @@ def prepare_upstream(prefix, commit=None): configure = ['../../configure', '--target=%s' % target, '--disable-examples', '--disable-install-docs', '--enable-multi-res-encoding', - '--size-limit=4000x3000' + '--size-limit=8192x4608' ] if 'darwin9' in target: diff --git a/media/libvpx/vpx_config_armv7-android-gcc.h b/media/libvpx/vpx_config_armv7-android-gcc.h index ff7e42f715c2..b2ef8c207f04 100644 --- a/media/libvpx/vpx_config_armv7-android-gcc.h +++ b/media/libvpx/vpx_config_armv7-android-gcc.h @@ -92,6 +92,6 @@ #define CONFIG_SPATIAL_SVC 0 #define CONFIG_FP_MB_STATS 0 #define CONFIG_EMULATE_HARDWARE 0 -#define DECODE_WIDTH_LIMIT 4000 -#define DECODE_HEIGHT_LIMIT 3000 +#define DECODE_WIDTH_LIMIT 8192 +#define DECODE_HEIGHT_LIMIT 4608 #endif /* VPX_CONFIG_H */ diff --git a/media/libvpx/vpx_config_generic-gnu.h b/media/libvpx/vpx_config_generic-gnu.h index 724edd6ede8f..c62e7b0571e9 100644 --- a/media/libvpx/vpx_config_generic-gnu.h +++ b/media/libvpx/vpx_config_generic-gnu.h @@ -92,6 +92,6 @@ #define CONFIG_SPATIAL_SVC 0 #define CONFIG_FP_MB_STATS 0 #define CONFIG_EMULATE_HARDWARE 0 -#define DECODE_WIDTH_LIMIT 4000 -#define DECODE_HEIGHT_LIMIT 3000 +#define DECODE_WIDTH_LIMIT 8192 +#define DECODE_HEIGHT_LIMIT 4608 #endif /* VPX_CONFIG_H */ diff --git a/media/libvpx/vpx_config_x86-darwin9-gcc.h b/media/libvpx/vpx_config_x86-darwin9-gcc.h index 7cca4b699d2a..51a41512ae29 100644 --- a/media/libvpx/vpx_config_x86-darwin9-gcc.h +++ b/media/libvpx/vpx_config_x86-darwin9-gcc.h @@ -92,6 +92,6 @@ #define CONFIG_SPATIAL_SVC 0 #define CONFIG_FP_MB_STATS 0 #define CONFIG_EMULATE_HARDWARE 0 -#define DECODE_WIDTH_LIMIT 4000 -#define DECODE_HEIGHT_LIMIT 3000 +#define DECODE_WIDTH_LIMIT 8192 +#define DECODE_HEIGHT_LIMIT 4608 #endif /* VPX_CONFIG_H */ diff --git a/media/libvpx/vpx_config_x86-linux-gcc.asm b/media/libvpx/vpx_config_x86-linux-gcc.asm index 63b48bb176c4..fe52e0682ecf 100644 --- a/media/libvpx/vpx_config_x86-linux-gcc.asm +++ b/media/libvpx/vpx_config_x86-linux-gcc.asm @@ -17,7 +17,7 @@ HAVE_SSE3 equ 1 HAVE_SSSE3 equ 1 HAVE_SSE4_1 equ 1 HAVE_AVX equ 1 -HAVE_AVX2 equ 0 +HAVE_AVX2 equ 1 HAVE_VPX_PORTS equ 1 HAVE_STDINT_H equ 1 HAVE_PTHREAD_H equ 1 diff --git a/media/libvpx/vpx_config_x86-linux-gcc.h b/media/libvpx/vpx_config_x86-linux-gcc.h index ff6b0186b807..a987397f08ab 100644 --- a/media/libvpx/vpx_config_x86-linux-gcc.h +++ b/media/libvpx/vpx_config_x86-linux-gcc.h @@ -92,6 +92,6 @@ #define CONFIG_SPATIAL_SVC 0 #define CONFIG_FP_MB_STATS 0 #define CONFIG_EMULATE_HARDWARE 0 -#define DECODE_WIDTH_LIMIT 4000 -#define DECODE_HEIGHT_LIMIT 3000 +#define DECODE_WIDTH_LIMIT 8192 +#define DECODE_HEIGHT_LIMIT 4608 #endif /* VPX_CONFIG_H */ diff --git a/media/libvpx/vpx_config_x86-win32-gcc.h b/media/libvpx/vpx_config_x86-win32-gcc.h index abc34be9d6dd..7654e5feeb93 100644 --- a/media/libvpx/vpx_config_x86-win32-gcc.h +++ b/media/libvpx/vpx_config_x86-win32-gcc.h @@ -93,6 +93,6 @@ #define CONFIG_SPATIAL_SVC 0 #define CONFIG_FP_MB_STATS 0 #define CONFIG_EMULATE_HARDWARE 0 -#define DECODE_WIDTH_LIMIT 4000 -#define DECODE_HEIGHT_LIMIT 3000 +#define DECODE_WIDTH_LIMIT 8192 +#define DECODE_HEIGHT_LIMIT 4608 #endif /* VPX_CONFIG_H */ diff --git a/media/libvpx/vpx_config_x86-win32-vs12.h b/media/libvpx/vpx_config_x86-win32-vs12.h index 9c1d36df8405..42525a303c65 100644 --- a/media/libvpx/vpx_config_x86-win32-vs12.h +++ b/media/libvpx/vpx_config_x86-win32-vs12.h @@ -92,6 +92,6 @@ #define CONFIG_SPATIAL_SVC 0 #define CONFIG_FP_MB_STATS 0 #define CONFIG_EMULATE_HARDWARE 0 -#define DECODE_WIDTH_LIMIT 4000 -#define DECODE_HEIGHT_LIMIT 3000 +#define DECODE_WIDTH_LIMIT 8192 +#define DECODE_HEIGHT_LIMIT 4608 #endif /* VPX_CONFIG_H */ diff --git a/media/libvpx/vpx_config_x86_64-darwin9-gcc.h b/media/libvpx/vpx_config_x86_64-darwin9-gcc.h index d04556d4585f..dd986e04a814 100644 --- a/media/libvpx/vpx_config_x86_64-darwin9-gcc.h +++ b/media/libvpx/vpx_config_x86_64-darwin9-gcc.h @@ -92,6 +92,6 @@ #define CONFIG_SPATIAL_SVC 0 #define CONFIG_FP_MB_STATS 0 #define CONFIG_EMULATE_HARDWARE 0 -#define DECODE_WIDTH_LIMIT 4000 -#define DECODE_HEIGHT_LIMIT 3000 +#define DECODE_WIDTH_LIMIT 8192 +#define DECODE_HEIGHT_LIMIT 4608 #endif /* VPX_CONFIG_H */ diff --git a/media/libvpx/vpx_config_x86_64-linux-gcc.asm b/media/libvpx/vpx_config_x86_64-linux-gcc.asm index 481d00df05c5..7c808c83f37c 100644 --- a/media/libvpx/vpx_config_x86_64-linux-gcc.asm +++ b/media/libvpx/vpx_config_x86_64-linux-gcc.asm @@ -17,7 +17,7 @@ HAVE_SSE3 equ 1 HAVE_SSSE3 equ 1 HAVE_SSE4_1 equ 1 HAVE_AVX equ 1 -HAVE_AVX2 equ 0 +HAVE_AVX2 equ 1 HAVE_VPX_PORTS equ 1 HAVE_STDINT_H equ 1 HAVE_PTHREAD_H equ 1 diff --git a/media/libvpx/vpx_config_x86_64-linux-gcc.h b/media/libvpx/vpx_config_x86_64-linux-gcc.h index d04556d4585f..dd986e04a814 100644 --- a/media/libvpx/vpx_config_x86_64-linux-gcc.h +++ b/media/libvpx/vpx_config_x86_64-linux-gcc.h @@ -92,6 +92,6 @@ #define CONFIG_SPATIAL_SVC 0 #define CONFIG_FP_MB_STATS 0 #define CONFIG_EMULATE_HARDWARE 0 -#define DECODE_WIDTH_LIMIT 4000 -#define DECODE_HEIGHT_LIMIT 3000 +#define DECODE_WIDTH_LIMIT 8192 +#define DECODE_HEIGHT_LIMIT 4608 #endif /* VPX_CONFIG_H */ diff --git a/media/libvpx/vpx_config_x86_64-win64-gcc.h b/media/libvpx/vpx_config_x86_64-win64-gcc.h index cf8a66268eb1..f9bb5c35a5b4 100644 --- a/media/libvpx/vpx_config_x86_64-win64-gcc.h +++ b/media/libvpx/vpx_config_x86_64-win64-gcc.h @@ -93,6 +93,6 @@ #define CONFIG_SPATIAL_SVC 0 #define CONFIG_FP_MB_STATS 0 #define CONFIG_EMULATE_HARDWARE 0 -#define DECODE_WIDTH_LIMIT 4000 -#define DECODE_HEIGHT_LIMIT 3000 +#define DECODE_WIDTH_LIMIT 8192 +#define DECODE_HEIGHT_LIMIT 4608 #endif /* VPX_CONFIG_H */ diff --git a/media/libvpx/vpx_config_x86_64-win64-vs12.h b/media/libvpx/vpx_config_x86_64-win64-vs12.h index 17cd9421ed24..65e45f5ba3b2 100644 --- a/media/libvpx/vpx_config_x86_64-win64-vs12.h +++ b/media/libvpx/vpx_config_x86_64-win64-vs12.h @@ -92,6 +92,6 @@ #define CONFIG_SPATIAL_SVC 0 #define CONFIG_FP_MB_STATS 0 #define CONFIG_EMULATE_HARDWARE 0 -#define DECODE_WIDTH_LIMIT 4000 -#define DECODE_HEIGHT_LIMIT 3000 +#define DECODE_WIDTH_LIMIT 8192 +#define DECODE_HEIGHT_LIMIT 4608 #endif /* VPX_CONFIG_H */ diff --git a/media/webrtc/trunk/webrtc/modules/audio_device/sndio/audio_device_sndio.cc b/media/webrtc/trunk/webrtc/modules/audio_device/sndio/audio_device_sndio.cc index f6343dcd6e0d..9f138633889e 100644 --- a/media/webrtc/trunk/webrtc/modules/audio_device/sndio/audio_device_sndio.cc +++ b/media/webrtc/trunk/webrtc/modules/audio_device/sndio/audio_device_sndio.cc @@ -747,14 +747,16 @@ int32_t AudioDeviceSndio::StartRecording() int32_t AudioDeviceSndio::StopRecording() { - CriticalSectionScoped lock(&_critSect); - - if (_recHandle == NULL) { - return 0; - } + CriticalSectionScoped lock(&_critSect); - _recording = false; + if (_recHandle == NULL) + { + return 0; + } + + _recording = false; + } if (_ptrThreadRec && !_ptrThreadRec->Stop()) { @@ -841,12 +843,15 @@ int32_t AudioDeviceSndio::StartPlayout() int32_t AudioDeviceSndio::StopPlayout() { - CriticalSectionScoped lock(&_critSect); - if (_playHandle == NULL) + { - return 0; + CriticalSectionScoped lock(&_critSect); + if (_playHandle == NULL) + { + return 0; + } + _playing = false; } - _playing = false; if (_ptrThreadPlay && !_ptrThreadPlay->Stop()) { diff --git a/mfbt/TypeTraits.h b/mfbt/TypeTraits.h index 48728e4493da..2952b54389a8 100644 --- a/mfbt/TypeTraits.h +++ b/mfbt/TypeTraits.h @@ -24,6 +24,18 @@ namespace mozilla { /* Forward declarations. */ template struct RemoveCV; +template struct AddRvalueReference; + +/* 20.2.4 Function template declval [declval] */ + +/** + * DeclVal simplifies the definition of expressions which occur as unevaluated + * operands. It converts T to a reference type, making it possible to use in + * decltype expressions even if T does not have a default constructor, e.g.: + * decltype(DeclVal().foo()) + */ +template +typename AddRvalueReference::Type DeclVal(); /* 20.9.3 Helper classes [meta.help] */ @@ -632,21 +644,25 @@ struct IsBaseOf namespace detail { +// This belongs inside ConvertibleTester, but it's pulled out to +// work around a bug in the compiler used for hazard builds. +template +static void ConvertibleTestHelper(To); + template struct ConvertibleTester { private: - static From create(); - - template - static char test(To to); + template(DeclVal()))> + static char test(int); template static int test(...); public: static const bool value = - sizeof(test(create())) == sizeof(char); + sizeof(test(0)) == sizeof(char); }; } // namespace detail @@ -864,17 +880,6 @@ struct AddRvalueReference : detail::AddRvalueReferenceHelper {}; -/* 20.2.4 Function template declval [declval] */ - -/** - * DeclVal simplifies the definition of expressions which occur as unevaluated - * operands. It converts T to a reference type, making it possible to use in - * decltype expressions even if T does not have a default constructor, e.g.: - * decltype(DeclVal().foo()) - */ -template -typename AddRvalueReference::Type DeclVal(); - /* 20.9.7.3 Sign modifications [meta.trans.sign] */ template diff --git a/mfbt/tests/TestTypeTraits.cpp b/mfbt/tests/TestTypeTraits.cpp index 0042fa968da7..3dfedac82ea2 100644 --- a/mfbt/tests/TestTypeTraits.cpp +++ b/mfbt/tests/TestTypeTraits.cpp @@ -337,6 +337,10 @@ TestIsBaseOf() "B is the same as B (and therefore, a base of B)"); } +class ExplicitCopyConstructor { + explicit ExplicitCopyConstructor(const ExplicitCopyConstructor&) = default; +}; + static void TestIsConvertible() { @@ -368,6 +372,10 @@ TestIsConvertible() static_assert(!IsConvertible::value, "A shouldn't convert to void"); static_assert(!IsConvertible::value, "void shouldn't convert to B"); + static_assert(!IsConvertible::value, + "IsConvertible should test for implicit convertibility"); + // These cases seem to require C++11 support to properly implement them, so // for now just disable them. //static_assert((!IsConvertible::value), diff --git a/netwerk/streamconv/converters/nsIndexedToHTML.cpp b/netwerk/streamconv/converters/nsIndexedToHTML.cpp index 12128ad845c5..29df642a49f2 100644 --- a/netwerk/streamconv/converters/nsIndexedToHTML.cpp +++ b/netwerk/streamconv/converters/nsIndexedToHTML.cpp @@ -533,30 +533,6 @@ nsIndexedToHTML::DoOnStartRequest(nsIRequest* request, nsISupports *aContext, buffer.AppendLiteral("\n"); - // If there is a quote character in the baseUri, then - // lets not add a base URL. The reason for this is that - // if we stick baseUri containing a quote into a quoted - // string, the quote character will prematurely close - // the base href string. This is a fall-back check; - // that's why it is OK to not use a base rather than - // trying to play nice and escaping the quotes. See bug - // 358128. - - if (!baseUri.Contains('"')) - { - // Great, the baseUri does not contain a char that - // will prematurely close the string. Go ahead an - // add a base href. - buffer.AppendLiteral("\n"); - } - else - { - NS_ERROR("broken protocol handler didn't escape double-quote."); - } - nsCString direction(NS_LITERAL_CSTRING("ltr")); nsCOMPtr reg = mozilla::services::GetXULChromeRegistryService(); diff --git a/testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm b/testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm index 871224c42a06..771a62ad6608 100644 --- a/testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm +++ b/testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm @@ -337,7 +337,7 @@ this.BrowserTestUtils = { * Waits for an event to be fired on a specified element. * * Usage: - * let promiseEvent = BrowserTestUtil.waitForEvent(element, "eventName"); + * let promiseEvent = BrowserTestUtils.waitForEvent(element, "eventName"); * // Do some processing here that will cause the event to be fired * // ... * // Now yield until the Promise is fulfilled diff --git a/testing/mozharness/mozharness/mozilla/testing/talos.py b/testing/mozharness/mozharness/mozilla/testing/talos.py index 806024fce212..d0cb517c8ddb 100755 --- a/testing/mozharness/mozharness/mozilla/testing/talos.py +++ b/testing/mozharness/mozharness/mozilla/testing/talos.py @@ -41,13 +41,13 @@ TalosErrorList = PythonErrorList + [ class TalosOutputParser(OutputParser): minidump_regex = re.compile(r'''talosError: "error executing: '(\S+) (\S+) (\S+)'"''') - RE_TALOSDATA = re.compile(r'.*?TALOSDATA:\s+(\[.*\])') + RE_PERF_DATA = re.compile(r'.*PERFHERDER_DATA:\s+(\{.*\})') worst_tbpl_status = TBPL_SUCCESS def __init__(self, **kwargs): super(TalosOutputParser, self).__init__(**kwargs) self.minidump_output = None - self.num_times_found_talosdata = 0 + self.num_times_found_perf_data = 0 def update_worst_log_and_tbpl_levels(self, log_level, tbpl_level): self.worst_log_level = self.worst_level(log_level, @@ -66,8 +66,8 @@ class TalosOutputParser(OutputParser): if m: self.minidump_output = (m.group(1), m.group(2), m.group(3)) - if self.RE_TALOSDATA.match(line): - self.num_times_found_talosdata += 1 + if self.RE_PERF_DATA.match(line): + self.num_times_found_perf_data += 1 # now let's check if buildbot should retry harness_retry_re = TinderBoxPrintRe['harness_error']['retry_regex'] @@ -368,9 +368,9 @@ class Talos(TestingMixin, MercurialScript, BlobUploadMixin): tbpl_level = TBPL_RETRY parser.update_worst_log_and_tbpl_levels(log_level, tbpl_level) - elif parser.num_times_found_talosdata != 1: - self.critical("TALOSDATA was seen %d times, expected 1." - % parser.num_times_found_talosdata) + elif parser.num_times_found_perf_data != 1: + self.critical("PERFHERDER_DATA was seen %d times, expected 1." + % parser.num_times_found_perf_data) parser.update_worst_log_and_tbpl_levels(WARNING, TBPL_WARNING) self.buildbot_status(parser.worst_tbpl_status, diff --git a/testing/talos/talos/output.py b/testing/talos/talos/output.py index adca9b426c2f..88f7b0dbcac2 100755 --- a/testing/talos/talos/output.py +++ b/testing/talos/talos/output.py @@ -1,3 +1,4 @@ + # 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/. @@ -6,7 +7,6 @@ import filter import json -import mozinfo import logging import post_file import time @@ -404,7 +404,7 @@ class PerfherderOutput(Output): # This is the output that treeherder expects to find when parsing the # log file - logging.info("TALOSDATA: %s" % json.dumps(results)) + logging.info("PERFHERDER_DATA: %s" % json.dumps(results)) if results_scheme in ('file'): json.dump(results, file(results_path, 'w'), indent=2, sort_keys=True) @@ -413,41 +413,6 @@ class PerfherderOutput(Output): """conform to current code- not needed for perfherder""" pass - # TODO: this is copied directly from the old datazilla output. Using it - # as we have established platform names already - def test_machine(self): - """return test machine platform in a form appropriate to datazilla""" - platform = mozinfo.os - version = mozinfo.version - processor = mozinfo.processor - if self.results.title.endswith(".e") and \ - not version.endswith('.e'): - # we are running this against e10s builds - version = '%s.e' % (version,) - - return dict(name=self.results.title, os=platform, osversion=version, - platform=processor) - - # TODO: this is copied from datazilla output code, do we need all of this? - def run_options(self, test): - """test options for datazilla""" - - options = {} - test_options = ['rss', 'cycles', 'tpmozafterpaint', 'responsiveness', - 'shutdown'] - if 'tpmanifest' in test.test_config: - test_options += ['tpchrome', 'tpcycles', 'tppagecycles', - 'tprender', 'tploadaboutblank', 'tpdelay'] - - for option in test_options: - if option not in test.test_config: - continue - options[option] = test.test_config[option] - if test.extensions is not None: - options['extensions'] = [{'name': extension} - for extension in test.extensions] - return options - def construct_results(self, vals, testname): if 'responsiveness' in testname: return self.responsiveness_Metric([val for (val, page) in vals]) @@ -463,33 +428,26 @@ class PerfherderOutput(Output): return filter.mean([i for i, j in vals]) def __call__(self): - # platform - machine = self.test_machine() - - # build information - browser_config = self.results.browser_config - - test_results = [] + suites = [] + test_results = { + 'framework': { + 'name': 'talos', + }, + 'suites': suites, + } for test in self.results.results: - test_result = { - 'test_machine': {}, - 'testrun': {}, - 'results': {}, - 'talos_counters': {}, - 'test_build': {} - } - - test_result['testrun']['suite'] = test.name() - test_result['testrun']['options'] = self.run_options(test) - test_result['testrun']['date'] = self.results.date - # serialize test results - results = {} tsresult = None - summary = {"suite": 0, "subtests": {}} if not test.using_xperf: + subtests = [] + suite = { + 'name': test.name(), + 'subtests': subtests, + } + suites.append(suite) vals = [] + replicates = {} # TODO: counters!!!! we don't have any, but they suffer the same for result in test.results: @@ -497,7 +455,7 @@ class PerfherderOutput(Output): # the same page name twice. It also ignores cycles for page, val in result.raw_values(): if page == 'NULL': - results.setdefault(test.name(), []).extend(val) + page = test.name() if tsresult is None: tsresult = r = TalosResults.Results() r.results = [{'index': 0, 'page': test.name(), @@ -506,40 +464,37 @@ class PerfherderOutput(Output): r = tsresult.results[0] if r['page'] == test.name(): r['runs'].extend(val) - else: - results.setdefault(page, []).extend(val) + replicates.setdefault(page, []).extend(val) tresults = [tsresult] if tsresult else test.results for result in tresults: filtered_results = \ - result.values(test_result['testrun']['suite'], + result.values(suite['name'], test.test_config['filters']) vals.extend([[i['value'], j] for i, j in filtered_results]) for val, page in filtered_results: - measurement_metadata = {} - if test.test_config.get('lower_is_better') is not None: - measurement_metadata['lowerIsBetter'] = test.test_config['lower_is_better'] - if test.test_config.get('unit'): - measurement_metadata['unit'] = test.test_config['unit'] if page == 'NULL': - summary['subtests'][test.name()] = val - summary['subtests'][test.name()].update(measurement_metadata) - else: - summary['subtests'][page] = val - summary['subtests'][page].update(measurement_metadata) + # no real subtests + page = test.name() + subtest = { + 'name': page, + 'value': val['filtered'], + 'replicates': replicates[page], + } + subtests.append(subtest) + if test.test_config.get('lower_is_better') is not None: + subtest['lowerIsBetter'] = test.test_config['lower_is_better'] + if test.test_config.get('unit'): + subtest['unit'] = test.test_config['unit'] - suite_summary = self.construct_results(vals, - testname=test.name()) - summary['suite'] = suite_summary + suite['value'] = self.construct_results(vals, + testname=test.name()) if test.test_config.get('lower_is_better') is not None: - summary['lowerIsBetter'] = test.test_config['lower_is_better'] - test_result['summary'] = summary - - for result, values in results.items(): - test_result['results'][result] = values + suite['lowerIsBetter'] = test.test_config['lower_is_better'] # counters results_aux data + counter_subtests = [] for cd in test.all_counter_results: for name, vals in cd.items(): # We want to add the xperf data as talos_counters @@ -553,42 +508,21 @@ class PerfherderOutput(Output): if 'mainthreadio' in name: continue + subtest = { + 'name': name, + 'value': 0.0, + } + counter_subtests.append(subtest) + if test.using_xperf: - test_result['talos_counters'][name] = {"mean": vals[0]} + subtest['value'] = vals[0] else: - # calculate mean and max value - varray = [] - counter_mean = 0 - counter_max = 0 + # calculate mean value if len(vals) > 0: - for v in vals: - varray.append(float(v)) - counter_mean = "%.2f" % filter.mean(varray) - counter_max = "%.2f" % max(varray) - test_result['talos_counters'][name] = { - "mean": counter_mean, - "max": counter_max - } - - if browser_config['develop'] and not browser_config['sourcestamp']: - browser_config['sourcestamp'] = '' - - test_result['test_build'] = { - 'version': browser_config['browser_version'], - 'revision': browser_config['sourcestamp'], - 'id': browser_config['buildid'], - 'branch': browser_config['branch_name'], - 'name': browser_config['browser_name'] - } - - test_result['test_machine'] = { - 'platform': machine['platform'], - 'osversion': machine['osversion'], - 'os': machine['os'], - 'name': machine['name'] - } - - test_results.append(test_result) + varray = [float(v) for v in vals] + subtest['value'] = filter.mean(varray) + if counter_subtests: + suites.append({'name': test.name(), 'subtests': counter_subtests}) return test_results # available output formats diff --git a/testing/web-platform/meta/html/browsers/the-window-object/window-properties.html.ini b/testing/web-platform/meta/html/browsers/the-window-object/window-properties.html.ini index 5f86b8fedb9e..0a2d80718d4e 100644 --- a/testing/web-platform/meta/html/browsers/the-window-object/window-properties.html.ini +++ b/testing/web-platform/meta/html/browsers/the-window-object/window-properties.html.ini @@ -12,6 +12,3 @@ [Window attribute: onmousewheel] expected: FAIL - [Window attribute: onstorage] - expected: FAIL - diff --git a/testing/web-platform/meta/html/dom/interfaces.html.ini b/testing/web-platform/meta/html/dom/interfaces.html.ini index 94328492ca3d..17f26f44db08 100644 --- a/testing/web-platform/meta/html/dom/interfaces.html.ini +++ b/testing/web-platform/meta/html/dom/interfaces.html.ini @@ -724,12 +724,6 @@ [HTMLLinkElement interface: document.createElement("link") must inherit property "crossOrigin" with the proper type (1)] expected: FAIL - [HTMLBodyElement interface: attribute onstorage] - expected: FAIL - - [HTMLBodyElement interface: document.createElement("body") must inherit property "onstorage" with the proper type (17)] - expected: FAIL - [HTMLPreElement must be primary interface of document.createElement("listing")] expected: FAIL @@ -2026,9 +2020,6 @@ [Window interface: attribute ontoggle] expected: FAIL - [Window interface: attribute onstorage] - expected: FAIL - [Window interface: operation createImageBitmap(ImageBitmapSource,long,long,long,long)] expected: FAIL @@ -2059,9 +2050,6 @@ [Window interface: window must inherit property "ontoggle" with the proper type (98)] expected: FAIL - [Window interface: window must inherit property "onstorage" with the proper type (112)] - expected: FAIL - [Window interface: calling createImageBitmap(ImageBitmapSource,long,long,long,long) on window with too few arguments must throw TypeError] expected: FAIL @@ -2521,12 +2509,6 @@ [HTMLMarqueeElement interface: document.createElement("marquee") must inherit property "stop" with the proper type (15)] expected: FAIL - [HTMLFrameSetElement interface: attribute onstorage] - expected: FAIL - - [HTMLFrameSetElement interface: document.createElement("frameset") must inherit property "onstorage" with the proper type (13)] - expected: FAIL - [Document interface: iframe.contentDocument must inherit property "styleSheetSets" with the proper type (32)] expected: FAIL diff --git a/testing/web-platform/meta/html/webappapis/scripting/events/event-handler-attributes-body-window.html.ini b/testing/web-platform/meta/html/webappapis/scripting/events/event-handler-attributes-body-window.html.ini deleted file mode 100644 index 977b550e2c1f..000000000000 --- a/testing/web-platform/meta/html/webappapis/scripting/events/event-handler-attributes-body-window.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[event-handler-attributes-body-window.html] - type: testharness - [storage] - expected: FAIL - diff --git a/testing/web-platform/meta/webstorage/event_body_attribute.html.ini b/testing/web-platform/meta/webstorage/event_body_attribute.html.ini index e078b873d67d..4d53fabc8b56 100644 --- a/testing/web-platform/meta/webstorage/event_body_attribute.html.ini +++ b/testing/web-platform/meta/webstorage/event_body_attribute.html.ini @@ -2,9 +2,3 @@ type: testharness disabled: if e10s: https://bugzilla.mozilla.org/show_bug.cgi?id=1205675 - [localStorage mutations fire StorageEvents that are caught by the event listener specified as an attribute on the body.] - expected: FAIL - - [sessionStorage mutations fire StorageEvents that are caught by the event listener specified as an attribute on the body.] - expected: FAIL - diff --git a/testing/web-platform/tests/webstorage/eventTestHarness.js b/testing/web-platform/tests/webstorage/eventTestHarness.js index 893d25058a24..7d9ed01870ea 100644 --- a/testing/web-platform/tests/webstorage/eventTestHarness.js +++ b/testing/web-platform/tests/webstorage/eventTestHarness.js @@ -4,9 +4,9 @@ document.body.appendChild(iframe); iframe.contentWindow.document.body.textContent = "Nothing to see here."; storageEventList = new Array(); -iframe.contentWindow.addEventListener("storage", function(e) { +iframe.contentWindow.onstorage = function(e) { window.parent.storageEventList.push(e); -}); +}; function runAfterNStorageEvents(callback, expectedNumEvents) { diff --git a/testing/web-platform/tests/webstorage/event_body_attribute.js b/testing/web-platform/tests/webstorage/event_body_attribute.js index d62563556227..a0e596da95cf 100644 --- a/testing/web-platform/tests/webstorage/event_body_attribute.js +++ b/testing/web-platform/tests/webstorage/event_body_attribute.js @@ -10,6 +10,9 @@ testStorages(function(storageString) { function step0(msg) { iframe.onload = t.step_func(step1); + // Null out the existing handler eventTestHarness.js set up; + // otherwise this test won't be testing much of anything useful. + iframe.contentWindow.onstorage = null; iframe.src = "resources/event_body_handler.html"; } diff --git a/testing/web-platform/tests/webstorage/resources/event_body_handler.html b/testing/web-platform/tests/webstorage/resources/event_body_handler.html index 1200f405e67b..11d8ec944759 100644 --- a/testing/web-platform/tests/webstorage/resources/event_body_handler.html +++ b/testing/web-platform/tests/webstorage/resources/event_body_handler.html @@ -9,6 +9,6 @@ function handleStorageEvent(e) { - + diff --git a/webapprt/locales/en-US/webapprt/overrides/dom.properties b/webapprt/locales/en-US/webapprt/overrides/dom.properties index f74d6a5ca664..11c8e065049f 100644 --- a/webapprt/locales/en-US/webapprt/overrides/dom.properties +++ b/webapprt/locales/en-US/webapprt/overrides/dom.properties @@ -67,7 +67,7 @@ nsIDOMWindowInternalWarning=Use of nsIDOMWindowInternal is deprecated. Use nsIDO FullScreenDeniedDisabled=Request for full-screen was denied because full-screen API is disabled by user preference. FullScreenDeniedFocusedPlugin=Request for full-screen was denied because a windowed plugin is focused. FullScreenDeniedHidden=Request for full-screen was denied because the document is no longer visible. -FullScreenDeniedContainerNotAllowed=Request for full-screen was denied because at least one of the document's containing element is not iframe or does not have an "allowfullscreen" attribute. +FullScreenDeniedContainerNotAllowed=Request for full-screen was denied because at least one of the document's containing elements is not an iframe or does not have an "allowfullscreen" attribute. FullScreenDeniedNotInputDriven=Request for full-screen was denied because Element.mozRequestFullScreen() was not called from inside a short running user-generated event handler. FullScreenDeniedNotInDocument=Request for full-screen was denied because requesting element is no longer in its document. FullScreenDeniedMovedDocument=Request for full-screen was denied because requesting element has moved document. diff --git a/widget/EventMessageList.h b/widget/EventMessageList.h index 48e6c81e94ef..690a30312ea5 100644 --- a/widget/EventMessageList.h +++ b/widget/EventMessageList.h @@ -98,6 +98,7 @@ NS_EVENT_MESSAGE(eHashChange) NS_EVENT_MESSAGE(eImageAbort) NS_EVENT_MESSAGE(eLoadError) NS_EVENT_MESSAGE(ePopState) +NS_EVENT_MESSAGE(eStorage) NS_EVENT_MESSAGE(eBeforeUnload) NS_EVENT_MESSAGE(eReadyStateChange) diff --git a/widget/cocoa/VibrancyManager.mm b/widget/cocoa/VibrancyManager.mm index 803edbbcaed1..bdadb1465f27 100644 --- a/widget/cocoa/VibrancyManager.mm +++ b/widget/cocoa/VibrancyManager.mm @@ -57,20 +57,12 @@ VibrancyManager::UpdateVibrantRegion(VibrancyType aType, const nsIntRegion& aReg vr.region = aRegion; } -static PLDHashOperator -ClearVibrantRegionFunc(const uint32_t& aVibrancyType, - VibrancyManager::VibrantRegion* aVibrantRegion, - void* aVM) -{ - static_cast(aVM)->ClearVibrantRegion(*aVibrantRegion); - return PL_DHASH_NEXT; -} - void VibrancyManager::ClearVibrantAreas() const { - mVibrantRegions.EnumerateRead(ClearVibrantRegionFunc, - const_cast(this)); + for (auto iter = mVibrantRegions.ConstIter(); !iter.Done(); iter.Next()) { + ClearVibrantRegion(*iter.UserData()); + } } void diff --git a/widget/gtk/nsWindow.cpp b/widget/gtk/nsWindow.cpp index 7ab78aba2660..4817a21767cc 100644 --- a/widget/gtk/nsWindow.cpp +++ b/widget/gtk/nsWindow.cpp @@ -3386,16 +3386,6 @@ nsWindow::OnDragDataReceivedEvent(GtkWidget *aWidget, } #if GTK_CHECK_VERSION(3,4,0) -static PLDHashOperator AppendTouchToEvent(GdkEventSequence* aKey, - dom::Touch* aData, - void* aArg) -{ - WidgetTouchEvent* event = reinterpret_cast(aArg); - event->touches.AppendElement(new dom::Touch(*aData)); - - return PL_DHASH_NEXT; -} - gboolean nsWindow::OnTouchEvent(GdkEventTouch* aEvent) { @@ -3441,7 +3431,9 @@ nsWindow::OnTouchEvent(GdkEventTouch* aEvent) if (aEvent->type == GDK_TOUCH_BEGIN || aEvent->type == GDK_TOUCH_UPDATE) { mTouches.Put(aEvent->sequence, touch.forget()); // add all touch points to event object - mTouches.EnumerateRead(AppendTouchToEvent, &event); + for (auto iter = mTouches.Iter(); !iter.Done(); iter.Next()) { + event.touches.AppendElement(new dom::Touch(*iter.UserData())); + } } else if (aEvent->type == GDK_TOUCH_END || aEvent->type == GDK_TOUCH_CANCEL) { *event.touches.AppendElement() = touch.forget(); diff --git a/widget/nsBaseWidget.cpp b/widget/nsBaseWidget.cpp index f72d4d3acb3b..4c2f4c6d3cc7 100644 --- a/widget/nsBaseWidget.cpp +++ b/widget/nsBaseWidget.cpp @@ -1,4 +1,5 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ @@ -1910,31 +1911,6 @@ nsIWidget::LookupRegisteredPluginWindow(uintptr_t aWindowID) #endif } -#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) -struct VisEnumContext { - uintptr_t parentWidget; - const nsTArray* list; - bool widgetVisibilityFlag; -}; - -static PLDHashOperator -RegisteredPluginEnumerator(const void* aWindowId, nsIWidget* aWidget, void* aUserArg) -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(aWindowId); - MOZ_ASSERT(aWidget); - MOZ_ASSERT(aUserArg); - - if (!aWidget->Destroyed()) { - VisEnumContext* pctx = static_cast(aUserArg); - if ((uintptr_t)aWidget->GetParent() == pctx->parentWidget) { - aWidget->Show(pctx->list->Contains((uintptr_t)aWindowId)); - } - } - return PLDHashOperator::PL_DHASH_NEXT; -} -#endif - // static void nsIWidget::UpdateRegisteredPluginWindowVisibility(uintptr_t aOwnerWidget, @@ -1946,11 +1922,23 @@ nsIWidget::UpdateRegisteredPluginWindowVisibility(uintptr_t aOwnerWidget, #else MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(sPluginWidgetList); + // Our visible list is associated with a compositor which is associated with - // a specific top level window. We hand the parent widget in here so the - // enumerator can skip the plugin widgets owned by other top level windows. - VisEnumContext ctx = { aOwnerWidget, &aPluginIds }; - sPluginWidgetList->EnumerateRead(RegisteredPluginEnumerator, static_cast(&ctx)); + // a specific top level window. We use the parent widget during iteration + // to skip the plugin widgets owned by other top level windows. + for (auto iter = sPluginWidgetList->Iter(); !iter.Done(); iter.Next()) { + const void* windowId = iter.Key(); + nsIWidget* widget = iter.UserData(); + + MOZ_ASSERT(windowId); + MOZ_ASSERT(widget); + + if (!widget->Destroyed()) { + if ((uintptr_t)widget->GetParent() == aOwnerWidget) { + widget->Show(aPluginIds.Contains((uintptr_t)windowId)); + } + } + } #endif } diff --git a/xpcom/base/nsMemoryReporterManager.cpp b/xpcom/base/nsMemoryReporterManager.cpp index 7bbc2df99d57..89ee5638b7e7 100644 --- a/xpcom/base/nsMemoryReporterManager.cpp +++ b/xpcom/base/nsMemoryReporterManager.cpp @@ -24,6 +24,7 @@ #include "mozilla/Attributes.h" #include "mozilla/PodOperations.h" #include "mozilla/Preferences.h" +#include "mozilla/Scoped.h" #include "mozilla/Services.h" #include "mozilla/Telemetry.h" #include "mozilla/dom/PMemoryReportRequestParent.h" // for dom::MemoryReport @@ -152,30 +153,6 @@ ResidentUniqueDistinguishedAmount(int64_t* aN) return GetProcSelfSmapsPrivate(aN); } -class ResidentUniqueReporter final : public nsIMemoryReporter -{ - ~ResidentUniqueReporter() {} - -public: - NS_DECL_ISUPPORTS - - NS_METHOD CollectReports(nsIHandleReportCallback* aHandleReport, - nsISupports* aData, bool aAnonymize) override - { - int64_t amount = 0; - nsresult rv = ResidentUniqueDistinguishedAmount(&amount); - NS_ENSURE_SUCCESS(rv, rv); - - return MOZ_COLLECT_REPORT( - "resident-unique", KIND_OTHER, UNITS_BYTES, amount, -"Memory mapped by the process that is present in physical memory and not " -"shared with any other processes. This is also known as the process's unique " -"set size (USS). This is the amount of RAM we'd expect to be freed if we " -"closed this process."); - } -}; -NS_IMPL_ISUPPORTS(ResidentUniqueReporter, nsIMemoryReporter) - #define HAVE_SYSTEM_HEAP_REPORTER 1 nsresult SystemHeapSize(int64_t* aSizeOut) @@ -582,30 +559,6 @@ ResidentUniqueDistinguishedAmount(int64_t* aN) return NS_OK; } -class ResidentUniqueReporter final : public nsIMemoryReporter -{ - ~ResidentUniqueReporter() {} - -public: - NS_DECL_ISUPPORTS - - NS_METHOD CollectReports(nsIHandleReportCallback* aHandleReport, - nsISupports* aData, bool aAnonymize) override - { - int64_t amount = 0; - nsresult rv = ResidentUniqueDistinguishedAmount(&amount); - NS_ENSURE_SUCCESS(rv, rv); - - return MOZ_COLLECT_REPORT( - "resident-unique", KIND_OTHER, UNITS_BYTES, amount, -"Memory mapped by the process that is present in physical memory and not " -"shared with any other processes. This is also known as the process's unique " -"set size (USS). This is the amount of RAM we'd expect to be freed if we " -"closed this process."); - } -}; -NS_IMPL_ISUPPORTS(ResidentUniqueReporter, nsIMemoryReporter) - #elif defined(XP_WIN) #include @@ -647,6 +600,55 @@ ResidentFastDistinguishedAmount(int64_t* aN) return ResidentDistinguishedAmount(aN); } +#define HAVE_RESIDENT_UNIQUE_REPORTER 1 + +static nsresult +ResidentUniqueDistinguishedAmount(int64_t* aN) +{ + // Determine how many entries we need. + PSAPI_WORKING_SET_INFORMATION tmp; + DWORD tmpSize = sizeof(tmp); + memset(&tmp, 0, tmpSize); + + HANDLE proc = GetCurrentProcess(); + QueryWorkingSet(proc, &tmp, tmpSize); + + // Fudge the size in case new entries are added between calls. + size_t entries = tmp.NumberOfEntries * 2; + + if (!entries) { + return NS_ERROR_FAILURE; + } + + DWORD infoArraySize = tmpSize + (entries * sizeof(PSAPI_WORKING_SET_BLOCK)); + mozilla::ScopedFreePtr infoArray( + static_cast(malloc(infoArraySize))); + + if (!infoArray) { + return NS_ERROR_FAILURE; + } + + if (!QueryWorkingSet(proc, infoArray, infoArraySize)) { + return NS_ERROR_FAILURE; + } + + entries = static_cast(infoArray->NumberOfEntries); + size_t privatePages = 0; + for (size_t i = 0; i < entries; i++) { + // Count shared pages that only one process is using as private. + if (!infoArray->WorkingSetInfo[i].Shared || + infoArray->WorkingSetInfo[i].ShareCount <= 1) { + privatePages++; + } + } + + SYSTEM_INFO si; + GetSystemInfo(&si); + + *aN = privatePages * si.dwPageSize; + return NS_OK; +} + #define HAVE_VSIZE_MAX_CONTIGUOUS_REPORTER 1 static nsresult VsizeMaxContiguousDistinguishedAmount(int64_t* aN) @@ -998,6 +1000,33 @@ NS_IMPL_ISUPPORTS(ResidentReporter, nsIMemoryReporter) #endif // HAVE_VSIZE_AND_RESIDENT_REPORTERS +#ifdef HAVE_RESIDENT_UNIQUE_REPORTER +class ResidentUniqueReporter final : public nsIMemoryReporter +{ + ~ResidentUniqueReporter() {} + +public: + NS_DECL_ISUPPORTS + + NS_METHOD CollectReports(nsIHandleReportCallback* aHandleReport, + nsISupports* aData, bool aAnonymize) override + { + int64_t amount = 0; + nsresult rv = ResidentUniqueDistinguishedAmount(&amount); + NS_ENSURE_SUCCESS(rv, rv); + + return MOZ_COLLECT_REPORT( + "resident-unique", KIND_OTHER, UNITS_BYTES, amount, +"Memory mapped by the process that is present in physical memory and not " +"shared with any other processes. This is also known as the process's unique " +"set size (USS). This is the amount of RAM we'd expect to be freed if we " +"closed this process."); + } +}; +NS_IMPL_ISUPPORTS(ResidentUniqueReporter, nsIMemoryReporter) + +#endif // HAVE_RESIDENT_UNIQUE_REPORTER + #ifdef HAVE_SYSTEM_HEAP_REPORTER class SystemHeapReporter final : public nsIMemoryReporter diff --git a/xpcom/string/nsSubstring.cpp b/xpcom/string/nsSubstring.cpp index 233de618399e..74cae7be1df9 100644 --- a/xpcom/string/nsSubstring.cpp +++ b/xpcom/string/nsSubstring.cpp @@ -319,8 +319,8 @@ nsStringBuffer::ToString(uint32_t aLen, nsACString& aStr, size_t nsStringBuffer::SizeOfIncludingThisMustBeUnshared(mozilla::MallocSizeOf aMallocSizeOf) const { - NS_ASSERTION(!IsReadonly(), - "shared StringBuffer in SizeOfIncludingThisMustBeUnshared"); + MOZ_ASSERT(!IsReadonly(), + "shared StringBuffer in SizeOfIncludingThisMustBeUnshared"); return aMallocSizeOf(this); }