зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to autoland
This commit is contained in:
Коммит
8bd6563760
|
@ -11,57 +11,25 @@ Components.utils.import("resource://gre/modules/Services.jsm");
|
||||||
var gPrivateWindow = null;
|
var gPrivateWindow = null;
|
||||||
var gPrivateBrowser = null;
|
var gPrivateBrowser = null;
|
||||||
|
|
||||||
function pageLoad(aEvent) {
|
|
||||||
// The plugin events are async dispatched and can come after the load event
|
|
||||||
// This just allows the events to fire before we then go on to test the states
|
|
||||||
executeSoon(gNextTest);
|
|
||||||
gNextTest = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function prepareTest(nextTest, url, browser) {
|
|
||||||
gNextTest = nextTest;
|
|
||||||
if (!browser)
|
|
||||||
browser = gTestBrowser;
|
|
||||||
|
|
||||||
ContentTask.spawn(browser, url, function(url) {
|
|
||||||
content.location = url;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function finishTest() {
|
function finishTest() {
|
||||||
clearAllPluginPermissions();
|
clearAllPluginPermissions();
|
||||||
gTestBrowser.removeEventListener("load", pageLoad, true);
|
|
||||||
gBrowser.removeCurrentTab();
|
gBrowser.removeCurrentTab();
|
||||||
if (gPrivateWindow) {
|
if (gPrivateWindow) {
|
||||||
gPrivateWindow.close();
|
gPrivateWindow.close();
|
||||||
}
|
}
|
||||||
window.focus();
|
window.focus();
|
||||||
finish();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function createPrivateWindow(nextTest, url) {
|
let createPrivateWindow = Task.async(function* createPrivateWindow(url) {
|
||||||
gPrivateWindow = OpenBrowserWindow({private: true});
|
gPrivateWindow = yield BrowserTestUtils.openNewBrowserWindow({private: true});
|
||||||
ok(!!gPrivateWindow, "should have created a private window.");
|
ok(!!gPrivateWindow, "should have created a private window.");
|
||||||
whenDelayedStartupFinished(gPrivateWindow, function() {
|
gPrivateBrowser = gPrivateWindow.getBrowser().selectedBrowser;
|
||||||
gPrivateBrowser = gPrivateWindow.getBrowser().selectedBrowser;
|
|
||||||
gPrivateBrowser.addEventListener("load", pageLoad, true);
|
|
||||||
gNextTest = function() {
|
|
||||||
prepareTest(nextTest, url, gPrivateBrowser);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function whenDelayedStartupFinished(aWindow, aCallback) {
|
BrowserTestUtils.loadURI(gPrivateBrowser, url);
|
||||||
Services.obs.addObserver(function observer(aSubject, aTopic) {
|
yield BrowserTestUtils.browserLoaded(gPrivateBrowser);
|
||||||
if (aWindow == aSubject) {
|
});
|
||||||
Services.obs.removeObserver(observer, aTopic);
|
|
||||||
executeSoon(aCallback);
|
|
||||||
}
|
|
||||||
}, "browser-delayed-startup-finished", false);
|
|
||||||
}
|
|
||||||
|
|
||||||
function test() {
|
add_task(function* test() {
|
||||||
waitForExplicitFinish();
|
|
||||||
registerCleanupFunction(function() {
|
registerCleanupFunction(function() {
|
||||||
clearAllPluginPermissions();
|
clearAllPluginPermissions();
|
||||||
getTestPlugin().enabledState = Ci.nsIPluginTag.STATE_ENABLED;
|
getTestPlugin().enabledState = Ci.nsIPluginTag.STATE_ENABLED;
|
||||||
|
@ -71,136 +39,150 @@ function test() {
|
||||||
let newTab = gBrowser.addTab();
|
let newTab = gBrowser.addTab();
|
||||||
gBrowser.selectedTab = newTab;
|
gBrowser.selectedTab = newTab;
|
||||||
gTestBrowser = gBrowser.selectedBrowser;
|
gTestBrowser = gBrowser.selectedBrowser;
|
||||||
gTestBrowser.addEventListener("load", pageLoad, true);
|
let promise = BrowserTestUtils.browserLoaded(gTestBrowser);
|
||||||
|
|
||||||
Services.prefs.setBoolPref("plugins.click_to_play", true);
|
Services.prefs.setBoolPref("plugins.click_to_play", true);
|
||||||
getTestPlugin().enabledState = Ci.nsIPluginTag.STATE_CLICKTOPLAY;
|
getTestPlugin().enabledState = Ci.nsIPluginTag.STATE_CLICKTOPLAY;
|
||||||
getTestPlugin("Second Test Plug-in").enabledState = Ci.nsIPluginTag.STATE_CLICKTOPLAY;
|
getTestPlugin("Second Test Plug-in").enabledState = Ci.nsIPluginTag.STATE_CLICKTOPLAY;
|
||||||
gNextTest = test1a;
|
yield promise;
|
||||||
}
|
});
|
||||||
|
|
||||||
function test1a() {
|
add_task(function* test1a() {
|
||||||
createPrivateWindow(test1b, gHttpTestRoot + "plugin_test.html");
|
yield createPrivateWindow(gHttpTestRoot + "plugin_test.html");
|
||||||
}
|
});
|
||||||
|
|
||||||
function test1b() {
|
add_task(function* test1b() {
|
||||||
let popupNotification = gPrivateWindow.PopupNotifications.getNotification("click-to-play-plugins", gPrivateBrowser);
|
let popupNotification = gPrivateWindow.PopupNotifications.getNotification("click-to-play-plugins", gPrivateBrowser);
|
||||||
ok(popupNotification, "Test 1b, Should have a click-to-play notification");
|
ok(popupNotification, "Test 1b, Should have a click-to-play notification");
|
||||||
|
|
||||||
let plugin = gPrivateBrowser.contentDocument.getElementById("test");
|
yield ContentTask.spawn(gPrivateBrowser, null, function() {
|
||||||
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
|
let plugin = content.document.getElementById("test");
|
||||||
ok(!objLoadingContent.activated, "Test 1b, Plugin should not be activated");
|
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
|
||||||
|
ok(!objLoadingContent.activated, "Test 1b, Plugin should not be activated");
|
||||||
|
});
|
||||||
|
|
||||||
// Check the button status
|
// Check the button status
|
||||||
let promiseShown = BrowserTestUtils.waitForEvent(gPrivateWindow.PopupNotifications.panel,
|
let promiseShown = BrowserTestUtils.waitForEvent(gPrivateWindow.PopupNotifications.panel,
|
||||||
"Shown");
|
"Shown");
|
||||||
popupNotification.reshow();
|
popupNotification.reshow();
|
||||||
promiseShown.then(() => {
|
|
||||||
let button1 = gPrivateWindow.PopupNotifications.panel.firstChild._primaryButton;
|
|
||||||
let button2 = gPrivateWindow.PopupNotifications.panel.firstChild._secondaryButton;
|
|
||||||
is(button1.getAttribute("action"), "_singleActivateNow", "Test 1b, Blocked plugin in private window should have a activate now button");
|
|
||||||
ok(button2.hidden, "Test 1b, Blocked plugin in a private window should not have a secondary button")
|
|
||||||
|
|
||||||
gPrivateWindow.close();
|
yield promiseShown;
|
||||||
prepareTest(test2a, gHttpTestRoot + "plugin_test.html");
|
let button1 = gPrivateWindow.PopupNotifications.panel.firstChild._primaryButton;
|
||||||
});
|
let button2 = gPrivateWindow.PopupNotifications.panel.firstChild._secondaryButton;
|
||||||
}
|
is(button1.getAttribute("action"), "_singleActivateNow", "Test 1b, Blocked plugin in private window should have a activate now button");
|
||||||
|
ok(button2.hidden, "Test 1b, Blocked plugin in a private window should not have a secondary button")
|
||||||
|
|
||||||
function test2a() {
|
gPrivateWindow.close();
|
||||||
|
BrowserTestUtils.loadURI(gTestBrowser, gHttpTestRoot + "plugin_test.html");
|
||||||
|
yield BrowserTestUtils.browserLoaded(gTestBrowser);
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(function* test2a() {
|
||||||
// enable test plugin on this site
|
// enable test plugin on this site
|
||||||
let popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
|
let popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
|
||||||
ok(popupNotification, "Test 2a, Should have a click-to-play notification");
|
ok(popupNotification, "Test 2a, Should have a click-to-play notification");
|
||||||
|
|
||||||
let plugin = gTestBrowser.contentDocument.getElementById("test");
|
yield ContentTask.spawn(gTestBrowser, null, function() {
|
||||||
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
|
let plugin = content.document.getElementById("test");
|
||||||
ok(!objLoadingContent.activated, "Test 2a, Plugin should not be activated");
|
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
|
||||||
|
ok(!objLoadingContent.activated, "Test 2a, Plugin should not be activated");
|
||||||
|
});
|
||||||
|
|
||||||
// Simulate clicking the "Allow Now" button.
|
// Simulate clicking the "Allow Now" button.
|
||||||
let promiseShown = BrowserTestUtils.waitForEvent(PopupNotifications.panel,
|
let promiseShown = BrowserTestUtils.waitForEvent(PopupNotifications.panel,
|
||||||
"Shown");
|
"Shown");
|
||||||
popupNotification.reshow();
|
popupNotification.reshow();
|
||||||
promiseShown.then(() => {
|
yield promiseShown;
|
||||||
PopupNotifications.panel.firstChild._secondaryButton.click();
|
|
||||||
|
|
||||||
|
PopupNotifications.panel.firstChild._secondaryButton.click();
|
||||||
|
|
||||||
|
yield ContentTask.spawn(gTestBrowser, null, function* () {
|
||||||
|
let plugin = content.document.getElementById("test");
|
||||||
|
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
|
||||||
let condition = () => objLoadingContent.activated;
|
let condition = () => objLoadingContent.activated;
|
||||||
waitForCondition(condition, test2b, "Test 2a, Waited too long for plugin to activate");
|
yield ContentTaskUtils.waitForCondition(condition, "Test 2a, Waited too long for plugin to activate");
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
|
|
||||||
function test2b() {
|
add_task(function* test2b() {
|
||||||
createPrivateWindow(test2c, gHttpTestRoot + "plugin_test.html");
|
yield createPrivateWindow(gHttpTestRoot + "plugin_test.html");
|
||||||
}
|
});
|
||||||
|
|
||||||
function test2c() {
|
add_task(function* test2c() {
|
||||||
let promise = TestUtils.topicObserved("PopupNotifications-updateNotShowing");
|
yield TestUtils.topicObserved("PopupNotifications-updateNotShowing");
|
||||||
promise.then(() => {
|
|
||||||
let popupNotification = gPrivateWindow.PopupNotifications.getNotification("click-to-play-plugins", gPrivateBrowser);
|
|
||||||
ok(popupNotification, "Test 2c, Should have a click-to-play notification");
|
|
||||||
|
|
||||||
let plugin = gPrivateBrowser.contentDocument.getElementById("test");
|
let popupNotification = gPrivateWindow.PopupNotifications.getNotification("click-to-play-plugins", gPrivateBrowser);
|
||||||
|
ok(popupNotification, "Test 2c, Should have a click-to-play notification");
|
||||||
|
|
||||||
|
yield ContentTask.spawn(gPrivateBrowser, null, function() {
|
||||||
|
let plugin = content.document.getElementById("test");
|
||||||
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
|
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
|
||||||
ok(objLoadingContent.activated, "Test 2c, Plugin should be activated");
|
ok(objLoadingContent.activated, "Test 2c, Plugin should be activated");
|
||||||
|
|
||||||
// Check the button status
|
|
||||||
let promiseShown = BrowserTestUtils.waitForEvent(gPrivateWindow.PopupNotifications.panel,
|
|
||||||
"Shown");
|
|
||||||
popupNotification.reshow();
|
|
||||||
promiseShown.then(() => {
|
|
||||||
let buttonContainer = gPrivateWindow.PopupNotifications.panel.firstChild._buttonContainer;
|
|
||||||
ok(buttonContainer.hidden, "Test 2c, Activated plugin in a private window should not have visible buttons");
|
|
||||||
|
|
||||||
clearAllPluginPermissions();
|
|
||||||
gPrivateWindow.close();
|
|
||||||
prepareTest(test3a, gHttpTestRoot + "plugin_test.html");
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
function test3a() {
|
// Check the button status
|
||||||
|
let promiseShown = BrowserTestUtils.waitForEvent(gPrivateWindow.PopupNotifications.panel,
|
||||||
|
"Shown");
|
||||||
|
popupNotification.reshow();
|
||||||
|
yield promiseShown;
|
||||||
|
let buttonContainer = gPrivateWindow.PopupNotifications.panel.firstChild._buttonContainer;
|
||||||
|
ok(buttonContainer.hidden, "Test 2c, Activated plugin in a private window should not have visible buttons");
|
||||||
|
|
||||||
|
clearAllPluginPermissions();
|
||||||
|
gPrivateWindow.close();
|
||||||
|
|
||||||
|
BrowserTestUtils.loadURI(gTestBrowser, gHttpTestRoot + "plugin_test.html");
|
||||||
|
yield BrowserTestUtils.browserLoaded(gTestBrowser);
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(function* test3a() {
|
||||||
// enable test plugin on this site
|
// enable test plugin on this site
|
||||||
let popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
|
let popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
|
||||||
ok(popupNotification, "Test 3a, Should have a click-to-play notification");
|
ok(popupNotification, "Test 3a, Should have a click-to-play notification");
|
||||||
|
|
||||||
let plugin = gTestBrowser.contentDocument.getElementById("test");
|
yield ContentTask.spawn(gTestBrowser, null, function() {
|
||||||
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
|
let plugin = content.document.getElementById("test");
|
||||||
ok(!objLoadingContent.activated, "Test 3a, Plugin should not be activated");
|
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
|
||||||
|
ok(!objLoadingContent.activated, "Test 3a, Plugin should not be activated");
|
||||||
|
});
|
||||||
|
|
||||||
// Simulate clicking the "Allow Always" button.
|
// Simulate clicking the "Allow Always" button.
|
||||||
let promiseShown = BrowserTestUtils.waitForEvent(PopupNotifications.panel,
|
let promiseShown = BrowserTestUtils.waitForEvent(PopupNotifications.panel,
|
||||||
"Shown");
|
"Shown");
|
||||||
popupNotification.reshow();
|
popupNotification.reshow();
|
||||||
promiseShown.then(() => {
|
yield promiseShown;
|
||||||
PopupNotifications.panel.firstChild._secondaryButton.click();
|
PopupNotifications.panel.firstChild._secondaryButton.click();
|
||||||
|
|
||||||
|
yield ContentTask.spawn(gTestBrowser, null, function* () {
|
||||||
|
let plugin = content.document.getElementById("test");
|
||||||
|
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
|
||||||
let condition = () => objLoadingContent.activated;
|
let condition = () => objLoadingContent.activated;
|
||||||
waitForCondition(condition, test3b, "Test 3a, Waited too long for plugin to activate");
|
yield ContentTaskUtils.waitForCondition(condition, "Test 3a, Waited too long for plugin to activate");
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
|
|
||||||
function test3b() {
|
add_task(function* test3b() {
|
||||||
createPrivateWindow(test3c, gHttpTestRoot + "plugin_test.html");
|
yield createPrivateWindow(gHttpTestRoot + "plugin_test.html");
|
||||||
}
|
});
|
||||||
|
|
||||||
function test3c() {
|
add_task(function* test3c() {
|
||||||
let promise = TestUtils.topicObserved("PopupNotifications-updateNotShowing");
|
yield TestUtils.topicObserved("PopupNotifications-updateNotShowing");
|
||||||
promise.then(() => {
|
let popupNotification = gPrivateWindow.PopupNotifications.getNotification("click-to-play-plugins", gPrivateBrowser);
|
||||||
let popupNotification = gPrivateWindow.PopupNotifications.getNotification("click-to-play-plugins", gPrivateBrowser);
|
ok(popupNotification, "Test 3c, Should have a click-to-play notification");
|
||||||
ok(popupNotification, "Test 3c, Should have a click-to-play notification");
|
|
||||||
|
|
||||||
// Check the button status
|
// Check the button status
|
||||||
let promiseShown = BrowserTestUtils.waitForEvent(gPrivateWindow.PopupNotifications.panel,
|
let promiseShown = BrowserTestUtils.waitForEvent(gPrivateWindow.PopupNotifications.panel,
|
||||||
"Shown");
|
"Shown");
|
||||||
popupNotification.reshow();
|
popupNotification.reshow();
|
||||||
promiseShown.then(() => {
|
yield promiseShown;
|
||||||
let buttonContainer = gPrivateWindow.PopupNotifications.panel.firstChild._buttonContainer;
|
let buttonContainer = gPrivateWindow.PopupNotifications.panel.firstChild._buttonContainer;
|
||||||
ok(buttonContainer.hidden, "Test 3c, Activated plugin in a private window should not have visible buttons");
|
ok(buttonContainer.hidden, "Test 3c, Activated plugin in a private window should not have visible buttons");
|
||||||
|
|
||||||
prepareTest(test3d, gHttpTestRoot + "plugin_two_types.html", gPrivateBrowser);
|
BrowserTestUtils.loadURI(gPrivateBrowser, gHttpTestRoot + "plugin_two_types.html");
|
||||||
});
|
yield BrowserTestUtils.browserLoaded(gPrivateBrowser);
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
function test3d() {
|
add_task(function* test3d() {
|
||||||
let popupNotification = gPrivateWindow.PopupNotifications.getNotification("click-to-play-plugins", gPrivateBrowser);
|
let popupNotification = gPrivateWindow.PopupNotifications.getNotification("click-to-play-plugins", gPrivateBrowser);
|
||||||
ok(popupNotification, "Test 3d, Should have a click-to-play notification");
|
ok(popupNotification, "Test 3d, Should have a click-to-play notification");
|
||||||
|
|
||||||
|
@ -208,31 +190,30 @@ function test3d() {
|
||||||
let promiseShown = BrowserTestUtils.waitForEvent(gPrivateWindow.PopupNotifications.panel,
|
let promiseShown = BrowserTestUtils.waitForEvent(gPrivateWindow.PopupNotifications.panel,
|
||||||
"Shown");
|
"Shown");
|
||||||
popupNotification.reshow();
|
popupNotification.reshow();
|
||||||
promiseShown.then(() => {
|
yield promiseShown;
|
||||||
let doc = gPrivateWindow.document;
|
let doc = gPrivateWindow.document;
|
||||||
for (let item of gPrivateWindow.PopupNotifications.panel.firstChild.childNodes) {
|
for (let item of gPrivateWindow.PopupNotifications.panel.firstChild.childNodes) {
|
||||||
let allowalways = doc.getAnonymousElementByAttribute(item, "anonid", "allowalways");
|
let allowalways = doc.getAnonymousElementByAttribute(item, "anonid", "allowalways");
|
||||||
ok(allowalways, "Test 3d, should have list item for allow always");
|
ok(allowalways, "Test 3d, should have list item for allow always");
|
||||||
let allownow = doc.getAnonymousElementByAttribute(item, "anonid", "allownow");
|
let allownow = doc.getAnonymousElementByAttribute(item, "anonid", "allownow");
|
||||||
ok(allownow, "Test 3d, should have list item for allow now");
|
ok(allownow, "Test 3d, should have list item for allow now");
|
||||||
let block = doc.getAnonymousElementByAttribute(item, "anonid", "block");
|
let block = doc.getAnonymousElementByAttribute(item, "anonid", "block");
|
||||||
ok(block, "Test 3d, should have list item for block");
|
ok(block, "Test 3d, should have list item for block");
|
||||||
|
|
||||||
if (item.action.pluginName === "Test") {
|
if (item.action.pluginName === "Test") {
|
||||||
is(item.value, "allowalways", "Test 3d, Plugin should bet set to 'allow always'");
|
is(item.value, "allowalways", "Test 3d, Plugin should bet set to 'allow always'");
|
||||||
ok(!allowalways.hidden, "Test 3d, Plugin set to 'always allow' should have a visible 'always allow' action.");
|
ok(!allowalways.hidden, "Test 3d, Plugin set to 'always allow' should have a visible 'always allow' action.");
|
||||||
ok(allownow.hidden, "Test 3d, Plugin set to 'always allow' should have an invisible 'allow now' action.");
|
ok(allownow.hidden, "Test 3d, Plugin set to 'always allow' should have an invisible 'allow now' action.");
|
||||||
ok(block.hidden, "Test 3d, Plugin set to 'always allow' should have an invisible 'block' action.");
|
ok(block.hidden, "Test 3d, Plugin set to 'always allow' should have an invisible 'block' action.");
|
||||||
} else if (item.action.pluginName === "Second Test") {
|
} else if (item.action.pluginName === "Second Test") {
|
||||||
is(item.value, "block", "Test 3d, Second plugin should bet set to 'block'");
|
is(item.value, "block", "Test 3d, Second plugin should bet set to 'block'");
|
||||||
ok(allowalways.hidden, "Test 3d, Plugin set to 'block' should have a visible 'always allow' action.");
|
ok(allowalways.hidden, "Test 3d, Plugin set to 'block' should have a visible 'always allow' action.");
|
||||||
ok(!allownow.hidden, "Test 3d, Plugin set to 'block' should have a visible 'allow now' action.");
|
ok(!allownow.hidden, "Test 3d, Plugin set to 'block' should have a visible 'allow now' action.");
|
||||||
ok(!block.hidden, "Test 3d, Plugin set to 'block' should have a visible 'block' action.");
|
ok(!block.hidden, "Test 3d, Plugin set to 'block' should have a visible 'block' action.");
|
||||||
} else {
|
} else {
|
||||||
ok(false, "Test 3d, Unexpected plugin '"+item.action.pluginName+"'");
|
ok(false, "Test 3d, Unexpected plugin '"+item.action.pluginName+"'");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
finishTest();
|
finishTest();
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
|
@ -13,3 +13,4 @@ skip-if = os == "linux" # Bug 952422
|
||||||
[browser_libraryDrop.js]
|
[browser_libraryDrop.js]
|
||||||
[browser_downloads_panel_block.js]
|
[browser_downloads_panel_block.js]
|
||||||
[browser_downloads_panel_footer.js]
|
[browser_downloads_panel_footer.js]
|
||||||
|
[browser_downloads_panel_height.js]
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This test exists because we use a <panelmultiview> element and it handles
|
||||||
|
* some of the height changes for us. We need to verify that the height is
|
||||||
|
* updated correctly if downloads are removed while the panel is hidden.
|
||||||
|
*/
|
||||||
|
add_task(function* test_height_reduced_after_removal() {
|
||||||
|
yield task_addDownloads([
|
||||||
|
{ state: nsIDM.DOWNLOAD_FINISHED },
|
||||||
|
]);
|
||||||
|
|
||||||
|
yield task_openPanel();
|
||||||
|
let panel = document.getElementById("downloadsPanel");
|
||||||
|
let heightBeforeRemoval = panel.getBoundingClientRect().height;
|
||||||
|
|
||||||
|
// We want to close the panel before we remove the download from the list.
|
||||||
|
DownloadsPanel.hidePanel();
|
||||||
|
yield task_resetState();
|
||||||
|
|
||||||
|
yield task_openPanel();
|
||||||
|
let heightAfterRemoval = panel.getBoundingClientRect().height;
|
||||||
|
Assert.greater(heightBeforeRemoval, heightAfterRemoval);
|
||||||
|
|
||||||
|
yield task_resetState();
|
||||||
|
});
|
|
@ -61,12 +61,7 @@ STUB_HOOK = $(NSINSTALL) -D '$(ABS_DIST)/$(PKG_INST_PATH)'; \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
SEARCHPLUGINS_NAMES = $(shell cat $(call MERGE_FILE,/searchplugins/list.txt)) ddg
|
SEARCHPLUGINS_FILENAMES := $(shell $(PYTHON) $(srcdir)/searchplugins.py $(srcdir)/search/list.json $(AB_CD))
|
||||||
ifeq (,$(filter-out en-US ru be kk tr uk zh-CN zh-TW,$(AB_CD)))
|
|
||||||
SEARCHPLUGINS_NAMES := $(subst google,google:hidden,$(SEARCHPLUGINS_NAMES))
|
|
||||||
SEARCHPLUGINS_NAMES += google-nocodes
|
|
||||||
endif
|
|
||||||
SEARCHPLUGINS_FILENAMES = $(subst :hidden,,$(SEARCHPLUGINS_NAMES))
|
|
||||||
SEARCHPLUGINS_PATH := .deps/generated_$(AB_CD)
|
SEARCHPLUGINS_PATH := .deps/generated_$(AB_CD)
|
||||||
SEARCHPLUGINS_TARGET := libs searchplugins
|
SEARCHPLUGINS_TARGET := libs searchplugins
|
||||||
SEARCHPLUGINS := $(foreach plugin,$(addsuffix .xml,$(SEARCHPLUGINS_FILENAMES)),$(or $(wildcard $(call EN_US_OR_L10N_FILE,searchplugins/$(plugin))),$(warning Missing searchplugin: $(plugin))))
|
SEARCHPLUGINS := $(foreach plugin,$(addsuffix .xml,$(SEARCHPLUGINS_FILENAMES)),$(or $(wildcard $(call EN_US_OR_L10N_FILE,searchplugins/$(plugin))),$(warning Missing searchplugin: $(plugin))))
|
||||||
|
@ -75,8 +70,8 @@ SEARCHPLUGINS := $(foreach plugin,$(addsuffix .xml,$(SEARCHPLUGINS_FILENAMES)),$
|
||||||
SEARCHPLUGINS_FLAGS := --silence-missing-directive-warnings
|
SEARCHPLUGINS_FLAGS := --silence-missing-directive-warnings
|
||||||
PP_TARGETS += SEARCHPLUGINS
|
PP_TARGETS += SEARCHPLUGINS
|
||||||
|
|
||||||
list-txt = $(SEARCHPLUGINS_PATH)/list.txt
|
list-json = $(SEARCHPLUGINS_PATH)/list.json
|
||||||
GARBAGE += $(list-txt)
|
GARBAGE += $(list-json)
|
||||||
|
|
||||||
libs:: searchplugins
|
libs:: searchplugins
|
||||||
|
|
||||||
|
@ -88,10 +83,9 @@ include $(topsrcdir)/config/rules.mk
|
||||||
|
|
||||||
include $(topsrcdir)/toolkit/locales/l10n.mk
|
include $(topsrcdir)/toolkit/locales/l10n.mk
|
||||||
|
|
||||||
$(list-txt): $(call mkdir_deps,$(SEARCHPLUGINS_PATH)) $(if $(IS_LANGUAGE_REPACK),FORCE)
|
$(list-json): $(call mkdir_deps,$(SEARCHPLUGINS_PATH)) $(if $(IS_LANGUAGE_REPACK),FORCE)
|
||||||
$(RM) $(list-txt)
|
$(shell $(PYTHON) $(srcdir)/searchjson.py $(srcdir)/search/list.json $(AB_CD) $(list-json))
|
||||||
$(foreach plugin,$(SEARCHPLUGINS_NAMES),printf '$(plugin)\n' >> $(list-txt);)
|
searchplugins:: $(list-json)
|
||||||
searchplugins:: $(list-txt)
|
|
||||||
|
|
||||||
$(STAGEDIST): $(DIST)/branding
|
$(STAGEDIST): $(DIST)/branding
|
||||||
|
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
amazondotcom
|
|
||||||
bing
|
|
||||||
google
|
|
||||||
twitter
|
|
||||||
wikipedia
|
|
||||||
yahoo
|
|
||||||
yahoo-en-CA:hidden
|
|
|
@ -8,11 +8,10 @@ def test(mod, path, entity = None):
|
||||||
if mod not in ("netwerk", "dom", "toolkit", "security/manager",
|
if mod not in ("netwerk", "dom", "toolkit", "security/manager",
|
||||||
"devtools/client", "devtools/shared",
|
"devtools/client", "devtools/shared",
|
||||||
"browser",
|
"browser",
|
||||||
"extensions/reporter", "extensions/spellcheck",
|
"extensions/spellcheck",
|
||||||
"other-licenses/branding/firefox",
|
"other-licenses/branding/firefox",
|
||||||
"browser/branding/official",
|
"browser/branding/official",
|
||||||
"services/sync",
|
"services/sync"):
|
||||||
"browser/extensions/pocket"):
|
|
||||||
return "ignore"
|
return "ignore"
|
||||||
if mod not in ("browser", "extensions/spellcheck"):
|
if mod not in ("browser", "extensions/spellcheck"):
|
||||||
# we only have exceptions for browser and extensions/spellcheck
|
# we only have exceptions for browser and extensions/spellcheck
|
||||||
|
|
|
@ -90,11 +90,11 @@
|
||||||
locale/browser/syncQuota.properties (%chrome/browser/syncQuota.properties)
|
locale/browser/syncQuota.properties (%chrome/browser/syncQuota.properties)
|
||||||
% resource search-plugins chrome://browser/locale/searchplugins/
|
% resource search-plugins chrome://browser/locale/searchplugins/
|
||||||
#if BUILD_FASTER
|
#if BUILD_FASTER
|
||||||
locale/browser/searchplugins/list.txt (%searchplugins/list.txt)
|
|
||||||
locale/browser/searchplugins/ (%searchplugins/*.xml)
|
locale/browser/searchplugins/ (%searchplugins/*.xml)
|
||||||
|
locale/browser/searchplugins/list.json (search/list.json)
|
||||||
#else
|
#else
|
||||||
locale/browser/searchplugins/list.txt (.deps/generated_@AB_CD@/list.txt)
|
|
||||||
locale/browser/searchplugins/ (.deps/generated_@AB_CD@/*.xml)
|
locale/browser/searchplugins/ (.deps/generated_@AB_CD@/*.xml)
|
||||||
|
locale/browser/searchplugins/list.json (.deps/generated_@AB_CD@/list.json)
|
||||||
#endif
|
#endif
|
||||||
% locale browser-region @AB_CD@ %locale/browser-region/
|
% locale browser-region @AB_CD@ %locale/browser-region/
|
||||||
locale/browser-region/region.properties (%chrome/browser-region/region.properties)
|
locale/browser-region/region.properties (%chrome/browser-region/region.properties)
|
||||||
|
|
|
@ -8,11 +8,9 @@ all = browser/locales/all-locales
|
||||||
|
|
||||||
[compare]
|
[compare]
|
||||||
dirs = browser
|
dirs = browser
|
||||||
extensions/reporter
|
|
||||||
other-licenses/branding/firefox
|
other-licenses/branding/firefox
|
||||||
browser/branding/official
|
browser/branding/official
|
||||||
devtools/client
|
devtools/client
|
||||||
browser/extensions/pocket
|
|
||||||
|
|
||||||
[includes]
|
[includes]
|
||||||
# non-central apps might want to use %(topsrcdir)s here, or other vars
|
# non-central apps might want to use %(topsrcdir)s here, or other vars
|
||||||
|
|
|
@ -0,0 +1,733 @@
|
||||||
|
{
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo", "amazondotcom", "bing", "ddg", "twitter", "wikipedia"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locales": {
|
||||||
|
"en-US": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo", "amazondotcom", "bing", "ddg", "twitter", "wikipedia"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"US": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"yahoo", "google-nocodes", "bing", "amazondotcom", "ddg", "twitter", "wikipedia"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"CA": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo-en-CA", "bing", "amazondotcom", "ddg", "twitter", "wikipedia"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ach": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo", "bing", "amazondotcom", "ddg", "twitter", "wikipedia"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"af": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo", "bing", "amazondotcom", "ddg", "wikipedia-af"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"an": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo-es", "bing", "wikipedia-an", "ddg", "twitter"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ar": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo", "bing", "amazondotcom", "ddg", "wikipedia-ar"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"as": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo-in", "amazondotcom", "ddg", "wikipedia-as"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ast": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo-es", "bing", "diccionariu-alla", "ddg", "wikipedia-ast"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"az": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "amazondotcom", "azerdict", "bing", "ddg", "wikipedia-az", "yandex-az"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"be": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"yandex.by", "yahoo", "google", "ddg", "be-x-old.wikipedia.org", "be.wikipedia.org", "ru.wikipedia.org-be", "tut.by"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"bg": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "diribg", "amazondotcom", "ddg", "portalbgdict", "wikipedia-bg"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"bn-BD": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo", "bing", "ddg", "wikipedia-bn"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"bn-IN": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo-in", "amazondotcom", "bing", "ddg", "rediff", "wikipedia-bn"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"br": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo-france", "amazon-france", "ddg", "freelang", "klask", "wikipedia-br"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"brx": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo-in", "bing", "ddg", "wikipedia-hi"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"bs": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo", "ddg", "olx", "twitter", "wikipedia-bs"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ca": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "bing", "diec2", "ddg", "twitter", "wikipedia-ca"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cak": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo-espanol", "bing", "amazondotcom", "ddg", "wikipedia-es"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cs": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "seznam-cz", "ddg", "heureka-cz", "mapy-cz", "wikipedia-cz"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cy": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo-en-GB", "amazon-en-GB", "ddg", "palasprint", "termau", "wikipedia-cy"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"da": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "bing", "amazon-co-uk", "ddg", "wikipedia-da"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"de": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo-de", "amazondotcom-de", "bing", "ddg", "leo_ende_de", "wikipedia-de"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dsb": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo-de", "bing", "amazondotcom-de", "ddg", "leo_ende_de", "wikipedia-dsb"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"el": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo", "amazon-en-GB", "bing", "ddg", "wikipedia-el"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"en-GB": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo-en-GB", "bing", "amazon-en-GB", "chambers-en-GB", "ddg", "twitter", "wikipedia"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"en-ZA": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "bing", "amazondotcom", "ddg", "twitter", "wikipedia"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"eo": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo", "bing", "amazondotcom", "ddg", "reta-vortaro", "wikipedia-eo"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"es-AR": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo-ar", "amazondotcom", "drae", "ddg", "mercadolibre-ar", "wikipedia-es"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"es-CL": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo-cl", "bing", "drae", "ddg", "mercadolibre-cl", "wikipedia-es"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"es-ES": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo-es", "bing", "drae", "ddg", "twitter", "wikipedia-es"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"es-MX": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo-mx", "bing", "ddg", "mercadolibre-mx", "wikipedia-es"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"et": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "neti-ee", "ddg", "osta-ee", "wikipedia-et", "eki-ee"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"eu": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo", "bing", "amazon-en-GB", "ddg", "elebila", "wikipedia-eu"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fa": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo", "amazondotcom", "bing", "ddg", "wikipedia-fa"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ff": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo-france", "bing", "amazon-france", "ddg", "cnrtl-tlfi-fr", "wikipedia-fr"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fi": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo-fi", "bing", "bookplus-fi", "ddg", "wikipedia-fi"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fr": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo-france", "bing", "amazon-france", "ddg", "cnrtl-tlfi-fr", "wikipedia-fr"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fy-NL": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo-fy-NL", "bing", "bolcom-fy-NL", "ddg", "marktplaats-fy-NL", "wikipedia-fy-NL"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ga-IE": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo-en-GB", "amazon-en-GB", "ddg", "tearma", "twitter", "wikipedia-ga-IE"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"gd": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo-en-GB", "faclair-beag", "amazon-en-GB", "bbc-alba", "ddg", "wikipedia-gd"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"gl": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo-es", "amazon-en-GB", "ddg", "wikipedia-gl"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"gn": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo-es", "bing", "amazondotcom", "ddg", "twitter", "wikipedia-gn"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"gu-IN": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo-in", "bing", "ddg", "gujaratilexicon", "wikipedia-gu"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"he": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo", "ddg", "wikipedia-he", "morfix-dic"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"hi-IN": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo-in", "bing", "ddg", "wikipedia-hi"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"hr": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo", "amazon-en-GB", "bing", "ddg", "eudict", "twitter", "wikipedia-hr"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"hsb": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo-de", "bing", "amazondotcom-de", "ddg", "leo_ende_de", "wikipedia-hsb"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"hu": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "ddg", "sztaki-en-hu", "vatera", "wikipedia-hu"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"hy-AM": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo", "amazondotcom", "ddg", "list-am", "wikipedia-hy"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo-id", "ddg", "wikipedia-id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"is": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo", "bing", "amazondotcom", "ddg", "leit-is", "wikipedia-is"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"it": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo-it", "bing", "amazon-it", "ddg", "hoepli", "wikipedia-it"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ja-JP-mac": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo-jp", "bing", "amazon-jp", "rakuten", "yahoo-jp-auctions", "oshiete-goo", "twitter-ja", "wikipedia-ja", "ddg"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ja": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo-jp", "bing", "amazon-jp", "rakuten", "yahoo-jp-auctions", "oshiete-goo", "twitter-ja", "wikipedia-ja", "ddg"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ka": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo", "bing", "amazondotcom", "ddg", "twitter", "wikipedia-ka"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"kk": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"yandex", "google", "ddg", "flip", "kaz-kk", "twitter", "wikipedia-kk"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"km": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo", "bing", "amazondotcom", "ddg", "twitter", "wikipedia-km"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"kn": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo-in", "bing", "amazondotcom", "ddg", "kannadastore", "wikipedia-kn"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ko": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "ddg", "naver-kr", "danawa-kr", "daum-kr", "wikipedia-kr"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"kok": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo-in", "bing", "ddg", "wikipedia-hi"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ks": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo-in", "bing", "ddg", "wikipedia-hi"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lij": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo-it", "bing", "amazon-it", "ddg", "paroledigenova-lij", "wikipedia-lij"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lt": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "wikipedia-lt", "bing", "amazondotcom", "ddg", "twitter"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ltg": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo", "dict-enlv", "ddg", "salidzinilv", "sslv", "wikipedia-lv"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lv": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo", "dict-enlv", "ddg", "salidzinilv", "sslv", "wikipedia-lv"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mai": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo-in", "bing", "ddg", "twitter", "wikipedia-hi"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mk": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo", "bing", "amazondotcom", "ddg", "wikipedia-mk"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ml": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "webdunia", "bing", "ddg", "rediff", "wikipedia", "wikipedia-ml"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mr": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo-in", "amazondotcom", "ddg", "rediff", "wikipedia-mr"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ms": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo", "bing", "amazondotcom", "ddg", "twitter", "wikipedia-ms"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"my": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo", "bing", "amazondotcom", "ddg", "twitter", "wikipedia-my"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nb-NO": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo-NO", "amazon-en-GB", "bing", "ddg", "gulesider-NO", "bok-NO", "qxl-NO", "wikipedia-NO"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ne-NP": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo", "bing", "ddg", "twitter", "wikipedia-ne"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nl": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "bing", "bolcom-nl", "ddg", "marktplaats-nl", "wikipedia-nl"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nn-NO": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "bing", "amazon-en-GB", "ddg", "gulesider-NO", "bok-NO", "qxl-NO", "wikipedia-NN"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"or": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo-in", "bing", "amazondotcom", "ddg", "wikipedia-or"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pa-IN": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo-in", "bing", "ddg", "wikipedia-pa"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pl": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "allegro-pl", "ddg", "pwn-pl", "wikipedia-pl", "wolnelektury-pl"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pt-BR": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo-br", "bing", "buscape", "ddg", "mercadolivre", "twitter", "wikipedia-br"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pt-PT": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "amazon-en-GB", "ddg", "priberam", "sapo", "wikipedia-ptpt"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rm": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo-ch", "bing", "ddg", "leo_ende_de", "pledarigrond", "wikipedia-rm"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ro": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo", "bing", "amazondotcom", "ddg", "wikipediaro"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ru": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"yandex", "google", "ddg", "ozonru", "priceru", "wikipedia-ru", "mailru"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sat": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo-in", "bing", "wikipedia-hi", "ddg"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"si": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo", "amazondotcom", "ddg", "wikipedia-si"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sk": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "azet-sk", "atlas-sk", "ddg", "dunaj-sk", "slovnik-sk", "wikipedia-sk", "zoznam-sk"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sl": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "ceneji", "ddg", "najdi-si", "odpiralni", "twitter", "wikipedia-sl"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"son": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo-france", "bing", "amazon-france", "ddg", "cnrtl-tlfi-fr", "wikipedia-fr"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sq": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo", "bing", "amazon-en-GB", "ddg", "wikipedia-sq"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sr": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "amazon-en-GB", "bing", "ddg", "wikipedia-sr", "pogodak"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sv-SE": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo-sv-SE", "bing", "allaannonser-sv-SE", "ddg", "prisjakt-sv-SE", "tyda-sv-SE", "wikipedia-sv-SE"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ta": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo-in", "ddg", "wikipedia-ta"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"te": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo-in", "amazondotcom", "ddg", "wikipedia-te", "wiktionary-te"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"th": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo", "amazondotcom", "bing", "ddg", "longdo", "wikipedia-th"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tr": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"yandex-tr", "google", "ddg", "twitter", "wikipedia-tr"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"uk": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yandex", "meta-ua", "ddg", "wikipedia-uk", "metamarket"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ur": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo", "bing", "amazondotcom", "ddg", "twitter", "wikipedia"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"uz": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo", "bing", "amazondotcom", "ddg", "twitter", "wikipedia-uz"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"vi": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "ddg", "wikipedia-vi", "zing-mp3"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"wo": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo", "bing", "amazondotcom", "ddg", "wikipedia-wo"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"xh": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "bing", "ddg", "wikipedia"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-CN": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"baidu", "google", "bing", "ddg", "wikipedia-zh-CN", "amazondotcn"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-TW": {
|
||||||
|
"default": {
|
||||||
|
"visibleDefaultEngines": [
|
||||||
|
"google", "yahoo-zh-TW", "ddg", "findbook-zh-TW", "wikipedia-zh-TW", "yahoo-zh-TW-HK", "yahoo-bid-zh-TW", "yahoo-answer-zh-TW"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
# 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/.
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import json
|
||||||
|
|
||||||
|
engines = []
|
||||||
|
|
||||||
|
locale = sys.argv[2]
|
||||||
|
output_file = sys.argv[3]
|
||||||
|
|
||||||
|
output = open(output_file, 'w')
|
||||||
|
|
||||||
|
with open(sys.argv[1]) as f:
|
||||||
|
searchinfo = json.load(f)
|
||||||
|
|
||||||
|
if locale in searchinfo["locales"]:
|
||||||
|
output.write(json.dumps(searchinfo["locales"][locale]))
|
||||||
|
else:
|
||||||
|
output.write(json.dumps(searchinfo["default"]))
|
||||||
|
|
||||||
|
output.close();
|
|
@ -0,0 +1,21 @@
|
||||||
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import json
|
||||||
|
|
||||||
|
engines = []
|
||||||
|
|
||||||
|
locale = sys.argv[2]
|
||||||
|
|
||||||
|
with open(sys.argv[1]) as f:
|
||||||
|
searchinfo = json.load(f)
|
||||||
|
|
||||||
|
if locale in searchinfo["locales"]:
|
||||||
|
for region in searchinfo["locales"][locale]:
|
||||||
|
engines = list(set(engines)|set(searchinfo["locales"][locale][region]["visibleDefaultEngines"]))
|
||||||
|
else:
|
||||||
|
engines = searchinfo["default"]["visibleDefaultEngines"]
|
||||||
|
|
||||||
|
print '\n'.join(engines)
|
|
@ -87,11 +87,12 @@ function testToggleToolboxButtons() {
|
||||||
let toolboxButtonNodes = [...doc.querySelectorAll(".command-button")];
|
let toolboxButtonNodes = [...doc.querySelectorAll(".command-button")];
|
||||||
let toggleableTools = toolbox.toolboxButtons;
|
let toggleableTools = toolbox.toolboxButtons;
|
||||||
|
|
||||||
// The noautohide button is only displayed in the browser toolbox
|
// The noautohide button is only displayed in the browser toolbox, and the element
|
||||||
|
// picker button is not toggleable.
|
||||||
toggleableTools = toggleableTools.filter(
|
toggleableTools = toggleableTools.filter(
|
||||||
tool => tool.id != "command-button-noautohide");
|
tool => tool.id != "command-button-noautohide" && tool.id != "command-button-pick");
|
||||||
toolboxButtonNodes = toolboxButtonNodes.filter(
|
toolboxButtonNodes = toolboxButtonNodes.filter(
|
||||||
btn => btn.id != "command-button-noautohide");
|
btn => btn.id != "command-button-noautohide" && btn.id != "command-button-pick");
|
||||||
|
|
||||||
is(checkNodes.length, toggleableTools.length,
|
is(checkNodes.length, toggleableTools.length,
|
||||||
"All of the buttons are toggleable.");
|
"All of the buttons are toggleable.");
|
||||||
|
|
|
@ -71,10 +71,6 @@ loader.lazyGetter(this, "registerHarOverlay", () => {
|
||||||
// addons that have manually inserted toolbarbuttons into DOM.
|
// addons that have manually inserted toolbarbuttons into DOM.
|
||||||
// (By default, supported target is only local tab)
|
// (By default, supported target is only local tab)
|
||||||
const ToolboxButtons = exports.ToolboxButtons = [
|
const ToolboxButtons = exports.ToolboxButtons = [
|
||||||
{ id: "command-button-pick",
|
|
||||||
isTargetSupported: target =>
|
|
||||||
target.getTrait("highlightable")
|
|
||||||
},
|
|
||||||
{ id: "command-button-frames",
|
{ id: "command-button-frames",
|
||||||
isTargetSupported: target => {
|
isTargetSupported: target => {
|
||||||
return target.activeTab && target.activeTab.traits.frames;
|
return target.activeTab && target.activeTab.traits.frames;
|
||||||
|
@ -958,7 +954,7 @@ Toolbox.prototype = {
|
||||||
* Add buttons to the UI as specified in the devtools.toolbox.toolbarSpec pref
|
* Add buttons to the UI as specified in the devtools.toolbox.toolbarSpec pref
|
||||||
*/
|
*/
|
||||||
_buildButtons: function () {
|
_buildButtons: function () {
|
||||||
if (!this.target.isAddon || this.target.isWebExtension) {
|
if (this.target.getTrait("highlightable")) {
|
||||||
this._buildPickerButton();
|
this._buildPickerButton();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1003,7 +999,6 @@ Toolbox.prototype = {
|
||||||
this._pickerButton.className =
|
this._pickerButton.className =
|
||||||
"command-button command-button-invertable devtools-button";
|
"command-button command-button-invertable devtools-button";
|
||||||
this._pickerButton.setAttribute("title", L10N.getStr("pickButton.tooltip"));
|
this._pickerButton.setAttribute("title", L10N.getStr("pickButton.tooltip"));
|
||||||
this._pickerButton.setAttribute("hidden", "true");
|
|
||||||
|
|
||||||
let container = this.doc.querySelector("#toolbox-picker-container");
|
let container = this.doc.querySelector("#toolbox-picker-container");
|
||||||
container.appendChild(this._pickerButton);
|
container.appendChild(this._pickerButton);
|
||||||
|
|
|
@ -37,7 +37,6 @@ pref("devtools.toolbox.splitconsoleEnabled", false);
|
||||||
pref("devtools.toolbox.splitconsoleHeight", 100);
|
pref("devtools.toolbox.splitconsoleHeight", 100);
|
||||||
|
|
||||||
// Toolbox Button preferences
|
// Toolbox Button preferences
|
||||||
pref("devtools.command-button-pick.enabled", true);
|
|
||||||
pref("devtools.command-button-frames.enabled", true);
|
pref("devtools.command-button-frames.enabled", true);
|
||||||
pref("devtools.command-button-splitconsole.enabled", true);
|
pref("devtools.command-button-splitconsole.enabled", true);
|
||||||
pref("devtools.command-button-paintflashing.enabled", false);
|
pref("devtools.command-button-paintflashing.enabled", false);
|
||||||
|
|
|
@ -38,6 +38,8 @@
|
||||||
#include "nsIDOMDocument.h"
|
#include "nsIDOMDocument.h"
|
||||||
#include "nsIDOMElement.h"
|
#include "nsIDOMElement.h"
|
||||||
|
|
||||||
|
#include "nsArray.h"
|
||||||
|
#include "nsArrayUtils.h"
|
||||||
#include "nsIDOMStorage.h"
|
#include "nsIDOMStorage.h"
|
||||||
#include "nsIContentViewer.h"
|
#include "nsIContentViewer.h"
|
||||||
#include "nsIDocumentLoaderFactory.h"
|
#include "nsIDocumentLoaderFactory.h"
|
||||||
|
@ -205,7 +207,6 @@
|
||||||
#include "nsISecureBrowserUI.h"
|
#include "nsISecureBrowserUI.h"
|
||||||
#include "nsISocketProvider.h"
|
#include "nsISocketProvider.h"
|
||||||
#include "nsIStringBundle.h"
|
#include "nsIStringBundle.h"
|
||||||
#include "nsISupportsArray.h"
|
|
||||||
#include "nsIURIFixup.h"
|
#include "nsIURIFixup.h"
|
||||||
#include "nsIURILoader.h"
|
#include "nsIURILoader.h"
|
||||||
#include "nsIURL.h"
|
#include "nsIURL.h"
|
||||||
|
@ -6684,21 +6685,20 @@ nsDocShell::RefreshURI(nsIURI* aURI, int32_t aDelay, bool aRepeat,
|
||||||
refreshTimer->mMetaRefresh = aMetaRefresh;
|
refreshTimer->mMetaRefresh = aMetaRefresh;
|
||||||
|
|
||||||
if (!mRefreshURIList) {
|
if (!mRefreshURIList) {
|
||||||
NS_ENSURE_SUCCESS(NS_NewISupportsArray(getter_AddRefs(mRefreshURIList)),
|
mRefreshURIList = nsArray::Create();
|
||||||
NS_ERROR_FAILURE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (busyFlags & BUSY_FLAGS_BUSY || (!mIsActive && mDisableMetaRefreshWhenInactive)) {
|
if (busyFlags & BUSY_FLAGS_BUSY || (!mIsActive && mDisableMetaRefreshWhenInactive)) {
|
||||||
// We don't want to create the timer right now. Instead queue up the request
|
// We don't want to create the timer right now. Instead queue up the request
|
||||||
// and trigger the timer in EndPageLoad() or whenever we become active.
|
// and trigger the timer in EndPageLoad() or whenever we become active.
|
||||||
mRefreshURIList->AppendElement(refreshTimer);
|
mRefreshURIList->AppendElement(refreshTimer, /*weak =*/ false);
|
||||||
} else {
|
} else {
|
||||||
// There is no page loading going on right now. Create the
|
// There is no page loading going on right now. Create the
|
||||||
// timer and fire it right away.
|
// timer and fire it right away.
|
||||||
nsCOMPtr<nsITimer> timer = do_CreateInstance("@mozilla.org/timer;1");
|
nsCOMPtr<nsITimer> timer = do_CreateInstance("@mozilla.org/timer;1");
|
||||||
NS_ENSURE_TRUE(timer, NS_ERROR_FAILURE);
|
NS_ENSURE_TRUE(timer, NS_ERROR_FAILURE);
|
||||||
|
|
||||||
mRefreshURIList->AppendElement(timer); // owning timer ref
|
mRefreshURIList->AppendElement(timer, /*weak =*/ false); // owning timer ref
|
||||||
timer->InitWithCallback(refreshTimer, aDelay, nsITimer::TYPE_ONE_SHOT);
|
timer->InitWithCallback(refreshTimer, aDelay, nsITimer::TYPE_ONE_SHOT);
|
||||||
}
|
}
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
|
@ -6715,7 +6715,7 @@ nsDocShell::ForceRefreshURIFromTimer(nsIURI* aURI,
|
||||||
// Remove aTimer from mRefreshURIList if needed
|
// Remove aTimer from mRefreshURIList if needed
|
||||||
if (mRefreshURIList) {
|
if (mRefreshURIList) {
|
||||||
uint32_t n = 0;
|
uint32_t n = 0;
|
||||||
mRefreshURIList->Count(&n);
|
mRefreshURIList->GetLength(&n);
|
||||||
|
|
||||||
for (uint32_t i = 0; i < n; ++i) {
|
for (uint32_t i = 0; i < n; ++i) {
|
||||||
nsCOMPtr<nsITimer> timer = do_QueryElementAt(mRefreshURIList, i);
|
nsCOMPtr<nsITimer> timer = do_QueryElementAt(mRefreshURIList, i);
|
||||||
|
@ -7082,14 +7082,14 @@ nsDocShell::SetupRefreshURI(nsIChannel* aChannel)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
DoCancelRefreshURITimers(nsISupportsArray* aTimerList)
|
DoCancelRefreshURITimers(nsIMutableArray* aTimerList)
|
||||||
{
|
{
|
||||||
if (!aTimerList) {
|
if (!aTimerList) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t n = 0;
|
uint32_t n = 0;
|
||||||
aTimerList->Count(&n);
|
aTimerList->GetLength(&n);
|
||||||
|
|
||||||
while (n) {
|
while (n) {
|
||||||
nsCOMPtr<nsITimer> timer(do_QueryElementAt(aTimerList, --n));
|
nsCOMPtr<nsITimer> timer(do_QueryElementAt(aTimerList, --n));
|
||||||
|
@ -7122,7 +7122,7 @@ nsDocShell::GetRefreshPending(bool* aResult)
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t count;
|
uint32_t count;
|
||||||
nsresult rv = mRefreshURIList->Count(&count);
|
nsresult rv = mRefreshURIList->GetLength(&count);
|
||||||
if (NS_SUCCEEDED(rv)) {
|
if (NS_SUCCEEDED(rv)) {
|
||||||
*aResult = (count != 0);
|
*aResult = (count != 0);
|
||||||
}
|
}
|
||||||
|
@ -7134,7 +7134,7 @@ nsDocShell::SuspendRefreshURIs()
|
||||||
{
|
{
|
||||||
if (mRefreshURIList) {
|
if (mRefreshURIList) {
|
||||||
uint32_t n = 0;
|
uint32_t n = 0;
|
||||||
mRefreshURIList->Count(&n);
|
mRefreshURIList->GetLength(&n);
|
||||||
|
|
||||||
for (uint32_t i = 0; i < n; ++i) {
|
for (uint32_t i = 0; i < n; ++i) {
|
||||||
nsCOMPtr<nsITimer> timer = do_QueryElementAt(mRefreshURIList, i);
|
nsCOMPtr<nsITimer> timer = do_QueryElementAt(mRefreshURIList, i);
|
||||||
|
@ -7152,7 +7152,7 @@ nsDocShell::SuspendRefreshURIs()
|
||||||
NS_ASSERTION(rt,
|
NS_ASSERTION(rt,
|
||||||
"RefreshURIList timer callbacks should only be RefreshTimer objects");
|
"RefreshURIList timer callbacks should only be RefreshTimer objects");
|
||||||
|
|
||||||
mRefreshURIList->ReplaceElementAt(rt, i);
|
mRefreshURIList->ReplaceElementAt(rt, i, /*weak =*/ false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7192,12 +7192,11 @@ nsDocShell::RefreshURIFromQueue()
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
uint32_t n = 0;
|
uint32_t n = 0;
|
||||||
mRefreshURIList->Count(&n);
|
mRefreshURIList->GetLength(&n);
|
||||||
|
|
||||||
while (n) {
|
while (n) {
|
||||||
nsCOMPtr<nsISupports> element;
|
nsCOMPtr<nsITimerCallback> refreshInfo =
|
||||||
mRefreshURIList->GetElementAt(--n, getter_AddRefs(element));
|
do_QueryElementAt(mRefreshURIList, --n);
|
||||||
nsCOMPtr<nsITimerCallback> refreshInfo(do_QueryInterface(element));
|
|
||||||
|
|
||||||
if (refreshInfo) {
|
if (refreshInfo) {
|
||||||
// This is the nsRefreshTimer object, waiting to be
|
// This is the nsRefreshTimer object, waiting to be
|
||||||
|
@ -7212,7 +7211,7 @@ nsDocShell::RefreshURIFromQueue()
|
||||||
// its corresponding timer object, so that in case another
|
// its corresponding timer object, so that in case another
|
||||||
// load comes through before the timer can go off, the timer will
|
// load comes through before the timer can go off, the timer will
|
||||||
// get cancelled in CancelRefreshURITimer()
|
// get cancelled in CancelRefreshURITimer()
|
||||||
mRefreshURIList->ReplaceElementAt(timer, n);
|
mRefreshURIList->ReplaceElementAt(timer, n, /*weak =*/ false);
|
||||||
timer->InitWithCallback(refreshInfo, delay, nsITimer::TYPE_ONE_SHOT);
|
timer->InitWithCallback(refreshInfo, delay, nsITimer::TYPE_ONE_SHOT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8735,7 +8734,7 @@ nsDocShell::RestoreFromHistory()
|
||||||
|
|
||||||
// Restore the refresh URI list. The refresh timers will be restarted
|
// Restore the refresh URI list. The refresh timers will be restarted
|
||||||
// when EndPageLoad() is called.
|
// when EndPageLoad() is called.
|
||||||
nsCOMPtr<nsISupportsArray> refreshURIList;
|
nsCOMPtr<nsIMutableArray> refreshURIList;
|
||||||
mLSHE->GetRefreshURIList(getter_AddRefs(refreshURIList));
|
mLSHE->GetRefreshURIList(getter_AddRefs(refreshURIList));
|
||||||
|
|
||||||
// Reattach to the window object.
|
// Reattach to the window object.
|
||||||
|
@ -8755,7 +8754,7 @@ nsDocShell::RestoreFromHistory()
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
{
|
{
|
||||||
nsCOMPtr<nsISupportsArray> refreshURIs;
|
nsCOMPtr<nsIMutableArray> refreshURIs;
|
||||||
mLSHE->GetRefreshURIList(getter_AddRefs(refreshURIs));
|
mLSHE->GetRefreshURIList(getter_AddRefs(refreshURIs));
|
||||||
nsCOMPtr<nsIDocShellTreeItem> childShell;
|
nsCOMPtr<nsIDocShellTreeItem> childShell;
|
||||||
mLSHE->ChildShellAt(0, getter_AddRefs(childShell));
|
mLSHE->ChildShellAt(0, getter_AddRefs(childShell));
|
||||||
|
|
|
@ -84,11 +84,11 @@ class nsIDOMNode;
|
||||||
class nsIDocShellTreeOwner;
|
class nsIDocShellTreeOwner;
|
||||||
class nsIGlobalHistory2;
|
class nsIGlobalHistory2;
|
||||||
class nsIHttpChannel;
|
class nsIHttpChannel;
|
||||||
|
class nsIMutableArray;
|
||||||
class nsIPrompt;
|
class nsIPrompt;
|
||||||
class nsISHistory;
|
class nsISHistory;
|
||||||
class nsISecureBrowserUI;
|
class nsISecureBrowserUI;
|
||||||
class nsIStringBundle;
|
class nsIStringBundle;
|
||||||
class nsISupportsArray;
|
|
||||||
class nsIURIFixup;
|
class nsIURIFixup;
|
||||||
class nsIURILoader;
|
class nsIURILoader;
|
||||||
class nsIWebBrowserFind;
|
class nsIWebBrowserFind;
|
||||||
|
@ -810,8 +810,8 @@ protected:
|
||||||
nsCString mContentTypeHint;
|
nsCString mContentTypeHint;
|
||||||
nsIntPoint mDefaultScrollbarPref; // persistent across doc loads
|
nsIntPoint mDefaultScrollbarPref; // persistent across doc loads
|
||||||
|
|
||||||
nsCOMPtr<nsISupportsArray> mRefreshURIList;
|
nsCOMPtr<nsIMutableArray> mRefreshURIList;
|
||||||
nsCOMPtr<nsISupportsArray> mSavedRefreshURIList;
|
nsCOMPtr<nsIMutableArray> mSavedRefreshURIList;
|
||||||
RefPtr<nsDSURIContentListener> mContentListener;
|
RefPtr<nsDSURIContentListener> mContentListener;
|
||||||
nsCOMPtr<nsIContentViewer> mContentViewer;
|
nsCOMPtr<nsIContentViewer> mContentViewer;
|
||||||
nsCOMPtr<nsIWidget> mParentWidget;
|
nsCOMPtr<nsIWidget> mParentWidget;
|
||||||
|
|
|
@ -12,12 +12,12 @@
|
||||||
|
|
||||||
#include "nsISupports.idl"
|
#include "nsISupports.idl"
|
||||||
|
|
||||||
|
interface nsIMutableArray;
|
||||||
interface nsILayoutHistoryState;
|
interface nsILayoutHistoryState;
|
||||||
interface nsIContentViewer;
|
interface nsIContentViewer;
|
||||||
interface nsIURI;
|
interface nsIURI;
|
||||||
interface nsIInputStream;
|
interface nsIInputStream;
|
||||||
interface nsIDocShellTreeItem;
|
interface nsIDocShellTreeItem;
|
||||||
interface nsISupportsArray;
|
|
||||||
interface nsIStructuredCloneContainer;
|
interface nsIStructuredCloneContainer;
|
||||||
interface nsIBFCacheEntry;
|
interface nsIBFCacheEntry;
|
||||||
interface nsIPrincipal;
|
interface nsIPrincipal;
|
||||||
|
@ -119,7 +119,7 @@ interface nsISHEntry : nsISupports
|
||||||
void clearChildShells();
|
void clearChildShells();
|
||||||
|
|
||||||
/** Saved refresh URI list for the content viewer */
|
/** Saved refresh URI list for the content viewer */
|
||||||
attribute nsISupportsArray refreshURIList;
|
attribute nsIMutableArray refreshURIList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ensure that the cached presentation members are self-consistent.
|
* Ensure that the cached presentation members are self-consistent.
|
||||||
|
|
|
@ -824,14 +824,14 @@ nsSHEntry::ClearChildShells()
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsSHEntry::GetRefreshURIList(nsISupportsArray** aList)
|
nsSHEntry::GetRefreshURIList(nsIMutableArray** aList)
|
||||||
{
|
{
|
||||||
NS_IF_ADDREF(*aList = mShared->mRefreshURIList);
|
NS_IF_ADDREF(*aList = mShared->mRefreshURIList);
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsSHEntry::SetRefreshURIList(nsISupportsArray* aList)
|
nsSHEntry::SetRefreshURIList(nsIMutableArray* aList)
|
||||||
{
|
{
|
||||||
mShared->mRefreshURIList = aList;
|
mShared->mRefreshURIList = aList;
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
#include "nsILayoutHistoryState.h"
|
#include "nsILayoutHistoryState.h"
|
||||||
#include "mozilla/Attributes.h"
|
#include "mozilla/Attributes.h"
|
||||||
#include "mozilla/Preferences.h"
|
#include "mozilla/Preferences.h"
|
||||||
#include "nsISupportsArray.h"
|
#include "nsArray.h"
|
||||||
|
|
||||||
namespace dom = mozilla::dom;
|
namespace dom = mozilla::dom;
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ class nsIContentViewer;
|
||||||
class nsIDocShellTreeItem;
|
class nsIDocShellTreeItem;
|
||||||
class nsILayoutHistoryState;
|
class nsILayoutHistoryState;
|
||||||
class nsDocShellEditorData;
|
class nsDocShellEditorData;
|
||||||
class nsISupportsArray;
|
class nsIMutableArray;
|
||||||
|
|
||||||
// A document may have multiple SHEntries, either due to hash navigations or
|
// A document may have multiple SHEntries, either due to hash navigations or
|
||||||
// calls to history.pushState. SHEntries corresponding to the same document
|
// calls to history.pushState. SHEntries corresponding to the same document
|
||||||
|
@ -89,7 +89,7 @@ private:
|
||||||
bool mExpired;
|
bool mExpired;
|
||||||
nsCOMPtr<nsISupports> mWindowState;
|
nsCOMPtr<nsISupports> mWindowState;
|
||||||
nsIntRect mViewerBounds;
|
nsIntRect mViewerBounds;
|
||||||
nsCOMPtr<nsISupportsArray> mRefreshURIList;
|
nsCOMPtr<nsIMutableArray> mRefreshURIList;
|
||||||
nsExpirationState mExpirationState;
|
nsExpirationState mExpirationState;
|
||||||
nsAutoPtr<nsDocShellEditorData> mEditorData;
|
nsAutoPtr<nsDocShellEditorData> mEditorData;
|
||||||
};
|
};
|
||||||
|
|
|
@ -50,7 +50,6 @@
|
||||||
#include "nsIXULWindow.h"
|
#include "nsIXULWindow.h"
|
||||||
#include "nsIEditor.h"
|
#include "nsIEditor.h"
|
||||||
#include "nsIMozBrowserFrame.h"
|
#include "nsIMozBrowserFrame.h"
|
||||||
#include "nsIPermissionManager.h"
|
|
||||||
#include "nsISHistory.h"
|
#include "nsISHistory.h"
|
||||||
#include "nsNullPrincipal.h"
|
#include "nsNullPrincipal.h"
|
||||||
#include "nsIScriptError.h"
|
#include "nsIScriptError.h"
|
||||||
|
@ -146,7 +145,6 @@ NS_INTERFACE_MAP_END
|
||||||
|
|
||||||
nsFrameLoader::nsFrameLoader(Element* aOwner, bool aNetworkCreated)
|
nsFrameLoader::nsFrameLoader(Element* aOwner, bool aNetworkCreated)
|
||||||
: mOwnerContent(aOwner)
|
: mOwnerContent(aOwner)
|
||||||
, mAppIdSentToPermissionManager(nsIScriptSecurityManager::NO_APP_ID)
|
|
||||||
, mDetachedSubdocFrame(nullptr)
|
, mDetachedSubdocFrame(nullptr)
|
||||||
, mRemoteBrowser(nullptr)
|
, mRemoteBrowser(nullptr)
|
||||||
, mChildID(0)
|
, mChildID(0)
|
||||||
|
@ -494,9 +492,6 @@ nsFrameLoader::ReallyStartLoadingInternal()
|
||||||
mURIToLoad = nullptr;
|
mURIToLoad = nullptr;
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
// Track the appId's reference count if this frame is in-process
|
|
||||||
ResetPermissionManagerStatus();
|
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1753,8 +1748,6 @@ nsFrameLoader::SetOwnerContent(Element* aContent)
|
||||||
if (RenderFrameParent* rfp = GetCurrentRenderFrame()) {
|
if (RenderFrameParent* rfp = GetCurrentRenderFrame()) {
|
||||||
rfp->OwnerContentChanged(aContent);
|
rfp->OwnerContentChanged(aContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
ResetPermissionManagerStatus();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
@ -3136,69 +3129,6 @@ nsFrameLoader::AttributeChanged(nsIDocument* aDocument,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
nsFrameLoader::ResetPermissionManagerStatus()
|
|
||||||
{
|
|
||||||
// The resetting of the permissions status can run only
|
|
||||||
// in the main process.
|
|
||||||
// only in-main-process && in-process frame is handled here and all other
|
|
||||||
// cases are handled by ContentParent.
|
|
||||||
if (XRE_IsContentProcess() || mRemoteFrame) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finding the new app Id:
|
|
||||||
// . first we check if the owner is an app frame
|
|
||||||
// . second, we check if the owner is a browser frame
|
|
||||||
// in both cases we populate the appId variable.
|
|
||||||
uint32_t appId = nsIScriptSecurityManager::NO_APP_ID;
|
|
||||||
if (OwnerIsAppFrame()) {
|
|
||||||
// You can't be both an app and a browser frame.
|
|
||||||
MOZ_ASSERT(!OwnerIsMozBrowserFrame());
|
|
||||||
|
|
||||||
nsCOMPtr<mozIApplication> ownApp = GetOwnApp();
|
|
||||||
MOZ_ASSERT(ownApp);
|
|
||||||
uint32_t ownAppId = nsIScriptSecurityManager::NO_APP_ID;
|
|
||||||
if (ownApp && NS_SUCCEEDED(ownApp->GetLocalId(&ownAppId))) {
|
|
||||||
appId = ownAppId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (OwnerIsMozBrowserFrame()) {
|
|
||||||
// You can't be both a browser and an app frame.
|
|
||||||
MOZ_ASSERT(!OwnerIsAppFrame());
|
|
||||||
|
|
||||||
nsCOMPtr<mozIApplication> containingApp = GetContainingApp();
|
|
||||||
uint32_t containingAppId = nsIScriptSecurityManager::NO_APP_ID;
|
|
||||||
if (containingApp && NS_SUCCEEDED(containingApp->GetLocalId(&containingAppId))) {
|
|
||||||
appId = containingAppId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Nothing changed.
|
|
||||||
if (appId == mAppIdSentToPermissionManager) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsCOMPtr<nsIPermissionManager> permMgr = services::GetPermissionManager();
|
|
||||||
if (!permMgr) {
|
|
||||||
NS_ERROR("No PermissionManager available!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If previously we registered an appId, we have to unregister it.
|
|
||||||
if (mAppIdSentToPermissionManager != nsIScriptSecurityManager::NO_APP_ID) {
|
|
||||||
permMgr->ReleaseAppId(mAppIdSentToPermissionManager);
|
|
||||||
mAppIdSentToPermissionManager = nsIScriptSecurityManager::NO_APP_ID;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register the new AppId.
|
|
||||||
if (appId != nsIScriptSecurityManager::NO_APP_ID) {
|
|
||||||
mAppIdSentToPermissionManager = appId;
|
|
||||||
permMgr->AddrefAppId(mAppIdSentToPermissionManager);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send the RequestNotifyAfterRemotePaint message to the current Tab.
|
* Send the RequestNotifyAfterRemotePaint message to the current Tab.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -325,10 +325,6 @@ private:
|
||||||
? nsGkAtoms::type : nsGkAtoms::mozframetype;
|
? nsGkAtoms::type : nsGkAtoms::mozframetype;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the permission manager's app-id refcount based on mOwnerContent's
|
|
||||||
// own-or-containing-app.
|
|
||||||
void ResetPermissionManagerStatus();
|
|
||||||
|
|
||||||
void InitializeBrowserAPI();
|
void InitializeBrowserAPI();
|
||||||
void DestroyBrowserFrameScripts();
|
void DestroyBrowserFrameScripts();
|
||||||
|
|
||||||
|
@ -354,9 +350,6 @@ private:
|
||||||
// our <browser> element.
|
// our <browser> element.
|
||||||
RefPtr<mozilla::dom::Element> mOwnerContentStrong;
|
RefPtr<mozilla::dom::Element> mOwnerContentStrong;
|
||||||
|
|
||||||
// Note: this variable must be modified only by ResetPermissionManagerStatus()
|
|
||||||
uint32_t mAppIdSentToPermissionManager;
|
|
||||||
|
|
||||||
// Stores the root frame of the subdocument while the subdocument is being
|
// Stores the root frame of the subdocument while the subdocument is being
|
||||||
// reframed. Used to restore the presentation after reframing.
|
// reframed. Used to restore the presentation after reframing.
|
||||||
nsWeakFrame mDetachedSubdocFrame;
|
nsWeakFrame mDetachedSubdocFrame;
|
||||||
|
|
|
@ -3165,7 +3165,7 @@ nsGlobalWindow::SetOpenerWindow(nsPIDOMWindowOuter* aOpener,
|
||||||
"Probably too late to call ComputeIsSecureContext again");
|
"Probably too late to call ComputeIsSecureContext again");
|
||||||
mHadOriginalOpener = true;
|
mHadOriginalOpener = true;
|
||||||
mOriginalOpenerWasSecureContext =
|
mOriginalOpenerWasSecureContext =
|
||||||
nsGlobalWindow::Cast(aOpener->GetCurrentInnerWindow())->IsSecureContext();
|
aOpener->GetCurrentInnerWindow()->IsSecureContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
|
@ -3860,6 +3860,12 @@ nsPIDOMWindowInner::CreatePerformanceObjectIfNeeded()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
nsPIDOMWindowInner::IsSecureContext() const
|
||||||
|
{
|
||||||
|
return nsGlobalWindow::Cast(this)->IsSecureContext();
|
||||||
|
}
|
||||||
|
|
||||||
SuspendTypes
|
SuspendTypes
|
||||||
nsPIDOMWindowOuter::GetMediaSuspend() const
|
nsPIDOMWindowOuter::GetMediaSuspend() const
|
||||||
{
|
{
|
||||||
|
|
|
@ -328,6 +328,10 @@ public:
|
||||||
return static_cast<nsGlobalWindow*>(
|
return static_cast<nsGlobalWindow*>(
|
||||||
reinterpret_cast<nsPIDOMWindow<nsISupports>*>(aPIWin));
|
reinterpret_cast<nsPIDOMWindow<nsISupports>*>(aPIWin));
|
||||||
}
|
}
|
||||||
|
static const nsGlobalWindow* Cast(const nsPIDOMWindowInner* aPIWin) {
|
||||||
|
return static_cast<const nsGlobalWindow*>(
|
||||||
|
reinterpret_cast<const nsPIDOMWindow<nsISupports>*>(aPIWin));
|
||||||
|
}
|
||||||
static nsGlobalWindow* Cast(mozIDOMWindow* aWin) {
|
static nsGlobalWindow* Cast(mozIDOMWindow* aWin) {
|
||||||
return Cast(nsPIDOMWindowInner::From(aWin));
|
return Cast(nsPIDOMWindowInner::From(aWin));
|
||||||
}
|
}
|
||||||
|
|
|
@ -811,6 +811,11 @@ public:
|
||||||
return mInnerObjectsFreed;
|
return mInnerObjectsFreed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether this window is a secure context.
|
||||||
|
*/
|
||||||
|
bool IsSecureContext() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void CreatePerformanceObjectIfNeeded();
|
void CreatePerformanceObjectIfNeeded();
|
||||||
};
|
};
|
||||||
|
|
|
@ -51,6 +51,7 @@
|
||||||
#include "mozilla/dom/WebIDLGlobalNameHash.h"
|
#include "mozilla/dom/WebIDLGlobalNameHash.h"
|
||||||
#include "mozilla/dom/WorkerPrivate.h"
|
#include "mozilla/dom/WorkerPrivate.h"
|
||||||
#include "mozilla/dom/WorkerScope.h"
|
#include "mozilla/dom/WorkerScope.h"
|
||||||
|
#include "mozilla/dom/XrayExpandoClass.h"
|
||||||
#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
|
#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
|
||||||
#include "nsDOMClassInfo.h"
|
#include "nsDOMClassInfo.h"
|
||||||
#include "ipc/ErrorIPCUtils.h"
|
#include "ipc/ErrorIPCUtils.h"
|
||||||
|
@ -1892,6 +1893,37 @@ XrayOwnPropertyKeys(JSContext* cx, JS::Handle<JSObject*> wrapper,
|
||||||
obj, flags, props);
|
obj, flags, props);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const JSClass*
|
||||||
|
XrayGetExpandoClass(JSContext* cx, JS::Handle<JSObject*> obj)
|
||||||
|
{
|
||||||
|
DOMObjectType type;
|
||||||
|
const NativePropertyHooks* nativePropertyHooks =
|
||||||
|
GetNativePropertyHooks(cx, obj, type);
|
||||||
|
if (!IsInstance(type)) {
|
||||||
|
// Non-instances don't need any special expando classes.
|
||||||
|
return &DefaultXrayExpandoObjectClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nativePropertyHooks->mXrayExpandoClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSObject*
|
||||||
|
GetCachedSlotStorageObjectSlow(JSContext* cx, JS::Handle<JSObject*> obj,
|
||||||
|
bool* isXray)
|
||||||
|
{
|
||||||
|
if (!xpc::WrapperFactory::IsXrayWrapper(obj)) {
|
||||||
|
JSObject* retval = js::UncheckedUnwrap(obj, /* stopAtWindowProxy = */ false);
|
||||||
|
MOZ_ASSERT(IsDOMObject(retval));
|
||||||
|
*isXray = false;
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
*isXray = true;
|
||||||
|
return xpc::EnsureXrayExpandoObject(cx, obj);;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFINE_XRAY_EXPANDO_CLASS(, DefaultXrayExpandoObjectClass, 0);
|
||||||
|
|
||||||
NativePropertyHooks sEmptyNativePropertyHooks = {
|
NativePropertyHooks sEmptyNativePropertyHooks = {
|
||||||
nullptr,
|
nullptr,
|
||||||
nullptr,
|
nullptr,
|
||||||
|
|
|
@ -2400,6 +2400,41 @@ XrayGetNativeProto(JSContext* cx, JS::Handle<JSObject*> obj,
|
||||||
return JS_WrapObject(cx, protop);
|
return JS_WrapObject(cx, protop);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Xray expando class to use for the given DOM object.
|
||||||
|
*/
|
||||||
|
const JSClass*
|
||||||
|
XrayGetExpandoClass(JSContext* cx, JS::Handle<JSObject*> obj);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the object which should be used to cache the return value of a property
|
||||||
|
* getter in the case of a [Cached] or [StoreInSlot] property. `obj` is the
|
||||||
|
* `this` value for our property getter that we're working with.
|
||||||
|
*
|
||||||
|
* This function can return null on failure to allocate the object, throwing on
|
||||||
|
* the JSContext in the process.
|
||||||
|
*
|
||||||
|
* The isXray outparam will be set to true if obj is an Xray and false
|
||||||
|
* otherwise.
|
||||||
|
*
|
||||||
|
* Note that the Slow version should only be called from
|
||||||
|
* GetCachedSlotStorageObject.
|
||||||
|
*/
|
||||||
|
JSObject*
|
||||||
|
GetCachedSlotStorageObjectSlow(JSContext* cx, JS::Handle<JSObject*> obj,
|
||||||
|
bool* isXray);
|
||||||
|
|
||||||
|
inline JSObject*
|
||||||
|
GetCachedSlotStorageObject(JSContext* cx, JS::Handle<JSObject*> obj,
|
||||||
|
bool* isXray) {
|
||||||
|
if (IsDOMObject(obj)) {
|
||||||
|
*isXray = false;
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetCachedSlotStorageObjectSlow(cx, obj, isXray);
|
||||||
|
}
|
||||||
|
|
||||||
extern NativePropertyHooks sEmptyNativePropertyHooks;
|
extern NativePropertyHooks sEmptyNativePropertyHooks;
|
||||||
|
|
||||||
extern const js::ClassOps sBoringInterfaceObjectClassClassOps;
|
extern const js::ClassOps sBoringInterfaceObjectClassClassOps;
|
||||||
|
|
|
@ -36,6 +36,18 @@ def memberReservedSlot(member, descriptor):
|
||||||
member.slotIndices[descriptor.interface.identifier.name])
|
member.slotIndices[descriptor.interface.identifier.name])
|
||||||
|
|
||||||
|
|
||||||
|
def memberXrayExpandoReservedSlot(member, descriptor):
|
||||||
|
return ("(xpc::JSSLOT_EXPANDO_COUNT + %d)" %
|
||||||
|
member.slotIndices[descriptor.interface.identifier.name])
|
||||||
|
|
||||||
|
|
||||||
|
def mayUseXrayExpandoSlots(attr):
|
||||||
|
assert not attr.getExtendedAttribute("NewObject")
|
||||||
|
# For attributes whose type is a Gecko interface we always use
|
||||||
|
# slots on the reflector for caching.
|
||||||
|
return not attr.type.isGeckoInterface()
|
||||||
|
|
||||||
|
|
||||||
def toStringBool(arg):
|
def toStringBool(arg):
|
||||||
return str(not not arg).lower()
|
return str(not not arg).lower()
|
||||||
|
|
||||||
|
@ -353,6 +365,11 @@ class CGNativePropertyHooks(CGThing):
|
||||||
parentHooks = (toBindingNamespace(parentProtoName) + "::sNativePropertyHooks"
|
parentHooks = (toBindingNamespace(parentProtoName) + "::sNativePropertyHooks"
|
||||||
if parentProtoName else 'nullptr')
|
if parentProtoName else 'nullptr')
|
||||||
|
|
||||||
|
if self.descriptor.wantsXrayExpandoClass:
|
||||||
|
expandoClass = "&sXrayExpandoObjectClass"
|
||||||
|
else:
|
||||||
|
expandoClass = "&DefaultXrayExpandoObjectClass"
|
||||||
|
|
||||||
return fill(
|
return fill(
|
||||||
"""
|
"""
|
||||||
const NativePropertyHooks sNativePropertyHooks[] = { {
|
const NativePropertyHooks sNativePropertyHooks[] = { {
|
||||||
|
@ -361,7 +378,8 @@ class CGNativePropertyHooks(CGThing):
|
||||||
{ ${regular}, ${chrome} },
|
{ ${regular}, ${chrome} },
|
||||||
${prototypeID},
|
${prototypeID},
|
||||||
${constructorID},
|
${constructorID},
|
||||||
${parentHooks}
|
${parentHooks},
|
||||||
|
${expandoClass}
|
||||||
} };
|
} };
|
||||||
""",
|
""",
|
||||||
resolveOwnProperty=resolveOwnProperty,
|
resolveOwnProperty=resolveOwnProperty,
|
||||||
|
@ -370,7 +388,8 @@ class CGNativePropertyHooks(CGThing):
|
||||||
chrome=chrome,
|
chrome=chrome,
|
||||||
prototypeID=prototypeID,
|
prototypeID=prototypeID,
|
||||||
constructorID=constructorID,
|
constructorID=constructorID,
|
||||||
parentHooks=parentHooks)
|
parentHooks=parentHooks,
|
||||||
|
expandoClass=expandoClass)
|
||||||
|
|
||||||
|
|
||||||
def NativePropertyHooks(descriptor):
|
def NativePropertyHooks(descriptor):
|
||||||
|
@ -529,6 +548,35 @@ class CGDOMProxyJSClass(CGThing):
|
||||||
descriptor=DOMClass(self.descriptor))
|
descriptor=DOMClass(self.descriptor))
|
||||||
|
|
||||||
|
|
||||||
|
class CGXrayExpandoJSClass(CGThing):
|
||||||
|
"""
|
||||||
|
Generate a JSClass for an Xray expando object. This is only
|
||||||
|
needed if we have members in slots (for [Cached] or [StoreInSlot]
|
||||||
|
stuff).
|
||||||
|
"""
|
||||||
|
def __init__(self, descriptor):
|
||||||
|
assert descriptor.interface.totalMembersInSlots != 0
|
||||||
|
assert descriptor.wantsXrays
|
||||||
|
assert descriptor.wantsXrayExpandoClass
|
||||||
|
CGThing.__init__(self)
|
||||||
|
self.descriptor = descriptor;
|
||||||
|
|
||||||
|
def declare(self):
|
||||||
|
return ""
|
||||||
|
|
||||||
|
def define(self):
|
||||||
|
return fill(
|
||||||
|
"""
|
||||||
|
// This may allocate too many slots, because we only really need
|
||||||
|
// slots for our non-interface-typed members that we cache. But
|
||||||
|
// allocating slots only for those would make the slot index
|
||||||
|
// computations much more complicated, so let's do this the simple
|
||||||
|
// way for now.
|
||||||
|
DEFINE_XRAY_EXPANDO_CLASS(static, sXrayExpandoObjectClass, ${memberSlots});
|
||||||
|
""",
|
||||||
|
memberSlots=self.descriptor.interface.totalMembersInSlots)
|
||||||
|
|
||||||
|
|
||||||
def PrototypeIDAndDepth(descriptor):
|
def PrototypeIDAndDepth(descriptor):
|
||||||
prototypeID = "prototypes::id::"
|
prototypeID = "prototypes::id::"
|
||||||
if descriptor.interface.hasInterfacePrototypeObject():
|
if descriptor.interface.hasInterfacePrototypeObject():
|
||||||
|
@ -3853,6 +3901,16 @@ class CGClearCachedValueMethod(CGAbstractMethod):
|
||||||
saveMember = ""
|
saveMember = ""
|
||||||
regetMember = ""
|
regetMember = ""
|
||||||
|
|
||||||
|
if self.descriptor.wantsXrays:
|
||||||
|
clearXrayExpandoSlots = fill(
|
||||||
|
"""
|
||||||
|
xpc::ClearXrayExpandoSlots(obj, ${xraySlotIndex});
|
||||||
|
""",
|
||||||
|
xraySlotIndex=memberXrayExpandoReservedSlot(self.member,
|
||||||
|
self.descriptor))
|
||||||
|
else :
|
||||||
|
clearXrayExpandoSlots = ""
|
||||||
|
|
||||||
return fill(
|
return fill(
|
||||||
"""
|
"""
|
||||||
$*{declObj}
|
$*{declObj}
|
||||||
|
@ -3862,12 +3920,14 @@ class CGClearCachedValueMethod(CGAbstractMethod):
|
||||||
}
|
}
|
||||||
$*{saveMember}
|
$*{saveMember}
|
||||||
js::SetReservedSlot(obj, ${slotIndex}, JS::UndefinedValue());
|
js::SetReservedSlot(obj, ${slotIndex}, JS::UndefinedValue());
|
||||||
|
$*{clearXrayExpandoSlots}
|
||||||
$*{regetMember}
|
$*{regetMember}
|
||||||
""",
|
""",
|
||||||
declObj=declObj,
|
declObj=declObj,
|
||||||
noopRetval=noopRetval,
|
noopRetval=noopRetval,
|
||||||
saveMember=saveMember,
|
saveMember=saveMember,
|
||||||
slotIndex=slotIndex,
|
slotIndex=slotIndex,
|
||||||
|
clearXrayExpandoSlots=clearXrayExpandoSlots,
|
||||||
regetMember=regetMember)
|
regetMember=regetMember)
|
||||||
|
|
||||||
|
|
||||||
|
@ -7470,7 +7530,11 @@ class CGPerSignatureCall(CGThing):
|
||||||
'returnsNewObject': returnsNewObject,
|
'returnsNewObject': returnsNewObject,
|
||||||
'isConstructorRetval': self.isConstructor,
|
'isConstructorRetval': self.isConstructor,
|
||||||
'successCode': successCode,
|
'successCode': successCode,
|
||||||
'obj': "reflector" if setSlot else "obj"
|
# 'obj' in this dictionary is the thing whose compartment we are
|
||||||
|
# trying to do the to-JS conversion in. We're going to put that
|
||||||
|
# thing in a variable named "conversionScope" if setSlot is true.
|
||||||
|
# Otherwise, just use "obj" for lack of anything better.
|
||||||
|
'obj': "conversionScope" if setSlot else "obj"
|
||||||
}
|
}
|
||||||
try:
|
try:
|
||||||
wrapCode += wrapForType(self.returnType, self.descriptor, resultTemplateValues)
|
wrapCode += wrapForType(self.returnType, self.descriptor, resultTemplateValues)
|
||||||
|
@ -7481,16 +7545,22 @@ class CGPerSignatureCall(CGThing):
|
||||||
self.descriptor.interface.identifier.name,
|
self.descriptor.interface.identifier.name,
|
||||||
self.idlNode.identifier.name))
|
self.idlNode.identifier.name))
|
||||||
if setSlot:
|
if setSlot:
|
||||||
# We need to make sure that our initial wrapping is done in the
|
# When using a slot on the Xray expando, we need to make sure that
|
||||||
# reflector compartment, but that we finally set args.rval() in the
|
# our initial conversion to a JS::Value is done in the caller
|
||||||
# caller compartment. We also need to make sure that the actual
|
# compartment. When using a slot on our reflector, we want to do
|
||||||
# wrapping steps happen inside a do/while that they can break out
|
# the conversion in the compartment of that reflector (that is,
|
||||||
# of.
|
# slotStorage). In both cases we want to make sure that we finally
|
||||||
|
# set up args.rval() to be in the caller compartment. We also need
|
||||||
|
# to make sure that the conversion steps happen inside a do/while
|
||||||
|
# that they can break out of on success.
|
||||||
|
#
|
||||||
|
# Of course we always have to wrap the value into the slotStorage
|
||||||
|
# compartment before we store it in slotStorage.
|
||||||
|
|
||||||
# postSteps are the steps that run while we're still in the
|
# postConversionSteps are the steps that run while we're still in
|
||||||
# reflector compartment but after we've finished the initial
|
# the compartment we do our conversion in but after we've finished
|
||||||
# wrapping into args.rval().
|
# the initial conversion into args.rval().
|
||||||
postSteps = ""
|
postConversionSteps = ""
|
||||||
if self.idlNode.getExtendedAttribute("Frozen"):
|
if self.idlNode.getExtendedAttribute("Frozen"):
|
||||||
assert self.idlNode.type.isSequence() or self.idlNode.type.isDictionary()
|
assert self.idlNode.type.isSequence() or self.idlNode.type.isDictionary()
|
||||||
freezeValue = CGGeneric(
|
freezeValue = CGGeneric(
|
||||||
|
@ -7501,9 +7571,23 @@ class CGPerSignatureCall(CGThing):
|
||||||
if self.idlNode.type.nullable():
|
if self.idlNode.type.nullable():
|
||||||
freezeValue = CGIfWrapper(freezeValue,
|
freezeValue = CGIfWrapper(freezeValue,
|
||||||
"args.rval().isObject()")
|
"args.rval().isObject()")
|
||||||
postSteps += freezeValue.define()
|
postConversionSteps += freezeValue.define()
|
||||||
postSteps += ("js::SetReservedSlot(reflector, %s, args.rval());\n" %
|
|
||||||
memberReservedSlot(self.idlNode, self.descriptor))
|
# slotStorageSteps are steps that run once we have entered the
|
||||||
|
# slotStorage compartment.
|
||||||
|
slotStorageSteps= fill(
|
||||||
|
"""
|
||||||
|
// Make a copy so that we don't do unnecessary wrapping on args.rval().
|
||||||
|
JS::Rooted<JS::Value> storedVal(cx, args.rval());
|
||||||
|
if (!${maybeWrap}(cx, &storedVal)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
js::SetReservedSlot(slotStorage, slotIndex, storedVal);
|
||||||
|
""",
|
||||||
|
maybeWrap=getMaybeWrapValueFuncForType(self.idlNode.type))
|
||||||
|
|
||||||
|
checkForXray = mayUseXrayExpandoSlots(self.idlNode)
|
||||||
|
|
||||||
# For the case of Cached attributes, go ahead and preserve our
|
# For the case of Cached attributes, go ahead and preserve our
|
||||||
# wrapper if needed. We need to do this because otherwise the
|
# wrapper if needed. We need to do this because otherwise the
|
||||||
# wrapper could get garbage-collected and the cached value would
|
# wrapper could get garbage-collected and the cached value would
|
||||||
|
@ -7514,22 +7598,48 @@ class CGPerSignatureCall(CGThing):
|
||||||
# already-preserved wrapper.
|
# already-preserved wrapper.
|
||||||
if (self.idlNode.getExtendedAttribute("Cached") and
|
if (self.idlNode.getExtendedAttribute("Cached") and
|
||||||
self.descriptor.wrapperCache):
|
self.descriptor.wrapperCache):
|
||||||
postSteps += "PreserveWrapper(self);\n"
|
preserveWrapper = dedent(
|
||||||
|
"""
|
||||||
|
PreserveWrapper(self);
|
||||||
|
""")
|
||||||
|
if checkForXray:
|
||||||
|
preserveWrapper = fill(
|
||||||
|
"""
|
||||||
|
if (!isXray) {
|
||||||
|
// In the Xray case we don't need to do this, because getting the
|
||||||
|
// expando object already preserved our wrapper.
|
||||||
|
$*{preserveWrapper}
|
||||||
|
}
|
||||||
|
""",
|
||||||
|
preserveWrapper=preserveWrapper)
|
||||||
|
slotStorageSteps += preserveWrapper
|
||||||
|
|
||||||
|
if checkForXray:
|
||||||
|
conversionScope = "isXray ? obj : slotStorage"
|
||||||
|
else:
|
||||||
|
conversionScope = "slotStorage"
|
||||||
|
|
||||||
wrapCode = fill(
|
wrapCode = fill(
|
||||||
"""
|
"""
|
||||||
{ // Make sure we wrap and store in the slot in reflector's compartment
|
{
|
||||||
JSAutoCompartment ac(cx, reflector);
|
JS::Rooted<JSObject*> conversionScope(cx, ${conversionScope});
|
||||||
|
JSAutoCompartment ac(cx, conversionScope);
|
||||||
do { // block we break out of when done wrapping
|
do { // block we break out of when done wrapping
|
||||||
$*{wrapCode}
|
$*{wrapCode}
|
||||||
} while (0);
|
} while (0);
|
||||||
$*{postSteps}
|
$*{postConversionSteps}
|
||||||
|
}
|
||||||
|
{ // And now store things in the compartment of our slotStorage.
|
||||||
|
JSAutoCompartment ac(cx, slotStorage);
|
||||||
|
$*{slotStorageSteps}
|
||||||
}
|
}
|
||||||
// And now make sure args.rval() is in the caller compartment
|
// And now make sure args.rval() is in the caller compartment
|
||||||
return ${maybeWrap}(cx, args.rval());
|
return ${maybeWrap}(cx, args.rval());
|
||||||
""",
|
""",
|
||||||
|
conversionScope=conversionScope,
|
||||||
wrapCode=wrapCode,
|
wrapCode=wrapCode,
|
||||||
postSteps=postSteps,
|
postConversionSteps=postConversionSteps,
|
||||||
|
slotStorageSteps=slotStorageSteps,
|
||||||
maybeWrap=getMaybeWrapValueFuncForType(self.idlNode.type))
|
maybeWrap=getMaybeWrapValueFuncForType(self.idlNode.type))
|
||||||
return wrapCode
|
return wrapCode
|
||||||
|
|
||||||
|
@ -8023,7 +8133,13 @@ class CGNavigatorGetterCall(CGPerSignatureCall):
|
||||||
True, descriptor, attr, getter=True)
|
True, descriptor, attr, getter=True)
|
||||||
|
|
||||||
def getArguments(self):
|
def getArguments(self):
|
||||||
return [(FakeArgument(BuiltinTypes[IDLBuiltinType.Types.object], self.idlNode), "reflector")]
|
# The navigator object should be associated with the global of
|
||||||
|
# the navigator it's coming from, which will be the global of
|
||||||
|
# the object whose slot it gets cached in. That's stored in
|
||||||
|
# "slotStorage".
|
||||||
|
return [(FakeArgument(BuiltinTypes[IDLBuiltinType.Types.object],
|
||||||
|
self.idlNode),
|
||||||
|
"slotStorage")]
|
||||||
|
|
||||||
|
|
||||||
class FakeIdentifier():
|
class FakeIdentifier():
|
||||||
|
@ -8637,27 +8753,64 @@ class CGSpecializedGetter(CGAbstractStaticMethod):
|
||||||
"can't use our slot for property '%s'!" %
|
"can't use our slot for property '%s'!" %
|
||||||
(self.descriptor.interface.identifier.name,
|
(self.descriptor.interface.identifier.name,
|
||||||
self.attr.identifier.name))
|
self.attr.identifier.name))
|
||||||
prefix = fill(
|
|
||||||
|
# We're going to store this return value in a slot on some object,
|
||||||
|
# to cache it. The question is, which object? For dictionary and
|
||||||
|
# sequence return values, we want to use a slot on the Xray expando
|
||||||
|
# if we're called via Xrays, and a slot on our reflector otherwise.
|
||||||
|
# On the other hand, when dealing with some interfacce types
|
||||||
|
# (navigator properties, window.document) we want to avoid calling
|
||||||
|
# the getter more than once. In the case of navigator properties
|
||||||
|
# that's because the getter actually creates a new object each time.
|
||||||
|
# In the case of window.document, it's because the getter can start
|
||||||
|
# returning null, which would get hidden in he non-Xray case by the
|
||||||
|
# fact that it's [StoreOnSlot], so the cached version is always
|
||||||
|
# around.
|
||||||
|
#
|
||||||
|
# The upshot is that we use the reflector slot for any getter whose
|
||||||
|
# type is a gecko interface, whether we're called via Xrays or not.
|
||||||
|
# Since [Cached] and [StoreInSlot] cannot be used with "NewObject",
|
||||||
|
# we know that in the interface type case the returned object is
|
||||||
|
# wrappercached. So creating Xrays to it is reasonable.
|
||||||
|
if mayUseXrayExpandoSlots(self.attr):
|
||||||
|
prefix = fill(
|
||||||
|
"""
|
||||||
|
// Have to either root across the getter call or reget after.
|
||||||
|
bool isXray;
|
||||||
|
JS::Rooted<JSObject*> slotStorage(cx, GetCachedSlotStorageObject(cx, obj, &isXray));
|
||||||
|
if (!slotStorage) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const size_t slotIndex = isXray ? ${xraySlotIndex} : ${slotIndex};
|
||||||
|
""",
|
||||||
|
xraySlotIndex=memberXrayExpandoReservedSlot(self.attr,
|
||||||
|
self.descriptor),
|
||||||
|
slotIndex=memberReservedSlot(self.attr, self.descriptor))
|
||||||
|
else:
|
||||||
|
prefix = fill(
|
||||||
|
"""
|
||||||
|
// Have to either root across the getter call or reget after.
|
||||||
|
JS::Rooted<JSObject*> slotStorage(cx, js::UncheckedUnwrap(obj, /* stopAtWindowProxy = */ false));
|
||||||
|
MOZ_ASSERT(IsDOMObject(slotStorage));
|
||||||
|
const size_t slotIndex = ${slotIndex};
|
||||||
|
""",
|
||||||
|
slotIndex=memberReservedSlot(self.attr, self.descriptor))
|
||||||
|
|
||||||
|
prefix += fill(
|
||||||
"""
|
"""
|
||||||
// Have to either root across the getter call or reget after.
|
MOZ_ASSERT(JSCLASS_RESERVED_SLOTS(js::GetObjectClass(slotStorage)) > slotIndex);
|
||||||
JS::Rooted<JSObject*> reflector(cx);
|
|
||||||
// Safe to do an unchecked unwrap, since we've gotten this far.
|
|
||||||
// Also make sure to unwrap outer windows, since we want the
|
|
||||||
// real DOM object.
|
|
||||||
reflector = IsDOMObject(obj) ? obj : js::UncheckedUnwrap(obj, /* stopAtWindowProxy = */ false);
|
|
||||||
{
|
{
|
||||||
// Scope for cachedVal
|
// Scope for cachedVal
|
||||||
JS::Value cachedVal = js::GetReservedSlot(reflector, ${slot});
|
JS::Value cachedVal = js::GetReservedSlot(slotStorage, slotIndex);
|
||||||
if (!cachedVal.isUndefined()) {
|
if (!cachedVal.isUndefined()) {
|
||||||
args.rval().set(cachedVal);
|
args.rval().set(cachedVal);
|
||||||
// The cached value is in the compartment of reflector,
|
// The cached value is in the compartment of slotStorage,
|
||||||
// so wrap into the caller compartment as needed.
|
// so wrap into the caller compartment as needed.
|
||||||
return ${maybeWrap}(cx, args.rval());
|
return ${maybeWrap}(cx, args.rval());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
""",
|
""",
|
||||||
slot=memberReservedSlot(self.attr, self.descriptor),
|
|
||||||
maybeWrap=getMaybeWrapValueFuncForType(self.attr.type))
|
maybeWrap=getMaybeWrapValueFuncForType(self.attr.type))
|
||||||
else:
|
else:
|
||||||
prefix = ""
|
prefix = ""
|
||||||
|
@ -12073,6 +12226,8 @@ class CGDescriptor(CGThing):
|
||||||
elif descriptor.needsXrayResolveHooks():
|
elif descriptor.needsXrayResolveHooks():
|
||||||
cgThings.append(CGResolveOwnPropertyViaResolve(descriptor))
|
cgThings.append(CGResolveOwnPropertyViaResolve(descriptor))
|
||||||
cgThings.append(CGEnumerateOwnPropertiesViaGetOwnPropertyNames(descriptor))
|
cgThings.append(CGEnumerateOwnPropertiesViaGetOwnPropertyNames(descriptor))
|
||||||
|
if descriptor.wantsXrayExpandoClass:
|
||||||
|
cgThings.append(CGXrayExpandoJSClass(descriptor))
|
||||||
|
|
||||||
# Now that we have our ResolveOwnProperty/EnumerateOwnProperties stuff
|
# Now that we have our ResolveOwnProperty/EnumerateOwnProperties stuff
|
||||||
# done, set up our NativePropertyHooks.
|
# done, set up our NativePropertyHooks.
|
||||||
|
@ -13473,6 +13628,10 @@ class CGBindingRoot(CGThing):
|
||||||
descriptorRequiresTelemetry(d) for d in descriptors)
|
descriptorRequiresTelemetry(d) for d in descriptors)
|
||||||
bindingHeaders["mozilla/dom/SimpleGlobalObject.h"] = any(
|
bindingHeaders["mozilla/dom/SimpleGlobalObject.h"] = any(
|
||||||
CGDictionary.dictionarySafeToJSONify(d) for d in dictionaries)
|
CGDictionary.dictionarySafeToJSONify(d) for d in dictionaries)
|
||||||
|
bindingHeaders["XrayWrapper.h"] = any(
|
||||||
|
d.wantsXrays and d.wantsXrayExpandoClass for d in descriptors)
|
||||||
|
bindingHeaders["mozilla/dom/XrayExpandoClass.h"] = any(
|
||||||
|
d.wantsXrays for d in descriptors)
|
||||||
|
|
||||||
cgthings.extend(traverseMethods)
|
cgthings.extend(traverseMethods)
|
||||||
cgthings.extend(unlinkMethods)
|
cgthings.extend(unlinkMethods)
|
||||||
|
|
|
@ -279,7 +279,18 @@ class Descriptor(DescriptorProvider):
|
||||||
self.config = config
|
self.config = config
|
||||||
self.interface = interface
|
self.interface = interface
|
||||||
|
|
||||||
self.wantsXrays = interface.isExposedInWindow()
|
self.wantsXrays = (not interface.isExternal() and
|
||||||
|
interface.isExposedInWindow())
|
||||||
|
|
||||||
|
if self.wantsXrays:
|
||||||
|
# We could try to restrict self.wantsXrayExpandoClass further. For
|
||||||
|
# example, we could set it to false if all of our slots store
|
||||||
|
# Gecko-interface-typed things, because we don't use Xray expando
|
||||||
|
# slots for those. But note that we would need to check the types
|
||||||
|
# of not only the members of "interface" but also of all its
|
||||||
|
# ancestors, because those can have members living in our slots too.
|
||||||
|
# For now, do the simple thing.
|
||||||
|
self.wantsXrayExpandoClass = (interface.totalMembersInSlots != 0)
|
||||||
|
|
||||||
# Read the desc, and fill in the relevant defaults.
|
# Read the desc, and fill in the relevant defaults.
|
||||||
ifaceName = self.interface.identifier.name
|
ifaceName = self.interface.identifier.name
|
||||||
|
|
|
@ -293,6 +293,10 @@ struct NativePropertyHooks
|
||||||
// The NativePropertyHooks instance for the parent interface (for
|
// The NativePropertyHooks instance for the parent interface (for
|
||||||
// ShimInterfaceInfo).
|
// ShimInterfaceInfo).
|
||||||
const NativePropertyHooks* mProtoHooks;
|
const NativePropertyHooks* mProtoHooks;
|
||||||
|
|
||||||
|
// The JSClass to use for expandos on our Xrays. Can be null, in which case
|
||||||
|
// Xrays will use a default class of their choice.
|
||||||
|
const JSClass* mXrayExpandoClass;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum DOMObjectType : uint8_t {
|
enum DOMObjectType : uint8_t {
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
/* -*- 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/. */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This file declares a macro for defining Xray expando classes and declares the
|
||||||
|
* default Xray expando class. The actual definition of that default class
|
||||||
|
* lives elsewhere.
|
||||||
|
*/
|
||||||
|
#ifndef mozilla_dom_XrayExpandoClass_h
|
||||||
|
#define mozilla_dom_XrayExpandoClass_h
|
||||||
|
|
||||||
|
/*
|
||||||
|
* maybeStatic_ Should be either `static` or nothing (because some Xray expando
|
||||||
|
* classes are not static).
|
||||||
|
*
|
||||||
|
* name_ should be the name of the variable.
|
||||||
|
*
|
||||||
|
* extraSlots_ should be how many extra slots to give the class, in addition to
|
||||||
|
* the ones Xray expandos want.
|
||||||
|
*/
|
||||||
|
#define DEFINE_XRAY_EXPANDO_CLASS(maybeStatic_, name_, extraSlots_) \
|
||||||
|
maybeStatic_ const JSClass name_ = { \
|
||||||
|
"XrayExpandoObject", \
|
||||||
|
JSCLASS_HAS_RESERVED_SLOTS(xpc::JSSLOT_EXPANDO_COUNT + \
|
||||||
|
(extraSlots_)) | \
|
||||||
|
JSCLASS_FOREGROUND_FINALIZE, \
|
||||||
|
&xpc::XrayExpandoObjectClassOps \
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace dom {
|
||||||
|
|
||||||
|
extern const JSClass DefaultXrayExpandoObjectClass;
|
||||||
|
|
||||||
|
} // namespace mozilla
|
||||||
|
} // namespace dom
|
||||||
|
|
||||||
|
#endif /* mozilla_dom_XrayExpandoClass_h */
|
|
@ -43,6 +43,7 @@ EXPORTS.mozilla.dom += [
|
||||||
'TypedArray.h',
|
'TypedArray.h',
|
||||||
'UnionMember.h',
|
'UnionMember.h',
|
||||||
'WebIDLGlobalNameHash.h',
|
'WebIDLGlobalNameHash.h',
|
||||||
|
'XrayExpandoClass.h',
|
||||||
]
|
]
|
||||||
|
|
||||||
# Generated bindings reference *Binding.h, not mozilla/dom/*Binding.h. And,
|
# Generated bindings reference *Binding.h, not mozilla/dom/*Binding.h. And,
|
||||||
|
|
|
@ -3458,6 +3458,14 @@ class IDLInterfaceMember(IDLObjectWithIdentifier, IDLExposureMixins):
|
||||||
raise WebIDLError("A [NewObject] method is not idempotent, "
|
raise WebIDLError("A [NewObject] method is not idempotent, "
|
||||||
"so it has to depend on something other than DOM state.",
|
"so it has to depend on something other than DOM state.",
|
||||||
[self.location])
|
[self.location])
|
||||||
|
if (self.getExtendedAttribute("Cached") or
|
||||||
|
self.getExtendedAttribute("StoreInSlot")):
|
||||||
|
raise WebIDLError("A [NewObject] attribute shouldnt be "
|
||||||
|
"[Cached] or [StoreInSlot], since the point "
|
||||||
|
"of those is to keep returning the same "
|
||||||
|
"thing across multiple calls, which is not "
|
||||||
|
"what [NewObject] does.",
|
||||||
|
[self.location])
|
||||||
|
|
||||||
def _setDependsOn(self, dependsOn):
|
def _setDependsOn(self, dependsOn):
|
||||||
if self.dependsOn != "Everything":
|
if self.dependsOn != "Everything":
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
# Import the WebIDL module, so we can do isinstance checks and whatnot
|
||||||
|
import WebIDL
|
||||||
|
|
||||||
|
def WebIDLTest(parser, harness):
|
||||||
|
# Basic functionality
|
||||||
|
parser.parse(
|
||||||
|
"""
|
||||||
|
interface Iface {
|
||||||
|
[NewObject] readonly attribute Iface attr;
|
||||||
|
[NewObject] Iface method();
|
||||||
|
};
|
||||||
|
""")
|
||||||
|
results = parser.finish()
|
||||||
|
harness.ok(results, "Should not have thrown on basic [NewObject] usage")
|
||||||
|
|
||||||
|
parser = parser.reset()
|
||||||
|
threw = False
|
||||||
|
try:
|
||||||
|
parser.parse(
|
||||||
|
"""
|
||||||
|
interface Iface {
|
||||||
|
[Pure, NewObject] readonly attribute Iface attr;
|
||||||
|
};
|
||||||
|
""")
|
||||||
|
results = parser.finish()
|
||||||
|
except:
|
||||||
|
threw = True
|
||||||
|
harness.ok(threw, "[NewObject] attributes must depend on something")
|
||||||
|
|
||||||
|
parser = parser.reset()
|
||||||
|
threw = False
|
||||||
|
try:
|
||||||
|
parser.parse(
|
||||||
|
"""
|
||||||
|
interface Iface {
|
||||||
|
[Pure, NewObject] Iface method();
|
||||||
|
};
|
||||||
|
""")
|
||||||
|
results = parser.finish()
|
||||||
|
except:
|
||||||
|
threw = True
|
||||||
|
harness.ok(threw, "[NewObject] methods must depend on something")
|
||||||
|
|
||||||
|
parser = parser.reset()
|
||||||
|
threw = False
|
||||||
|
try:
|
||||||
|
parser.parse(
|
||||||
|
"""
|
||||||
|
interface Iface {
|
||||||
|
[Cached, NewObject, Affects=Nothing] readonly attribute Iface attr;
|
||||||
|
};
|
||||||
|
""")
|
||||||
|
results = parser.finish()
|
||||||
|
except:
|
||||||
|
threw = True
|
||||||
|
harness.ok(threw, "[NewObject] attributes must not be [Cached]")
|
||||||
|
|
||||||
|
parser = parser.reset()
|
||||||
|
threw = False
|
||||||
|
try:
|
||||||
|
parser.parse(
|
||||||
|
"""
|
||||||
|
interface Iface {
|
||||||
|
[StoreInSlot, NewObject, Affects=Nothing] readonly attribute Iface attr;
|
||||||
|
};
|
||||||
|
""")
|
||||||
|
results = parser.finish()
|
||||||
|
except:
|
||||||
|
threw = True
|
||||||
|
harness.ok(threw, "[NewObject] attributes must not be [StoreInSlot]")
|
|
@ -148,6 +148,42 @@ function test()
|
||||||
// ECMAScript-defined properties live on the prototype, overriding any named properties.
|
// ECMAScript-defined properties live on the prototype, overriding any named properties.
|
||||||
checkXrayProperty(coll, "toString", [ undefined, undefined, win.Object.prototype.toString ]);
|
checkXrayProperty(coll, "toString", [ undefined, undefined, win.Object.prototype.toString ]);
|
||||||
|
|
||||||
|
// NavigatorProperty things should come from the target compartment, always.
|
||||||
|
var contacts = win.navigator.mozContacts;
|
||||||
|
isnot(contacts, undefined,
|
||||||
|
"Must have a mozContacts for this to work. Find another " +
|
||||||
|
"NavigatorProperty, or remove all this NavigatorProperty code")
|
||||||
|
ok(Cu.isXrayWrapper(contacts),
|
||||||
|
"Should have an Xray for the underlying value, not a value newly " +
|
||||||
|
"created for us");
|
||||||
|
is(Cu.getGlobalForObject(contacts), win,
|
||||||
|
"contacts should come from the underlying window");
|
||||||
|
// Unfortunately, I can't figure out a decent way to test that we're not
|
||||||
|
// creating a separate thing from the thing content sees, because none of the
|
||||||
|
// NavigatorProperty things are available to content.
|
||||||
|
|
||||||
|
// Frozen arrays should come from our compartment, not the target one.
|
||||||
|
var languages1 = win.navigator.languages;
|
||||||
|
isnot(languages1, undefined, "Must have .languages");
|
||||||
|
ok(Array.isArray(languages1), ".languages should be an array");
|
||||||
|
ok(Object.isFrozen(languages1), ".languages should be a frozen array");
|
||||||
|
ok(!Cu.isXrayWrapper(languages1), "Should have our own version of array");
|
||||||
|
is(Cu.getGlobalForObject(languages1), window,
|
||||||
|
"languages1 should come from our window");
|
||||||
|
// We want to get .languages in the content compartment, but without waiving
|
||||||
|
// Xrays altogether.
|
||||||
|
var languages2 = win.eval("navigator.languages");
|
||||||
|
isnot(languages2, undefined, "Must still have .languages");
|
||||||
|
ok(Array.isArray(languages2), ".languages should still be an array");
|
||||||
|
ok(Cu.isXrayWrapper(languages2), "Should have xray for content version of array");
|
||||||
|
is(Cu.getGlobalForObject(languages2), win,
|
||||||
|
"languages2 come from the underlying window");
|
||||||
|
ok(Object.isFrozen(languages2.wrappedJSObject),
|
||||||
|
".languages should still be a frozen array underneath");
|
||||||
|
isnot(languages1, languages2, "Must have distinct arrays");
|
||||||
|
isnot(languages1, languages2.wrappedJSObject,
|
||||||
|
"Must have distinct arrays no matter how we slice it");
|
||||||
|
|
||||||
SimpleTest.finish();
|
SimpleTest.finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1907,33 +1907,9 @@ ContentParent::ActorDestroy(ActorDestroyReason why)
|
||||||
// least until after the current task finishes running.
|
// least until after the current task finishes running.
|
||||||
NS_DispatchToCurrentThread(new DelayedDeleteContentParentTask(this));
|
NS_DispatchToCurrentThread(new DelayedDeleteContentParentTask(this));
|
||||||
|
|
||||||
// Release the appId's reference count of any processes
|
|
||||||
// created by this ContentParent and the frame opened by this ContentParent
|
|
||||||
// if this ContentParent crashes.
|
|
||||||
ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
|
ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
|
||||||
nsTArray<ContentParentId> childIDArray =
|
nsTArray<ContentParentId> childIDArray =
|
||||||
cpm->GetAllChildProcessById(this->ChildID());
|
cpm->GetAllChildProcessById(this->ChildID());
|
||||||
if (why == AbnormalShutdown) {
|
|
||||||
nsCOMPtr<nsIPermissionManager> permMgr = services::GetPermissionManager();
|
|
||||||
if(permMgr) {
|
|
||||||
// Release the appId's reference count of its child-processes
|
|
||||||
for (uint32_t i = 0; i < childIDArray.Length(); i++) {
|
|
||||||
nsTArray<TabContext> tabCtxs = cpm->GetTabContextByContentProcess(childIDArray[i]);
|
|
||||||
for (uint32_t j = 0 ; j < tabCtxs.Length() ; j++) {
|
|
||||||
if (tabCtxs[j].OwnOrContainingAppId() != nsIScriptSecurityManager::NO_APP_ID) {
|
|
||||||
permMgr->ReleaseAppId(tabCtxs[j].OwnOrContainingAppId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Release the appId's reference count belong to itself
|
|
||||||
nsTArray<TabContext> tabCtxs = cpm->GetTabContextByContentProcess(mChildID);
|
|
||||||
for (uint32_t i = 0; i < tabCtxs.Length() ; i++) {
|
|
||||||
if (tabCtxs[i].OwnOrContainingAppId()!= nsIScriptSecurityManager::NO_APP_ID) {
|
|
||||||
permMgr->ReleaseAppId(tabCtxs[i].OwnOrContainingAppId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Destroy any processes created by this ContentParent
|
// Destroy any processes created by this ContentParent
|
||||||
for(uint32_t i = 0; i < childIDArray.Length(); i++) {
|
for(uint32_t i = 0; i < childIDArray.Length(); i++) {
|
||||||
|
@ -4616,10 +4592,6 @@ ContentParent::AllocateTabId(const TabId& aOpenerTabId,
|
||||||
if (XRE_IsParentProcess()) {
|
if (XRE_IsParentProcess()) {
|
||||||
ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
|
ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
|
||||||
tabId = cpm->AllocateTabId(aOpenerTabId, aContext, aCpId);
|
tabId = cpm->AllocateTabId(aOpenerTabId, aContext, aCpId);
|
||||||
// Add appId's reference count in oop case
|
|
||||||
if (tabId) {
|
|
||||||
PermissionManagerAddref(aCpId, tabId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
ContentChild::GetSingleton()->SendAllocateTabId(aOpenerTabId,
|
ContentChild::GetSingleton()->SendAllocateTabId(aOpenerTabId,
|
||||||
|
@ -4636,11 +4608,6 @@ ContentParent::DeallocateTabId(const TabId& aTabId,
|
||||||
bool aMarkedDestroying)
|
bool aMarkedDestroying)
|
||||||
{
|
{
|
||||||
if (XRE_IsParentProcess()) {
|
if (XRE_IsParentProcess()) {
|
||||||
// Release appId's reference count in oop case
|
|
||||||
if (aTabId) {
|
|
||||||
PermissionManagerRelease(aCpId, aTabId);
|
|
||||||
}
|
|
||||||
|
|
||||||
ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
|
ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
|
||||||
ContentParent* cp = cpm->GetContentProcessById(aCpId);
|
ContentParent* cp = cpm->GetContentProcessById(aCpId);
|
||||||
|
|
||||||
|
@ -5035,38 +5002,6 @@ ContentParent::RecvCreateWindow(PBrowserParent* aThisTab,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* static */ bool
|
|
||||||
ContentParent::PermissionManagerAddref(const ContentParentId& aCpId,
|
|
||||||
const TabId& aTabId)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(XRE_IsParentProcess(),
|
|
||||||
"Call PermissionManagerAddref in content process!");
|
|
||||||
ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
|
|
||||||
uint32_t appId = cpm->GetAppIdByProcessAndTabId(aCpId, aTabId);
|
|
||||||
nsCOMPtr<nsIPermissionManager> permMgr = services::GetPermissionManager();
|
|
||||||
if (appId != nsIScriptSecurityManager::NO_APP_ID && permMgr) {
|
|
||||||
permMgr->AddrefAppId(appId);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* static */ bool
|
|
||||||
ContentParent::PermissionManagerRelease(const ContentParentId& aCpId,
|
|
||||||
const TabId& aTabId)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(XRE_IsParentProcess(),
|
|
||||||
"Call PermissionManagerRelease in content process!");
|
|
||||||
ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
|
|
||||||
uint32_t appId = cpm->GetAppIdByProcessAndTabId(aCpId, aTabId);
|
|
||||||
nsCOMPtr<nsIPermissionManager> permMgr = services::GetPermissionManager();
|
|
||||||
if (appId != nsIScriptSecurityManager::NO_APP_ID && permMgr) {
|
|
||||||
permMgr->ReleaseAppId(appId);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ContentParent::RecvProfile(const nsCString& aProfile)
|
ContentParent::RecvProfile(const nsCString& aProfile)
|
||||||
{
|
{
|
||||||
|
|
|
@ -334,18 +334,6 @@ public:
|
||||||
const ContentParentId& aCpId,
|
const ContentParentId& aCpId,
|
||||||
bool aMarkedDestroying);
|
bool aMarkedDestroying);
|
||||||
|
|
||||||
/*
|
|
||||||
* Add the appId's reference count by the given ContentParentId and TabId
|
|
||||||
*/
|
|
||||||
static bool
|
|
||||||
PermissionManagerAddref(const ContentParentId& aCpId, const TabId& aTabId);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Release the appId's reference count by the given ContentParentId and TabId
|
|
||||||
*/
|
|
||||||
static bool
|
|
||||||
PermissionManagerRelease(const ContentParentId& aCpId, const TabId& aTabId);
|
|
||||||
|
|
||||||
void ReportChildAlreadyBlocked();
|
void ReportChildAlreadyBlocked();
|
||||||
|
|
||||||
bool RequestRunToCompletion();
|
bool RequestRunToCompletion();
|
||||||
|
|
|
@ -24,10 +24,3 @@ interface Body {
|
||||||
[Throws]
|
[Throws]
|
||||||
Promise<USVString> text();
|
Promise<USVString> text();
|
||||||
};
|
};
|
||||||
|
|
||||||
[NoInterfaceObject, Exposed=(Window,Worker)]
|
|
||||||
interface GlobalFetch {
|
|
||||||
[Throws]
|
|
||||||
Promise<Response> fetch(RequestInfo input, optional RequestInit init);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
|
@ -50,26 +50,6 @@ partial interface ImageBitmap {
|
||||||
void close();
|
void close();
|
||||||
};
|
};
|
||||||
|
|
||||||
[NoInterfaceObject, Exposed=(Window,Worker)]
|
|
||||||
interface ImageBitmapFactories {
|
|
||||||
[Throws]
|
|
||||||
Promise<ImageBitmap> createImageBitmap(ImageBitmapSource aImage);
|
|
||||||
[Throws]
|
|
||||||
Promise<ImageBitmap> createImageBitmap(ImageBitmapSource aImage, long aSx, long aSy, long aSw, long aSh);
|
|
||||||
|
|
||||||
// Extensions
|
|
||||||
// Bug 1141979 - [FoxEye] Extend ImageBitmap with interfaces to access its
|
|
||||||
// underlying image data
|
|
||||||
//
|
|
||||||
// Note:
|
|
||||||
// Overloaded functions cannot have different "extended attributes",
|
|
||||||
// so I cannot add preference on the extended version of createImageBitmap().
|
|
||||||
// To work around, I will then check the preference at run time and throw if
|
|
||||||
// the preference is set to be false.
|
|
||||||
[Throws]
|
|
||||||
Promise<ImageBitmap> createImageBitmap(ImageBitmapSource aImage, long aOffset, long aLength, ImageBitmapFormat aFormat, ImagePixelLayout aLayout);
|
|
||||||
};
|
|
||||||
|
|
||||||
// ImageBitmap-extensions
|
// ImageBitmap-extensions
|
||||||
// Bug 1141979 - [FoxEye] Extend ImageBitmap with interfaces to access its
|
// Bug 1141979 - [FoxEye] Extend ImageBitmap with interfaces to access its
|
||||||
// underlying image data
|
// underlying image data
|
||||||
|
@ -429,4 +409,4 @@ partial interface ImageBitmap {
|
||||||
long mappedDataLength (ImageBitmapFormat aFormat);
|
long mappedDataLength (ImageBitmapFormat aFormat);
|
||||||
[Throws, Func="mozilla::dom::ImageBitmap::ExtensionsEnabled"]
|
[Throws, Func="mozilla::dom::ImageBitmap::ExtensionsEnabled"]
|
||||||
Promise<ImagePixelLayout> mapDataInto (ImageBitmapFormat aFormat, BufferSource aBuffer, long aOffset);
|
Promise<ImagePixelLayout> mapDataInto (ImageBitmapFormat aFormat, BufferSource aBuffer, long aOffset);
|
||||||
};
|
};
|
||||||
|
|
|
@ -98,26 +98,6 @@ partial interface Window {
|
||||||
attribute EventHandler oninstall;
|
attribute EventHandler oninstall;
|
||||||
};
|
};
|
||||||
|
|
||||||
// http://www.whatwg.org/specs/web-apps/current-work/
|
|
||||||
[NoInterfaceObject, Exposed=(Window,Worker)]
|
|
||||||
interface WindowTimers {
|
|
||||||
[Throws] long setTimeout(Function handler, optional long timeout = 0, any... arguments);
|
|
||||||
[Throws] long setTimeout(DOMString handler, optional long timeout = 0, any... unused);
|
|
||||||
void clearTimeout(optional long handle = 0);
|
|
||||||
[Throws] long setInterval(Function handler, optional long timeout, any... arguments);
|
|
||||||
[Throws] long setInterval(DOMString handler, optional long timeout, any... unused);
|
|
||||||
void clearInterval(optional long handle = 0);
|
|
||||||
};
|
|
||||||
Window implements WindowTimers;
|
|
||||||
|
|
||||||
// http://www.whatwg.org/specs/web-apps/current-work/
|
|
||||||
[NoInterfaceObject, Exposed=(Window,Worker)]
|
|
||||||
interface WindowBase64 {
|
|
||||||
[Throws] DOMString btoa(DOMString btoa);
|
|
||||||
[Throws] DOMString atob(DOMString atob);
|
|
||||||
};
|
|
||||||
Window implements WindowBase64;
|
|
||||||
|
|
||||||
// http://www.whatwg.org/specs/web-apps/current-work/
|
// http://www.whatwg.org/specs/web-apps/current-work/
|
||||||
[NoInterfaceObject]
|
[NoInterfaceObject]
|
||||||
interface WindowSessionStorage {
|
interface WindowSessionStorage {
|
||||||
|
@ -425,11 +405,6 @@ partial interface Window {
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// https://w3c.github.io/webappsec-secure-contexts/#monkey-patching-global-object
|
|
||||||
partial interface Window {
|
|
||||||
readonly attribute boolean isSecureContext;
|
|
||||||
};
|
|
||||||
|
|
||||||
#ifdef HAVE_SIDEBAR
|
#ifdef HAVE_SIDEBAR
|
||||||
// Mozilla extension
|
// Mozilla extension
|
||||||
partial interface Window {
|
partial interface Window {
|
||||||
|
@ -517,5 +492,4 @@ partial interface Window {
|
||||||
};
|
};
|
||||||
|
|
||||||
Window implements ChromeWindow;
|
Window implements ChromeWindow;
|
||||||
Window implements GlobalFetch;
|
Window implements WindowOrWorkerGlobalScope;
|
||||||
Window implements ImageBitmapFactories;
|
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* 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/.
|
||||||
|
*
|
||||||
|
* The origin of this IDL file is:
|
||||||
|
* https://html.spec.whatwg.org/multipage/webappapis.html#windoworworkerglobalscope-mixin
|
||||||
|
* https://fetch.spec.whatwg.org/#fetch-method
|
||||||
|
* https://w3c.github.io/webappsec-secure-contexts/#monkey-patching-global-object
|
||||||
|
*/
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/webappapis.html#windoworworkerglobalscope-mixin
|
||||||
|
[NoInterfaceObject, Exposed=(Window,Worker)]
|
||||||
|
interface WindowOrWorkerGlobalScope {
|
||||||
|
// XXXbz We don't implement 'origin' yet on either window or worker globals.
|
||||||
|
// See bug 1306170.
|
||||||
|
// [Replaceable] readonly attribute USVString origin;
|
||||||
|
|
||||||
|
// base64 utility methods
|
||||||
|
[Throws]
|
||||||
|
DOMString btoa(DOMString btoa);
|
||||||
|
[Throws]
|
||||||
|
DOMString atob(DOMString atob);
|
||||||
|
|
||||||
|
// timers
|
||||||
|
// NOTE: We're using overloads where the spec uses a union. Should
|
||||||
|
// be black-box the same.
|
||||||
|
[Throws]
|
||||||
|
long setTimeout(Function handler, optional long timeout = 0, any... arguments);
|
||||||
|
[Throws]
|
||||||
|
long setTimeout(DOMString handler, optional long timeout = 0, any... unused);
|
||||||
|
void clearTimeout(optional long handle = 0);
|
||||||
|
[Throws]
|
||||||
|
long setInterval(Function handler, optional long timeout, any... arguments);
|
||||||
|
[Throws]
|
||||||
|
long setInterval(DOMString handler, optional long timeout, any... unused);
|
||||||
|
void clearInterval(optional long handle = 0);
|
||||||
|
|
||||||
|
// ImageBitmap
|
||||||
|
[Throws]
|
||||||
|
Promise<ImageBitmap> createImageBitmap(ImageBitmapSource aImage);
|
||||||
|
[Throws]
|
||||||
|
Promise<ImageBitmap> createImageBitmap(ImageBitmapSource aImage, long aSx, long aSy, long aSw, long aSh);
|
||||||
|
};
|
||||||
|
|
||||||
|
// https://fetch.spec.whatwg.org/#fetch-method
|
||||||
|
partial interface WindowOrWorkerGlobalScope {
|
||||||
|
[NewObject] Promise<Response> fetch(RequestInfo input, optional RequestInit init);
|
||||||
|
};
|
||||||
|
|
||||||
|
// https://w3c.github.io/webappsec-secure-contexts/#monkey-patching-global-object
|
||||||
|
partial interface WindowOrWorkerGlobalScope {
|
||||||
|
readonly attribute boolean isSecureContext;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Mozilla extensions
|
||||||
|
partial interface WindowOrWorkerGlobalScope {
|
||||||
|
// Extensions to ImageBitmap bits.
|
||||||
|
// Bug 1141979 - [FoxEye] Extend ImageBitmap with interfaces to access its
|
||||||
|
// underlying image data
|
||||||
|
//
|
||||||
|
// Note:
|
||||||
|
// Overloaded functions cannot have different "extended attributes",
|
||||||
|
// so I cannot add preference on the extended version of createImageBitmap().
|
||||||
|
// To work around, I will then check the preference at run time and throw if
|
||||||
|
// the preference is set to be false.
|
||||||
|
[Throws]
|
||||||
|
Promise<ImageBitmap> createImageBitmap(ImageBitmapSource aImage, long aOffset, long aLength, ImageBitmapFormat aFormat, ImagePixelLayout aLayout);
|
||||||
|
};
|
|
@ -41,12 +41,9 @@ partial interface WorkerGlobalScope {
|
||||||
readonly attribute CacheStorage caches;
|
readonly attribute CacheStorage caches;
|
||||||
};
|
};
|
||||||
|
|
||||||
WorkerGlobalScope implements WindowTimers;
|
|
||||||
WorkerGlobalScope implements WindowBase64;
|
|
||||||
WorkerGlobalScope implements GlobalFetch;
|
|
||||||
WorkerGlobalScope implements GlobalCrypto;
|
WorkerGlobalScope implements GlobalCrypto;
|
||||||
WorkerGlobalScope implements IDBEnvironment;
|
WorkerGlobalScope implements IDBEnvironment;
|
||||||
WorkerGlobalScope implements ImageBitmapFactories;
|
WorkerGlobalScope implements WindowOrWorkerGlobalScope;
|
||||||
|
|
||||||
// Not implemented yet: bug 1072107.
|
// Not implemented yet: bug 1072107.
|
||||||
// WorkerGlobalScope implements FontFaceSource;
|
// WorkerGlobalScope implements FontFaceSource;
|
||||||
|
|
|
@ -610,6 +610,7 @@ WEBIDL_FILES = [
|
||||||
'WheelEvent.webidl',
|
'WheelEvent.webidl',
|
||||||
'WidevineCDMManifest.webidl',
|
'WidevineCDMManifest.webidl',
|
||||||
'WifiOptions.webidl',
|
'WifiOptions.webidl',
|
||||||
|
'WindowOrWorkerGlobalScope.webidl',
|
||||||
'WindowRoot.webidl',
|
'WindowRoot.webidl',
|
||||||
'Worker.webidl',
|
'Worker.webidl',
|
||||||
'WorkerDebuggerGlobalScope.webidl',
|
'WorkerDebuggerGlobalScope.webidl',
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include "GeckoProfiler.h"
|
#include "GeckoProfiler.h"
|
||||||
#include "jsfriendapi.h"
|
#include "jsfriendapi.h"
|
||||||
#include "mozilla/ArrayUtils.h"
|
#include "mozilla/ArrayUtils.h"
|
||||||
|
#include "mozilla/AsyncEventDispatcher.h"
|
||||||
#include "mozilla/Atomics.h"
|
#include "mozilla/Atomics.h"
|
||||||
#include "mozilla/CycleCollectedJSContext.h"
|
#include "mozilla/CycleCollectedJSContext.h"
|
||||||
#include "mozilla/Telemetry.h"
|
#include "mozilla/Telemetry.h"
|
||||||
|
@ -2446,6 +2447,11 @@ RuntimeService::CreateSharedWorkerFromLoadInfo(JSContext* aCx,
|
||||||
// will reset the loadInfo's window.
|
// will reset the loadInfo's window.
|
||||||
nsCOMPtr<nsPIDOMWindowInner> window = aLoadInfo->mWindow;
|
nsCOMPtr<nsPIDOMWindowInner> window = aLoadInfo->mWindow;
|
||||||
|
|
||||||
|
// shouldAttachToWorkerPrivate tracks whether our SharedWorker should actually
|
||||||
|
// get attached to the WorkerPrivate we're using. It will become false if the
|
||||||
|
// WorkerPrivate already exists and its secure context state doesn't match
|
||||||
|
// what we want for the new SharedWorker.
|
||||||
|
bool shouldAttachToWorkerPrivate = true;
|
||||||
bool created = false;
|
bool created = false;
|
||||||
ErrorResult rv;
|
ErrorResult rv;
|
||||||
if (!workerPrivate) {
|
if (!workerPrivate) {
|
||||||
|
@ -2456,10 +2462,20 @@ RuntimeService::CreateSharedWorkerFromLoadInfo(JSContext* aCx,
|
||||||
|
|
||||||
created = true;
|
created = true;
|
||||||
} else {
|
} else {
|
||||||
|
// Check whether the secure context state matches. The current compartment
|
||||||
|
// of aCx is the compartment of the SharedWorker constructor that was
|
||||||
|
// invoked, which is the compartment of the document that will be hooked up
|
||||||
|
// to the worker, so that's what we want to check.
|
||||||
|
shouldAttachToWorkerPrivate =
|
||||||
|
workerPrivate->IsSecureContext() ==
|
||||||
|
JS_GetIsSecureContext(js::GetContextCompartment(aCx));
|
||||||
|
|
||||||
// If we're attaching to an existing SharedWorker private, then we
|
// If we're attaching to an existing SharedWorker private, then we
|
||||||
// must update the overriden load group to account for our document's
|
// must update the overriden load group to account for our document's
|
||||||
// load group.
|
// load group.
|
||||||
workerPrivate->UpdateOverridenLoadGroup(aLoadInfo->mLoadGroup);
|
if (shouldAttachToWorkerPrivate) {
|
||||||
|
workerPrivate->UpdateOverridenLoadGroup(aLoadInfo->mLoadGroup);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// We don't actually care about this MessageChannel, but we use it to 'steal'
|
// We don't actually care about this MessageChannel, but we use it to 'steal'
|
||||||
|
@ -2473,6 +2489,16 @@ RuntimeService::CreateSharedWorkerFromLoadInfo(JSContext* aCx,
|
||||||
RefPtr<SharedWorker> sharedWorker = new SharedWorker(window, workerPrivate,
|
RefPtr<SharedWorker> sharedWorker = new SharedWorker(window, workerPrivate,
|
||||||
channel->Port1());
|
channel->Port1());
|
||||||
|
|
||||||
|
if (!shouldAttachToWorkerPrivate) {
|
||||||
|
// We're done here. Just queue up our error event and return our
|
||||||
|
// dead-on-arrival SharedWorker.
|
||||||
|
RefPtr<AsyncEventDispatcher> errorEvent =
|
||||||
|
new AsyncEventDispatcher(sharedWorker, NS_LITERAL_STRING("error"), false);
|
||||||
|
errorEvent->PostDOMEvent();
|
||||||
|
sharedWorker.forget(aSharedWorker);
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
if (!workerPrivate->RegisterSharedWorker(sharedWorker, channel->Port2())) {
|
if (!workerPrivate->RegisterSharedWorker(sharedWorker, channel->Port2())) {
|
||||||
NS_WARNING("Worker is unreachable, this shouldn't happen!");
|
NS_WARNING("Worker is unreachable, this shouldn't happen!");
|
||||||
sharedWorker->Close();
|
sharedWorker->Close();
|
||||||
|
|
|
@ -155,7 +155,6 @@ private:
|
||||||
ServiceWorkerManagerParent::ServiceWorkerManagerParent()
|
ServiceWorkerManagerParent::ServiceWorkerManagerParent()
|
||||||
: mService(ServiceWorkerManagerService::GetOrCreate())
|
: mService(ServiceWorkerManagerService::GetOrCreate())
|
||||||
, mID(++sServiceWorkerManagerParentID)
|
, mID(++sServiceWorkerManagerParentID)
|
||||||
, mActorDestroyed(false)
|
|
||||||
{
|
{
|
||||||
AssertIsOnBackgroundThread();
|
AssertIsOnBackgroundThread();
|
||||||
mService->RegisterActor(this);
|
mService->RegisterActor(this);
|
||||||
|
@ -166,17 +165,6 @@ ServiceWorkerManagerParent::~ServiceWorkerManagerParent()
|
||||||
AssertIsOnBackgroundThread();
|
AssertIsOnBackgroundThread();
|
||||||
}
|
}
|
||||||
|
|
||||||
already_AddRefed<ContentParent>
|
|
||||||
ServiceWorkerManagerParent::GetContentParent() const
|
|
||||||
{
|
|
||||||
AssertIsOnBackgroundThread();
|
|
||||||
|
|
||||||
// This object must be released on main-thread.
|
|
||||||
RefPtr<ContentParent> parent =
|
|
||||||
BackgroundParent::GetContentParent(Manager());
|
|
||||||
return parent.forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ServiceWorkerManagerParent::RecvRegister(
|
ServiceWorkerManagerParent::RecvRegister(
|
||||||
const ServiceWorkerRegistrationData& aData)
|
const ServiceWorkerRegistrationData& aData)
|
||||||
|
@ -320,8 +308,6 @@ ServiceWorkerManagerParent::ActorDestroy(ActorDestroyReason aWhy)
|
||||||
{
|
{
|
||||||
AssertIsOnBackgroundThread();
|
AssertIsOnBackgroundThread();
|
||||||
|
|
||||||
mActorDestroyed = true;
|
|
||||||
|
|
||||||
if (mService) {
|
if (mService) {
|
||||||
// This object is about to be released and with it, also mService will be
|
// This object is about to be released and with it, also mService will be
|
||||||
// released too.
|
// released too.
|
||||||
|
|
|
@ -29,17 +29,11 @@ class ServiceWorkerManagerParent final : public PServiceWorkerManagerParent
|
||||||
public:
|
public:
|
||||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ServiceWorkerManagerParent)
|
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ServiceWorkerManagerParent)
|
||||||
|
|
||||||
bool ActorDestroyed() const
|
|
||||||
{
|
|
||||||
return mActorDestroyed;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t ID() const
|
uint64_t ID() const
|
||||||
{
|
{
|
||||||
return mID;
|
return mID;
|
||||||
}
|
}
|
||||||
|
|
||||||
already_AddRefed<ContentParent> GetContentParent() const;
|
|
||||||
private:
|
private:
|
||||||
ServiceWorkerManagerParent();
|
ServiceWorkerManagerParent();
|
||||||
~ServiceWorkerManagerParent();
|
~ServiceWorkerManagerParent();
|
||||||
|
@ -69,8 +63,6 @@ private:
|
||||||
// We use this ID in the Service in order to avoid the sending of messages to
|
// We use this ID in the Service in order to avoid the sending of messages to
|
||||||
// ourself.
|
// ourself.
|
||||||
uint64_t mID;
|
uint64_t mID;
|
||||||
|
|
||||||
bool mActorDestroyed;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace workers
|
} // namespace workers
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
#include "ServiceWorkerManagerParent.h"
|
#include "ServiceWorkerManagerParent.h"
|
||||||
#include "ServiceWorkerRegistrar.h"
|
#include "ServiceWorkerRegistrar.h"
|
||||||
#include "mozilla/dom/ContentParent.h"
|
#include "mozilla/dom/ContentParent.h"
|
||||||
#include "mozilla/dom/TabParent.h"
|
|
||||||
#include "mozilla/ipc/BackgroundParent.h"
|
#include "mozilla/ipc/BackgroundParent.h"
|
||||||
#include "mozilla/Unused.h"
|
#include "mozilla/Unused.h"
|
||||||
#include "nsAutoPtr.h"
|
#include "nsAutoPtr.h"
|
||||||
|
@ -24,84 +23,6 @@ namespace {
|
||||||
|
|
||||||
ServiceWorkerManagerService* sInstance = nullptr;
|
ServiceWorkerManagerService* sInstance = nullptr;
|
||||||
|
|
||||||
struct NotifySoftUpdateData
|
|
||||||
{
|
|
||||||
RefPtr<ServiceWorkerManagerParent> mParent;
|
|
||||||
RefPtr<ContentParent> mContentParent;
|
|
||||||
|
|
||||||
~NotifySoftUpdateData()
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(!mContentParent);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class NotifySoftUpdateIfPrincipalOkRunnable final : public Runnable
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
NotifySoftUpdateIfPrincipalOkRunnable(
|
|
||||||
nsAutoPtr<nsTArray<NotifySoftUpdateData>>& aData,
|
|
||||||
const PrincipalOriginAttributes& aOriginAttributes,
|
|
||||||
const nsAString& aScope)
|
|
||||||
: mData(aData)
|
|
||||||
, mOriginAttributes(aOriginAttributes)
|
|
||||||
, mScope(aScope)
|
|
||||||
, mBackgroundThread(NS_GetCurrentThread())
|
|
||||||
{
|
|
||||||
AssertIsInMainProcess();
|
|
||||||
AssertIsOnBackgroundThread();
|
|
||||||
|
|
||||||
MOZ_ASSERT(mData && !aData);
|
|
||||||
MOZ_ASSERT(mBackgroundThread);
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMETHOD
|
|
||||||
Run() override
|
|
||||||
{
|
|
||||||
if (NS_IsMainThread()) {
|
|
||||||
for (uint32_t i = 0; i < mData->Length(); ++i) {
|
|
||||||
NotifySoftUpdateData& data = mData->ElementAt(i);
|
|
||||||
nsTArray<TabContext> contextArray =
|
|
||||||
data.mContentParent->GetManagedTabContext();
|
|
||||||
// mContentParent needs to be released in the main thread.
|
|
||||||
data.mContentParent = nullptr;
|
|
||||||
// We only send the notification about the soft update to the
|
|
||||||
// tabs/apps with the same appId and inIsolatedMozBrowser values.
|
|
||||||
// Sending a notification to the wrong process will make the process
|
|
||||||
// to be killed.
|
|
||||||
for (uint32_t j = 0; j < contextArray.Length(); ++j) {
|
|
||||||
if ((contextArray[j].OwnOrContainingAppId() == mOriginAttributes.mAppId) &&
|
|
||||||
(contextArray[j].IsIsolatedMozBrowserElement() == mOriginAttributes.mInIsolatedMozBrowser)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Array entries with no mParent won't receive any notification.
|
|
||||||
data.mParent = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MOZ_ALWAYS_SUCCEEDS(mBackgroundThread->Dispatch(this, NS_DISPATCH_NORMAL));
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
AssertIsOnBackgroundThread();
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < mData->Length(); ++i) {
|
|
||||||
NotifySoftUpdateData& data = mData->ElementAt(i);
|
|
||||||
MOZ_ASSERT(!(data.mContentParent));
|
|
||||||
ServiceWorkerManagerParent* parent = data.mParent;
|
|
||||||
if (parent && !parent->ActorDestroyed()) {
|
|
||||||
Unused << parent->SendNotifySoftUpdate(mOriginAttributes, mScope);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
nsAutoPtr<nsTArray<NotifySoftUpdateData>> mData;
|
|
||||||
PrincipalOriginAttributes mOriginAttributes;
|
|
||||||
nsString mScope;
|
|
||||||
nsCOMPtr<nsIThread> mBackgroundThread;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
ServiceWorkerManagerService::ServiceWorkerManagerService()
|
ServiceWorkerManagerService::ServiceWorkerManagerService()
|
||||||
|
@ -197,43 +118,22 @@ ServiceWorkerManagerService::PropagateSoftUpdate(
|
||||||
{
|
{
|
||||||
AssertIsOnBackgroundThread();
|
AssertIsOnBackgroundThread();
|
||||||
|
|
||||||
nsAutoPtr<nsTArray<NotifySoftUpdateData>> notifySoftUpdateDataArray(
|
|
||||||
new nsTArray<NotifySoftUpdateData>());
|
|
||||||
DebugOnly<bool> parentFound = false;
|
DebugOnly<bool> parentFound = false;
|
||||||
for (auto iter = mAgents.Iter(); !iter.Done(); iter.Next()) {
|
for (auto iter = mAgents.Iter(); !iter.Done(); iter.Next()) {
|
||||||
RefPtr<ServiceWorkerManagerParent> parent = iter.Get()->GetKey();
|
RefPtr<ServiceWorkerManagerParent> parent = iter.Get()->GetKey();
|
||||||
MOZ_ASSERT(parent);
|
MOZ_ASSERT(parent);
|
||||||
|
|
||||||
|
nsString scope(aScope);
|
||||||
|
Unused << parent->SendNotifySoftUpdate(aOriginAttributes,
|
||||||
|
scope);
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
if (parent->ID() == aParentID) {
|
if (parent->ID() == aParentID) {
|
||||||
parentFound = true;
|
parentFound = true;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
RefPtr<ContentParent> contentParent = parent->GetContentParent();
|
|
||||||
|
|
||||||
// If the ContentParent is null we are dealing with a same-process actor.
|
|
||||||
if (!contentParent) {
|
|
||||||
Unused << parent->SendNotifySoftUpdate(aOriginAttributes,
|
|
||||||
nsString(aScope));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
NotifySoftUpdateData* data = notifySoftUpdateDataArray->AppendElement();
|
|
||||||
data->mContentParent.swap(contentParent);
|
|
||||||
data->mParent.swap(parent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (notifySoftUpdateDataArray->IsEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
RefPtr<NotifySoftUpdateIfPrincipalOkRunnable> runnable =
|
|
||||||
new NotifySoftUpdateIfPrincipalOkRunnable(notifySoftUpdateDataArray,
|
|
||||||
aOriginAttributes, aScope);
|
|
||||||
MOZ_ASSERT(!notifySoftUpdateDataArray);
|
|
||||||
MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable));
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
MOZ_ASSERT(parentFound);
|
MOZ_ASSERT(parentFound);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
#include "nsIWorkerDebugger.h"
|
#include "nsIWorkerDebugger.h"
|
||||||
#include "nsIXPConnect.h"
|
#include "nsIXPConnect.h"
|
||||||
#include "nsPIDOMWindow.h"
|
#include "nsPIDOMWindow.h"
|
||||||
|
#include "nsGlobalWindow.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include "ImageContainer.h"
|
#include "ImageContainer.h"
|
||||||
|
@ -2182,7 +2183,8 @@ WorkerPrivateParent<Derived>::WorkerPrivateParent(
|
||||||
mWorkerName(aWorkerName), mLoadingWorkerScript(false),
|
mWorkerName(aWorkerName), mLoadingWorkerScript(false),
|
||||||
mBusyCount(0), mParentStatus(Pending), mParentFrozen(false),
|
mBusyCount(0), mParentStatus(Pending), mParentFrozen(false),
|
||||||
mParentWindowPaused(false), mIsChromeWorker(aIsChromeWorker),
|
mParentWindowPaused(false), mIsChromeWorker(aIsChromeWorker),
|
||||||
mMainThreadObjectsForgotten(false), mWorkerType(aWorkerType),
|
mMainThreadObjectsForgotten(false), mIsSecureContext(false),
|
||||||
|
mWorkerType(aWorkerType),
|
||||||
mCreationTimeStamp(TimeStamp::Now()),
|
mCreationTimeStamp(TimeStamp::Now()),
|
||||||
mCreationTimeHighRes((double)PR_Now() / PR_USEC_PER_MSEC)
|
mCreationTimeHighRes((double)PR_Now() / PR_USEC_PER_MSEC)
|
||||||
{
|
{
|
||||||
|
@ -2202,8 +2204,14 @@ WorkerPrivateParent<Derived>::WorkerPrivateParent(
|
||||||
if (aParent) {
|
if (aParent) {
|
||||||
aParent->AssertIsOnWorkerThread();
|
aParent->AssertIsOnWorkerThread();
|
||||||
|
|
||||||
|
// Note that this copies our parent's secure context state into mJSSettings.
|
||||||
aParent->CopyJSSettings(mJSSettings);
|
aParent->CopyJSSettings(mJSSettings);
|
||||||
|
|
||||||
|
// And manually set our mIsSecureContext, though it's not really relevant to
|
||||||
|
// dedicated workers...
|
||||||
|
mIsSecureContext = aParent->IsSecureContext();
|
||||||
|
MOZ_ASSERT_IF(mIsChromeWorker, mIsSecureContext);
|
||||||
|
|
||||||
MOZ_ASSERT(IsDedicatedWorker());
|
MOZ_ASSERT(IsDedicatedWorker());
|
||||||
mNowBaseTimeStamp = aParent->NowBaseTimeStamp();
|
mNowBaseTimeStamp = aParent->NowBaseTimeStamp();
|
||||||
mNowBaseTimeHighRes = aParent->NowBaseTime();
|
mNowBaseTimeHighRes = aParent->NowBaseTime();
|
||||||
|
@ -2213,6 +2221,26 @@ WorkerPrivateParent<Derived>::WorkerPrivateParent(
|
||||||
|
|
||||||
RuntimeService::GetDefaultJSSettings(mJSSettings);
|
RuntimeService::GetDefaultJSSettings(mJSSettings);
|
||||||
|
|
||||||
|
// Our secure context state depends on the kind of worker we have.
|
||||||
|
if (UsesSystemPrincipal() || IsServiceWorker()) {
|
||||||
|
mIsSecureContext = true;
|
||||||
|
} else if (mLoadInfo.mWindow) {
|
||||||
|
// Shared and dedicated workers both inherit the loading window's secure
|
||||||
|
// context state. Shared workers then prevent windows with a different
|
||||||
|
// secure context state from attaching to them.
|
||||||
|
mIsSecureContext = mLoadInfo.mWindow->IsSecureContext();
|
||||||
|
} else {
|
||||||
|
MOZ_ASSERT_UNREACHABLE("non-chrome worker that is not a service worker "
|
||||||
|
"that has no parent and no associated window");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mIsSecureContext) {
|
||||||
|
mJSSettings.chrome.compartmentOptions
|
||||||
|
.creationOptions().setSecureContext(true);
|
||||||
|
mJSSettings.content.compartmentOptions
|
||||||
|
.creationOptions().setSecureContext(true);
|
||||||
|
}
|
||||||
|
|
||||||
if (IsDedicatedWorker() && mLoadInfo.mWindow &&
|
if (IsDedicatedWorker() && mLoadInfo.mWindow &&
|
||||||
mLoadInfo.mWindow->GetPerformance()) {
|
mLoadInfo.mWindow->GetPerformance()) {
|
||||||
mNowBaseTimeStamp = mLoadInfo.mWindow->GetPerformance()->GetDOMTiming()->
|
mNowBaseTimeStamp = mLoadInfo.mWindow->GetPerformance()->GetDOMTiming()->
|
||||||
|
|
|
@ -202,6 +202,15 @@ private:
|
||||||
bool mParentWindowPaused;
|
bool mParentWindowPaused;
|
||||||
bool mIsChromeWorker;
|
bool mIsChromeWorker;
|
||||||
bool mMainThreadObjectsForgotten;
|
bool mMainThreadObjectsForgotten;
|
||||||
|
// mIsSecureContext is set once in our constructor; after that it can be read
|
||||||
|
// from various threads. We could make this const if we were OK with setting
|
||||||
|
// it in the initializer list via calling some function that takes all sorts
|
||||||
|
// of state (loadinfo, worker type, parent).
|
||||||
|
//
|
||||||
|
// It's a bit unfortunate that we have to have an out-of-band boolean for
|
||||||
|
// this, but we need access to this state from the parent thread, and we can't
|
||||||
|
// use our global object's secure state there.
|
||||||
|
bool mIsSecureContext;
|
||||||
WorkerType mWorkerType;
|
WorkerType mWorkerType;
|
||||||
TimeStamp mCreationTimeStamp;
|
TimeStamp mCreationTimeStamp;
|
||||||
DOMHighResTimeStamp mCreationTimeHighRes;
|
DOMHighResTimeStamp mCreationTimeHighRes;
|
||||||
|
@ -823,6 +832,16 @@ public:
|
||||||
IMPL_EVENT_HANDLER(message)
|
IMPL_EVENT_HANDLER(message)
|
||||||
IMPL_EVENT_HANDLER(error)
|
IMPL_EVENT_HANDLER(error)
|
||||||
|
|
||||||
|
// Check whether this worker is a secure context. For use from the parent
|
||||||
|
// thread only; the canonical "is secure context" boolean is stored on the
|
||||||
|
// compartment of the worker global. The only reason we don't
|
||||||
|
// AssertIsOnParentThread() here is so we can assert that this value matches
|
||||||
|
// the one on the compartment, which has to be done from the worker thread.
|
||||||
|
bool IsSecureContext() const
|
||||||
|
{
|
||||||
|
return mIsSecureContext;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
void
|
void
|
||||||
AssertIsOnParentThread() const;
|
AssertIsOnParentThread() const;
|
||||||
|
|
|
@ -173,6 +173,15 @@ WorkerGlobalScope::GetCaches(ErrorResult& aRv)
|
||||||
return ref.forget();
|
return ref.forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
WorkerGlobalScope::IsSecureContext() const
|
||||||
|
{
|
||||||
|
bool globalSecure =
|
||||||
|
JS_GetIsSecureContext(js::GetObjectCompartment(GetWrapperPreserveColor()));
|
||||||
|
MOZ_ASSERT(globalSecure == mWorkerPrivate->IsSecureContext());
|
||||||
|
return globalSecure;
|
||||||
|
}
|
||||||
|
|
||||||
already_AddRefed<WorkerLocation>
|
already_AddRefed<WorkerLocation>
|
||||||
WorkerGlobalScope::Location()
|
WorkerGlobalScope::Location()
|
||||||
{
|
{
|
||||||
|
|
|
@ -163,6 +163,8 @@ public:
|
||||||
already_AddRefed<cache::CacheStorage>
|
already_AddRefed<cache::CacheStorage>
|
||||||
GetCaches(ErrorResult& aRv);
|
GetCaches(ErrorResult& aRv);
|
||||||
|
|
||||||
|
bool IsSecureContext() const;
|
||||||
|
|
||||||
already_AddRefed<Promise>
|
already_AddRefed<Promise>
|
||||||
CreateImageBitmap(const ImageBitmapSource& aImage, ErrorResult& aRv);
|
CreateImageBitmap(const ImageBitmapSource& aImage, ErrorResult& aRv);
|
||||||
|
|
||||||
|
|
|
@ -2434,63 +2434,6 @@ nsPermissionManager::RemovePermissionsWithAttributes(mozilla::OriginAttributesPa
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
|
||||||
nsPermissionManager::RemoveExpiredPermissionsForApp(uint32_t aAppId)
|
|
||||||
{
|
|
||||||
ENSURE_NOT_CHILD_PROCESS;
|
|
||||||
|
|
||||||
if (aAppId == nsIScriptSecurityManager::NO_APP_ID) {
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto iter = mPermissionTable.Iter(); !iter.Done(); iter.Next()) {
|
|
||||||
PermissionHashKey* entry = iter.Get();
|
|
||||||
nsCOMPtr<nsIPrincipal> principal;
|
|
||||||
GetPrincipalFromOrigin(entry->GetKey()->mOrigin, getter_AddRefs(principal));
|
|
||||||
|
|
||||||
if (principal->GetAppId() != aAppId) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < entry->GetPermissions().Length(); ++i) {
|
|
||||||
PermissionEntry& permEntry = entry->GetPermissions()[i];
|
|
||||||
if (permEntry.mExpireType != nsIPermissionManager::EXPIRE_SESSION) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (permEntry.mNonSessionExpireType ==
|
|
||||||
nsIPermissionManager::EXPIRE_SESSION) {
|
|
||||||
PermissionEntry oldPermEntry = entry->GetPermissions()[i];
|
|
||||||
|
|
||||||
entry->GetPermissions().RemoveElementAt(i);
|
|
||||||
|
|
||||||
NotifyObserversWithPermission(principal,
|
|
||||||
mTypeArray.ElementAt(oldPermEntry.mType),
|
|
||||||
oldPermEntry.mPermission,
|
|
||||||
oldPermEntry.mExpireType,
|
|
||||||
oldPermEntry.mExpireTime,
|
|
||||||
u"deleted");
|
|
||||||
|
|
||||||
--i;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
permEntry.mPermission = permEntry.mNonSessionPermission;
|
|
||||||
permEntry.mExpireType = permEntry.mNonSessionExpireType;
|
|
||||||
permEntry.mExpireTime = permEntry.mNonSessionExpireTime;
|
|
||||||
|
|
||||||
NotifyObserversWithPermission(principal,
|
|
||||||
mTypeArray.ElementAt(permEntry.mType),
|
|
||||||
permEntry.mPermission,
|
|
||||||
permEntry.mExpireType,
|
|
||||||
permEntry.mExpireTime,
|
|
||||||
u"changed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
//*****************************************************************************
|
//*****************************************************************************
|
||||||
//*** nsPermissionManager private methods
|
//*** nsPermissionManager private methods
|
||||||
//*****************************************************************************
|
//*****************************************************************************
|
||||||
|
@ -2901,55 +2844,6 @@ nsPermissionManager::UpdateDB(OperationType aOp,
|
||||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
|
||||||
nsPermissionManager::AddrefAppId(uint32_t aAppId)
|
|
||||||
{
|
|
||||||
if (aAppId == nsIScriptSecurityManager::NO_APP_ID) {
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool found = false;
|
|
||||||
for (uint32_t i = 0; i < mAppIdRefcounts.Length(); ++i) {
|
|
||||||
if (mAppIdRefcounts[i].mAppId == aAppId) {
|
|
||||||
++mAppIdRefcounts[i].mCounter;
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!found) {
|
|
||||||
ApplicationCounter app = { aAppId, 1 };
|
|
||||||
mAppIdRefcounts.AppendElement(app);
|
|
||||||
}
|
|
||||||
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMETHODIMP
|
|
||||||
nsPermissionManager::ReleaseAppId(uint32_t aAppId)
|
|
||||||
{
|
|
||||||
// An app has been released, maybe we have to reset its session.
|
|
||||||
|
|
||||||
if (aAppId == nsIScriptSecurityManager::NO_APP_ID) {
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < mAppIdRefcounts.Length(); ++i) {
|
|
||||||
if (mAppIdRefcounts[i].mAppId == aAppId) {
|
|
||||||
--mAppIdRefcounts[i].mCounter;
|
|
||||||
|
|
||||||
if (!mAppIdRefcounts[i].mCounter) {
|
|
||||||
mAppIdRefcounts.RemoveElementAt(i);
|
|
||||||
return RemoveExpiredPermissionsForApp(aAppId);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsPermissionManager::UpdateExpireTime(nsIPrincipal* aPrincipal,
|
nsPermissionManager::UpdateExpireTime(nsIPrincipal* aPrincipal,
|
||||||
const char* aType,
|
const char* aType,
|
||||||
|
|
|
@ -254,8 +254,6 @@ private:
|
||||||
int64_t aExpireTime,
|
int64_t aExpireTime,
|
||||||
int64_t aModificationTime);
|
int64_t aModificationTime);
|
||||||
|
|
||||||
nsresult RemoveExpiredPermissionsForApp(uint32_t aAppId);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method removes all permissions modified after the specified time.
|
* This method removes all permissions modified after the specified time.
|
||||||
*/
|
*/
|
||||||
|
@ -282,13 +280,6 @@ private:
|
||||||
// An array to store the strings identifying the different types.
|
// An array to store the strings identifying the different types.
|
||||||
nsTArray<nsCString> mTypeArray;
|
nsTArray<nsCString> mTypeArray;
|
||||||
|
|
||||||
// A list of struct for counting applications
|
|
||||||
struct ApplicationCounter {
|
|
||||||
uint32_t mAppId;
|
|
||||||
uint32_t mCounter;
|
|
||||||
};
|
|
||||||
nsTArray<ApplicationCounter> mAppIdRefcounts;
|
|
||||||
|
|
||||||
// Initially, |false|. Set to |true| once shutdown has started, to avoid
|
// Initially, |false|. Set to |true| once shutdown has started, to avoid
|
||||||
// reopening the database.
|
// reopening the database.
|
||||||
bool mIsShuttingDown;
|
bool mIsShuttingDown;
|
||||||
|
|
|
@ -274,6 +274,7 @@ const ClassOps MapObject::classOps_ = {
|
||||||
const Class MapObject::class_ = {
|
const Class MapObject::class_ = {
|
||||||
"Map",
|
"Map",
|
||||||
JSCLASS_HAS_PRIVATE |
|
JSCLASS_HAS_PRIVATE |
|
||||||
|
JSCLASS_HAS_RESERVED_SLOTS(MapObject::SlotCount) |
|
||||||
JSCLASS_HAS_CACHED_PROTO(JSProto_Map) |
|
JSCLASS_HAS_CACHED_PROTO(JSProto_Map) |
|
||||||
JSCLASS_FOREGROUND_FINALIZE,
|
JSCLASS_FOREGROUND_FINALIZE,
|
||||||
&MapObject::classOps_
|
&MapObject::classOps_
|
||||||
|
@ -372,7 +373,7 @@ MapObject::mark(JSTracer* trc, JSObject* obj)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct UnbarrieredHashPolicy {
|
struct js::UnbarrieredHashPolicy {
|
||||||
typedef Value Lookup;
|
typedef Value Lookup;
|
||||||
static HashNumber hash(const Lookup& v) { return v.asRawBits(); }
|
static HashNumber hash(const Lookup& v) { return v.asRawBits(); }
|
||||||
static bool match(const Value& k, const Lookup& l) { return k == l; }
|
static bool match(const Value& k, const Lookup& l) { return k == l; }
|
||||||
|
@ -380,42 +381,102 @@ struct UnbarrieredHashPolicy {
|
||||||
static void makeEmpty(Value* vp) { vp->setMagic(JS_HASH_KEY_EMPTY); }
|
static void makeEmpty(Value* vp) { vp->setMagic(JS_HASH_KEY_EMPTY); }
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename TableType>
|
using NurseryKeysVector = Vector<JSObject*, 0, SystemAllocPolicy>;
|
||||||
class OrderedHashTableRef : public gc::BufferableRef
|
|
||||||
|
template <typename TableObject>
|
||||||
|
static NurseryKeysVector*
|
||||||
|
GetNurseryKeys(TableObject* t)
|
||||||
{
|
{
|
||||||
TableType* table;
|
Value value = t->getReservedSlot(TableObject::NurseryKeysSlot);
|
||||||
Value key;
|
return reinterpret_cast<NurseryKeysVector*>(value.toPrivate());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename TableObject>
|
||||||
|
static NurseryKeysVector*
|
||||||
|
AllocNurseryKeys(TableObject* t)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(!GetNurseryKeys(t));
|
||||||
|
auto keys = js_new<NurseryKeysVector>();
|
||||||
|
if (!keys)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
t->setReservedSlot(TableObject::NurseryKeysSlot, PrivateValue(keys));
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename TableObject>
|
||||||
|
static void
|
||||||
|
DeleteNurseryKeys(TableObject* t)
|
||||||
|
{
|
||||||
|
auto keys = GetNurseryKeys(t);
|
||||||
|
MOZ_ASSERT(keys);
|
||||||
|
js_delete(keys);
|
||||||
|
t->setReservedSlot(TableObject::NurseryKeysSlot, PrivateValue(nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
// A generic store buffer entry that traces all nursery keys for an ordered hash
|
||||||
|
// map or set.
|
||||||
|
template <typename ObjectT>
|
||||||
|
class js::OrderedHashTableRef : public gc::BufferableRef
|
||||||
|
{
|
||||||
|
ObjectT* object;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit OrderedHashTableRef(TableType* t, const Value& k) : table(t), key(k) {}
|
explicit OrderedHashTableRef(ObjectT* obj) : object(obj) {}
|
||||||
|
|
||||||
void trace(JSTracer* trc) override {
|
void trace(JSTracer* trc) override {
|
||||||
MOZ_ASSERT(UnbarrieredHashPolicy::hash(key) ==
|
auto table = reinterpret_cast<typename ObjectT::UnbarrieredTable*>(object->getData());
|
||||||
HashableValue::Hasher::hash(*reinterpret_cast<HashableValue*>(&key)));
|
NurseryKeysVector* keys = GetNurseryKeys(object);
|
||||||
Value prior = key;
|
MOZ_ASSERT(keys);
|
||||||
TraceManuallyBarrieredEdge(trc, &key, "ordered hash table key");
|
for (JSObject* obj : *keys) {
|
||||||
table->rekeyOneEntry(prior, key);
|
MOZ_ASSERT(obj);
|
||||||
|
Value key = ObjectValue(*obj);
|
||||||
|
Value prior = key;
|
||||||
|
MOZ_ASSERT(UnbarrieredHashPolicy::hash(key) ==
|
||||||
|
HashableValue::Hasher::hash(*reinterpret_cast<HashableValue*>(&key)));
|
||||||
|
TraceManuallyBarrieredEdge(trc, &key, "ordered hash table key");
|
||||||
|
table->rekeyOneEntry(prior, key);
|
||||||
|
}
|
||||||
|
DeleteNurseryKeys(object);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
inline static void
|
template <typename ObjectT>
|
||||||
WriteBarrierPost(JSRuntime* rt, ValueMap* map, const Value& key)
|
inline static MOZ_MUST_USE bool
|
||||||
|
WriteBarrierPostImpl(JSRuntime* rt, ObjectT* obj, const Value& keyValue)
|
||||||
{
|
{
|
||||||
typedef OrderedHashMap<Value, Value, UnbarrieredHashPolicy, RuntimeAllocPolicy> UnbarrieredMap;
|
if (MOZ_LIKELY(!keyValue.isObject()))
|
||||||
if (MOZ_UNLIKELY(key.isObject() && IsInsideNursery(&key.toObject()))) {
|
return true;
|
||||||
rt->gc.storeBuffer.putGeneric(OrderedHashTableRef<UnbarrieredMap>(
|
|
||||||
reinterpret_cast<UnbarrieredMap*>(map), key));
|
JSObject* key = &keyValue.toObject();
|
||||||
|
if (!IsInsideNursery(key))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
NurseryKeysVector* keys = GetNurseryKeys(obj);
|
||||||
|
if (!keys) {
|
||||||
|
keys = AllocNurseryKeys(obj);
|
||||||
|
if (!keys)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
rt->gc.storeBuffer.putGeneric(OrderedHashTableRef<ObjectT>(obj));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!keys->append(key))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline static void
|
inline static MOZ_MUST_USE bool
|
||||||
WriteBarrierPost(JSRuntime* rt, ValueSet* set, const Value& key)
|
WriteBarrierPost(JSRuntime* rt, MapObject* map, const Value& key)
|
||||||
{
|
{
|
||||||
typedef OrderedHashSet<Value, UnbarrieredHashPolicy, RuntimeAllocPolicy> UnbarrieredSet;
|
return WriteBarrierPostImpl(rt, map, key);
|
||||||
if (MOZ_UNLIKELY(key.isObject() && IsInsideNursery(&key.toObject()))) {
|
}
|
||||||
rt->gc.storeBuffer.putGeneric(OrderedHashTableRef<UnbarrieredSet>(
|
|
||||||
reinterpret_cast<UnbarrieredSet*>(set), key));
|
inline static MOZ_MUST_USE bool
|
||||||
}
|
WriteBarrierPost(JSRuntime* rt, SetObject* set, const Value& key)
|
||||||
|
{
|
||||||
|
return WriteBarrierPostImpl(rt, set, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
@ -449,11 +510,13 @@ MapObject::set(JSContext* cx, HandleObject obj, HandleValue k, HandleValue v)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
HeapPtr<Value> rval(v);
|
HeapPtr<Value> rval(v);
|
||||||
if (!map->put(key, rval)) {
|
if (!WriteBarrierPost(cx->runtime(), &obj->as<MapObject>(), key.value()) ||
|
||||||
|
!map->put(key, rval))
|
||||||
|
{
|
||||||
ReportOutOfMemory(cx);
|
ReportOutOfMemory(cx);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
WriteBarrierPost(cx->runtime(), map, key.value());
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -471,6 +534,7 @@ MapObject::create(JSContext* cx, HandleObject proto /* = nullptr */)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
mapObj->setPrivate(map.release());
|
mapObj->setPrivate(map.release());
|
||||||
|
mapObj->setReservedSlot(NurseryKeysSlot, PrivateValue(nullptr));
|
||||||
return mapObj;
|
return mapObj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -550,11 +614,12 @@ MapObject::construct(JSContext* cx, unsigned argc, Value* vp)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
HeapPtr<Value> rval(val);
|
HeapPtr<Value> rval(val);
|
||||||
if (!map->put(hkey, rval)) {
|
if (!WriteBarrierPost(cx->runtime(), obj, key) ||
|
||||||
|
!map->put(hkey, rval))
|
||||||
|
{
|
||||||
ReportOutOfMemory(cx);
|
ReportOutOfMemory(cx);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
WriteBarrierPost(cx->runtime(), map, key);
|
|
||||||
} else {
|
} else {
|
||||||
if (!args2.init(2))
|
if (!args2.init(2))
|
||||||
return false;
|
return false;
|
||||||
|
@ -700,11 +765,13 @@ MapObject::set_impl(JSContext* cx, const CallArgs& args)
|
||||||
ValueMap& map = extract(args);
|
ValueMap& map = extract(args);
|
||||||
ARG0_KEY(cx, args, key);
|
ARG0_KEY(cx, args, key);
|
||||||
HeapPtr<Value> rval(args.get(1));
|
HeapPtr<Value> rval(args.get(1));
|
||||||
if (!map.put(key, rval)) {
|
if (!WriteBarrierPost(cx->runtime(), &args.thisv().toObject().as<MapObject>(), key.value()) ||
|
||||||
|
!map.put(key, rval))
|
||||||
|
{
|
||||||
ReportOutOfMemory(cx);
|
ReportOutOfMemory(cx);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
WriteBarrierPost(cx->runtime(), &map, key.value());
|
|
||||||
args.rval().set(args.thisv());
|
args.rval().set(args.thisv());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1017,6 +1084,7 @@ const ClassOps SetObject::classOps_ = {
|
||||||
const Class SetObject::class_ = {
|
const Class SetObject::class_ = {
|
||||||
"Set",
|
"Set",
|
||||||
JSCLASS_HAS_PRIVATE |
|
JSCLASS_HAS_PRIVATE |
|
||||||
|
JSCLASS_HAS_RESERVED_SLOTS(SetObject::SlotCount) |
|
||||||
JSCLASS_HAS_CACHED_PROTO(JSProto_Set) |
|
JSCLASS_HAS_CACHED_PROTO(JSProto_Set) |
|
||||||
JSCLASS_FOREGROUND_FINALIZE,
|
JSCLASS_FOREGROUND_FINALIZE,
|
||||||
&SetObject::classOps_
|
&SetObject::classOps_
|
||||||
|
@ -1098,11 +1166,12 @@ SetObject::add(JSContext* cx, HandleObject obj, HandleValue k)
|
||||||
if (!key.setValue(cx, k))
|
if (!key.setValue(cx, k))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!set->put(key)) {
|
if (!WriteBarrierPost(cx->runtime(), &obj->as<SetObject>(), key.value()) ||
|
||||||
|
!set->put(key))
|
||||||
|
{
|
||||||
ReportOutOfMemory(cx);
|
ReportOutOfMemory(cx);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
WriteBarrierPost(cx->runtime(), set, key.value());
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1120,6 +1189,7 @@ SetObject::create(JSContext* cx, HandleObject proto /* = nullptr */)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
obj->setPrivate(set.release());
|
obj->setPrivate(set.release());
|
||||||
|
obj->setReservedSlot(NurseryKeysSlot, PrivateValue(nullptr));
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1189,11 +1259,12 @@ SetObject::construct(JSContext* cx, unsigned argc, Value* vp)
|
||||||
if (isOriginalAdder) {
|
if (isOriginalAdder) {
|
||||||
if (!key.setValue(cx, keyVal))
|
if (!key.setValue(cx, keyVal))
|
||||||
return false;
|
return false;
|
||||||
if (!set->put(key)) {
|
if (!WriteBarrierPost(cx->runtime(), obj, keyVal) ||
|
||||||
|
!set->put(key))
|
||||||
|
{
|
||||||
ReportOutOfMemory(cx);
|
ReportOutOfMemory(cx);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
WriteBarrierPost(cx->runtime(), set, keyVal);
|
|
||||||
} else {
|
} else {
|
||||||
if (!args2.init(1))
|
if (!args2.init(1))
|
||||||
return false;
|
return false;
|
||||||
|
@ -1306,11 +1377,12 @@ SetObject::add_impl(JSContext* cx, const CallArgs& args)
|
||||||
|
|
||||||
ValueSet& set = extract(args);
|
ValueSet& set = extract(args);
|
||||||
ARG0_KEY(cx, args, key);
|
ARG0_KEY(cx, args, key);
|
||||||
if (!set.put(key)) {
|
if (!WriteBarrierPost(cx->runtime(), &args.thisv().toObject().as<SetObject>(), key.value()) ||
|
||||||
|
!set.put(key))
|
||||||
|
{
|
||||||
ReportOutOfMemory(cx);
|
ReportOutOfMemory(cx);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
WriteBarrierPost(cx->runtime(), &set, key.value());
|
|
||||||
args.rval().set(args.thisv());
|
args.rval().set(args.thisv());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,6 +74,11 @@ typedef OrderedHashSet<HashableValue,
|
||||||
HashableValue::Hasher,
|
HashableValue::Hasher,
|
||||||
RuntimeAllocPolicy> ValueSet;
|
RuntimeAllocPolicy> ValueSet;
|
||||||
|
|
||||||
|
template <typename ObjectT>
|
||||||
|
class OrderedHashTableRef;
|
||||||
|
|
||||||
|
struct UnbarrieredHashPolicy;
|
||||||
|
|
||||||
class MapObject : public NativeObject {
|
class MapObject : public NativeObject {
|
||||||
public:
|
public:
|
||||||
enum IteratorKind { Keys, Values, Entries };
|
enum IteratorKind { Keys, Values, Entries };
|
||||||
|
@ -88,6 +93,8 @@ class MapObject : public NativeObject {
|
||||||
static JSObject* initClass(JSContext* cx, JSObject* obj);
|
static JSObject* initClass(JSContext* cx, JSObject* obj);
|
||||||
static const Class class_;
|
static const Class class_;
|
||||||
|
|
||||||
|
enum { NurseryKeysSlot, SlotCount };
|
||||||
|
|
||||||
static MOZ_MUST_USE bool getKeysAndValuesInterleaved(JSContext* cx, HandleObject obj,
|
static MOZ_MUST_USE bool getKeysAndValuesInterleaved(JSContext* cx, HandleObject obj,
|
||||||
JS::MutableHandle<GCVector<JS::Value>> entries);
|
JS::MutableHandle<GCVector<JS::Value>> entries);
|
||||||
static MOZ_MUST_USE bool entries(JSContext* cx, unsigned argc, Value* vp);
|
static MOZ_MUST_USE bool entries(JSContext* cx, unsigned argc, Value* vp);
|
||||||
|
@ -110,6 +117,9 @@ class MapObject : public NativeObject {
|
||||||
static MOZ_MUST_USE bool iterator(JSContext *cx, IteratorKind kind, HandleObject obj,
|
static MOZ_MUST_USE bool iterator(JSContext *cx, IteratorKind kind, HandleObject obj,
|
||||||
MutableHandleValue iter);
|
MutableHandleValue iter);
|
||||||
|
|
||||||
|
using UnbarrieredTable = OrderedHashMap<Value, Value, UnbarrieredHashPolicy, RuntimeAllocPolicy>;
|
||||||
|
friend class OrderedHashTableRef<MapObject>;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const ClassOps classOps_;
|
static const ClassOps classOps_;
|
||||||
|
|
||||||
|
@ -180,6 +190,8 @@ class SetObject : public NativeObject {
|
||||||
static JSObject* initClass(JSContext* cx, JSObject* obj);
|
static JSObject* initClass(JSContext* cx, JSObject* obj);
|
||||||
static const Class class_;
|
static const Class class_;
|
||||||
|
|
||||||
|
enum { NurseryKeysSlot, SlotCount };
|
||||||
|
|
||||||
static MOZ_MUST_USE bool keys(JSContext *cx, HandleObject obj,
|
static MOZ_MUST_USE bool keys(JSContext *cx, HandleObject obj,
|
||||||
JS::MutableHandle<GCVector<JS::Value>> keys);
|
JS::MutableHandle<GCVector<JS::Value>> keys);
|
||||||
static MOZ_MUST_USE bool values(JSContext *cx, unsigned argc, Value *vp);
|
static MOZ_MUST_USE bool values(JSContext *cx, unsigned argc, Value *vp);
|
||||||
|
@ -196,6 +208,9 @@ class SetObject : public NativeObject {
|
||||||
MutableHandleValue iter);
|
MutableHandleValue iter);
|
||||||
static MOZ_MUST_USE bool delete_(JSContext *cx, HandleObject obj, HandleValue key, bool *rval);
|
static MOZ_MUST_USE bool delete_(JSContext *cx, HandleObject obj, HandleValue key, bool *rval);
|
||||||
|
|
||||||
|
using UnbarrieredTable = OrderedHashSet<Value, UnbarrieredHashPolicy, RuntimeAllocPolicy>;
|
||||||
|
friend class OrderedHashTableRef<SetObject>;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const ClassOps classOps_;
|
static const ClassOps classOps_;
|
||||||
|
|
||||||
|
|
|
@ -76,10 +76,6 @@ following forms:
|
||||||
: The code completed normally, returning <i>value</i>. <i>Value</i> is a
|
: The code completed normally, returning <i>value</i>. <i>Value</i> is a
|
||||||
debuggee value.
|
debuggee value.
|
||||||
|
|
||||||
<code>{ yield: <i>value</i> }</code>
|
|
||||||
: <i>(Not yet implemented.)</i> The running code is a generator frame
|
|
||||||
which has yielded <i>value</i>. <i>Value</i> is a debuggee value.
|
|
||||||
|
|
||||||
<code>{ throw: <i>value</i> }</code>
|
<code>{ throw: <i>value</i> }</code>
|
||||||
: The code threw <i>value</i> as an exception. <i>Value</i> is a debuggee
|
: The code threw <i>value</i> as an exception. <i>Value</i> is a debuggee
|
||||||
value.
|
value.
|
||||||
|
@ -115,14 +111,6 @@ resumption value has one of the following forms:
|
||||||
the function is the constructor for a subclass, then a non-object
|
the function is the constructor for a subclass, then a non-object
|
||||||
value may result in a TypeError.
|
value may result in a TypeError.
|
||||||
|
|
||||||
<code>{ yield: <i>value</i> }</code>
|
|
||||||
: <i>(Not yet implemented.)</i> Yield <i>value</i> immediately as the
|
|
||||||
next value of the current frame, which must be a generator frame.
|
|
||||||
<i>Value</i> is a debuggee value. The current frame must be a generator
|
|
||||||
frame that has not yet completed in some other way. You may use `yield`
|
|
||||||
resumption values to substitute a new value or one already yielded by a
|
|
||||||
generator, or to make a generator yield additional values.
|
|
||||||
|
|
||||||
<code>{ throw: <i>value</i> }</code>
|
<code>{ throw: <i>value</i> }</code>
|
||||||
: Throw <i>value</i> as an exception from the current bytecode
|
: Throw <i>value</i> as an exception from the current bytecode
|
||||||
instruction. <i>Value</i> must be a debuggee value.
|
instruction. <i>Value</i> must be a debuggee value.
|
||||||
|
|
|
@ -145,93 +145,9 @@ methods of other kinds of objects.
|
||||||
[`Debugger.DebuggeeWouldRun`][wouldrun]
|
[`Debugger.DebuggeeWouldRun`][wouldrun]
|
||||||
exception.
|
exception.
|
||||||
|
|
||||||
<code>getVariableDescriptor(<i>name</i>)</code>
|
|
||||||
: Return an property descriptor describing the variable bound to
|
|
||||||
<i>name</i> in this environment, of the sort returned by
|
|
||||||
`Debugger.Object.prototype.getOwnPropertyDescriptor`. <i>Name</i> must
|
|
||||||
be a string whose value is a valid ECMAScript identifier name.
|
|
||||||
|
|
||||||
If this is an `"object"` or `"with"` environment record, this simply
|
|
||||||
returns the descriptor for the given property of the environment's
|
|
||||||
object. If this is a declarative environment record, this returns a
|
|
||||||
descriptor reflecting the binding's mutability as the descriptor's
|
|
||||||
`writable` property, and its deletability as the descriptor's
|
|
||||||
`configurable` property. All declarative environment record bindings are
|
|
||||||
marked as `enumerable`. <i>(This isn't great; the semantics of variables
|
|
||||||
in declarative enviroments don't really match those of properties, so
|
|
||||||
writing code that operates properly on descriptors for either kind may
|
|
||||||
be difficult.)</i>
|
|
||||||
|
|
||||||
If this environment binds no variable named <i>name</i>, throw a
|
|
||||||
`ReferenceError`.
|
|
||||||
|
|
||||||
<code>defineVariable(<i>name</i>, <i>descriptor</i>)</code>
|
|
||||||
: Create or reconfigure the variable bound to <i>name</i> in this
|
|
||||||
environment according to <i>descriptor</i>. <i>Descriptor</i> is the
|
|
||||||
sort of value returned by `getVariableDescriptor`. On success, return
|
|
||||||
`undefined`; on failure, throw an appropriate exception. <i>Name</i>
|
|
||||||
must be a string whose value is a valid ECMAScript identifier name.
|
|
||||||
|
|
||||||
If implementation restrictions prevent SpiderMonkey from creating or
|
|
||||||
reconfiguring the variable as requested, this call throws an `Error`
|
|
||||||
exception.
|
|
||||||
|
|
||||||
<code>deleteVariable(<i>name</i>)</code>
|
|
||||||
: Delete this environment's binding for <i>name</i>.
|
|
||||||
|
|
||||||
If this environment binds no variable named <i>name</i>, throw a
|
|
||||||
`ReferenceError`.
|
|
||||||
|
|
||||||
If implementation restrictions prevent SpiderMonkey from deleting the
|
|
||||||
variable as requested, this call throws an `Error` exception.
|
|
||||||
|
|
||||||
<code>find(<i>name</i>)</code>
|
<code>find(<i>name</i>)</code>
|
||||||
: Return a reference to the innermost environment, starting with this
|
: Return a reference to the innermost environment, starting with this
|
||||||
environment, that binds <i>name</i>. If <i>name</i> is not in scope in
|
environment, that binds <i>name</i>. If <i>name</i> is not in scope in
|
||||||
this environment, return `null`. <i>Name</i> must be a string whose
|
this environment, return `null`. <i>Name</i> must be a string whose
|
||||||
value is a valid ECMAScript identifier name.
|
value is a valid ECMAScript identifier name.
|
||||||
|
|
||||||
<code>eval(<i>code</i>)</code> <i>(future plan)</i>
|
|
||||||
: Evaluate <i>code</i> in this environment, and return a
|
|
||||||
[completion value][cv] describing how it completed. <i>Code</i> is a
|
|
||||||
string. All extant handler methods, breakpoints, watchpoints, and so on
|
|
||||||
remain active during the call. This function follows the
|
|
||||||
[invocation function conventions][inv fr].
|
|
||||||
|
|
||||||
<i>Code</i> is interpreted as strict mode code when it contains a Use
|
|
||||||
Strict Directive.
|
|
||||||
|
|
||||||
If <i>code</i> is not strict mode code, then variable declarations in
|
|
||||||
<i>code</i> affect this environment. (In the terms used by the
|
|
||||||
ECMAScript specification, the `VariableEnvironment` of the execution
|
|
||||||
context for the eval code is the `VariableEnvironment` this
|
|
||||||
`Debugger.Environment` instance represents.) If implementation
|
|
||||||
restrictions prevent SpiderMonkey from extending this environment as
|
|
||||||
requested, this call throws an `Error` exception.
|
|
||||||
|
|
||||||
<code>evalWithBindings(<i>code</i>, <i>bindings</i>)</code> <i>(future plan)</i>
|
|
||||||
: Like `eval`, but evaluate <i>code</i> in this environment, extended with
|
|
||||||
bindings from the object <i>bindings</i>. For each own enumerable
|
|
||||||
property of <i>bindings</i> named <i>name</i> whose value is
|
|
||||||
<i>value</i>, include a variable in the environment in which <i>code</i>
|
|
||||||
is evaluated named <i>name</i>, whose value is <i>value</i>. Each
|
|
||||||
<i>value</i> must be a debuggee value. (This is not like a `with`
|
|
||||||
statement: <i>code</i> may access, assign to, and delete the introduced
|
|
||||||
bindings without having any effect on the <i>bindings</i> object.)
|
|
||||||
|
|
||||||
This method allows debugger code to introduce temporary bindings that
|
|
||||||
are visible to the given debuggee code and which refer to debugger-held
|
|
||||||
debuggee values, and do so without mutating any existing debuggee
|
|
||||||
environment.
|
|
||||||
|
|
||||||
Note that, like `eval`, declarations in the <i>code</i> passed to
|
|
||||||
`evalWithBindings` affect this environment, even as <i>code</i> is
|
|
||||||
evaluated with <i>bindings</i> visible. (In the terms used by the
|
|
||||||
ECMAScript specification, the `VariableEnvironment` of the execution
|
|
||||||
context for the eval code is the `VariableEnvironment` this environment
|
|
||||||
represents, and the <i>bindings</i> appear in a new declarative
|
|
||||||
environment, which is the eval code's `LexicalEnvironment`.) If
|
|
||||||
implementation restrictions prevent SpiderMonkey from extending this
|
|
||||||
environment as requested, this call throws an `Error` exception.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -28,9 +28,6 @@ throws an exception. Note that frames only become inactive at times that
|
||||||
are predictable for the debugger: when the debuggee runs, or when the
|
are predictable for the debugger: when the debuggee runs, or when the
|
||||||
debugger removes frames from the stack itself.
|
debugger removes frames from the stack itself.
|
||||||
|
|
||||||
Stack frames that represent the control state of generator-iterator objects
|
|
||||||
behave in a special way, described in [Generator Frames][generator] below.
|
|
||||||
|
|
||||||
|
|
||||||
## Visible Frames
|
## Visible Frames
|
||||||
|
|
||||||
|
@ -132,9 +129,7 @@ its prototype:
|
||||||
|
|
||||||
`older`
|
`older`
|
||||||
: The next-older visible frame, in which control will resume when this
|
: The next-older visible frame, in which control will resume when this
|
||||||
frame completes. If there is no older frame, this is `null`. (On a
|
frame completes. If there is no older frame, this is `null`.
|
||||||
suspended generator frame, the value of this property is `null`; see
|
|
||||||
[Generator Frames][generator].)
|
|
||||||
|
|
||||||
`depth`
|
`depth`
|
||||||
: The depth of this frame, counting from oldest to youngest; the oldest
|
: The depth of this frame, counting from oldest to youngest; the oldest
|
||||||
|
@ -142,8 +137,7 @@ its prototype:
|
||||||
|
|
||||||
`live`
|
`live`
|
||||||
: True if the frame this `Debugger.Frame` instance refers to is still on
|
: True if the frame this `Debugger.Frame` instance refers to is still on
|
||||||
the stack (or, in the case of generator-iterator objects, has not yet
|
the stack; false if it has completed execution or been
|
||||||
finished its iteration); false if it has completed execution or been
|
|
||||||
popped in some other way.
|
popped in some other way.
|
||||||
|
|
||||||
`script`
|
`script`
|
||||||
|
@ -254,11 +248,6 @@ the compartment to which the handler method belongs.
|
||||||
an `undefined` resumption value leaves the frame's throwing and
|
an `undefined` resumption value leaves the frame's throwing and
|
||||||
termination process undisturbed.
|
termination process undisturbed.
|
||||||
|
|
||||||
<i>(Not yet implemented.)</i> When a generator frame yields a value,
|
|
||||||
SpiderMonkey calls its `Debugger.Frame` instance's `onPop` handler
|
|
||||||
method, if present, passing a `yield` resumption value; however, the
|
|
||||||
`Debugger.Frame` instance remains live.
|
|
||||||
|
|
||||||
If multiple [`Debugger`][debugger-object] instances each have
|
If multiple [`Debugger`][debugger-object] instances each have
|
||||||
`Debugger.Frame` instances for a given stack frame with `onPop`
|
`Debugger.Frame` instances for a given stack frame with `onPop`
|
||||||
handlers set, their handlers are run in an unspecified order. The
|
handlers set, their handlers are run in an unspecified order. The
|
||||||
|
@ -269,17 +258,6 @@ the compartment to which the handler method belongs.
|
||||||
when unwinding a frame due to an over-recursion or out-of-memory
|
when unwinding a frame due to an over-recursion or out-of-memory
|
||||||
exception.
|
exception.
|
||||||
|
|
||||||
`onResume`
|
|
||||||
: This property must be either `undefined` or a function. If it is a
|
|
||||||
function, SpiderMonkey calls it if the current frame is a generator
|
|
||||||
frame whose execution has just been resumed. The function should return
|
|
||||||
a [resumption value][rv] indicating how execution should proceed. On
|
|
||||||
newly created frames, this property's value is `undefined`.
|
|
||||||
|
|
||||||
If the program resumed the generator by calling its `send` method and
|
|
||||||
passing a value, then <i>value</i> is that value. Otherwise,
|
|
||||||
<i>value</i> is `undefined`.
|
|
||||||
|
|
||||||
|
|
||||||
## Function Properties of the Debugger.Frame Prototype Object
|
## Function Properties of the Debugger.Frame Prototype Object
|
||||||
|
|
||||||
|
@ -291,7 +269,7 @@ methods of other kinds of objects.
|
||||||
: Evaluate <i>code</i> in the execution context of this frame, and return
|
: Evaluate <i>code</i> in the execution context of this frame, and return
|
||||||
a [completion value][cv] describing how it completed. <i>Code</i> is a
|
a [completion value][cv] describing how it completed. <i>Code</i> is a
|
||||||
string. If this frame's `environment` property is `null`, throw a
|
string. If this frame's `environment` property is `null`, throw a
|
||||||
`TypeError`. All extant handler methods, breakpoints, watchpoints, and
|
`TypeError`. All extant handler methods, breakpoints, and
|
||||||
so on remain active during the call. This function follows the
|
so on remain active during the call. This function follows the
|
||||||
[invocation function conventions][inv fr].
|
[invocation function conventions][inv fr].
|
||||||
|
|
||||||
|
@ -348,59 +326,3 @@ methods of other kinds of objects.
|
||||||
|
|
||||||
The <i>options</i> argument is as for
|
The <i>options</i> argument is as for
|
||||||
[`Debugger.Frame.prototype.eval`][fr eval], described above.
|
[`Debugger.Frame.prototype.eval`][fr eval], described above.
|
||||||
|
|
||||||
|
|
||||||
## Generator Frames
|
|
||||||
|
|
||||||
<i>Not all behavior described in this section has been implemented
|
|
||||||
yet.</i>
|
|
||||||
|
|
||||||
SpiderMonkey supports generator-iterator objects, which produce a series of
|
|
||||||
values by repeatedly suspending the execution of a function or expression.
|
|
||||||
For example, calling a function that uses `yield` produces a
|
|
||||||
generator-iterator object, as does evaluating a generator expression like
|
|
||||||
`(i*i for each (i in [1,2,3]))`.
|
|
||||||
|
|
||||||
A generator-iterator object refers to a stack frame with no fixed
|
|
||||||
continuation frame. While the generator's code is running, its continuation
|
|
||||||
is whatever frame called its `next` method; while the generator is
|
|
||||||
suspended, it has no particular continuation frame; and when it resumes
|
|
||||||
again, the continuation frame for that resumption could be different from
|
|
||||||
that of the previous resumption.
|
|
||||||
|
|
||||||
A `Debugger.Frame` instance representing a generator frame differs from an
|
|
||||||
ordinary stack frame as follows:
|
|
||||||
|
|
||||||
* A generator frame's `generator` property is true.
|
|
||||||
|
|
||||||
* A generator frame disappears from the stack each time the generator
|
|
||||||
yields a value and is suspended, and reappears atop the stack when it is
|
|
||||||
resumed to produce the generator's next value. The same `Debugger.Frame`
|
|
||||||
instance refers to the generator frame until it returns, throws an
|
|
||||||
exception, or is terminated.
|
|
||||||
|
|
||||||
* A generator frame's `older` property refers to the frame's continuation
|
|
||||||
frame while the generator is running, and is `null` while the generator
|
|
||||||
is suspended.
|
|
||||||
|
|
||||||
* A generator frame's `depth` property reflects the frame's position on
|
|
||||||
the stack when the generator is resumed, and is `null` while the
|
|
||||||
generator is suspended.
|
|
||||||
|
|
||||||
* A generator frame's `live` property remains true until the frame
|
|
||||||
returns, throws an exception, or is terminated. Thus, generator frames
|
|
||||||
can be live while not present on the stack.
|
|
||||||
|
|
||||||
The other `Debugger.Frame` methods and accessor properties work as
|
|
||||||
described on generator frames, even when the generator frame is suspended.
|
|
||||||
You may examine a suspended generator frame's variables, and use its
|
|
||||||
`script` and `offset` members to see which `yield` it is suspended at.
|
|
||||||
|
|
||||||
A `Debugger.Frame` instance referring to a generator-iterator frame has a
|
|
||||||
strong reference to the generator-iterator object; the frame (and its
|
|
||||||
object) will live as long as the `Debugger.Frame` instance does. However,
|
|
||||||
when the generator function returns, throws an exception, or is terminated,
|
|
||||||
thus ending the iteration, the `Debugger.Frame` instance becomes inactive
|
|
||||||
and its `live` property becomes `false`, just as would occur for any other
|
|
||||||
sort of frame that is popped. A non-live `Debugger.Frame` instance no
|
|
||||||
longer holds a strong reference to the generator-iterator object.
|
|
||||||
|
|
|
@ -456,7 +456,7 @@ code), the call throws a [`Debugger.DebuggeeWouldRun`][wouldrun] exception.
|
||||||
value, or `{ asConstructor: true }` to invoke the referent as a
|
value, or `{ asConstructor: true }` to invoke the referent as a
|
||||||
constructor, in which case SpiderMonkey provides an appropriate `this`
|
constructor, in which case SpiderMonkey provides an appropriate `this`
|
||||||
value itself. Each <i>argument</i> must be a debuggee value. All extant
|
value itself. Each <i>argument</i> must be a debuggee value. All extant
|
||||||
handler methods, breakpoints, watchpoints, and so on remain active
|
handler methods, breakpoints, and so on remain active
|
||||||
during the call. If the referent is not callable, throw a `TypeError`.
|
during the call. If the referent is not callable, throw a `TypeError`.
|
||||||
This function follows the [invocation function conventions][inv fr].
|
This function follows the [invocation function conventions][inv fr].
|
||||||
|
|
||||||
|
@ -469,7 +469,7 @@ code), the call throws a [`Debugger.DebuggeeWouldRun`][wouldrun] exception.
|
||||||
an appropriate `this` value itself. <i>Arguments</i> must either be an
|
an appropriate `this` value itself. <i>Arguments</i> must either be an
|
||||||
array (in the debugger) of debuggee values, or `null` or `undefined`,
|
array (in the debugger) of debuggee values, or `null` or `undefined`,
|
||||||
which are treated as an empty array. All extant handler methods,
|
which are treated as an empty array. All extant handler methods,
|
||||||
breakpoints, watchpoints, and so on remain active during the call. If
|
breakpoints, and so on remain active during the call. If
|
||||||
the referent is not callable, throw a `TypeError`. This function
|
the referent is not callable, throw a `TypeError`. This function
|
||||||
follows the [invocation function conventions][inv fr].
|
follows the [invocation function conventions][inv fr].
|
||||||
|
|
||||||
|
@ -477,7 +477,7 @@ code), the call throws a [`Debugger.DebuggeeWouldRun`][wouldrun] exception.
|
||||||
: If the referent is a global object, evaluate <i>code</i> in that global
|
: If the referent is a global object, evaluate <i>code</i> in that global
|
||||||
environment, and return a [completion value][cv] describing how it completed.
|
environment, and return a [completion value][cv] describing how it completed.
|
||||||
<i>Code</i> is a string. All extant handler methods, breakpoints,
|
<i>Code</i> is a string. All extant handler methods, breakpoints,
|
||||||
watchpoints, and so on remain active during the call. This function
|
and so on remain active during the call. This function
|
||||||
follows the [invocation function conventions][inv fr].
|
follows the [invocation function conventions][inv fr].
|
||||||
If the referent is not a global object, throw a `TypeError` exception.
|
If the referent is not a global object, throw a `TypeError` exception.
|
||||||
|
|
||||||
|
@ -522,96 +522,6 @@ code), the call throws a [`Debugger.DebuggeeWouldRun`][wouldrun] exception.
|
||||||
lexical scope's enclosing scope is the global object. If the referent is
|
lexical scope's enclosing scope is the global object. If the referent is
|
||||||
not a global object, throw a `TypeError`.
|
not a global object, throw a `TypeError`.
|
||||||
|
|
||||||
<code>setObjectWatchpoint(<i>handler</i>)</code> <i>(future plan)</i>
|
|
||||||
: Set a watchpoint on all the referent's own properties, reporting events
|
|
||||||
by calling <i>handler</i>'s methods. Any previous watchpoint handler on
|
|
||||||
this `Debugger.Object` instance is replaced. If <i>handler</i> is null,
|
|
||||||
the referent is no longer watched. <i>Handler</i> may have the following
|
|
||||||
methods, called under the given circumstances:
|
|
||||||
|
|
||||||
<code>add(<i>frame</i>, <i>name</i>, <i>descriptor</i>)</code>
|
|
||||||
: A property named <i>name</i> has been added to the referent.
|
|
||||||
<i>Descriptor</i> is a property descriptor of the sort accepted by
|
|
||||||
`Debugger.Object.prototype.defineProperty`, giving the newly added
|
|
||||||
property's attributes.
|
|
||||||
|
|
||||||
<code>delete(<i>frame</i>, <i>name</i>)</code>
|
|
||||||
: The property named <i>name</i> is about to be deleted from the referent.
|
|
||||||
|
|
||||||
<code>change(<i>frame</i>, <i>name</i>, <i>oldDescriptor</i>, <i>newDescriptor</i>)</code>
|
|
||||||
: The existing property named <i>name</i> on the referent is being changed
|
|
||||||
from those given by <i>oldDescriptor</i> to those given by
|
|
||||||
<i>newDescriptor</i>. This handler method is only called when attributes
|
|
||||||
of the property other than its value are being changed; if only the
|
|
||||||
value is changing, SpiderMonkey calls the handler's `set` method.
|
|
||||||
|
|
||||||
<code>set(<i>frame</i>, <i>oldValue</i>, <i>newValue</i>)</code>
|
|
||||||
: The data property named <i>name</i> of the referent is about to have its
|
|
||||||
value changed from <i>oldValue</i> to <i>newValue</i>.
|
|
||||||
|
|
||||||
SpiderMonkey only calls this method on assignments to data properties
|
|
||||||
that will succeed; assignments to un-writable data properties fail
|
|
||||||
without notifying the debugger.
|
|
||||||
|
|
||||||
<code>extensionsPrevented(<i>frame</i>)</code>
|
|
||||||
: The referent has been made non-extensible, as if by a call to
|
|
||||||
`Object.preventExtensions`.
|
|
||||||
|
|
||||||
For all watchpoint handler methods:
|
|
||||||
|
|
||||||
* Handler calls receive the handler object itself as the `this` value.
|
|
||||||
|
|
||||||
* The <i>frame</i> argument is the current stack frame, whose code is
|
|
||||||
about to perform the operation on the object being reported.
|
|
||||||
|
|
||||||
* If the method returns `undefined`, then SpiderMonkey makes the announced
|
|
||||||
change to the object, and continues execution normally. If the method
|
|
||||||
returns an object:
|
|
||||||
|
|
||||||
* If the object has a `superseded` property whose value is a true value,
|
|
||||||
then SpiderMonkey does not make the announced change.
|
|
||||||
|
|
||||||
* If the object has a `resume` property, its value is taken as a
|
|
||||||
[resumption value][rv], indicating how
|
|
||||||
execution should proceed. (However, `return` resumption values are not
|
|
||||||
supported.)
|
|
||||||
|
|
||||||
* If a given method is absent from <i>handler</i>, then events of that
|
|
||||||
sort are ignored. The watchpoint consults <i>handler</i>'s properties
|
|
||||||
each time an event occurs, so adding methods to or removing methods from
|
|
||||||
<i>handler</i> after setting the watchpoint enables or disables
|
|
||||||
reporting of the corresponding events.
|
|
||||||
|
|
||||||
* Values passed to <i>handler</i>'s methods are debuggee values.
|
|
||||||
Descriptors passed to <i>handler</i>'s methods are ordinary objects in
|
|
||||||
the debugger's compartment, except for `value`, `get`, and `set`
|
|
||||||
properties in descriptors, which are debuggee values; they are the sort
|
|
||||||
of value expected by `Debugger.Object.prototype.defineProperty`.
|
|
||||||
|
|
||||||
* Watchpoint handler calls are cross-compartment, intra-thread calls: the
|
|
||||||
call takes place in the same thread that changed the property, and in
|
|
||||||
<i>handler</i>'s method's compartment (typically the same as the
|
|
||||||
debugger's compartment).
|
|
||||||
|
|
||||||
The new watchpoint belongs to the [`Debugger`][debugger-object] instance to which this
|
|
||||||
`Debugger.Object` instance belongs; disabling the [`Debugger`][debugger-object] instance
|
|
||||||
disables this watchpoint.
|
|
||||||
|
|
||||||
`clearObjectWatchpoint()` <i>(future plan)</i>
|
|
||||||
: Remove any object watchpoint set on the referent.
|
|
||||||
|
|
||||||
<code>setPropertyWatchpoint(<i>name</i>, <i>handler</i>)</code> <i>(future plan)</i>
|
|
||||||
: Set a watchpoint on the referent's property named <i>name</i>, reporting
|
|
||||||
events by calling <i>handler</i>'s methods. Any previous watchpoint
|
|
||||||
handler on this property for this `Debugger.Object` instance is
|
|
||||||
replaced. If <i>handler</i> is null, the property is no longer watched.
|
|
||||||
<i>Handler</i> is as described for
|
|
||||||
`Debugger.Object.prototype.setObjectWatchpoint`, except that it does not
|
|
||||||
receive `extensionsPrevented` events.
|
|
||||||
|
|
||||||
<code>clearPropertyWatchpoint(<i>name</i>)</code> <i>(future plan)</i>
|
|
||||||
: Remove any watchpoint set on the referent's property named <i>name</i>.
|
|
||||||
|
|
||||||
`unwrap()`
|
`unwrap()`
|
||||||
: If the referent is a wrapper that this `Debugger.Object`'s compartment
|
: If the referent is a wrapper that this `Debugger.Object`'s compartment
|
||||||
is permitted to unwrap, return a `Debugger.Object` instance referring to
|
is permitted to unwrap, return a `Debugger.Object` instance referring to
|
||||||
|
|
|
@ -176,12 +176,6 @@ from its prototype:
|
||||||
|
|
||||||
**If the instance refers to WebAssembly code**, throw a `TypeError`.
|
**If the instance refers to WebAssembly code**, throw a `TypeError`.
|
||||||
|
|
||||||
`strictMode`
|
|
||||||
: **If the instance refers to a `JSScript`**, this is `true` if this
|
|
||||||
script's code is ECMAScript strict mode code, and `false` otherwise.
|
|
||||||
|
|
||||||
**If the instance refers to WebAssembly code**, throw a `TypeError`.
|
|
||||||
|
|
||||||
`format`
|
`format`
|
||||||
: **If the instance refers to a `JSScript`**, `"js"`.
|
: **If the instance refers to a `JSScript`**, `"js"`.
|
||||||
|
|
||||||
|
@ -193,20 +187,6 @@ The functions described below may only be called with a `this` value
|
||||||
referring to a `Debugger.Script` instance; they may not be used as
|
referring to a `Debugger.Script` instance; they may not be used as
|
||||||
methods of other kinds of objects.
|
methods of other kinds of objects.
|
||||||
|
|
||||||
<code>decompile([<i>pretty</i>])</code>
|
|
||||||
: **If the instance refers to a `JSScript`**, return a string containing
|
|
||||||
JavaScript source code equivalent to this script in its effect and
|
|
||||||
result. If <i>pretty</i> is present and true, produce indented code with
|
|
||||||
line breaks.
|
|
||||||
|
|
||||||
(Note that [`Debugger.Object`][object] instances referring to functions also have
|
|
||||||
a `decompile` method, whose result includes the function header and
|
|
||||||
parameter names, so it is probably better to write
|
|
||||||
<code><i>f</i>.decompile()</code> than to write
|
|
||||||
<code><i>f</i>.getFunctionScript().decompile()</code>.)
|
|
||||||
|
|
||||||
**If the instance refers to WebAssembly code**, throw a `TypeError`.
|
|
||||||
|
|
||||||
`getAllOffsets()`
|
`getAllOffsets()`
|
||||||
: **If the instance refers to a `JSScript`**, return an array <i>L</i>
|
: **If the instance refers to a `JSScript`**, return an array <i>L</i>
|
||||||
describing the relationship between bytecode instruction offsets and
|
describing the relationship between bytecode instruction offsets and
|
||||||
|
@ -325,8 +305,8 @@ methods of other kinds of objects.
|
||||||
|
|
||||||
`getChildScripts()`
|
`getChildScripts()`
|
||||||
: **If the instance refers to a `JSScript`**, return a new array whose
|
: **If the instance refers to a `JSScript`**, return a new array whose
|
||||||
elements are Debugger.Script objects for each function and each generator
|
elements are Debugger.Script objects for each function
|
||||||
expression in this script. Only direct children are included; nested
|
in this script. Only direct children are included; nested
|
||||||
children can be reached by walking the tree.
|
children can be reached by walking the tree.
|
||||||
|
|
||||||
**If the instance refers to WebAssembly code**, throw a `TypeError`.
|
**If the instance refers to WebAssembly code**, throw a `TypeError`.
|
||||||
|
|
|
@ -77,17 +77,6 @@ from its prototype:
|
||||||
representation. The format is yet to be specified in the WebAssembly
|
representation. The format is yet to be specified in the WebAssembly
|
||||||
standard. Currently, the text is an s-expression based syntax.
|
standard. Currently, the text is an s-expression based syntax.
|
||||||
|
|
||||||
`enclosingStart` <i>(future plan)</i>
|
|
||||||
: The position within the enclosing document at which this source's text
|
|
||||||
starts. This is a zero-based character offset. (The length of this
|
|
||||||
script within the enclosing document is `source.length`.)
|
|
||||||
|
|
||||||
`lineCount` <i>(future plan)</i>
|
|
||||||
: The number of lines in the source code. If there are characters after
|
|
||||||
the last newline in the source code, those count as a final line;
|
|
||||||
otherwise, `lineCount` is equal to the number of newlines in the source
|
|
||||||
code.
|
|
||||||
|
|
||||||
`url`
|
`url`
|
||||||
: **If the instance refers to JavaScript source**, the URL from which this
|
: **If the instance refers to JavaScript source**, the URL from which this
|
||||||
source was loaded, if this source was loaded from a URL. Otherwise, this
|
source was loaded, if this source was loaded from a URL. Otherwise, this
|
||||||
|
@ -229,43 +218,3 @@ from its prototype:
|
||||||
**If the instance refers to WebAssembly code**, `introductionScript` is
|
**If the instance refers to WebAssembly code**, `introductionScript` is
|
||||||
the [`Debugger.Script`][script] instance referring to the same underlying
|
the [`Debugger.Script`][script] instance referring to the same underlying
|
||||||
WebAssembly module. `introductionOffset` is `undefined`.
|
WebAssembly module. `introductionOffset` is `undefined`.
|
||||||
|
|
||||||
|
|
||||||
## Function Properties of the Debugger.Source Prototype Object
|
|
||||||
|
|
||||||
<code>substring(<i>start</i>, [<i>end</i>])</code> <i>(future plan)</i>
|
|
||||||
: Return a substring of this instance's `source` property, starting at
|
|
||||||
<i>start</i> and extending to, but not including, <i>end</i>. If
|
|
||||||
<i>end</i> is `undefined`, the substring returned extends to the end of
|
|
||||||
the source.
|
|
||||||
|
|
||||||
Both indices are zero-based. If either is `NaN` or negative, it is
|
|
||||||
replaced with zero. If either is greater than the length of the source,
|
|
||||||
it is replaced with the length of the source. If <i>start</i> is larger
|
|
||||||
than <i>end</i>, they are swapped. (This is meant to be consistent with
|
|
||||||
the way `String.prototype.substring` interprets its arguments.)
|
|
||||||
|
|
||||||
<code>lineToPosition(<i>line</i>)</code> <i>(future plan)</i>
|
|
||||||
: Return an object of the form
|
|
||||||
<code>{ line:<i>line</i>, start:<i>start</i>, length:<i>length</i> }</code>, where
|
|
||||||
<i>start</i> is the character index within `source` of the first
|
|
||||||
character of line number <i>line</i>, and <i>length</i> is the length of
|
|
||||||
that line in characters, including the final newline, if any. The first
|
|
||||||
line is numbered one. If <i>line</i> is negative or greater than the
|
|
||||||
number of lines in this `Debugger.Source` instance, then return `null`.
|
|
||||||
|
|
||||||
<code>positionToLine(<i>start</i>)</code> <i>(future plan)</i>
|
|
||||||
: Return an object of the form
|
|
||||||
<code>{ line:<i>line</i>, start:<i>start</i>, length:<i>length</i> }</code>, where
|
|
||||||
<i>line</i> is the line number containing the character position
|
|
||||||
<i>start</i>, and <i>length</i> is the length of that line in
|
|
||||||
characters, including the final newline, if any. The first line is
|
|
||||||
numbered one. If <i>start</i> is negative or greater than the length of
|
|
||||||
the source code, then return `null`.
|
|
||||||
|
|
||||||
<code>findScripts(<i>query</i>)</code> <i>(future plan)</i>
|
|
||||||
: Return an array of [`Debugger.Script`][script] instances for all debuggee scripts
|
|
||||||
matching <i>query</i> that are produced from this `Debugger.Source`
|
|
||||||
instance. Aside from the restriction to scripts produced from this
|
|
||||||
source, <i>query</i> is interpreted as for
|
|
||||||
`Debugger.prototype.findScripts`.
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ its prototype:
|
||||||
|
|
||||||
`enabled`
|
`enabled`
|
||||||
: A boolean value indicating whether this `Debugger` instance's handlers,
|
: A boolean value indicating whether this `Debugger` instance's handlers,
|
||||||
breakpoints, watchpoints, and the like are currently enabled. It is an
|
breakpoints, and the like are currently enabled. It is an
|
||||||
accessor property with a getter and setter: assigning to it enables or
|
accessor property with a getter and setter: assigning to it enables or
|
||||||
disables this `Debugger` instance; reading it produces true if the
|
disables this `Debugger` instance; reading it produces true if the
|
||||||
instance is enabled, or false otherwise. This property is initially
|
instance is enabled, or false otherwise. This property is initially
|
||||||
|
@ -57,7 +57,7 @@ its prototype:
|
||||||
|
|
||||||
`uncaughtExceptionHook`
|
`uncaughtExceptionHook`
|
||||||
: Either `null` or a function that SpiderMonkey calls when a call to a
|
: Either `null` or a function that SpiderMonkey calls when a call to a
|
||||||
debug event handler, breakpoint handler, watchpoint handler, or similar
|
debug event handler, breakpoint handler, or similar
|
||||||
function throws some exception, which we refer to as
|
function throws some exception, which we refer to as
|
||||||
<i>debugger-exception</i> here. Exceptions thrown in the debugger are
|
<i>debugger-exception</i> here. Exceptions thrown in the debugger are
|
||||||
not propagated to debuggee code; instead, SpiderMonkey calls this
|
not propagated to debuggee code; instead, SpiderMonkey calls this
|
||||||
|
@ -154,29 +154,6 @@ compartment.
|
||||||
SpiderMonkey only calls `onEnterFrame` to report
|
SpiderMonkey only calls `onEnterFrame` to report
|
||||||
[visible][vf], non-`"debugger"` frames.
|
[visible][vf], non-`"debugger"` frames.
|
||||||
|
|
||||||
<code>onThrow(<i>frame</i>, <i>value</i>) <i>(future plan)</i></code>
|
|
||||||
: The exception <i>value</i> is being thrown by <i>frame</i>, which is
|
|
||||||
running debuggee code. This method should return a
|
|
||||||
[resumption value][rv] specifying how the debuggee's execution should
|
|
||||||
proceed. If it returns `undefined`, the exception is thrown as normal.
|
|
||||||
|
|
||||||
A call to the `onThrow` handler is typically followed by one or more
|
|
||||||
calls to the `onExceptionUnwind` handler.
|
|
||||||
|
|
||||||
*(pending discussion)* If the debuggee executes
|
|
||||||
`try { throw 0; } finally { f(); }` and `f()` executes without error,
|
|
||||||
the `onThrow` handler is called only once. The debugger is not notified
|
|
||||||
when the exception is set aside in order to execute the `finally` block,
|
|
||||||
nor when it is restored after the `finally` block completes normally.
|
|
||||||
|
|
||||||
*(An alternative design here would be: onException(status, frame, value)
|
|
||||||
where status is one of the strings "throw", "unwind", "catch",
|
|
||||||
"finally", "rethrow". JS\_SaveExceptionState would trigger a "finally"
|
|
||||||
event, JS\_RestoreExceptionState would trigger a "rethrow",
|
|
||||||
JS\_ClearPendingException would trigger a "catch"; not sure what
|
|
||||||
JS\_DropExceptionState or a return/throw from a finally block should
|
|
||||||
do.)*
|
|
||||||
|
|
||||||
<code>onExceptionUnwind(<i>frame</i>, <i>value</i>)</code>
|
<code>onExceptionUnwind(<i>frame</i>, <i>value</i>)</code>
|
||||||
: The exception <i>value</i> has been thrown, and has propagated to
|
: The exception <i>value</i> has been thrown, and has propagated to
|
||||||
<i>frame</i>; <i>frame</i> is the youngest remaining stack frame, and is a
|
<i>frame</i>; <i>frame</i> is the youngest remaining stack frame, and is a
|
||||||
|
@ -468,9 +445,6 @@ other kinds of objects.
|
||||||
`clearAllBreakpoints()`
|
`clearAllBreakpoints()`
|
||||||
: Remove all breakpoints set using this `Debugger` instance.
|
: Remove all breakpoints set using this `Debugger` instance.
|
||||||
|
|
||||||
`clearAllWatchpoints()` <i>(future plan)</i>
|
|
||||||
: Clear all watchpoints owned by this `Debugger` instance.
|
|
||||||
|
|
||||||
`findAllGlobals()`
|
`findAllGlobals()`
|
||||||
: Return an array of [`Debugger.Object`][object] instances referring to all the
|
: Return an array of [`Debugger.Object`][object] instances referring to all the
|
||||||
global objects present in this JavaScript instance.
|
global objects present in this JavaScript instance.
|
||||||
|
|
|
@ -24,7 +24,6 @@ markdown Debugger.Environment.md Debugger-API/Debugger.Environment
|
||||||
markdown Debugger.Frame.md Debugger-API/Debugger.Frame
|
markdown Debugger.Frame.md Debugger-API/Debugger.Frame
|
||||||
label 'frame' "Debugger.Frame"
|
label 'frame' "Debugger.Frame"
|
||||||
label 'vf' '#visible-frames' "Debugger.Frame: Visible Frames"
|
label 'vf' '#visible-frames' "Debugger.Frame: Visible Frames"
|
||||||
label 'generator' '#generator-frames' "Debugger.Frame: Generator Frames"
|
|
||||||
label 'inv fr' '#invf' "Debugger.Frame: Invocation Frames"
|
label 'inv fr' '#invf' "Debugger.Frame: Invocation Frames"
|
||||||
label 'fr eval' '#eval' "Debugger.Frame: Eval"
|
label 'fr eval' '#eval' "Debugger.Frame: Eval"
|
||||||
|
|
||||||
|
|
|
@ -4461,6 +4461,13 @@ BytecodeEmitter::emitDestructuringLHS(ParseNode* target, DestructuringFlavor fla
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
BytecodeEmitter::emitConditionallyExecutedDestructuringLHS(ParseNode* target, DestructuringFlavor flav)
|
||||||
|
{
|
||||||
|
TDZCheckCache tdzCache(this);
|
||||||
|
return emitDestructuringLHS(target, flav);
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
BytecodeEmitter::emitIteratorNext(ParseNode* pn, bool allowSelfHosted)
|
BytecodeEmitter::emitIteratorNext(ParseNode* pn, bool allowSelfHosted)
|
||||||
{
|
{
|
||||||
|
@ -4513,6 +4520,76 @@ BytecodeEmitter::emitDestructuringOpsArrayHelper(ParseNode* pattern, Destructuri
|
||||||
MOZ_ASSERT(pattern->isArity(PN_LIST));
|
MOZ_ASSERT(pattern->isArity(PN_LIST));
|
||||||
MOZ_ASSERT(this->stackDepth != 0);
|
MOZ_ASSERT(this->stackDepth != 0);
|
||||||
|
|
||||||
|
// Here's pseudo code for |let [a, b, , c=y, ...d] = x;|
|
||||||
|
//
|
||||||
|
// let x, y;
|
||||||
|
// let a, b, c, d;
|
||||||
|
// let tmp, done, iter, result; // stack values
|
||||||
|
//
|
||||||
|
// iter = x[Symbol.iterator]();
|
||||||
|
//
|
||||||
|
// // ==== emitted by loop for a ====
|
||||||
|
// result = iter.next();
|
||||||
|
// done = result.done;
|
||||||
|
//
|
||||||
|
// if (done) {
|
||||||
|
// a = undefined;
|
||||||
|
//
|
||||||
|
// result = undefined;
|
||||||
|
// done = true;
|
||||||
|
// } else {
|
||||||
|
// a = result.value;
|
||||||
|
//
|
||||||
|
// // Do next element's .next() and .done access here
|
||||||
|
// result = iter.next();
|
||||||
|
// done = result.done;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // ==== emitted by loop for b ====
|
||||||
|
// if (done) {
|
||||||
|
// b = undefined;
|
||||||
|
//
|
||||||
|
// result = undefined;
|
||||||
|
// done = true;
|
||||||
|
// } else {
|
||||||
|
// b = result.value;
|
||||||
|
//
|
||||||
|
// result = iter.next();
|
||||||
|
// done = result.done;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // ==== emitted by loop for elision ====
|
||||||
|
// if (done) {
|
||||||
|
// result = undefined
|
||||||
|
// done = true
|
||||||
|
// } else {
|
||||||
|
// result.value;
|
||||||
|
//
|
||||||
|
// result = iter.next();
|
||||||
|
// done = result.done;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // ==== emitted by loop for c ====
|
||||||
|
// if (done) {
|
||||||
|
// c = y;
|
||||||
|
// } else {
|
||||||
|
// tmp = result.value;
|
||||||
|
// if (tmp === undefined)
|
||||||
|
// tmp = y;
|
||||||
|
// c = tmp;
|
||||||
|
//
|
||||||
|
// // Don't do next element's .next() and .done access if
|
||||||
|
// // this is the last non-spread element.
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // ==== emitted by loop for d ====
|
||||||
|
// if (done) {
|
||||||
|
// // Assing empty array when completed
|
||||||
|
// d = [];
|
||||||
|
// } else {
|
||||||
|
// d = [...iter];
|
||||||
|
// }
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Use an iterator to destructure the RHS, instead of index lookup. We
|
* Use an iterator to destructure the RHS, instead of index lookup. We
|
||||||
* must leave the *original* value on the stack.
|
* must leave the *original* value on the stack.
|
||||||
|
@ -4524,20 +4601,36 @@ BytecodeEmitter::emitDestructuringOpsArrayHelper(ParseNode* pattern, Destructuri
|
||||||
bool needToPopIterator = true;
|
bool needToPopIterator = true;
|
||||||
|
|
||||||
for (ParseNode* member = pattern->pn_head; member; member = member->pn_next) {
|
for (ParseNode* member = pattern->pn_head; member; member = member->pn_next) {
|
||||||
/*
|
bool isHead = member == pattern->pn_head;
|
||||||
* Now push the property name currently being matched, which is the
|
if (member->isKind(PNK_SPREAD)) {
|
||||||
* current property name "label" on the left of a colon in the object
|
JumpList beq;
|
||||||
* initializer.
|
JumpList end;
|
||||||
*/
|
unsigned noteIndex = -1;
|
||||||
ParseNode* pndefault = nullptr;
|
if (!isHead) {
|
||||||
ParseNode* elem = member;
|
// If spread is not the first element of the pattern,
|
||||||
if (elem->isKind(PNK_ASSIGN)) {
|
// iterator can already be completed.
|
||||||
pndefault = elem->pn_right;
|
if (!newSrcNote(SRC_IF_ELSE, ¬eIndex))
|
||||||
elem = elem->pn_left;
|
return false;
|
||||||
}
|
if (!emitJump(JSOP_IFEQ, &beq)) // ... OBJ? ITER
|
||||||
|
return false;
|
||||||
|
|
||||||
if (elem->isKind(PNK_SPREAD)) {
|
int32_t depth = stackDepth;
|
||||||
/* Create a new array with the rest of the iterator */
|
if (!emit1(JSOP_POP)) // ... OBJ?
|
||||||
|
return false;
|
||||||
|
if (!emitUint32Operand(JSOP_NEWARRAY, 0)) // ... OBJ? ARRAY
|
||||||
|
return false;
|
||||||
|
if (!emitConditionallyExecutedDestructuringLHS(member, flav)) // ... OBJ?
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!emitJump(JSOP_GOTO, &end))
|
||||||
|
return false;
|
||||||
|
if (!emitJumpTargetAndPatch(beq))
|
||||||
|
return false;
|
||||||
|
stackDepth = depth;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If iterator is not completed, create a new array with the rest
|
||||||
|
// of the iterator.
|
||||||
if (!emitUint32Operand(JSOP_NEWARRAY, 0)) // ... OBJ? ITER ARRAY
|
if (!emitUint32Operand(JSOP_NEWARRAY, 0)) // ... OBJ? ITER ARRAY
|
||||||
return false;
|
return false;
|
||||||
if (!emitNumberOp(0)) // ... OBJ? ITER ARRAY INDEX
|
if (!emitNumberOp(0)) // ... OBJ? ITER ARRAY INDEX
|
||||||
|
@ -4546,68 +4639,136 @@ BytecodeEmitter::emitDestructuringOpsArrayHelper(ParseNode* pattern, Destructuri
|
||||||
return false;
|
return false;
|
||||||
if (!emit1(JSOP_POP)) // ... OBJ? ARRAY
|
if (!emit1(JSOP_POP)) // ... OBJ? ARRAY
|
||||||
return false;
|
return false;
|
||||||
|
if (!emitConditionallyExecutedDestructuringLHS(member, flav)) // ... OBJ?
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!isHead) {
|
||||||
|
if (!emitJumpTargetAndPatch(end))
|
||||||
|
return false;
|
||||||
|
if (!setSrcNoteOffset(noteIndex, 0, end.offset - beq.offset))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
needToPopIterator = false;
|
needToPopIterator = false;
|
||||||
|
MOZ_ASSERT(!member->pn_next);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ParseNode* pndefault = nullptr;
|
||||||
|
ParseNode* subpattern = member;
|
||||||
|
if (subpattern->isKind(PNK_ASSIGN)) {
|
||||||
|
pndefault = subpattern->pn_right;
|
||||||
|
subpattern = subpattern->pn_left;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isElision = subpattern->isKind(PNK_ELISION);
|
||||||
|
bool hasNextNonSpread = member->pn_next && !member->pn_next->isKind(PNK_SPREAD);
|
||||||
|
bool hasNextSpread = member->pn_next && member->pn_next->isKind(PNK_SPREAD);
|
||||||
|
|
||||||
|
MOZ_ASSERT(!subpattern->isKind(PNK_SPREAD));
|
||||||
|
|
||||||
|
auto emitNext = [pattern](ExclusiveContext* cx, BytecodeEmitter* bce) {
|
||||||
|
if (!bce->emit1(JSOP_DUP)) // ... OBJ? ITER ITER
|
||||||
|
return false;
|
||||||
|
if (!bce->emitIteratorNext(pattern)) // ... OBJ? ITER RESULT
|
||||||
|
return false;
|
||||||
|
if (!bce->emit1(JSOP_DUP)) // ... OBJ? ITER RESULT RESULT
|
||||||
|
return false;
|
||||||
|
if (!bce->emitAtomOp(cx->names().done, JSOP_GETPROP)) // ... OBJ? ITER RESULT DONE?
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isHead) {
|
||||||
|
if (!emitNext(cx, this)) // ... OBJ? ITER RESULT DONE?
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned noteIndex;
|
||||||
|
if (!newSrcNote(SRC_IF_ELSE, ¬eIndex))
|
||||||
|
return false;
|
||||||
|
JumpList beq;
|
||||||
|
if (!emitJump(JSOP_IFEQ, &beq)) // ... OBJ? ITER RESULT
|
||||||
|
return false;
|
||||||
|
|
||||||
|
int32_t depth = stackDepth;
|
||||||
|
if (!emit1(JSOP_POP)) // ... OBJ? ITER
|
||||||
|
return false;
|
||||||
|
if (pndefault) {
|
||||||
|
// Emit only pndefault tree here, as undefined check in emitDefault
|
||||||
|
// should always be true.
|
||||||
|
if (!emitConditionallyExecutedTree(pndefault)) // ... OBJ? ITER VALUE
|
||||||
|
return false;
|
||||||
} else {
|
} else {
|
||||||
if (!emit1(JSOP_DUP)) // ... OBJ? ITER ITER
|
if (!isElision) {
|
||||||
|
if (!emit1(JSOP_UNDEFINED)) // ... OBJ? ITER UNDEFINED
|
||||||
|
return false;
|
||||||
|
if (!emit1(JSOP_NOP_DESTRUCTURING))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!isElision) {
|
||||||
|
if (!emitConditionallyExecutedDestructuringLHS(subpattern, flav)) // ... OBJ? ITER
|
||||||
return false;
|
return false;
|
||||||
if (!emitIteratorNext(pattern)) // ... OBJ? ITER RESULT
|
} else if (pndefault) {
|
||||||
return false;
|
|
||||||
if (!emit1(JSOP_DUP)) // ... OBJ? ITER RESULT RESULT
|
|
||||||
return false;
|
|
||||||
if (!emitAtomOp(cx->names().done, JSOP_GETPROP)) // ... OBJ? ITER RESULT DONE?
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Emit (result.done ? undefined : result.value)
|
|
||||||
// This is mostly copied from emitConditionalExpression, except that this code
|
|
||||||
// does not push new values onto the stack.
|
|
||||||
unsigned noteIndex;
|
|
||||||
if (!newSrcNote(SRC_COND, ¬eIndex))
|
|
||||||
return false;
|
|
||||||
JumpList beq;
|
|
||||||
if (!emitJump(JSOP_IFEQ, &beq))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!emit1(JSOP_POP)) // ... OBJ? ITER
|
if (!emit1(JSOP_POP)) // ... OBJ? ITER
|
||||||
return false;
|
return false;
|
||||||
if (!emit1(JSOP_UNDEFINED)) // ... OBJ? ITER UNDEFINED
|
}
|
||||||
|
|
||||||
|
// Setup next element's result when the iterator is done.
|
||||||
|
if (hasNextNonSpread) {
|
||||||
|
if (!emit1(JSOP_UNDEFINED)) // ... OBJ? ITER RESULT
|
||||||
return false;
|
return false;
|
||||||
if (!emit1(JSOP_NOP_DESTRUCTURING))
|
if (!emit1(JSOP_NOP_DESTRUCTURING))
|
||||||
return false;
|
return false;
|
||||||
|
if (!emit1(JSOP_TRUE)) // ... OBJ? ITER RESULT DONE?
|
||||||
/* Jump around else, fixup the branch, emit else, fixup jump. */
|
|
||||||
JumpList jmp;
|
|
||||||
if (!emitJump(JSOP_GOTO, &jmp))
|
|
||||||
return false;
|
return false;
|
||||||
if (!emitJumpTargetAndPatch(beq))
|
} else if (hasNextSpread) {
|
||||||
return false;
|
if (!emit1(JSOP_TRUE)) // ... OBJ? ITER DONE?
|
||||||
|
|
||||||
if (!emitAtomOp(cx->names().value, JSOP_GETPROP)) // ... OBJ? ITER VALUE
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!emitJumpTargetAndPatch(jmp))
|
|
||||||
return false;
|
|
||||||
if (!setSrcNoteOffset(noteIndex, 0, jmp.offset - beq.offset))
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pndefault && !emitDefault(pndefault))
|
JumpList end;
|
||||||
|
if (!emitJump(JSOP_GOTO, &end))
|
||||||
|
return false;
|
||||||
|
if (!emitJumpTargetAndPatch(beq))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Destructure into the pattern the element contains.
|
stackDepth = depth;
|
||||||
ParseNode* subpattern = elem;
|
if (!emitAtomOp(cx->names().value, JSOP_GETPROP)) // ... OBJ? ITER VALUE
|
||||||
if (subpattern->isKind(PNK_ELISION)) {
|
return false;
|
||||||
// The value destructuring into an elision just gets ignored.
|
|
||||||
if (!emit1(JSOP_POP)) // ... OBJ? ITER
|
if (pndefault) {
|
||||||
|
if (!emitDefault(pndefault)) // ... OBJ? ITER VALUE
|
||||||
return false;
|
return false;
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!emitDestructuringLHS(subpattern, flav))
|
if (!isElision) {
|
||||||
|
if (!emitConditionallyExecutedDestructuringLHS(subpattern, flav)) // ... OBJ? ITER
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
if (!emit1(JSOP_POP)) // ... OBJ? ITER
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup next element's result when the iterator is not done.
|
||||||
|
if (hasNextNonSpread) {
|
||||||
|
if (!emitNext(cx, this)) // ... OBJ? ITER RESULT DONE?
|
||||||
|
return false;
|
||||||
|
} else if (hasNextSpread) {
|
||||||
|
if (!emit1(JSOP_FALSE)) // ... OBJ? ITER DONE?
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!emitJumpTargetAndPatch(end))
|
||||||
|
return false;
|
||||||
|
if (!setSrcNoteOffset(noteIndex, 0, end.offset - beq.offset))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (needToPopIterator && !emit1(JSOP_POP))
|
if (needToPopIterator) {
|
||||||
return false;
|
if (!emit1(JSOP_POP)) // ... OBJ?
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -6674,13 +6835,14 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto)
|
||||||
RootedFunction fun(cx, funbox->function());
|
RootedFunction fun(cx, funbox->function());
|
||||||
RootedAtom name(cx, fun->name());
|
RootedAtom name(cx, fun->name());
|
||||||
MOZ_ASSERT_IF(fun->isInterpretedLazy(), fun->lazyScript());
|
MOZ_ASSERT_IF(fun->isInterpretedLazy(), fun->lazyScript());
|
||||||
|
MOZ_ASSERT_IF(pn->isOp(JSOP_FUNWITHPROTO), needsProto);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set the |wasEmitted| flag in the funbox once the function has been
|
* Set the |wasEmitted| flag in the funbox once the function has been
|
||||||
* emitted. Function definitions that need hoisting to the top of the
|
* emitted. Function definitions that need hoisting to the top of the
|
||||||
* function will be seen by emitFunction in two places.
|
* function will be seen by emitFunction in two places.
|
||||||
*/
|
*/
|
||||||
if (funbox->wasEmitted) {
|
if (funbox->wasEmitted && pn->functionIsHoisted()) {
|
||||||
// Annex B block-scoped functions are hoisted like any other
|
// Annex B block-scoped functions are hoisted like any other
|
||||||
// block-scoped function to the top of their scope. When their
|
// block-scoped function to the top of their scope. When their
|
||||||
// definitions are seen for the second time, we need to emit the
|
// definitions are seen for the second time, we need to emit the
|
||||||
|
@ -6804,7 +6966,7 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (needsProto) {
|
if (needsProto) {
|
||||||
MOZ_ASSERT(pn->getOp() == JSOP_LAMBDA);
|
MOZ_ASSERT(pn->getOp() == JSOP_FUNWITHPROTO || pn->getOp() == JSOP_LAMBDA);
|
||||||
pn->setOp(JSOP_FUNWITHPROTO);
|
pn->setOp(JSOP_FUNWITHPROTO);
|
||||||
}
|
}
|
||||||
return emitIndex32(pn->getOp(), index);
|
return emitIndex32(pn->getOp(), index);
|
||||||
|
@ -9651,6 +9813,15 @@ CGConstList::finish(ConstArray* array)
|
||||||
array->vector[i] = list[i];
|
array->vector[i] = list[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
CGObjectList::isAdded(ObjectBox* objbox)
|
||||||
|
{
|
||||||
|
// An objbox added to CGObjectList as non-first element has non-null
|
||||||
|
// emitLink member. The first element has null emitLink.
|
||||||
|
// Check for firstbox to cover the first element.
|
||||||
|
return objbox->emitLink || objbox == firstbox;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Find the index of the given object for code generator.
|
* Find the index of the given object for code generator.
|
||||||
*
|
*
|
||||||
|
@ -9662,9 +9833,15 @@ CGConstList::finish(ConstArray* array)
|
||||||
unsigned
|
unsigned
|
||||||
CGObjectList::add(ObjectBox* objbox)
|
CGObjectList::add(ObjectBox* objbox)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(!objbox->emitLink);
|
if (isAdded(objbox))
|
||||||
|
return indexOf(objbox->object);
|
||||||
|
|
||||||
objbox->emitLink = lastbox;
|
objbox->emitLink = lastbox;
|
||||||
lastbox = objbox;
|
lastbox = objbox;
|
||||||
|
|
||||||
|
// See the comment in CGObjectList::isAdded.
|
||||||
|
if (!firstbox)
|
||||||
|
firstbox = objbox;
|
||||||
return length++;
|
return length++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9691,7 +9868,12 @@ CGObjectList::finish(ObjectArray* array)
|
||||||
MOZ_ASSERT(!*cursor);
|
MOZ_ASSERT(!*cursor);
|
||||||
MOZ_ASSERT(objbox->object->isTenured());
|
MOZ_ASSERT(objbox->object->isTenured());
|
||||||
*cursor = objbox->object;
|
*cursor = objbox->object;
|
||||||
} while ((objbox = objbox->emitLink) != nullptr);
|
|
||||||
|
ObjectBox* tmp = objbox->emitLink;
|
||||||
|
// Clear emitLink for CGObjectList::isAdded.
|
||||||
|
objbox->emitLink = nullptr;
|
||||||
|
objbox = tmp;
|
||||||
|
} while (objbox != nullptr);
|
||||||
MOZ_ASSERT(cursor == array->vector);
|
MOZ_ASSERT(cursor == array->vector);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,10 +43,12 @@ class CGConstList {
|
||||||
|
|
||||||
struct CGObjectList {
|
struct CGObjectList {
|
||||||
uint32_t length; /* number of emitted so far objects */
|
uint32_t length; /* number of emitted so far objects */
|
||||||
|
ObjectBox* firstbox; /* first emitted object */
|
||||||
ObjectBox* lastbox; /* last emitted object */
|
ObjectBox* lastbox; /* last emitted object */
|
||||||
|
|
||||||
CGObjectList() : length(0), lastbox(nullptr) {}
|
CGObjectList() : length(0), firstbox(nullptr), lastbox(nullptr) {}
|
||||||
|
|
||||||
|
bool isAdded(ObjectBox* objbox);
|
||||||
unsigned add(ObjectBox* objbox);
|
unsigned add(ObjectBox* objbox);
|
||||||
unsigned indexOf(JSObject* obj);
|
unsigned indexOf(JSObject* obj);
|
||||||
void finish(ObjectArray* array);
|
void finish(ObjectArray* array);
|
||||||
|
@ -643,6 +645,8 @@ struct MOZ_STACK_CLASS BytecodeEmitter
|
||||||
// the stack and emits code to destructure a single lhs expression (either a
|
// the stack and emits code to destructure a single lhs expression (either a
|
||||||
// name or a compound []/{} expression).
|
// name or a compound []/{} expression).
|
||||||
MOZ_MUST_USE bool emitDestructuringLHS(ParseNode* target, DestructuringFlavor flav);
|
MOZ_MUST_USE bool emitDestructuringLHS(ParseNode* target, DestructuringFlavor flav);
|
||||||
|
MOZ_MUST_USE bool emitConditionallyExecutedDestructuringLHS(ParseNode* target,
|
||||||
|
DestructuringFlavor flav);
|
||||||
|
|
||||||
MOZ_MUST_USE bool emitDestructuringOps(ParseNode* pattern, DestructuringFlavor flav);
|
MOZ_MUST_USE bool emitDestructuringOps(ParseNode* pattern, DestructuringFlavor flav);
|
||||||
MOZ_MUST_USE bool emitDestructuringOpsHelper(ParseNode* pattern, DestructuringFlavor flav);
|
MOZ_MUST_USE bool emitDestructuringOpsHelper(ParseNode* pattern, DestructuringFlavor flav);
|
||||||
|
|
|
@ -635,12 +635,14 @@ class ParseNode
|
||||||
MOZ_ASSERT(pn_arity == PN_CODE && getKind() == PNK_FUNCTION);
|
MOZ_ASSERT(pn_arity == PN_CODE && getKind() == PNK_FUNCTION);
|
||||||
MOZ_ASSERT(isOp(JSOP_LAMBDA) || // lambda, genexpr
|
MOZ_ASSERT(isOp(JSOP_LAMBDA) || // lambda, genexpr
|
||||||
isOp(JSOP_LAMBDA_ARROW) || // arrow function
|
isOp(JSOP_LAMBDA_ARROW) || // arrow function
|
||||||
|
isOp(JSOP_FUNWITHPROTO) || // already emitted lambda with needsProto
|
||||||
isOp(JSOP_DEFFUN) || // non-body-level function statement
|
isOp(JSOP_DEFFUN) || // non-body-level function statement
|
||||||
isOp(JSOP_NOP) || // body-level function stmt in global code
|
isOp(JSOP_NOP) || // body-level function stmt in global code
|
||||||
isOp(JSOP_GETLOCAL) || // body-level function stmt in function code
|
isOp(JSOP_GETLOCAL) || // body-level function stmt in function code
|
||||||
isOp(JSOP_GETARG) || // body-level function redeclaring formal
|
isOp(JSOP_GETARG) || // body-level function redeclaring formal
|
||||||
isOp(JSOP_INITLEXICAL)); // block-level function stmt
|
isOp(JSOP_INITLEXICAL)); // block-level function stmt
|
||||||
return !isOp(JSOP_LAMBDA) && !isOp(JSOP_LAMBDA_ARROW) && !isOp(JSOP_DEFFUN);
|
return !isOp(JSOP_LAMBDA) && !isOp(JSOP_LAMBDA_ARROW) &&
|
||||||
|
!isOp(JSOP_FUNWITHPROTO) && !isOp(JSOP_DEFFUN);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -28,6 +28,10 @@ class ArenaCellSet;
|
||||||
* GC's remembered set. Entries in the store buffer that cannot be represented
|
* GC's remembered set. Entries in the store buffer that cannot be represented
|
||||||
* with the simple pointer-to-a-pointer scheme must derive from this class and
|
* with the simple pointer-to-a-pointer scheme must derive from this class and
|
||||||
* use the generic store buffer interface.
|
* use the generic store buffer interface.
|
||||||
|
*
|
||||||
|
* A single BufferableRef entry in the generic buffer can represent many entries
|
||||||
|
* in the remembered set. For example js::OrderedHashTableRef represents all
|
||||||
|
* the incoming edges corresponding to keys in an ordered hash table.
|
||||||
*/
|
*/
|
||||||
class BufferableRef
|
class BufferableRef
|
||||||
{
|
{
|
||||||
|
|
|
@ -39,10 +39,10 @@ function assertIterable(expectCalls, fn, expectResult) {
|
||||||
assertIterable([1,1,1,1],
|
assertIterable([1,1,1,1],
|
||||||
it => { var [a] = it; return [a]; },
|
it => { var [a] = it; return [a]; },
|
||||||
[1]);
|
[1]);
|
||||||
assertIterable([3,3,1,1],
|
assertIterable([2,2,1,1],
|
||||||
it => { var [a,b,c] = it; return [a,b,c]; },
|
it => { var [a,b,c] = it; return [a,b,c]; },
|
||||||
[1,undefined,undefined]);
|
[1,undefined,undefined]);
|
||||||
assertIterable([3,3,1,1],
|
assertIterable([2,2,1,1],
|
||||||
it => { var [a,b,...rest] = it; return [a,b,...rest]; },
|
it => { var [a,b,...rest] = it; return [a,b,...rest]; },
|
||||||
[1,undefined]);
|
[1,undefined]);
|
||||||
assertIterable([5,5,4,4],
|
assertIterable([5,5,4,4],
|
||||||
|
|
|
@ -31,18 +31,26 @@ function assertBuiltinFunction(o, name, arity) {
|
||||||
|
|
||||||
assertEq(typeof fn, "function");
|
assertEq(typeof fn, "function");
|
||||||
assertEq(Object.getPrototypeOf(fn), Function.prototype);
|
assertEq(Object.getPrototypeOf(fn), Function.prototype);
|
||||||
// FIXME: Proxy should only have [[Construct]] if target has [[Construct]] (bug 929467)
|
assertEq(isConstructor(fn), false);
|
||||||
// assertEq(isConstructor(fn), false);
|
|
||||||
|
|
||||||
arraysEqual(Object.getOwnPropertyNames(fn).sort(), ["length", "name", "arguments", "caller"].sort());
|
assertEq(arraysEqual(Object.getOwnPropertyNames(fn).sort(), ["length", "name"].sort()), true);
|
||||||
|
|
||||||
// Also test "name", "arguments" and "caller" in addition to "length"?
|
|
||||||
assertDataDescriptor(Object.getOwnPropertyDescriptor(fn, "length"), {
|
assertDataDescriptor(Object.getOwnPropertyDescriptor(fn, "length"), {
|
||||||
value: arity,
|
value: arity,
|
||||||
writable: false,
|
writable: false,
|
||||||
enumerable: false,
|
enumerable: false,
|
||||||
configurable: true
|
configurable: true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var functionName = typeof name === "symbol"
|
||||||
|
? String(name).replace(/^Symbol\((.+)\)$/, "[$1]")
|
||||||
|
: name;
|
||||||
|
assertDataDescriptor(Object.getOwnPropertyDescriptor(fn, "name"), {
|
||||||
|
value: functionName,
|
||||||
|
writable: false,
|
||||||
|
enumerable: false,
|
||||||
|
configurable: true
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -59,7 +67,7 @@ assertEq(Object.getPrototypeOf(iterProto),
|
||||||
Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]())));
|
Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]())));
|
||||||
|
|
||||||
// Own properties for StringIterator.prototype: "next"
|
// Own properties for StringIterator.prototype: "next"
|
||||||
arraysEqual(Object.getOwnPropertyNames(iterProto).sort(), ["next"]);
|
assertEq(arraysEqual(Object.getOwnPropertyNames(iterProto).sort(), ["next"]), true);
|
||||||
|
|
||||||
// StringIterator.prototype.next is a built-in function
|
// StringIterator.prototype.next is a built-in function
|
||||||
assertBuiltinFunction(iterProto, "next", 0);
|
assertBuiltinFunction(iterProto, "next", 0);
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
gczeal(0);
|
||||||
|
|
||||||
var values = {
|
var values = {
|
||||||
input1: null,
|
input1: null,
|
||||||
input2: undefined,
|
input2: undefined,
|
||||||
|
|
|
@ -728,7 +728,7 @@ class MacroAssembler : public MacroAssemblerSpecific
|
||||||
inline void orPtr(Register src, Register dest) PER_ARCH;
|
inline void orPtr(Register src, Register dest) PER_ARCH;
|
||||||
inline void orPtr(Imm32 imm, Register dest) PER_ARCH;
|
inline void orPtr(Imm32 imm, Register dest) PER_ARCH;
|
||||||
|
|
||||||
inline void and64(Register64 src, Register64 dest) DEFINED_ON(x86, x64, arm);
|
inline void and64(Register64 src, Register64 dest) DEFINED_ON(x86, x64, arm, mips32, mips64);
|
||||||
inline void or64(Register64 src, Register64 dest) PER_ARCH;
|
inline void or64(Register64 src, Register64 dest) PER_ARCH;
|
||||||
inline void xor64(Register64 src, Register64 dest) PER_ARCH;
|
inline void xor64(Register64 src, Register64 dest) PER_ARCH;
|
||||||
|
|
||||||
|
@ -738,9 +738,9 @@ class MacroAssembler : public MacroAssemblerSpecific
|
||||||
inline void xorPtr(Register src, Register dest) PER_ARCH;
|
inline void xorPtr(Register src, Register dest) PER_ARCH;
|
||||||
inline void xorPtr(Imm32 imm, Register dest) PER_ARCH;
|
inline void xorPtr(Imm32 imm, Register dest) PER_ARCH;
|
||||||
|
|
||||||
inline void and64(const Operand& src, Register64 dest) DEFINED_ON(x64);
|
inline void and64(const Operand& src, Register64 dest) DEFINED_ON(x64, mips64);
|
||||||
inline void or64(const Operand& src, Register64 dest) DEFINED_ON(x64);
|
inline void or64(const Operand& src, Register64 dest) DEFINED_ON(x64, mips64);
|
||||||
inline void xor64(const Operand& src, Register64 dest) DEFINED_ON(x64);
|
inline void xor64(const Operand& src, Register64 dest) DEFINED_ON(x64, mips64);
|
||||||
|
|
||||||
// ===============================================================
|
// ===============================================================
|
||||||
// Arithmetic functions
|
// Arithmetic functions
|
||||||
|
@ -762,8 +762,8 @@ class MacroAssembler : public MacroAssemblerSpecific
|
||||||
|
|
||||||
inline void add64(Register64 src, Register64 dest) PER_ARCH;
|
inline void add64(Register64 src, Register64 dest) PER_ARCH;
|
||||||
inline void add64(Imm32 imm, Register64 dest) PER_ARCH;
|
inline void add64(Imm32 imm, Register64 dest) PER_ARCH;
|
||||||
inline void add64(Imm64 imm, Register64 dest) DEFINED_ON(x86, x64, arm);
|
inline void add64(Imm64 imm, Register64 dest) DEFINED_ON(x86, x64, arm, mips32, mips64);
|
||||||
inline void add64(const Operand& src, Register64 dest) DEFINED_ON(x64);
|
inline void add64(const Operand& src, Register64 dest) DEFINED_ON(x64, mips64);
|
||||||
|
|
||||||
inline void addFloat32(FloatRegister src, FloatRegister dest) PER_SHARED_ARCH;
|
inline void addFloat32(FloatRegister src, FloatRegister dest) PER_SHARED_ARCH;
|
||||||
|
|
||||||
|
@ -780,9 +780,9 @@ class MacroAssembler : public MacroAssemblerSpecific
|
||||||
inline void subPtr(ImmWord imm, Register dest) DEFINED_ON(x64);
|
inline void subPtr(ImmWord imm, Register dest) DEFINED_ON(x64);
|
||||||
inline void subPtr(const Address& addr, Register dest) DEFINED_ON(mips_shared, arm, arm64, x86, x64);
|
inline void subPtr(const Address& addr, Register dest) DEFINED_ON(mips_shared, arm, arm64, x86, x64);
|
||||||
|
|
||||||
inline void sub64(Register64 src, Register64 dest) DEFINED_ON(x86, x64, arm);
|
inline void sub64(Register64 src, Register64 dest) DEFINED_ON(x86, x64, arm, mips32, mips64);
|
||||||
inline void sub64(Imm64 imm, Register64 dest) DEFINED_ON(x86, x64, arm);
|
inline void sub64(Imm64 imm, Register64 dest) DEFINED_ON(x86, x64, arm, mips32, mips64);
|
||||||
inline void sub64(const Operand& src, Register64 dest) DEFINED_ON(x64);
|
inline void sub64(const Operand& src, Register64 dest) DEFINED_ON(x64, mips64);
|
||||||
|
|
||||||
inline void subFloat32(FloatRegister src, FloatRegister dest) PER_SHARED_ARCH;
|
inline void subFloat32(FloatRegister src, FloatRegister dest) PER_SHARED_ARCH;
|
||||||
|
|
||||||
|
@ -795,12 +795,12 @@ class MacroAssembler : public MacroAssemblerSpecific
|
||||||
|
|
||||||
inline void mul64(const Operand& src, const Register64& dest) DEFINED_ON(x64);
|
inline void mul64(const Operand& src, const Register64& dest) DEFINED_ON(x64);
|
||||||
inline void mul64(const Operand& src, const Register64& dest, const Register temp)
|
inline void mul64(const Operand& src, const Register64& dest, const Register temp)
|
||||||
DEFINED_ON(x64);
|
DEFINED_ON(x64, mips64);
|
||||||
inline void mul64(Imm64 imm, const Register64& dest) PER_ARCH;
|
inline void mul64(Imm64 imm, const Register64& dest) PER_ARCH;
|
||||||
inline void mul64(Imm64 imm, const Register64& dest, const Register temp)
|
inline void mul64(Imm64 imm, const Register64& dest, const Register temp)
|
||||||
DEFINED_ON(x86, x64, arm);
|
DEFINED_ON(x86, x64, arm, mips32, mips64);
|
||||||
inline void mul64(const Register64& src, const Register64& dest, const Register temp)
|
inline void mul64(const Register64& src, const Register64& dest, const Register temp)
|
||||||
DEFINED_ON(x86, x64, arm);
|
DEFINED_ON(x86, x64, arm, mips32, mips64);
|
||||||
|
|
||||||
inline void mulBy3(Register src, Register dest) PER_ARCH;
|
inline void mulBy3(Register src, Register dest) PER_ARCH;
|
||||||
|
|
||||||
|
@ -832,7 +832,7 @@ class MacroAssembler : public MacroAssemblerSpecific
|
||||||
inline void dec32(RegisterOrInt32Constant* key);
|
inline void dec32(RegisterOrInt32Constant* key);
|
||||||
|
|
||||||
inline void neg32(Register reg) PER_SHARED_ARCH;
|
inline void neg32(Register reg) PER_SHARED_ARCH;
|
||||||
inline void neg64(Register64 reg) DEFINED_ON(x86, x64, arm);
|
inline void neg64(Register64 reg) DEFINED_ON(x86, x64, arm, mips32, mips64);
|
||||||
|
|
||||||
inline void negateFloat(FloatRegister reg) PER_SHARED_ARCH;
|
inline void negateFloat(FloatRegister reg) PER_SHARED_ARCH;
|
||||||
|
|
||||||
|
@ -875,16 +875,20 @@ class MacroAssembler : public MacroAssemblerSpecific
|
||||||
|
|
||||||
inline void lshift64(Imm32 imm, Register64 dest) PER_ARCH;
|
inline void lshift64(Imm32 imm, Register64 dest) PER_ARCH;
|
||||||
inline void rshift64(Imm32 imm, Register64 dest) PER_ARCH;
|
inline void rshift64(Imm32 imm, Register64 dest) PER_ARCH;
|
||||||
inline void rshift64Arithmetic(Imm32 imm, Register64 dest) DEFINED_ON(x86, x64, arm);
|
inline void rshift64Arithmetic(Imm32 imm, Register64 dest)
|
||||||
|
DEFINED_ON(x86, x64, arm, mips32, mips64);
|
||||||
|
|
||||||
// On x86_shared these have the constraint that shift must be in CL.
|
// On x86_shared these have the constraint that shift must be in CL.
|
||||||
inline void lshift32(Register shift, Register srcDest) PER_SHARED_ARCH;
|
inline void lshift32(Register shift, Register srcDest) PER_SHARED_ARCH;
|
||||||
inline void rshift32(Register shift, Register srcDest) PER_SHARED_ARCH;
|
inline void rshift32(Register shift, Register srcDest) PER_SHARED_ARCH;
|
||||||
inline void rshift32Arithmetic(Register shift, Register srcDest) PER_SHARED_ARCH;
|
inline void rshift32Arithmetic(Register shift, Register srcDest) PER_SHARED_ARCH;
|
||||||
|
|
||||||
inline void lshift64(Register shift, Register64 srcDest) DEFINED_ON(x86, x64, arm);
|
inline void lshift64(Register shift, Register64 srcDest)
|
||||||
inline void rshift64(Register shift, Register64 srcDest) DEFINED_ON(x86, x64, arm);
|
DEFINED_ON(x86, x64, arm, mips32, mips64);
|
||||||
inline void rshift64Arithmetic(Register shift, Register64 srcDest) DEFINED_ON(x86, x64, arm);
|
inline void rshift64(Register shift, Register64 srcDest)
|
||||||
|
DEFINED_ON(x86, x64, arm, mips32, mips64);
|
||||||
|
inline void rshift64Arithmetic(Register shift, Register64 srcDest)
|
||||||
|
DEFINED_ON(x86, x64, arm, mips32, mips64);
|
||||||
|
|
||||||
// ===============================================================
|
// ===============================================================
|
||||||
// Rotation functions
|
// Rotation functions
|
||||||
|
@ -896,18 +900,18 @@ class MacroAssembler : public MacroAssemblerSpecific
|
||||||
inline void rotateLeft64(Imm32 count, Register64 input, Register64 dest) DEFINED_ON(x64);
|
inline void rotateLeft64(Imm32 count, Register64 input, Register64 dest) DEFINED_ON(x64);
|
||||||
inline void rotateLeft64(Register count, Register64 input, Register64 dest) DEFINED_ON(x64);
|
inline void rotateLeft64(Register count, Register64 input, Register64 dest) DEFINED_ON(x64);
|
||||||
inline void rotateLeft64(Imm32 count, Register64 input, Register64 dest, Register temp)
|
inline void rotateLeft64(Imm32 count, Register64 input, Register64 dest, Register temp)
|
||||||
DEFINED_ON(x86, x64, arm);
|
DEFINED_ON(x86, x64, arm, mips32, mips64);
|
||||||
inline void rotateLeft64(Register count, Register64 input, Register64 dest, Register temp)
|
inline void rotateLeft64(Register count, Register64 input, Register64 dest, Register temp)
|
||||||
DEFINED_ON(x86, x64, arm);
|
DEFINED_ON(x86, x64, arm, mips32, mips64);
|
||||||
|
|
||||||
inline void rotateRight(Imm32 count, Register input, Register dest) PER_SHARED_ARCH;
|
inline void rotateRight(Imm32 count, Register input, Register dest) PER_SHARED_ARCH;
|
||||||
inline void rotateRight(Register count, Register input, Register dest) PER_SHARED_ARCH;
|
inline void rotateRight(Register count, Register input, Register dest) PER_SHARED_ARCH;
|
||||||
inline void rotateRight64(Imm32 count, Register64 input, Register64 dest) DEFINED_ON(x64);
|
inline void rotateRight64(Imm32 count, Register64 input, Register64 dest) DEFINED_ON(x64);
|
||||||
inline void rotateRight64(Register count, Register64 input, Register64 dest) DEFINED_ON(x64);
|
inline void rotateRight64(Register count, Register64 input, Register64 dest) DEFINED_ON(x64);
|
||||||
inline void rotateRight64(Imm32 count, Register64 input, Register64 dest, Register temp)
|
inline void rotateRight64(Imm32 count, Register64 input, Register64 dest, Register temp)
|
||||||
DEFINED_ON(x86, x64, arm);
|
DEFINED_ON(x86, x64, arm, mips32, mips64);
|
||||||
inline void rotateRight64(Register count, Register64 input, Register64 dest, Register temp)
|
inline void rotateRight64(Register count, Register64 input, Register64 dest, Register temp)
|
||||||
DEFINED_ON(x86, x64, arm);
|
DEFINED_ON(x86, x64, arm, mips32, mips64);
|
||||||
|
|
||||||
// ===============================================================
|
// ===============================================================
|
||||||
// Bit counting functions
|
// Bit counting functions
|
||||||
|
@ -916,8 +920,8 @@ class MacroAssembler : public MacroAssemblerSpecific
|
||||||
inline void clz32(Register src, Register dest, bool knownNotZero) PER_SHARED_ARCH;
|
inline void clz32(Register src, Register dest, bool knownNotZero) PER_SHARED_ARCH;
|
||||||
inline void ctz32(Register src, Register dest, bool knownNotZero) PER_SHARED_ARCH;
|
inline void ctz32(Register src, Register dest, bool knownNotZero) PER_SHARED_ARCH;
|
||||||
|
|
||||||
inline void clz64(Register64 src, Register dest) DEFINED_ON(x86, x64, arm);
|
inline void clz64(Register64 src, Register dest) DEFINED_ON(x86, x64, arm, mips32, mips64);
|
||||||
inline void ctz64(Register64 src, Register dest) DEFINED_ON(x86, x64, arm);
|
inline void ctz64(Register64 src, Register dest) DEFINED_ON(x86, x64, arm, mips32, mips64);
|
||||||
|
|
||||||
// On x86_shared, temp may be Invalid only if the chip has the POPCNT instruction.
|
// On x86_shared, temp may be Invalid only if the chip has the POPCNT instruction.
|
||||||
// On ARM, temp may never be Invalid.
|
// On ARM, temp may never be Invalid.
|
||||||
|
@ -925,7 +929,8 @@ class MacroAssembler : public MacroAssemblerSpecific
|
||||||
DEFINED_ON(arm, x86_shared, mips_shared);
|
DEFINED_ON(arm, x86_shared, mips_shared);
|
||||||
|
|
||||||
// temp may be invalid only if the chip has the POPCNT instruction.
|
// temp may be invalid only if the chip has the POPCNT instruction.
|
||||||
inline void popcnt64(Register64 src, Register64 dest, Register temp) DEFINED_ON(x86, x64, arm);
|
inline void popcnt64(Register64 src, Register64 dest, Register temp)
|
||||||
|
DEFINED_ON(x86, x64, arm, mips32, mips64);
|
||||||
|
|
||||||
// ===============================================================
|
// ===============================================================
|
||||||
// Branch functions
|
// Branch functions
|
||||||
|
@ -962,9 +967,9 @@ class MacroAssembler : public MacroAssemblerSpecific
|
||||||
// When a fail label is not defined it will fall through to next instruction,
|
// When a fail label is not defined it will fall through to next instruction,
|
||||||
// else jump to the fail label.
|
// else jump to the fail label.
|
||||||
inline void branch64(Condition cond, Register64 lhs, Imm64 val, Label* success,
|
inline void branch64(Condition cond, Register64 lhs, Imm64 val, Label* success,
|
||||||
Label* fail = nullptr) DEFINED_ON(x86, x64, arm);
|
Label* fail = nullptr) DEFINED_ON(x86, x64, arm, mips32, mips64);
|
||||||
inline void branch64(Condition cond, Register64 lhs, Register64 rhs, Label* success,
|
inline void branch64(Condition cond, Register64 lhs, Register64 rhs, Label* success,
|
||||||
Label* fail = nullptr) DEFINED_ON(x86, x64, arm);
|
Label* fail = nullptr) DEFINED_ON(x86, x64, arm, mips32, mips64);
|
||||||
// On x86 and x64 NotEqual and Equal conditions are allowed for the branch64 variants
|
// On x86 and x64 NotEqual and Equal conditions are allowed for the branch64 variants
|
||||||
// with Address as lhs. On others only the NotEqual condition.
|
// with Address as lhs. On others only the NotEqual condition.
|
||||||
inline void branch64(Condition cond, const Address& lhs, Imm64 val, Label* label) PER_ARCH;
|
inline void branch64(Condition cond, const Address& lhs, Imm64 val, Label* label) PER_ARCH;
|
||||||
|
|
|
@ -1342,6 +1342,12 @@ AssemblerMIPSShared::as_cvtsd(FloatRegister fd, FloatRegister fs)
|
||||||
return writeInst(InstReg(op_cop1, rs_d, zero, fs, fd, ff_cvt_s_fmt).encode());
|
return writeInst(InstReg(op_cop1, rs_d, zero, fs, fd, ff_cvt_s_fmt).encode());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BufferOffset
|
||||||
|
AssemblerMIPSShared::as_cvtsl(FloatRegister fd, FloatRegister fs)
|
||||||
|
{
|
||||||
|
return writeInst(InstReg(op_cop1, rs_l, zero, fs, fd, ff_cvt_s_fmt).encode());
|
||||||
|
}
|
||||||
|
|
||||||
BufferOffset
|
BufferOffset
|
||||||
AssemblerMIPSShared::as_cvtsw(FloatRegister fd, FloatRegister fs)
|
AssemblerMIPSShared::as_cvtsw(FloatRegister fd, FloatRegister fs)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1226,7 +1226,7 @@ class AssemblerMIPSShared : public AssemblerShared
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
static bool SupportsUnalignedAccesses() {
|
static bool SupportsUnalignedAccesses() {
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
static bool SupportsSimd() {
|
static bool SupportsSimd() {
|
||||||
return js::jit::SupportsSimd;
|
return js::jit::SupportsSimd;
|
||||||
|
|
|
@ -42,6 +42,42 @@ CodeGeneratorMIPSShared::CodeGeneratorMIPSShared(MIRGenerator* gen, LIRGraph* gr
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Operand
|
||||||
|
CodeGeneratorMIPSShared::ToOperand(const LAllocation& a)
|
||||||
|
{
|
||||||
|
if (a.isGeneralReg())
|
||||||
|
return Operand(a.toGeneralReg()->reg());
|
||||||
|
if (a.isFloatReg())
|
||||||
|
return Operand(a.toFloatReg()->reg());
|
||||||
|
return Operand(masm.getStackPointer(), ToStackOffset(&a));
|
||||||
|
}
|
||||||
|
|
||||||
|
Operand
|
||||||
|
CodeGeneratorMIPSShared::ToOperand(const LAllocation* a)
|
||||||
|
{
|
||||||
|
return ToOperand(*a);
|
||||||
|
}
|
||||||
|
|
||||||
|
Operand
|
||||||
|
CodeGeneratorMIPSShared::ToOperand(const LDefinition* def)
|
||||||
|
{
|
||||||
|
return ToOperand(def->output());
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef JS_PUNBOX64
|
||||||
|
Operand
|
||||||
|
CodeGeneratorMIPSShared::ToOperandOrRegister64(const LInt64Allocation input)
|
||||||
|
{
|
||||||
|
return ToOperand(input.value());
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
Register64
|
||||||
|
CodeGeneratorMIPSShared::ToOperandOrRegister64(const LInt64Allocation input)
|
||||||
|
{
|
||||||
|
return ToRegister64(input);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void
|
void
|
||||||
CodeGeneratorMIPSShared::branchToBlock(Assembler::FloatFormat fmt, FloatRegister lhs, FloatRegister rhs,
|
CodeGeneratorMIPSShared::branchToBlock(Assembler::FloatFormat fmt, FloatRegister lhs, FloatRegister rhs,
|
||||||
MBasicBlock* mir, Assembler::DoubleCondition cond)
|
MBasicBlock* mir, Assembler::DoubleCondition cond)
|
||||||
|
@ -296,6 +332,22 @@ CodeGeneratorMIPSShared::visitAddI(LAddI* ins)
|
||||||
bailoutFrom(&overflow, ins->snapshot());
|
bailoutFrom(&overflow, ins->snapshot());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGeneratorMIPSShared::visitAddI64(LAddI64* lir)
|
||||||
|
{
|
||||||
|
const LInt64Allocation lhs = lir->getInt64Operand(LAddI64::Lhs);
|
||||||
|
const LInt64Allocation rhs = lir->getInt64Operand(LAddI64::Rhs);
|
||||||
|
|
||||||
|
MOZ_ASSERT(ToOutRegister64(lir) == ToRegister64(lhs));
|
||||||
|
|
||||||
|
if (IsConstant(rhs)) {
|
||||||
|
masm.add64(Imm64(ToInt64(rhs)), ToRegister64(lhs));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
masm.add64(ToOperandOrRegister64(rhs), ToRegister64(lhs));
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CodeGeneratorMIPSShared::visitSubI(LSubI* ins)
|
CodeGeneratorMIPSShared::visitSubI(LSubI* ins)
|
||||||
{
|
{
|
||||||
|
@ -323,6 +375,22 @@ CodeGeneratorMIPSShared::visitSubI(LSubI* ins)
|
||||||
bailoutFrom(&overflow, ins->snapshot());
|
bailoutFrom(&overflow, ins->snapshot());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGeneratorMIPSShared::visitSubI64(LSubI64* lir)
|
||||||
|
{
|
||||||
|
const LInt64Allocation lhs = lir->getInt64Operand(LSubI64::Lhs);
|
||||||
|
const LInt64Allocation rhs = lir->getInt64Operand(LSubI64::Rhs);
|
||||||
|
|
||||||
|
MOZ_ASSERT(ToOutRegister64(lir) == ToRegister64(lhs));
|
||||||
|
|
||||||
|
if (IsConstant(rhs)) {
|
||||||
|
masm.sub64(Imm64(ToInt64(rhs)), ToRegister64(lhs));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
masm.sub64(ToOperandOrRegister64(rhs), ToRegister64(lhs));
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CodeGeneratorMIPSShared::visitMulI(LMulI* ins)
|
CodeGeneratorMIPSShared::visitMulI(LMulI* ins)
|
||||||
{
|
{
|
||||||
|
@ -445,6 +513,47 @@ CodeGeneratorMIPSShared::visitMulI(LMulI* ins)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGeneratorMIPSShared::visitMulI64(LMulI64* lir)
|
||||||
|
{
|
||||||
|
const LInt64Allocation lhs = lir->getInt64Operand(LMulI64::Lhs);
|
||||||
|
const LInt64Allocation rhs = lir->getInt64Operand(LMulI64::Rhs);
|
||||||
|
|
||||||
|
MOZ_ASSERT(ToRegister64(lhs) == ToOutRegister64(lir));
|
||||||
|
|
||||||
|
if (IsConstant(rhs)) {
|
||||||
|
int64_t constant = ToInt64(rhs);
|
||||||
|
switch (constant) {
|
||||||
|
case -1:
|
||||||
|
masm.neg64(ToRegister64(lhs));
|
||||||
|
return;
|
||||||
|
case 0:
|
||||||
|
masm.xor64(ToRegister64(lhs), ToRegister64(lhs));
|
||||||
|
return;
|
||||||
|
case 1:
|
||||||
|
// nop
|
||||||
|
return;
|
||||||
|
case 2:
|
||||||
|
masm.add64(ToRegister64(lhs), ToRegister64(lhs));
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
if (constant > 0) {
|
||||||
|
// Use shift if constant is power of 2.
|
||||||
|
int32_t shift = mozilla::FloorLog2(constant);
|
||||||
|
if (int64_t(1) << shift == constant) {
|
||||||
|
masm.lshift64(Imm32(shift), ToRegister64(lhs));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Register temp = ToTempRegisterOrInvalid(lir->temp());
|
||||||
|
masm.mul64(Imm64(constant), ToRegister64(lhs), temp);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Register temp = ToTempRegisterOrInvalid(lir->temp());
|
||||||
|
masm.mul64(ToOperandOrRegister64(rhs), ToRegister64(lhs), temp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CodeGeneratorMIPSShared::visitDivI(LDivI* ins)
|
CodeGeneratorMIPSShared::visitDivI(LDivI* ins)
|
||||||
{
|
{
|
||||||
|
@ -755,6 +864,38 @@ CodeGeneratorMIPSShared::visitBitOpI(LBitOpI* ins)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGeneratorMIPSShared::visitBitOpI64(LBitOpI64* lir)
|
||||||
|
{
|
||||||
|
const LInt64Allocation lhs = lir->getInt64Operand(LBitOpI64::Lhs);
|
||||||
|
const LInt64Allocation rhs = lir->getInt64Operand(LBitOpI64::Rhs);
|
||||||
|
|
||||||
|
MOZ_ASSERT(ToOutRegister64(lir) == ToRegister64(lhs));
|
||||||
|
|
||||||
|
switch (lir->bitop()) {
|
||||||
|
case JSOP_BITOR:
|
||||||
|
if (IsConstant(rhs))
|
||||||
|
masm.or64(Imm64(ToInt64(rhs)), ToRegister64(lhs));
|
||||||
|
else
|
||||||
|
masm.or64(ToOperandOrRegister64(rhs), ToRegister64(lhs));
|
||||||
|
break;
|
||||||
|
case JSOP_BITXOR:
|
||||||
|
if (IsConstant(rhs))
|
||||||
|
masm.xor64(Imm64(ToInt64(rhs)), ToRegister64(lhs));
|
||||||
|
else
|
||||||
|
masm.xor64(ToOperandOrRegister64(rhs), ToRegister64(lhs));
|
||||||
|
break;
|
||||||
|
case JSOP_BITAND:
|
||||||
|
if (IsConstant(rhs))
|
||||||
|
masm.and64(Imm64(ToInt64(rhs)), ToRegister64(lhs));
|
||||||
|
else
|
||||||
|
masm.and64(ToOperandOrRegister64(rhs), ToRegister64(lhs));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
MOZ_CRASH("unexpected binary opcode");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CodeGeneratorMIPSShared::visitShiftI(LShiftI* ins)
|
CodeGeneratorMIPSShared::visitShiftI(LShiftI* ins)
|
||||||
{
|
{
|
||||||
|
@ -814,6 +955,78 @@ CodeGeneratorMIPSShared::visitShiftI(LShiftI* ins)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGeneratorMIPSShared::visitShiftI64(LShiftI64* lir)
|
||||||
|
{
|
||||||
|
const LInt64Allocation lhs = lir->getInt64Operand(LShiftI64::Lhs);
|
||||||
|
LAllocation* rhs = lir->getOperand(LShiftI64::Rhs);
|
||||||
|
|
||||||
|
MOZ_ASSERT(ToOutRegister64(lir) == ToRegister64(lhs));
|
||||||
|
|
||||||
|
if (rhs->isConstant()) {
|
||||||
|
int32_t shift = int32_t(rhs->toConstant()->toInt64() & 0x3F);
|
||||||
|
switch (lir->bitop()) {
|
||||||
|
case JSOP_LSH:
|
||||||
|
if (shift)
|
||||||
|
masm.lshift64(Imm32(shift), ToRegister64(lhs));
|
||||||
|
break;
|
||||||
|
case JSOP_RSH:
|
||||||
|
if (shift)
|
||||||
|
masm.rshift64Arithmetic(Imm32(shift), ToRegister64(lhs));
|
||||||
|
break;
|
||||||
|
case JSOP_URSH:
|
||||||
|
if (shift)
|
||||||
|
masm.rshift64(Imm32(shift), ToRegister64(lhs));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
MOZ_CRASH("Unexpected shift op");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (lir->bitop()) {
|
||||||
|
case JSOP_LSH:
|
||||||
|
masm.lshift64(ToRegister(rhs), ToRegister64(lhs));
|
||||||
|
break;
|
||||||
|
case JSOP_RSH:
|
||||||
|
masm.rshift64Arithmetic(ToRegister(rhs), ToRegister64(lhs));
|
||||||
|
break;
|
||||||
|
case JSOP_URSH:
|
||||||
|
masm.rshift64(ToRegister(rhs), ToRegister64(lhs));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
MOZ_CRASH("Unexpected shift op");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGeneratorMIPSShared::visitRotateI64(LRotateI64* lir)
|
||||||
|
{
|
||||||
|
MRotate* mir = lir->mir();
|
||||||
|
LAllocation* count = lir->count();
|
||||||
|
|
||||||
|
Register64 input = ToRegister64(lir->input());
|
||||||
|
Register64 output = ToOutRegister64(lir);
|
||||||
|
Register temp = ToTempRegisterOrInvalid(lir->temp());
|
||||||
|
|
||||||
|
MOZ_ASSERT(input == output);
|
||||||
|
|
||||||
|
if (count->isConstant()) {
|
||||||
|
int32_t c = int32_t(count->toConstant()->toInt64() & 0x3F);
|
||||||
|
if (!c)
|
||||||
|
return;
|
||||||
|
if (mir->isLeftRotate())
|
||||||
|
masm.rotateLeft64(Imm32(c), input, output, temp);
|
||||||
|
else
|
||||||
|
masm.rotateRight64(Imm32(c), input, output, temp);
|
||||||
|
} else {
|
||||||
|
if (mir->isLeftRotate())
|
||||||
|
masm.rotateLeft64(ToRegister(count), input, output, temp);
|
||||||
|
else
|
||||||
|
masm.rotateRight64(ToRegister(count), input, output, temp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CodeGeneratorMIPSShared::visitUrshD(LUrshD* ins)
|
CodeGeneratorMIPSShared::visitUrshD(LUrshD* ins)
|
||||||
{
|
{
|
||||||
|
@ -860,6 +1073,16 @@ CodeGeneratorMIPSShared::visitPopcntI(LPopcntI* ins)
|
||||||
masm.popcnt32(input, output, tmp);
|
masm.popcnt32(input, output, tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGeneratorMIPSShared::visitPopcntI64(LPopcntI64* ins)
|
||||||
|
{
|
||||||
|
Register64 input = ToRegister64(ins->getInt64Operand(0));
|
||||||
|
Register64 output = ToOutRegister64(ins);
|
||||||
|
Register tmp = ToRegister(ins->getTemp(0));
|
||||||
|
|
||||||
|
masm.popcnt64(input, output, tmp);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CodeGeneratorMIPSShared::visitPowHalfD(LPowHalfD* ins)
|
CodeGeneratorMIPSShared::visitPowHalfD(LPowHalfD* ins)
|
||||||
{
|
{
|
||||||
|
@ -1309,7 +1532,29 @@ CodeGeneratorMIPSShared::visitOutOfLineWasmTruncateCheck(OutOfLineWasmTruncateCh
|
||||||
masm.as_truncwd(ScratchFloat32Reg, ScratchDoubleReg);
|
masm.as_truncwd(ScratchFloat32Reg, ScratchDoubleReg);
|
||||||
masm.jump(ool->rejoin());
|
masm.jump(ool->rejoin());
|
||||||
}
|
}
|
||||||
|
} else if (toType == MIRType::Int64) {
|
||||||
|
if (fromType == MIRType::Double) {
|
||||||
|
masm.loadConstantDouble(double(INT64_MIN), ScratchDoubleReg);
|
||||||
|
masm.branchDouble(Assembler::DoubleLessThan, input, ScratchDoubleReg, &fail);
|
||||||
|
|
||||||
|
masm.loadConstantDouble(double(INT64_MAX) + 1.0, ScratchDoubleReg);
|
||||||
|
masm.branchDouble(Assembler::DoubleGreaterThanOrEqual, input,
|
||||||
|
ScratchDoubleReg, &fail);
|
||||||
|
masm.jump(ool->rejoin());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if (toType == MIRType::Int64) {
|
||||||
|
if (fromType == MIRType::Double) {
|
||||||
|
masm.loadConstantDouble(double(-1), ScratchDoubleReg);
|
||||||
|
masm.branchDouble(Assembler::DoubleLessThanOrEqual, input, ScratchDoubleReg, &fail);
|
||||||
|
|
||||||
|
masm.loadConstantDouble(double(UINT64_MAX) + 1.0, ScratchDoubleReg);
|
||||||
|
masm.branchDouble(Assembler::DoubleGreaterThanOrEqual, input,
|
||||||
|
ScratchDoubleReg, &fail);
|
||||||
|
masm.jump(ool->rejoin());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle errors.
|
// Handle errors.
|
||||||
|
@ -1643,8 +1888,9 @@ CodeGeneratorMIPSShared::visitWasmCallI64(LWasmCallI64* ins)
|
||||||
emitWasmCallBase(ins);
|
emitWasmCallBase(ins);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
void
|
void
|
||||||
CodeGeneratorMIPSShared::visitWasmLoad(LWasmLoad* lir)
|
CodeGeneratorMIPSShared::emitWasmLoad(T* lir)
|
||||||
{
|
{
|
||||||
const MWasmLoad* mir = lir->mir();
|
const MWasmLoad* mir = lir->mir();
|
||||||
|
|
||||||
|
@ -1680,21 +1926,55 @@ CodeGeneratorMIPSShared::visitWasmLoad(LWasmLoad* lir)
|
||||||
|
|
||||||
memoryBarrier(mir->barrierBefore());
|
memoryBarrier(mir->barrierBefore());
|
||||||
|
|
||||||
|
BaseIndex address(HeapReg, ptr, TimesOne);
|
||||||
|
|
||||||
|
if (mir->isUnaligned()) {
|
||||||
|
Register temp = ToRegister(lir->getTemp(1));
|
||||||
|
|
||||||
|
if (isFloat) {
|
||||||
|
if (byteSize == 4)
|
||||||
|
masm.loadUnalignedFloat32(address, temp, ToFloatRegister(lir->output()));
|
||||||
|
else
|
||||||
|
masm.loadUnalignedDouble(address, temp, ToFloatRegister(lir->output()));
|
||||||
|
} else {
|
||||||
|
masm.ma_load_unaligned(ToRegister(lir->output()), address, temp,
|
||||||
|
static_cast<LoadStoreSize>(8 * byteSize),
|
||||||
|
isSigned ? SignExtend : ZeroExtend);
|
||||||
|
}
|
||||||
|
|
||||||
|
memoryBarrier(mir->barrierAfter());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (isFloat) {
|
if (isFloat) {
|
||||||
if (byteSize == 4) {
|
if (byteSize == 4)
|
||||||
masm.loadFloat32(BaseIndex(HeapReg, ptr, TimesOne), ToFloatRegister(lir->output()));
|
masm.loadFloat32(address, ToFloatRegister(lir->output()));
|
||||||
} else
|
else
|
||||||
masm.loadDouble(BaseIndex(HeapReg, ptr, TimesOne), ToFloatRegister(lir->output()));
|
masm.loadDouble(address, ToFloatRegister(lir->output()));
|
||||||
} else {
|
} else {
|
||||||
masm.ma_load(ToRegister(lir->output()), BaseIndex(HeapReg, ptr, TimesOne),
|
masm.ma_load(ToRegister(lir->output()), address,
|
||||||
static_cast<LoadStoreSize>(8 * byteSize), isSigned ? SignExtend : ZeroExtend);
|
static_cast<LoadStoreSize>(8 * byteSize),
|
||||||
|
isSigned ? SignExtend : ZeroExtend);
|
||||||
}
|
}
|
||||||
|
|
||||||
memoryBarrier(mir->barrierAfter());
|
memoryBarrier(mir->barrierAfter());
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CodeGeneratorMIPSShared::visitWasmStore(LWasmStore* lir)
|
CodeGeneratorMIPSShared::visitWasmLoad(LWasmLoad* lir)
|
||||||
|
{
|
||||||
|
emitWasmLoad(lir);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGeneratorMIPSShared::visitWasmUnalignedLoad(LWasmUnalignedLoad* lir)
|
||||||
|
{
|
||||||
|
emitWasmLoad(lir);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void
|
||||||
|
CodeGeneratorMIPSShared::emitWasmStore(T* lir)
|
||||||
{
|
{
|
||||||
const MWasmStore* mir = lir->mir();
|
const MWasmStore* mir = lir->mir();
|
||||||
|
|
||||||
|
@ -1731,19 +2011,52 @@ CodeGeneratorMIPSShared::visitWasmStore(LWasmStore* lir)
|
||||||
|
|
||||||
memoryBarrier(mir->barrierBefore());
|
memoryBarrier(mir->barrierBefore());
|
||||||
|
|
||||||
|
BaseIndex address(HeapReg, ptr, TimesOne);
|
||||||
|
|
||||||
|
if (mir->isUnaligned()) {
|
||||||
|
Register temp = ToRegister(lir->getTemp(1));
|
||||||
|
|
||||||
|
if (isFloat) {
|
||||||
|
if (byteSize == 4)
|
||||||
|
masm.storeUnalignedFloat32(ToFloatRegister(lir->value()), temp, address);
|
||||||
|
else
|
||||||
|
masm.storeUnalignedDouble(ToFloatRegister(lir->value()), temp, address);
|
||||||
|
} else {
|
||||||
|
masm.ma_store_unaligned(ToRegister(lir->value()), address, temp,
|
||||||
|
static_cast<LoadStoreSize>(8 * byteSize),
|
||||||
|
isSigned ? SignExtend : ZeroExtend);
|
||||||
|
}
|
||||||
|
|
||||||
|
memoryBarrier(mir->barrierAfter());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (isFloat) {
|
if (isFloat) {
|
||||||
if (byteSize == 4) {
|
if (byteSize == 4) {
|
||||||
masm.storeFloat32(ToFloatRegister(lir->value()), BaseIndex(HeapReg, ptr, TimesOne));
|
masm.storeFloat32(ToFloatRegister(lir->value()), address);
|
||||||
} else
|
} else
|
||||||
masm.storeDouble(ToFloatRegister(lir->value()), BaseIndex(HeapReg, ptr, TimesOne));
|
masm.storeDouble(ToFloatRegister(lir->value()), address);
|
||||||
} else {
|
} else {
|
||||||
masm.ma_store(ToRegister(lir->value()), BaseIndex(HeapReg, ptr, TimesOne),
|
masm.ma_store(ToRegister(lir->value()), address,
|
||||||
static_cast<LoadStoreSize>(8 * byteSize), isSigned ? SignExtend : ZeroExtend);
|
static_cast<LoadStoreSize>(8 * byteSize),
|
||||||
|
isSigned ? SignExtend : ZeroExtend);
|
||||||
}
|
}
|
||||||
|
|
||||||
memoryBarrier(mir->barrierAfter());
|
memoryBarrier(mir->barrierAfter());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGeneratorMIPSShared::visitWasmStore(LWasmStore* lir)
|
||||||
|
{
|
||||||
|
emitWasmStore(lir);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGeneratorMIPSShared::visitWasmUnalignedStore(LWasmUnalignedStore* lir)
|
||||||
|
{
|
||||||
|
emitWasmStore(lir);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CodeGeneratorMIPSShared::visitAsmJSLoadHeap(LAsmJSLoadHeap* ins)
|
CodeGeneratorMIPSShared::visitAsmJSLoadHeap(LAsmJSLoadHeap* ins)
|
||||||
{
|
{
|
||||||
|
@ -2023,6 +2336,17 @@ CodeGeneratorMIPSShared::visitAsmJSPassStackArg(LAsmJSPassStackArg* ins)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGeneratorMIPSShared::visitAsmJSPassStackArgI64(LAsmJSPassStackArgI64* ins)
|
||||||
|
{
|
||||||
|
const MAsmJSPassStackArg* mir = ins->mir();
|
||||||
|
Address dst(StackPointer, mir->spOffset());
|
||||||
|
if (IsConstant(ins->arg()))
|
||||||
|
masm.store64(Imm64(ToInt64(ins->arg())), dst);
|
||||||
|
else
|
||||||
|
masm.store64(ToRegister64(ins->arg()), dst);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CodeGeneratorMIPSShared::visitAsmSelect(LAsmSelect* ins)
|
CodeGeneratorMIPSShared::visitAsmSelect(LAsmSelect* ins)
|
||||||
{
|
{
|
||||||
|
|
|
@ -26,6 +26,16 @@ class CodeGeneratorMIPSShared : public CodeGeneratorShared
|
||||||
protected:
|
protected:
|
||||||
NonAssertingLabel deoptLabel_;
|
NonAssertingLabel deoptLabel_;
|
||||||
|
|
||||||
|
Operand ToOperand(const LAllocation& a);
|
||||||
|
Operand ToOperand(const LAllocation* a);
|
||||||
|
Operand ToOperand(const LDefinition* def);
|
||||||
|
|
||||||
|
#ifdef JS_PUNBOX64
|
||||||
|
Operand ToOperandOrRegister64(const LInt64Allocation input);
|
||||||
|
#else
|
||||||
|
Register64 ToOperandOrRegister64(const LInt64Allocation input);
|
||||||
|
#endif
|
||||||
|
|
||||||
MoveOperand toMoveOperand(LAllocation a) const;
|
MoveOperand toMoveOperand(LAllocation a) const;
|
||||||
|
|
||||||
template <typename T1, typename T2>
|
template <typename T1, typename T2>
|
||||||
|
@ -109,6 +119,11 @@ class CodeGeneratorMIPSShared : public CodeGeneratorShared
|
||||||
emitBranch(reg, Imm32(0), cond, ifTrue, ifFalse);
|
emitBranch(reg, Imm32(0), cond, ifTrue, ifFalse);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void emitWasmLoad(T* ins);
|
||||||
|
template <typename T>
|
||||||
|
void emitWasmStore(T* ins);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Instruction visitors.
|
// Instruction visitors.
|
||||||
virtual void visitMinMaxD(LMinMaxD* ins);
|
virtual void visitMinMaxD(LMinMaxD* ins);
|
||||||
|
@ -118,11 +133,15 @@ class CodeGeneratorMIPSShared : public CodeGeneratorShared
|
||||||
virtual void visitSqrtD(LSqrtD* ins);
|
virtual void visitSqrtD(LSqrtD* ins);
|
||||||
virtual void visitSqrtF(LSqrtF* ins);
|
virtual void visitSqrtF(LSqrtF* ins);
|
||||||
virtual void visitAddI(LAddI* ins);
|
virtual void visitAddI(LAddI* ins);
|
||||||
|
virtual void visitAddI64(LAddI64* ins);
|
||||||
virtual void visitSubI(LSubI* ins);
|
virtual void visitSubI(LSubI* ins);
|
||||||
|
virtual void visitSubI64(LSubI64* ins);
|
||||||
virtual void visitBitNotI(LBitNotI* ins);
|
virtual void visitBitNotI(LBitNotI* ins);
|
||||||
virtual void visitBitOpI(LBitOpI* ins);
|
virtual void visitBitOpI(LBitOpI* ins);
|
||||||
|
virtual void visitBitOpI64(LBitOpI64* ins);
|
||||||
|
|
||||||
virtual void visitMulI(LMulI* ins);
|
virtual void visitMulI(LMulI* ins);
|
||||||
|
virtual void visitMulI64(LMulI64* ins);
|
||||||
|
|
||||||
virtual void visitDivI(LDivI* ins);
|
virtual void visitDivI(LDivI* ins);
|
||||||
virtual void visitDivPowTwoI(LDivPowTwoI* ins);
|
virtual void visitDivPowTwoI(LDivPowTwoI* ins);
|
||||||
|
@ -131,11 +150,14 @@ class CodeGeneratorMIPSShared : public CodeGeneratorShared
|
||||||
virtual void visitModMaskI(LModMaskI* ins);
|
virtual void visitModMaskI(LModMaskI* ins);
|
||||||
virtual void visitPowHalfD(LPowHalfD* ins);
|
virtual void visitPowHalfD(LPowHalfD* ins);
|
||||||
virtual void visitShiftI(LShiftI* ins);
|
virtual void visitShiftI(LShiftI* ins);
|
||||||
|
virtual void visitShiftI64(LShiftI64* ins);
|
||||||
|
virtual void visitRotateI64(LRotateI64* lir);
|
||||||
virtual void visitUrshD(LUrshD* ins);
|
virtual void visitUrshD(LUrshD* ins);
|
||||||
|
|
||||||
virtual void visitClzI(LClzI* ins);
|
virtual void visitClzI(LClzI* ins);
|
||||||
virtual void visitCtzI(LCtzI* ins);
|
virtual void visitCtzI(LCtzI* ins);
|
||||||
virtual void visitPopcntI(LPopcntI* ins);
|
virtual void visitPopcntI(LPopcntI* ins);
|
||||||
|
virtual void visitPopcntI64(LPopcntI64* lir);
|
||||||
|
|
||||||
virtual void visitTestIAndBranch(LTestIAndBranch* test);
|
virtual void visitTestIAndBranch(LTestIAndBranch* test);
|
||||||
virtual void visitCompare(LCompare* comp);
|
virtual void visitCompare(LCompare* comp);
|
||||||
|
@ -197,7 +219,9 @@ class CodeGeneratorMIPSShared : public CodeGeneratorShared
|
||||||
void visitWasmCall(LWasmCall* ins);
|
void visitWasmCall(LWasmCall* ins);
|
||||||
void visitWasmCallI64(LWasmCallI64* ins);
|
void visitWasmCallI64(LWasmCallI64* ins);
|
||||||
void visitWasmLoad(LWasmLoad* ins);
|
void visitWasmLoad(LWasmLoad* ins);
|
||||||
|
void visitWasmUnalignedLoad(LWasmUnalignedLoad* ins);
|
||||||
void visitWasmStore(LWasmStore* ins);
|
void visitWasmStore(LWasmStore* ins);
|
||||||
|
void visitWasmUnalignedStore(LWasmUnalignedStore* ins);
|
||||||
void visitWasmAddOffset(LWasmAddOffset* ins);
|
void visitWasmAddOffset(LWasmAddOffset* ins);
|
||||||
void visitAsmJSLoadHeap(LAsmJSLoadHeap* ins);
|
void visitAsmJSLoadHeap(LAsmJSLoadHeap* ins);
|
||||||
void visitAsmJSStoreHeap(LAsmJSStoreHeap* ins);
|
void visitAsmJSStoreHeap(LAsmJSStoreHeap* ins);
|
||||||
|
@ -207,6 +231,7 @@ class CodeGeneratorMIPSShared : public CodeGeneratorShared
|
||||||
void visitAsmJSAtomicBinopHeapForEffect(LAsmJSAtomicBinopHeapForEffect* ins);
|
void visitAsmJSAtomicBinopHeapForEffect(LAsmJSAtomicBinopHeapForEffect* ins);
|
||||||
|
|
||||||
void visitAsmJSPassStackArg(LAsmJSPassStackArg* ins);
|
void visitAsmJSPassStackArg(LAsmJSPassStackArg* ins);
|
||||||
|
void visitAsmJSPassStackArgI64(LAsmJSPassStackArgI64* ins);
|
||||||
void visitAsmSelect(LAsmSelect* ins);
|
void visitAsmSelect(LAsmSelect* ins);
|
||||||
void visitAsmReinterpret(LAsmReinterpret* ins);
|
void visitAsmReinterpret(LAsmReinterpret* ins);
|
||||||
|
|
||||||
|
|
|
@ -277,6 +277,124 @@ class LUDivOrMod : public LBinaryMath<0>
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class LInt64ToFloatingPoint : public LInstructionHelper<1, INT64_PIECES, 0>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LIR_HEADER(Int64ToFloatingPoint);
|
||||||
|
|
||||||
|
explicit LInt64ToFloatingPoint(const LInt64Allocation& in) {
|
||||||
|
setInt64Operand(0, in);
|
||||||
|
}
|
||||||
|
|
||||||
|
MInt64ToFloatingPoint* mir() const {
|
||||||
|
return mir_->toInt64ToFloatingPoint();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace details {
|
||||||
|
|
||||||
|
// Base class for the int64 and non-int64 variants.
|
||||||
|
template<size_t NumDefs>
|
||||||
|
class LWasmUnalignedLoadBase : public details::LWasmLoadBase<NumDefs, 2>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef LWasmLoadBase<NumDefs, 2> Base;
|
||||||
|
|
||||||
|
explicit LWasmUnalignedLoadBase(const LAllocation& ptr, const LDefinition& valueHelper)
|
||||||
|
: Base(ptr)
|
||||||
|
{
|
||||||
|
Base::setTemp(0, LDefinition::BogusTemp());
|
||||||
|
Base::setTemp(1, valueHelper);
|
||||||
|
}
|
||||||
|
const LAllocation* ptr() {
|
||||||
|
return Base::getOperand(0);
|
||||||
|
}
|
||||||
|
const LDefinition* ptrCopy() {
|
||||||
|
return Base::getTemp(0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace details
|
||||||
|
|
||||||
|
class LWasmUnalignedLoad : public details::LWasmUnalignedLoadBase<1>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit LWasmUnalignedLoad(const LAllocation& ptr, const LDefinition& valueHelper)
|
||||||
|
: LWasmUnalignedLoadBase(ptr, valueHelper)
|
||||||
|
{}
|
||||||
|
LIR_HEADER(WasmUnalignedLoad);
|
||||||
|
};
|
||||||
|
|
||||||
|
class LWasmUnalignedLoadI64 : public details::LWasmUnalignedLoadBase<INT64_PIECES>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit LWasmUnalignedLoadI64(const LAllocation& ptr, const LDefinition& valueHelper)
|
||||||
|
: LWasmUnalignedLoadBase(ptr, valueHelper)
|
||||||
|
{}
|
||||||
|
LIR_HEADER(WasmUnalignedLoadI64);
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace details {
|
||||||
|
|
||||||
|
// Base class for the int64 and non-int64 variants.
|
||||||
|
template<size_t NumOps>
|
||||||
|
class LWasmUnalignedStoreBase : public LInstructionHelper<0, NumOps, 2>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef LInstructionHelper<0, NumOps, 2> Base;
|
||||||
|
|
||||||
|
static const size_t PtrIndex = 0;
|
||||||
|
static const size_t ValueIndex = 1;
|
||||||
|
|
||||||
|
LWasmUnalignedStoreBase(const LAllocation& ptr, const LDefinition& valueHelper)
|
||||||
|
{
|
||||||
|
Base::setOperand(0, ptr);
|
||||||
|
Base::setTemp(0, LDefinition::BogusTemp());
|
||||||
|
Base::setTemp(1, valueHelper);
|
||||||
|
}
|
||||||
|
MWasmStore* mir() const {
|
||||||
|
return Base::mir_->toWasmStore();
|
||||||
|
}
|
||||||
|
const LAllocation* ptr() {
|
||||||
|
return Base::getOperand(PtrIndex);
|
||||||
|
}
|
||||||
|
const LDefinition* ptrCopy() {
|
||||||
|
return Base::getTemp(0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace details
|
||||||
|
|
||||||
|
class LWasmUnalignedStore : public details::LWasmUnalignedStoreBase<2>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LIR_HEADER(WasmUnalignedStore);
|
||||||
|
LWasmUnalignedStore(const LAllocation& ptr, const LAllocation& value,
|
||||||
|
const LDefinition& valueHelper)
|
||||||
|
: LWasmUnalignedStoreBase(ptr, valueHelper)
|
||||||
|
{
|
||||||
|
setOperand(1, value);
|
||||||
|
}
|
||||||
|
const LAllocation* value() {
|
||||||
|
return Base::getOperand(ValueIndex);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class LWasmUnalignedStoreI64 : public details::LWasmUnalignedStoreBase<1 + INT64_PIECES>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LIR_HEADER(WasmUnalignedStoreI64);
|
||||||
|
LWasmUnalignedStoreI64(const LAllocation& ptr, const LInt64Allocation& value,
|
||||||
|
const LDefinition& valueHelper)
|
||||||
|
: LWasmUnalignedStoreBase(ptr, valueHelper)
|
||||||
|
{
|
||||||
|
setInt64Operand(1, value);
|
||||||
|
}
|
||||||
|
const LInt64Allocation value() {
|
||||||
|
return getInt64Operand(ValueIndex);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace jit
|
} // namespace jit
|
||||||
} // namespace js
|
} // namespace js
|
||||||
|
|
||||||
|
|
|
@ -64,13 +64,37 @@ void
|
||||||
LIRGeneratorMIPSShared::lowerForALUInt64(LInstructionHelper<INT64_PIECES, 2 * INT64_PIECES, 0>* ins,
|
LIRGeneratorMIPSShared::lowerForALUInt64(LInstructionHelper<INT64_PIECES, 2 * INT64_PIECES, 0>* ins,
|
||||||
MDefinition* mir, MDefinition* lhs, MDefinition* rhs)
|
MDefinition* mir, MDefinition* lhs, MDefinition* rhs)
|
||||||
{
|
{
|
||||||
MOZ_CRASH("NYI");
|
ins->setInt64Operand(0, useInt64RegisterAtStart(lhs));
|
||||||
|
ins->setInt64Operand(INT64_PIECES,
|
||||||
|
lhs != rhs ? useInt64OrConstant(rhs) : useInt64OrConstantAtStart(rhs));
|
||||||
|
defineInt64ReuseInput(ins, mir, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
LIRGeneratorMIPSShared::lowerForMulInt64(LMulI64* ins, MMul* mir, MDefinition* lhs, MDefinition* rhs)
|
LIRGeneratorMIPSShared::lowerForMulInt64(LMulI64* ins, MMul* mir, MDefinition* lhs, MDefinition* rhs)
|
||||||
{
|
{
|
||||||
MOZ_CRASH("NYI");
|
bool needsTemp = false;
|
||||||
|
|
||||||
|
#ifdef JS_CODEGEN_MIPS32
|
||||||
|
needsTemp = true;
|
||||||
|
if (rhs->isConstant()) {
|
||||||
|
int64_t constant = rhs->toConstant()->toInt64();
|
||||||
|
int32_t shift = mozilla::FloorLog2(constant);
|
||||||
|
// See special cases in CodeGeneratorMIPSShared::visitMulI64
|
||||||
|
if (constant >= -1 && constant <= 2)
|
||||||
|
needsTemp = false;
|
||||||
|
if (int64_t(1) << shift == constant)
|
||||||
|
needsTemp = false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ins->setInt64Operand(0, useInt64RegisterAtStart(lhs));
|
||||||
|
ins->setInt64Operand(INT64_PIECES,
|
||||||
|
lhs != rhs ? useInt64OrConstant(rhs) : useInt64OrConstantAtStart(rhs));
|
||||||
|
if (needsTemp)
|
||||||
|
ins->setTemp(0, temp());
|
||||||
|
|
||||||
|
defineInt64ReuseInput(ins, mir, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<size_t Temps>
|
template<size_t Temps>
|
||||||
|
@ -78,7 +102,18 @@ void
|
||||||
LIRGeneratorMIPSShared::lowerForShiftInt64(LInstructionHelper<INT64_PIECES, INT64_PIECES + 1, Temps>* ins,
|
LIRGeneratorMIPSShared::lowerForShiftInt64(LInstructionHelper<INT64_PIECES, INT64_PIECES + 1, Temps>* ins,
|
||||||
MDefinition* mir, MDefinition* lhs, MDefinition* rhs)
|
MDefinition* mir, MDefinition* lhs, MDefinition* rhs)
|
||||||
{
|
{
|
||||||
MOZ_CRASH("NYI");
|
ins->setInt64Operand(0, useInt64RegisterAtStart(lhs));
|
||||||
|
#if defined(JS_NUNBOX32)
|
||||||
|
if (mir->isRotate())
|
||||||
|
ins->setTemp(0, temp());
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static_assert(LShiftI64::Rhs == INT64_PIECES, "Assume Rhs is located at INT64_PIECES.");
|
||||||
|
static_assert(LRotateI64::Count == INT64_PIECES, "Assume Count is located at INT64_PIECES.");
|
||||||
|
|
||||||
|
ins->setOperand(INT64_PIECES, useRegisterOrConstant(rhs));
|
||||||
|
|
||||||
|
defineInt64ReuseInput(ins, mir, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
template void LIRGeneratorMIPSShared::lowerForShiftInt64(
|
template void LIRGeneratorMIPSShared::lowerForShiftInt64(
|
||||||
|
@ -208,18 +243,6 @@ LIRGeneratorMIPSShared::lowerModI(MMod* mod)
|
||||||
define(lir, mod);
|
define(lir, mod);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
LIRGeneratorMIPSShared::lowerDivI64(MDiv* div)
|
|
||||||
{
|
|
||||||
MOZ_CRASH("NYI");
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
LIRGeneratorMIPSShared::lowerModI64(MMod* mod)
|
|
||||||
{
|
|
||||||
MOZ_CRASH("NYI");
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
LIRGeneratorMIPSShared::visitPowHalf(MPowHalf* ins)
|
LIRGeneratorMIPSShared::visitPowHalf(MPowHalf* ins)
|
||||||
{
|
{
|
||||||
|
@ -299,18 +322,36 @@ LIRGeneratorMIPSShared::visitWasmLoad(MWasmLoad* ins)
|
||||||
MDefinition* base = ins->base();
|
MDefinition* base = ins->base();
|
||||||
MOZ_ASSERT(base->type() == MIRType::Int32);
|
MOZ_ASSERT(base->type() == MIRType::Int32);
|
||||||
|
|
||||||
#ifdef JS_CODEGEN_MIPS64
|
LAllocation ptr = useRegisterAtStart(base);
|
||||||
|
|
||||||
|
if (ins->isUnaligned()) {
|
||||||
|
if (ins->type() == MIRType::Int64) {
|
||||||
|
auto* lir = new(alloc()) LWasmUnalignedLoadI64(ptr, temp());
|
||||||
|
if (ins->offset())
|
||||||
|
lir->setTemp(0, tempCopy(base, 0));
|
||||||
|
|
||||||
|
defineInt64(lir, ins);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* lir = new(alloc()) LWasmUnalignedLoad(ptr, temp());
|
||||||
|
if (ins->offset())
|
||||||
|
lir->setTemp(0, tempCopy(base, 0));
|
||||||
|
|
||||||
|
define(lir, ins);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (ins->type() == MIRType::Int64) {
|
if (ins->type() == MIRType::Int64) {
|
||||||
auto* lir = new(alloc()) LWasmLoadI64(useRegisterAtStart(base));
|
auto* lir = new(alloc()) LWasmLoadI64(ptr);
|
||||||
if (ins->offset())
|
if (ins->offset())
|
||||||
lir->setTemp(0, tempCopy(base, 0));
|
lir->setTemp(0, tempCopy(base, 0));
|
||||||
|
|
||||||
defineInt64(lir, ins);
|
defineInt64(lir, ins);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
auto* lir = new(alloc()) LWasmLoad(useRegisterAtStart(base));
|
auto* lir = new(alloc()) LWasmLoad(ptr);
|
||||||
if (ins->offset())
|
if (ins->offset())
|
||||||
lir->setTemp(0, tempCopy(base, 0));
|
lir->setTemp(0, tempCopy(base, 0));
|
||||||
|
|
||||||
|
@ -324,10 +365,40 @@ LIRGeneratorMIPSShared::visitWasmStore(MWasmStore* ins)
|
||||||
MOZ_ASSERT(base->type() == MIRType::Int32);
|
MOZ_ASSERT(base->type() == MIRType::Int32);
|
||||||
|
|
||||||
MDefinition* value = ins->value();
|
MDefinition* value = ins->value();
|
||||||
LAllocation valueAlloc = useRegisterAtStart(value);
|
|
||||||
LAllocation baseAlloc = useRegisterAtStart(base);
|
LAllocation baseAlloc = useRegisterAtStart(base);
|
||||||
auto* lir = new(alloc()) LWasmStore(baseAlloc, valueAlloc);
|
|
||||||
|
|
||||||
|
if (ins->isUnaligned()) {
|
||||||
|
if (ins->type() == MIRType::Int64) {
|
||||||
|
LInt64Allocation valueAlloc = useInt64RegisterAtStart(value);
|
||||||
|
auto* lir = new(alloc()) LWasmUnalignedStoreI64(baseAlloc, valueAlloc, temp());
|
||||||
|
if (ins->offset())
|
||||||
|
lir->setTemp(0, tempCopy(base, 0));
|
||||||
|
|
||||||
|
add(lir, ins);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LAllocation valueAlloc = useRegisterAtStart(value);
|
||||||
|
auto* lir = new(alloc()) LWasmUnalignedStore(baseAlloc, valueAlloc, temp());
|
||||||
|
if (ins->offset())
|
||||||
|
lir->setTemp(0, tempCopy(base, 0));
|
||||||
|
|
||||||
|
add(lir, ins);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ins->type() == MIRType::Int64) {
|
||||||
|
LInt64Allocation valueAlloc = useInt64RegisterAtStart(value);
|
||||||
|
auto* lir = new(alloc()) LWasmStoreI64(baseAlloc, valueAlloc);
|
||||||
|
if (ins->offset())
|
||||||
|
lir->setTemp(0, tempCopy(base, 0));
|
||||||
|
|
||||||
|
add(lir, ins);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LAllocation valueAlloc = useRegisterAtStart(value);
|
||||||
|
auto* lir = new(alloc()) LWasmStore(baseAlloc, valueAlloc);
|
||||||
if (ins->offset())
|
if (ins->offset())
|
||||||
lir->setTemp(0, tempCopy(base, 0));
|
lir->setTemp(0, tempCopy(base, 0));
|
||||||
|
|
||||||
|
@ -637,13 +708,20 @@ LIRGeneratorMIPSShared::visitAtomicTypedArrayElementBinop(MAtomicTypedArrayEleme
|
||||||
void
|
void
|
||||||
LIRGeneratorMIPSShared::visitWasmTruncateToInt64(MWasmTruncateToInt64* ins)
|
LIRGeneratorMIPSShared::visitWasmTruncateToInt64(MWasmTruncateToInt64* ins)
|
||||||
{
|
{
|
||||||
MOZ_CRASH("NYI");
|
MDefinition* opd = ins->input();
|
||||||
|
MOZ_ASSERT(opd->type() == MIRType::Double || opd->type() == MIRType::Float32);
|
||||||
|
|
||||||
|
defineInt64(new(alloc()) LWasmTruncateToInt64(useRegister(opd)), ins);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
LIRGeneratorMIPSShared::visitInt64ToFloatingPoint(MInt64ToFloatingPoint* ins)
|
LIRGeneratorMIPSShared::visitInt64ToFloatingPoint(MInt64ToFloatingPoint* ins)
|
||||||
{
|
{
|
||||||
MOZ_CRASH("NYI");
|
MDefinition* opd = ins->input();
|
||||||
|
MOZ_ASSERT(opd->type() == MIRType::Int64);
|
||||||
|
MOZ_ASSERT(IsFloatingPointType(ins->type()));
|
||||||
|
|
||||||
|
define(new(alloc()) LInt64ToFloatingPoint(useInt64Register(opd)), ins);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -673,5 +751,5 @@ LIRGeneratorMIPSShared::visitCopySign(MCopySign* ins)
|
||||||
void
|
void
|
||||||
LIRGeneratorMIPSShared::visitExtendInt32ToInt64(MExtendInt32ToInt64* ins)
|
LIRGeneratorMIPSShared::visitExtendInt32ToInt64(MExtendInt32ToInt64* ins)
|
||||||
{
|
{
|
||||||
MOZ_CRASH("NYI");
|
defineInt64(new(alloc()) LExtendInt32ToInt64(useRegisterAtStart(ins->input())), ins);
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,9 +27,6 @@ class LIRGeneratorMIPSShared : public LIRGeneratorShared
|
||||||
LAllocation useByteOpRegisterOrNonDoubleConstant(MDefinition* mir);
|
LAllocation useByteOpRegisterOrNonDoubleConstant(MDefinition* mir);
|
||||||
LDefinition tempByteOpRegister();
|
LDefinition tempByteOpRegister();
|
||||||
|
|
||||||
void lowerInt64PhiInput(MPhi*, uint32_t, LBlock*, size_t) { MOZ_CRASH("NYI"); }
|
|
||||||
void defineInt64Phi(MPhi*, size_t) { MOZ_CRASH("NYI"); }
|
|
||||||
|
|
||||||
bool needTempForPostBarrier() { return false; }
|
bool needTempForPostBarrier() { return false; }
|
||||||
|
|
||||||
void lowerForShift(LInstructionHelper<1, 2, 0>* ins, MDefinition* mir, MDefinition* lhs,
|
void lowerForShift(LInstructionHelper<1, 2, 0>* ins, MDefinition* mir, MDefinition* lhs,
|
||||||
|
@ -69,8 +66,6 @@ class LIRGeneratorMIPSShared : public LIRGeneratorShared
|
||||||
MDefinition* lhs, MDefinition* rhs);
|
MDefinition* lhs, MDefinition* rhs);
|
||||||
void lowerDivI(MDiv* div);
|
void lowerDivI(MDiv* div);
|
||||||
void lowerModI(MMod* mod);
|
void lowerModI(MMod* mod);
|
||||||
void lowerDivI64(MDiv* div);
|
|
||||||
void lowerModI64(MMod* mod);
|
|
||||||
void lowerMulI(MMul* mul, MDefinition* lhs, MDefinition* rhs);
|
void lowerMulI(MMul* mul, MDefinition* lhs, MDefinition* rhs);
|
||||||
void lowerUDiv(MDiv* div);
|
void lowerUDiv(MDiv* div);
|
||||||
void lowerUMod(MMod* mod);
|
void lowerUMod(MMod* mod);
|
||||||
|
|
|
@ -445,6 +445,53 @@ MacroAssemblerMIPSShared::ma_load(Register dest, const BaseIndex& src,
|
||||||
asMasm().ma_load(dest, Address(SecondScratchReg, src.offset), size, extension);
|
asMasm().ma_load(dest, Address(SecondScratchReg, src.offset), size, extension);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssemblerMIPSShared::ma_load_unaligned(Register dest, const BaseIndex& src, Register temp,
|
||||||
|
LoadStoreSize size, LoadStoreExtension extension)
|
||||||
|
{
|
||||||
|
int16_t lowOffset, hiOffset;
|
||||||
|
Register base;
|
||||||
|
|
||||||
|
asMasm().computeScaledAddress(src, SecondScratchReg);
|
||||||
|
|
||||||
|
if (Imm16::IsInSignedRange(src.offset) && Imm16::IsInSignedRange(src.offset + size / 8 - 1)) {
|
||||||
|
base = SecondScratchReg;
|
||||||
|
lowOffset = Imm16(src.offset).encode();
|
||||||
|
hiOffset = Imm16(src.offset + size / 8 - 1).encode();
|
||||||
|
} else {
|
||||||
|
ma_li(ScratchRegister, Imm32(src.offset));
|
||||||
|
as_daddu(ScratchRegister, SecondScratchReg, ScratchRegister);
|
||||||
|
base = ScratchRegister;
|
||||||
|
lowOffset = Imm16(0).encode();
|
||||||
|
hiOffset = Imm16(size / 8 - 1).encode();
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (size) {
|
||||||
|
case SizeHalfWord:
|
||||||
|
as_lbu(dest, base, lowOffset);
|
||||||
|
if (extension != ZeroExtend)
|
||||||
|
as_lbu(temp, base, hiOffset);
|
||||||
|
else
|
||||||
|
as_lb(temp, base, hiOffset);
|
||||||
|
as_ins(dest, temp, 8, 24);
|
||||||
|
break;
|
||||||
|
case SizeWord:
|
||||||
|
as_lwl(dest, base, hiOffset);
|
||||||
|
as_lwr(dest, base, lowOffset);
|
||||||
|
#ifdef JS_CODEGEN_MIPS64
|
||||||
|
if (extension != ZeroExtend)
|
||||||
|
as_dext(dest, dest, 0, 32);
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case SizeDouble:
|
||||||
|
as_ldl(dest, base, hiOffset);
|
||||||
|
as_ldr(dest, base, lowOffset);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
MOZ_CRASH("Invalid argument for ma_load");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MacroAssemblerMIPSShared::ma_store(Register data, const BaseIndex& dest,
|
MacroAssemblerMIPSShared::ma_store(Register data, const BaseIndex& dest,
|
||||||
LoadStoreSize size, LoadStoreExtension extension)
|
LoadStoreSize size, LoadStoreExtension extension)
|
||||||
|
@ -545,6 +592,46 @@ MacroAssemblerMIPSShared::ma_store(Imm32 imm, const BaseIndex& dest,
|
||||||
asMasm().ma_store(ScratchRegister, Address(SecondScratchReg, 0), size, extension);
|
asMasm().ma_store(ScratchRegister, Address(SecondScratchReg, 0), size, extension);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssemblerMIPSShared::ma_store_unaligned(Register data, const BaseIndex& dest, Register temp,
|
||||||
|
LoadStoreSize size, LoadStoreExtension extension)
|
||||||
|
{
|
||||||
|
int16_t lowOffset, hiOffset;
|
||||||
|
Register base;
|
||||||
|
|
||||||
|
asMasm().computeEffectiveAddress(dest, SecondScratchReg);
|
||||||
|
|
||||||
|
if (Imm16::IsInSignedRange(dest.offset) && Imm16::IsInSignedRange(dest.offset + size / 8 - 1)) {
|
||||||
|
base = SecondScratchReg;
|
||||||
|
lowOffset = Imm16(dest.offset).encode();
|
||||||
|
hiOffset = Imm16(dest.offset + size / 8 - 1).encode();
|
||||||
|
} else {
|
||||||
|
ma_li(ScratchRegister, Imm32(dest.offset));
|
||||||
|
as_daddu(ScratchRegister, SecondScratchReg, ScratchRegister);
|
||||||
|
base = ScratchRegister;
|
||||||
|
lowOffset = Imm16(0).encode();
|
||||||
|
hiOffset = Imm16(size / 8 - 1).encode();
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (size) {
|
||||||
|
case SizeHalfWord:
|
||||||
|
as_sb(data, base, lowOffset);
|
||||||
|
as_ext(temp, data, 8, 8);
|
||||||
|
as_sb(temp, base, hiOffset);
|
||||||
|
break;
|
||||||
|
case SizeWord:
|
||||||
|
as_swl(data, base, hiOffset);
|
||||||
|
as_swr(data, base, lowOffset);
|
||||||
|
break;
|
||||||
|
case SizeDouble:
|
||||||
|
as_sdl(data, base, hiOffset);
|
||||||
|
as_sdr(data, base, lowOffset);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
MOZ_CRASH("Invalid argument for ma_store");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Branches when done from within mips-specific code.
|
// Branches when done from within mips-specific code.
|
||||||
void
|
void
|
||||||
MacroAssemblerMIPSShared::ma_b(Register lhs, Register rhs, Label* label, Condition c, JumpKind jumpKind)
|
MacroAssemblerMIPSShared::ma_b(Register lhs, Register rhs, Label* label, Condition c, JumpKind jumpKind)
|
||||||
|
@ -909,6 +996,15 @@ MacroAssemblerMIPSShared::ma_lis(FloatRegister dest, float value)
|
||||||
moveToFloat32(ScratchRegister, dest);
|
moveToFloat32(ScratchRegister, dest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssemblerMIPSShared::ma_lis(FloatRegister dest, wasm::RawF32 value)
|
||||||
|
{
|
||||||
|
Imm32 imm(value.bits());
|
||||||
|
|
||||||
|
ma_li(ScratchRegister, imm);
|
||||||
|
moveToFloat32(ScratchRegister, dest);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MacroAssemblerMIPSShared::ma_liNegZero(FloatRegister dest)
|
MacroAssemblerMIPSShared::ma_liNegZero(FloatRegister dest)
|
||||||
{
|
{
|
||||||
|
|
|
@ -104,12 +104,16 @@ class MacroAssemblerMIPSShared : public Assembler
|
||||||
// load
|
// load
|
||||||
void ma_load(Register dest, const BaseIndex& src, LoadStoreSize size = SizeWord,
|
void ma_load(Register dest, const BaseIndex& src, LoadStoreSize size = SizeWord,
|
||||||
LoadStoreExtension extension = SignExtend);
|
LoadStoreExtension extension = SignExtend);
|
||||||
|
void ma_load_unaligned(Register dest, const BaseIndex& src, Register temp,
|
||||||
|
LoadStoreSize size, LoadStoreExtension extension);
|
||||||
|
|
||||||
// store
|
// store
|
||||||
void ma_store(Register data, const BaseIndex& dest, LoadStoreSize size = SizeWord,
|
void ma_store(Register data, const BaseIndex& dest, LoadStoreSize size = SizeWord,
|
||||||
LoadStoreExtension extension = SignExtend);
|
LoadStoreExtension extension = SignExtend);
|
||||||
void ma_store(Imm32 imm, const BaseIndex& dest, LoadStoreSize size = SizeWord,
|
void ma_store(Imm32 imm, const BaseIndex& dest, LoadStoreSize size = SizeWord,
|
||||||
LoadStoreExtension extension = SignExtend);
|
LoadStoreExtension extension = SignExtend);
|
||||||
|
void ma_store_unaligned(Register data, const BaseIndex& dest, Register temp,
|
||||||
|
LoadStoreSize size, LoadStoreExtension extension);
|
||||||
|
|
||||||
// arithmetic based ops
|
// arithmetic based ops
|
||||||
// add
|
// add
|
||||||
|
@ -159,6 +163,7 @@ class MacroAssemblerMIPSShared : public Assembler
|
||||||
|
|
||||||
// fp instructions
|
// fp instructions
|
||||||
void ma_lis(FloatRegister dest, float value);
|
void ma_lis(FloatRegister dest, float value);
|
||||||
|
void ma_lis(FloatRegister dest, wasm::RawF32 value);
|
||||||
void ma_liNegZero(FloatRegister dest);
|
void ma_liNegZero(FloatRegister dest);
|
||||||
|
|
||||||
void ma_sd(FloatRegister fd, BaseIndex address);
|
void ma_sd(FloatRegister fd, BaseIndex address);
|
||||||
|
|
|
@ -33,6 +33,21 @@ ABIArgGenerator::next(MIRType type)
|
||||||
current_ = ABIArg(usedArgSlots_ * sizeof(intptr_t));
|
current_ = ABIArg(usedArgSlots_ * sizeof(intptr_t));
|
||||||
usedArgSlots_++;
|
usedArgSlots_++;
|
||||||
break;
|
break;
|
||||||
|
case MIRType::Int64:
|
||||||
|
if (!usedArgSlots_) {
|
||||||
|
current_ = ABIArg(a0, a1);
|
||||||
|
usedArgSlots_ = 2;
|
||||||
|
} else if (usedArgSlots_ <= 2) {
|
||||||
|
current_ = ABIArg(a2, a3);
|
||||||
|
usedArgSlots_ = 4;
|
||||||
|
} else {
|
||||||
|
if (usedArgSlots_ < NumIntArgRegs)
|
||||||
|
usedArgSlots_ = NumIntArgRegs;
|
||||||
|
usedArgSlots_ += usedArgSlots_ % 2;
|
||||||
|
current_ = ABIArg(usedArgSlots_ * sizeof(intptr_t));
|
||||||
|
usedArgSlots_ += 2;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case MIRType::Float32:
|
case MIRType::Float32:
|
||||||
if (!usedArgSlots_) {
|
if (!usedArgSlots_) {
|
||||||
current_ = ABIArg(f12.asSingle());
|
current_ = ABIArg(f12.asSingle());
|
||||||
|
@ -223,6 +238,51 @@ Assembler::TraceDataRelocations(JSTracer* trc, JitCode* code, CompactBufferReade
|
||||||
::TraceDataRelocations(trc, code->raw(), reader);
|
::TraceDataRelocations(trc, code->raw(), reader);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Assembler::Condition
|
||||||
|
Assembler::UnsignedCondition(Condition cond)
|
||||||
|
{
|
||||||
|
switch (cond) {
|
||||||
|
case Zero:
|
||||||
|
case NonZero:
|
||||||
|
return cond;
|
||||||
|
case LessThan:
|
||||||
|
case Below:
|
||||||
|
return Below;
|
||||||
|
case LessThanOrEqual:
|
||||||
|
case BelowOrEqual:
|
||||||
|
return BelowOrEqual;
|
||||||
|
case GreaterThan:
|
||||||
|
case Above:
|
||||||
|
return Above;
|
||||||
|
case AboveOrEqual:
|
||||||
|
case GreaterThanOrEqual:
|
||||||
|
return AboveOrEqual;
|
||||||
|
default:
|
||||||
|
MOZ_CRASH("unexpected condition");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Assembler::Condition
|
||||||
|
Assembler::ConditionWithoutEqual(Condition cond)
|
||||||
|
{
|
||||||
|
switch (cond) {
|
||||||
|
case LessThan:
|
||||||
|
case LessThanOrEqual:
|
||||||
|
return LessThan;
|
||||||
|
case Below:
|
||||||
|
case BelowOrEqual:
|
||||||
|
return Below;
|
||||||
|
case GreaterThan:
|
||||||
|
case GreaterThanOrEqual:
|
||||||
|
return GreaterThan;
|
||||||
|
case Above:
|
||||||
|
case AboveOrEqual:
|
||||||
|
return Above;
|
||||||
|
default:
|
||||||
|
MOZ_CRASH("unexpected condition");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Assembler::trace(JSTracer* trc)
|
Assembler::trace(JSTracer* trc)
|
||||||
{
|
{
|
||||||
|
|
|
@ -127,6 +127,9 @@ class Assembler : public AssemblerMIPSShared
|
||||||
: AssemblerMIPSShared()
|
: AssemblerMIPSShared()
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
|
static Condition UnsignedCondition(Condition cond);
|
||||||
|
static Condition ConditionWithoutEqual(Condition cond);
|
||||||
|
|
||||||
// MacroAssemblers hold onto gcthings, so they are traced by the GC.
|
// MacroAssemblers hold onto gcthings, so they are traced by the GC.
|
||||||
void trace(JSTracer* trc);
|
void trace(JSTracer* trc);
|
||||||
|
|
||||||
|
|
|
@ -300,6 +300,527 @@ CodeGeneratorMIPS::visitCompareBitwiseAndBranch(LCompareBitwiseAndBranch* lir)
|
||||||
emitBranch(lhs.payloadReg(), rhs.payloadReg(), cond, lir->ifTrue(), lir->ifFalse());
|
emitBranch(lhs.payloadReg(), rhs.payloadReg(), cond, lir->ifTrue(), lir->ifFalse());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGeneratorMIPS::visitCompareI64(LCompareI64* lir)
|
||||||
|
{
|
||||||
|
MCompare* mir = lir->mir();
|
||||||
|
MOZ_ASSERT(mir->compareType() == MCompare::Compare_Int64 ||
|
||||||
|
mir->compareType() == MCompare::Compare_UInt64);
|
||||||
|
|
||||||
|
const LInt64Allocation lhs = lir->getInt64Operand(LCompareI64::Lhs);
|
||||||
|
const LInt64Allocation rhs = lir->getInt64Operand(LCompareI64::Rhs);
|
||||||
|
Register64 lhsRegs = ToRegister64(lhs);
|
||||||
|
Register output = ToRegister(lir->output());
|
||||||
|
|
||||||
|
bool isSigned = mir->compareType() == MCompare::Compare_Int64;
|
||||||
|
Assembler::Condition condition = JSOpToCondition(lir->jsop(), isSigned);
|
||||||
|
Label done;
|
||||||
|
|
||||||
|
masm.move32(Imm32(1), output);
|
||||||
|
if (IsConstant(rhs)) {
|
||||||
|
Imm64 imm = Imm64(ToInt64(rhs));
|
||||||
|
masm.branch64(condition, lhsRegs, imm, &done);
|
||||||
|
} else {
|
||||||
|
Register64 rhsRegs = ToRegister64(rhs);
|
||||||
|
masm.branch64(condition, lhsRegs, rhsRegs, &done);
|
||||||
|
}
|
||||||
|
|
||||||
|
masm.move32(Imm32(0), output);
|
||||||
|
masm.bind(&done);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGeneratorMIPS::visitCompareI64AndBranch(LCompareI64AndBranch* lir)
|
||||||
|
{
|
||||||
|
MCompare* mir = lir->cmpMir();
|
||||||
|
MOZ_ASSERT(mir->compareType() == MCompare::Compare_Int64 ||
|
||||||
|
mir->compareType() == MCompare::Compare_UInt64);
|
||||||
|
|
||||||
|
const LInt64Allocation lhs = lir->getInt64Operand(LCompareI64::Lhs);
|
||||||
|
const LInt64Allocation rhs = lir->getInt64Operand(LCompareI64::Rhs);
|
||||||
|
Register64 lhsRegs = ToRegister64(lhs);
|
||||||
|
|
||||||
|
bool isSigned = mir->compareType() == MCompare::Compare_Int64;
|
||||||
|
Assembler::Condition condition = JSOpToCondition(lir->jsop(), isSigned);
|
||||||
|
|
||||||
|
Label* trueLabel = getJumpLabelForBranch(lir->ifTrue());
|
||||||
|
Label* falseLabel = getJumpLabelForBranch(lir->ifFalse());
|
||||||
|
|
||||||
|
if (isNextBlock(lir->ifFalse()->lir())) {
|
||||||
|
falseLabel = nullptr;
|
||||||
|
} else if (isNextBlock(lir->ifTrue()->lir())) {
|
||||||
|
condition = Assembler::InvertCondition(condition);
|
||||||
|
trueLabel = falseLabel;
|
||||||
|
falseLabel = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsConstant(rhs)) {
|
||||||
|
Imm64 imm = Imm64(ToInt64(rhs));
|
||||||
|
masm.branch64(condition, lhsRegs, imm, trueLabel, falseLabel);
|
||||||
|
} else {
|
||||||
|
Register64 rhsRegs = ToRegister64(rhs);
|
||||||
|
masm.branch64(condition, lhsRegs, rhsRegs, trueLabel, falseLabel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGeneratorMIPS::visitDivOrModI64(LDivOrModI64* lir)
|
||||||
|
{
|
||||||
|
Register64 lhs = ToRegister64(lir->getInt64Operand(LDivOrModI64::Lhs));
|
||||||
|
Register64 rhs = ToRegister64(lir->getInt64Operand(LDivOrModI64::Rhs));
|
||||||
|
Register64 output = ToOutRegister64(lir);
|
||||||
|
|
||||||
|
MOZ_ASSERT(output == ReturnReg64);
|
||||||
|
|
||||||
|
// All inputs are useAtStart for a call instruction. As a result we cannot
|
||||||
|
// ask for a non-aliasing temp. Using the following to get such a temp.
|
||||||
|
AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
|
||||||
|
regs.take(lhs.low);
|
||||||
|
regs.take(lhs.high);
|
||||||
|
if (lhs != rhs) {
|
||||||
|
regs.take(rhs.low);
|
||||||
|
regs.take(rhs.high);
|
||||||
|
}
|
||||||
|
Register temp = regs.takeAny();
|
||||||
|
Label done;
|
||||||
|
|
||||||
|
// Handle divide by zero.
|
||||||
|
if (lir->canBeDivideByZero())
|
||||||
|
masm.branchTest64(Assembler::Zero, rhs, rhs, temp, wasm::JumpTarget::IntegerDivideByZero);
|
||||||
|
|
||||||
|
// Handle an integer overflow exception from INT64_MIN / -1.
|
||||||
|
if (lir->canBeNegativeOverflow()) {
|
||||||
|
Label notmin;
|
||||||
|
masm.branch64(Assembler::NotEqual, lhs, Imm64(INT64_MIN), ¬min);
|
||||||
|
masm.branch64(Assembler::NotEqual, rhs, Imm64(-1), ¬min);
|
||||||
|
if (lir->mir()->isMod())
|
||||||
|
masm.xor64(output, output);
|
||||||
|
else
|
||||||
|
masm.jump(wasm::JumpTarget::IntegerOverflow);
|
||||||
|
masm.jump(&done);
|
||||||
|
masm.bind(¬min);
|
||||||
|
}
|
||||||
|
|
||||||
|
masm.setupUnalignedABICall(temp);
|
||||||
|
masm.passABIArg(lhs.high);
|
||||||
|
masm.passABIArg(lhs.low);
|
||||||
|
masm.passABIArg(rhs.high);
|
||||||
|
masm.passABIArg(rhs.low);
|
||||||
|
|
||||||
|
MOZ_ASSERT(gen->compilingAsmJS());
|
||||||
|
if (lir->mir()->isMod())
|
||||||
|
masm.callWithABI(wasm::SymbolicAddress::ModI64);
|
||||||
|
else
|
||||||
|
masm.callWithABI(wasm::SymbolicAddress::DivI64);
|
||||||
|
MOZ_ASSERT(ReturnReg64 == output);
|
||||||
|
|
||||||
|
masm.bind(&done);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGeneratorMIPS::visitUDivOrModI64(LUDivOrModI64* lir)
|
||||||
|
{
|
||||||
|
Register64 lhs = ToRegister64(lir->getInt64Operand(LDivOrModI64::Lhs));
|
||||||
|
Register64 rhs = ToRegister64(lir->getInt64Operand(LDivOrModI64::Rhs));
|
||||||
|
|
||||||
|
MOZ_ASSERT(ToOutRegister64(lir) == ReturnReg64);
|
||||||
|
|
||||||
|
// All inputs are useAtStart for a call instruction. As a result we cannot
|
||||||
|
// ask for a non-aliasing temp. Using the following to get such a temp.
|
||||||
|
AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
|
||||||
|
regs.take(lhs.low);
|
||||||
|
regs.take(lhs.high);
|
||||||
|
if (lhs != rhs) {
|
||||||
|
regs.take(rhs.low);
|
||||||
|
regs.take(rhs.high);
|
||||||
|
}
|
||||||
|
Register temp = regs.takeAny();
|
||||||
|
|
||||||
|
// Prevent divide by zero.
|
||||||
|
if (lir->canBeDivideByZero())
|
||||||
|
masm.branchTest64(Assembler::Zero, rhs, rhs, temp, wasm::JumpTarget::IntegerDivideByZero);
|
||||||
|
|
||||||
|
masm.setupUnalignedABICall(temp);
|
||||||
|
masm.passABIArg(lhs.high);
|
||||||
|
masm.passABIArg(lhs.low);
|
||||||
|
masm.passABIArg(rhs.high);
|
||||||
|
masm.passABIArg(rhs.low);
|
||||||
|
|
||||||
|
MOZ_ASSERT(gen->compilingAsmJS());
|
||||||
|
if (lir->mir()->isMod())
|
||||||
|
masm.callWithABI(wasm::SymbolicAddress::UModI64);
|
||||||
|
else
|
||||||
|
masm.callWithABI(wasm::SymbolicAddress::UDivI64);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void
|
||||||
|
CodeGeneratorMIPS::emitWasmLoadI64(T* lir)
|
||||||
|
{
|
||||||
|
const MWasmLoad* mir = lir->mir();
|
||||||
|
Register64 output = ToOutRegister64(lir);
|
||||||
|
|
||||||
|
uint32_t offset = mir->offset();
|
||||||
|
MOZ_ASSERT(offset < wasm::OffsetGuardLimit);
|
||||||
|
|
||||||
|
Register ptr = ToRegister(lir->ptr());
|
||||||
|
|
||||||
|
if (offset) {
|
||||||
|
Register ptrPlusOffset = ToRegister(lir->ptrCopy());
|
||||||
|
masm.addPtr(Imm32(offset), ptrPlusOffset);
|
||||||
|
ptr = ptrPlusOffset;
|
||||||
|
} else {
|
||||||
|
MOZ_ASSERT(lir->ptrCopy()->isBogusTemp());
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned byteSize = mir->byteSize();
|
||||||
|
bool isSigned;
|
||||||
|
switch (mir->accessType()) {
|
||||||
|
case Scalar::Int8: isSigned = true; break;
|
||||||
|
case Scalar::Uint8: isSigned = false; break;
|
||||||
|
case Scalar::Int16: isSigned = true; break;
|
||||||
|
case Scalar::Uint16: isSigned = false; break;
|
||||||
|
case Scalar::Int32: isSigned = true; break;
|
||||||
|
case Scalar::Uint32: isSigned = false; break;
|
||||||
|
case Scalar::Int64: isSigned = true; break;
|
||||||
|
default: MOZ_CRASH("unexpected array type");
|
||||||
|
}
|
||||||
|
|
||||||
|
memoryBarrier(mir->barrierBefore());
|
||||||
|
|
||||||
|
MOZ_ASSERT(INT64LOW_OFFSET == 0);
|
||||||
|
if (mir->isUnaligned()) {
|
||||||
|
Register temp = ToRegister(lir->getTemp(1));
|
||||||
|
|
||||||
|
if (byteSize <= 4) {
|
||||||
|
masm.ma_load_unaligned(output.low, BaseIndex(HeapReg, ptr, TimesOne),
|
||||||
|
temp, static_cast<LoadStoreSize>(8 * byteSize),
|
||||||
|
isSigned ? SignExtend : ZeroExtend);
|
||||||
|
if (!isSigned)
|
||||||
|
masm.move32(Imm32(0), output.high);
|
||||||
|
else
|
||||||
|
masm.ma_sra(output.high, output.low, Imm32(31));
|
||||||
|
} else {
|
||||||
|
ScratchRegisterScope scratch(masm);
|
||||||
|
masm.ma_load_unaligned(output.low, BaseIndex(HeapReg, ptr, TimesOne),
|
||||||
|
temp, SizeWord, isSigned ? SignExtend : ZeroExtend);
|
||||||
|
masm.ma_addu(scratch, ptr, Imm32(INT64HIGH_OFFSET));
|
||||||
|
masm.ma_load_unaligned(output.high, BaseIndex(HeapReg, scratch, TimesOne),
|
||||||
|
temp, SizeWord, isSigned ? SignExtend : ZeroExtend);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (byteSize <= 4) {
|
||||||
|
masm.ma_load(output.low, BaseIndex(HeapReg, ptr, TimesOne),
|
||||||
|
static_cast<LoadStoreSize>(8 * byteSize), isSigned ? SignExtend : ZeroExtend);
|
||||||
|
if (!isSigned)
|
||||||
|
masm.move32(Imm32(0), output.high);
|
||||||
|
else
|
||||||
|
masm.ma_sra(output.high, output.low, Imm32(31));
|
||||||
|
} else {
|
||||||
|
ScratchRegisterScope scratch(masm);
|
||||||
|
masm.ma_load(output.low, BaseIndex(HeapReg, ptr, TimesOne), SizeWord);
|
||||||
|
masm.ma_addu(scratch, ptr, Imm32(INT64HIGH_OFFSET));
|
||||||
|
masm.ma_load(output.high, BaseIndex(HeapReg, scratch, TimesOne), SizeWord);
|
||||||
|
}
|
||||||
|
|
||||||
|
memoryBarrier(mir->barrierAfter());
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGeneratorMIPS::visitWasmLoadI64(LWasmLoadI64* lir)
|
||||||
|
{
|
||||||
|
emitWasmLoadI64(lir);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGeneratorMIPS::visitWasmUnalignedLoadI64(LWasmUnalignedLoadI64* lir)
|
||||||
|
{
|
||||||
|
emitWasmLoadI64(lir);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void
|
||||||
|
CodeGeneratorMIPS::emitWasmStoreI64(T* lir)
|
||||||
|
{
|
||||||
|
const MWasmStore* mir = lir->mir();
|
||||||
|
Register64 value = ToRegister64(lir->getInt64Operand(lir->ValueIndex));
|
||||||
|
|
||||||
|
uint32_t offset = mir->offset();
|
||||||
|
MOZ_ASSERT(offset < wasm::OffsetGuardLimit);
|
||||||
|
|
||||||
|
Register ptr = ToRegister(lir->ptr());
|
||||||
|
|
||||||
|
if (offset) {
|
||||||
|
Register ptrPlusOffset = ToRegister(lir->ptrCopy());
|
||||||
|
masm.addPtr(Imm32(offset), ptrPlusOffset);
|
||||||
|
ptr = ptrPlusOffset;
|
||||||
|
} else {
|
||||||
|
MOZ_ASSERT(lir->ptrCopy()->isBogusTemp());
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned byteSize = mir->byteSize();
|
||||||
|
bool isSigned;
|
||||||
|
switch (mir->accessType()) {
|
||||||
|
case Scalar::Int8: isSigned = true; break;
|
||||||
|
case Scalar::Uint8: isSigned = false; break;
|
||||||
|
case Scalar::Int16: isSigned = true; break;
|
||||||
|
case Scalar::Uint16: isSigned = false; break;
|
||||||
|
case Scalar::Int32: isSigned = true; break;
|
||||||
|
case Scalar::Uint32: isSigned = false; break;
|
||||||
|
case Scalar::Int64: isSigned = true; break;
|
||||||
|
default: MOZ_CRASH("unexpected array type");
|
||||||
|
}
|
||||||
|
|
||||||
|
memoryBarrier(mir->barrierBefore());
|
||||||
|
|
||||||
|
MOZ_ASSERT(INT64LOW_OFFSET == 0);
|
||||||
|
if (mir->isUnaligned()) {
|
||||||
|
Register temp = ToRegister(lir->getTemp(1));
|
||||||
|
|
||||||
|
if (byteSize <= 4) {
|
||||||
|
masm.ma_store_unaligned(value.low, BaseIndex(HeapReg, ptr, TimesOne),
|
||||||
|
temp, static_cast<LoadStoreSize>(8 * byteSize),
|
||||||
|
isSigned ? SignExtend : ZeroExtend);
|
||||||
|
} else {
|
||||||
|
ScratchRegisterScope scratch(masm);
|
||||||
|
masm.ma_store_unaligned(value.low, BaseIndex(HeapReg, ptr, TimesOne),
|
||||||
|
temp, SizeWord, isSigned ? SignExtend : ZeroExtend);
|
||||||
|
masm.ma_addu(scratch, ptr, Imm32(INT64HIGH_OFFSET));
|
||||||
|
masm.ma_store_unaligned(value.high, BaseIndex(HeapReg, scratch, TimesOne),
|
||||||
|
temp, SizeWord, isSigned ? SignExtend : ZeroExtend);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (byteSize <= 4) {
|
||||||
|
masm.ma_store(value.low, BaseIndex(HeapReg, ptr, TimesOne),
|
||||||
|
static_cast<LoadStoreSize>(8 * byteSize));
|
||||||
|
} else {
|
||||||
|
ScratchRegisterScope scratch(masm);
|
||||||
|
masm.ma_store(value.low, BaseIndex(HeapReg, ptr, TimesOne), SizeWord);
|
||||||
|
masm.ma_addu(scratch, ptr, Imm32(INT64HIGH_OFFSET));
|
||||||
|
masm.ma_store(value.high, BaseIndex(HeapReg, scratch, TimesOne), SizeWord);
|
||||||
|
}
|
||||||
|
|
||||||
|
memoryBarrier(mir->barrierAfter());
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGeneratorMIPS::visitWasmStoreI64(LWasmStoreI64* lir)
|
||||||
|
{
|
||||||
|
emitWasmStoreI64(lir);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGeneratorMIPS::visitWasmUnalignedStoreI64(LWasmUnalignedStoreI64* lir)
|
||||||
|
{
|
||||||
|
emitWasmStoreI64(lir);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGeneratorMIPS::visitWasmLoadGlobalVarI64(LWasmLoadGlobalVarI64* ins)
|
||||||
|
{
|
||||||
|
const MWasmLoadGlobalVar* mir = ins->mir();
|
||||||
|
unsigned addr = mir->globalDataOffset() - AsmJSGlobalRegBias;
|
||||||
|
MOZ_ASSERT(mir->type() == MIRType::Int64);
|
||||||
|
Register64 output = ToOutRegister64(ins);
|
||||||
|
|
||||||
|
masm.load32(Address(GlobalReg, addr + INT64LOW_OFFSET), output.low);
|
||||||
|
masm.load32(Address(GlobalReg, addr + INT64HIGH_OFFSET), output.high);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGeneratorMIPS::visitWasmStoreGlobalVarI64(LWasmStoreGlobalVarI64* ins)
|
||||||
|
{
|
||||||
|
const MWasmStoreGlobalVar* mir = ins->mir();
|
||||||
|
unsigned addr = mir->globalDataOffset() - AsmJSGlobalRegBias;
|
||||||
|
MOZ_ASSERT (mir->value()->type() == MIRType::Int64);
|
||||||
|
Register64 input = ToRegister64(ins->value());
|
||||||
|
|
||||||
|
masm.store32(input.low, Address(GlobalReg, addr + INT64LOW_OFFSET));
|
||||||
|
masm.store32(input.high, Address(GlobalReg, addr + INT64HIGH_OFFSET));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGeneratorMIPS::visitAsmSelectI64(LAsmSelectI64* lir)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(lir->mir()->type() == MIRType::Int64);
|
||||||
|
Register cond = ToRegister(lir->condExpr());
|
||||||
|
const LInt64Allocation trueExpr = lir->trueExpr();
|
||||||
|
const LInt64Allocation falseExpr = lir->falseExpr();
|
||||||
|
|
||||||
|
Register64 output = ToOutRegister64(lir);
|
||||||
|
|
||||||
|
masm.move64(ToRegister64(trueExpr), output);
|
||||||
|
|
||||||
|
if (falseExpr.low().isRegister()) {
|
||||||
|
masm.as_movz(output.low, ToRegister(falseExpr.low()), cond);
|
||||||
|
masm.as_movz(output.high, ToRegister(falseExpr.high()), cond);
|
||||||
|
} else {
|
||||||
|
Label done;
|
||||||
|
masm.ma_b(cond, cond, &done, Assembler::NonZero, ShortJump);
|
||||||
|
masm.loadPtr(ToAddress(falseExpr.low()), output.low);
|
||||||
|
masm.loadPtr(ToAddress(falseExpr.high()), output.high);
|
||||||
|
masm.bind(&done);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGeneratorMIPS::visitAsmReinterpretFromI64(LAsmReinterpretFromI64* lir)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(lir->mir()->type() == MIRType::Double);
|
||||||
|
MOZ_ASSERT(lir->mir()->input()->type() == MIRType::Int64);
|
||||||
|
Register64 input = ToRegister64(lir->getInt64Operand(0));
|
||||||
|
FloatRegister output = ToFloatRegister(lir->output());
|
||||||
|
|
||||||
|
masm.moveToDoubleLo(input.low, output);
|
||||||
|
masm.moveToDoubleHi(input.high, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGeneratorMIPS::visitAsmReinterpretToI64(LAsmReinterpretToI64* lir)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(lir->mir()->type() == MIRType::Int64);
|
||||||
|
MOZ_ASSERT(lir->mir()->input()->type() == MIRType::Double);
|
||||||
|
FloatRegister input = ToFloatRegister(lir->getOperand(0));
|
||||||
|
Register64 output = ToOutRegister64(lir);
|
||||||
|
|
||||||
|
masm.moveFromDoubleLo(input, output.low);
|
||||||
|
masm.moveFromDoubleHi(input, output.high);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGeneratorMIPS::visitExtendInt32ToInt64(LExtendInt32ToInt64* lir)
|
||||||
|
{
|
||||||
|
Register input = ToRegister(lir->input());
|
||||||
|
Register64 output = ToOutRegister64(lir);
|
||||||
|
|
||||||
|
if (input != output.low)
|
||||||
|
masm.move32(input, output.low);
|
||||||
|
if (lir->mir()->isUnsigned())
|
||||||
|
masm.move32(Imm32(0), output.high);
|
||||||
|
else
|
||||||
|
masm.ma_sra(output.high, output.low, Imm32(31));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGeneratorMIPS::visitWrapInt64ToInt32(LWrapInt64ToInt32* lir)
|
||||||
|
{
|
||||||
|
const LInt64Allocation& input = lir->getInt64Operand(0);
|
||||||
|
Register output = ToRegister(lir->output());
|
||||||
|
|
||||||
|
if (lir->mir()->bottomHalf())
|
||||||
|
masm.move32(ToRegister(input.low()), output);
|
||||||
|
else
|
||||||
|
masm.move32(ToRegister(input.high()), output);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGeneratorMIPS::visitClzI64(LClzI64* lir)
|
||||||
|
{
|
||||||
|
Register64 input = ToRegister64(lir->getInt64Operand(0));
|
||||||
|
Register64 output = ToOutRegister64(lir);
|
||||||
|
masm.clz64(input, output.low);
|
||||||
|
masm.move32(Imm32(0), output.high);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGeneratorMIPS::visitCtzI64(LCtzI64* lir)
|
||||||
|
{
|
||||||
|
Register64 input = ToRegister64(lir->getInt64Operand(0));
|
||||||
|
Register64 output = ToOutRegister64(lir);
|
||||||
|
masm.ctz64(input, output.low);
|
||||||
|
masm.move32(Imm32(0), output.high);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGeneratorMIPS::visitNotI64(LNotI64* lir)
|
||||||
|
{
|
||||||
|
Register64 input = ToRegister64(lir->getInt64Operand(0));
|
||||||
|
Register output = ToRegister(lir->output());
|
||||||
|
|
||||||
|
masm.as_or(output, input.low, input.high);
|
||||||
|
masm.cmp32Set(Assembler::Equal, output, Imm32(0), output);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGeneratorMIPS::visitWasmTruncateToInt64(LWasmTruncateToInt64* lir)
|
||||||
|
{
|
||||||
|
FloatRegister input = ToFloatRegister(lir->input());
|
||||||
|
FloatRegister scratch = input;
|
||||||
|
Register64 output = ToOutRegister64(lir);
|
||||||
|
MWasmTruncateToInt64* mir = lir->mir();
|
||||||
|
MIRType fromType = mir->input()->type();
|
||||||
|
|
||||||
|
auto* ool = new(alloc()) OutOfLineWasmTruncateCheck(mir, input);
|
||||||
|
addOutOfLineCode(ool, mir);
|
||||||
|
|
||||||
|
if (fromType == MIRType::Double) {
|
||||||
|
masm.branchDouble(Assembler::DoubleUnordered, input, input, ool->entry());
|
||||||
|
} else if (fromType == MIRType::Float32) {
|
||||||
|
masm.branchFloat(Assembler::DoubleUnordered, input, input, ool->entry());
|
||||||
|
scratch = ScratchDoubleReg;
|
||||||
|
masm.convertFloat32ToDouble(input, scratch);
|
||||||
|
} else {
|
||||||
|
MOZ_CRASH("unexpected type in visitOutOfLineWasmTruncateCheck");
|
||||||
|
}
|
||||||
|
|
||||||
|
masm.setupUnalignedABICall(output.high);
|
||||||
|
masm.passABIArg(scratch, MoveOp::DOUBLE);
|
||||||
|
if (lir->mir()->isUnsigned())
|
||||||
|
masm.callWithABI(wasm::SymbolicAddress::TruncateDoubleToUint64);
|
||||||
|
else
|
||||||
|
masm.callWithABI(wasm::SymbolicAddress::TruncateDoubleToInt64);
|
||||||
|
masm.ma_b(output.high, Imm32(0x80000000), ool->rejoin(), Assembler::NotEqual);
|
||||||
|
masm.ma_b(output.low, Imm32(0x00000000), ool->rejoin(), Assembler::NotEqual);
|
||||||
|
masm.ma_b(ool->entry());
|
||||||
|
|
||||||
|
masm.bind(ool->rejoin());
|
||||||
|
|
||||||
|
MOZ_ASSERT(ReturnReg64 == output);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGeneratorMIPS::visitInt64ToFloatingPoint(LInt64ToFloatingPoint* lir)
|
||||||
|
{
|
||||||
|
Register64 input = ToRegister64(lir->getInt64Operand(0));
|
||||||
|
FloatRegister output = ToFloatRegister(lir->output());
|
||||||
|
|
||||||
|
MInt64ToFloatingPoint* mir = lir->mir();
|
||||||
|
MIRType toType = mir->type();
|
||||||
|
|
||||||
|
AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
|
||||||
|
regs.take(input.low);
|
||||||
|
regs.take(input.high);
|
||||||
|
Register temp = regs.takeAny();
|
||||||
|
|
||||||
|
masm.setupUnalignedABICall(temp);
|
||||||
|
masm.passABIArg(input.high);
|
||||||
|
masm.passABIArg(input.low);
|
||||||
|
|
||||||
|
if (lir->mir()->isUnsigned())
|
||||||
|
masm.callWithABI(wasm::SymbolicAddress::Uint64ToFloatingPoint, MoveOp::DOUBLE);
|
||||||
|
else
|
||||||
|
masm.callWithABI(wasm::SymbolicAddress::Int64ToFloatingPoint, MoveOp::DOUBLE);
|
||||||
|
|
||||||
|
MOZ_ASSERT_IF(toType == MIRType::Double, output == ReturnDoubleReg);
|
||||||
|
if (toType == MIRType::Float32) {
|
||||||
|
MOZ_ASSERT(output == ReturnFloat32Reg);
|
||||||
|
masm.convertDoubleToFloat32(ReturnDoubleReg, output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGeneratorMIPS::visitTestI64AndBranch(LTestI64AndBranch* lir)
|
||||||
|
{
|
||||||
|
Register64 input = ToRegister64(lir->getInt64Operand(0));
|
||||||
|
|
||||||
|
branchToBlock(input.high, Imm32(0), lir->ifTrue(), Assembler::NonZero);
|
||||||
|
emitBranch(input.low, Imm32(0), Assembler::NonZero, lir->ifTrue(), lir->ifFalse());
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CodeGeneratorMIPS::setReturnDoubleRegs(LiveRegisterSet* regs)
|
CodeGeneratorMIPS::setReturnDoubleRegs(LiveRegisterSet* regs)
|
||||||
{
|
{
|
||||||
|
|
|
@ -33,11 +33,37 @@ class CodeGeneratorMIPS : public CodeGeneratorMIPSShared
|
||||||
|
|
||||||
void emitTableSwitchDispatch(MTableSwitch* mir, Register index, Register base);
|
void emitTableSwitchDispatch(MTableSwitch* mir, Register index, Register base);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void emitWasmLoadI64(T* ins);
|
||||||
|
template <typename T>
|
||||||
|
void emitWasmStoreI64(T* ins);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void visitCompareB(LCompareB* lir);
|
void visitCompareB(LCompareB* lir);
|
||||||
void visitCompareBAndBranch(LCompareBAndBranch* lir);
|
void visitCompareBAndBranch(LCompareBAndBranch* lir);
|
||||||
void visitCompareBitwise(LCompareBitwise* lir);
|
void visitCompareBitwise(LCompareBitwise* lir);
|
||||||
void visitCompareBitwiseAndBranch(LCompareBitwiseAndBranch* lir);
|
void visitCompareBitwiseAndBranch(LCompareBitwiseAndBranch* lir);
|
||||||
|
void visitCompareI64(LCompareI64* lir);
|
||||||
|
void visitCompareI64AndBranch(LCompareI64AndBranch* lir);
|
||||||
|
void visitDivOrModI64(LDivOrModI64* lir);
|
||||||
|
void visitUDivOrModI64(LUDivOrModI64* lir);
|
||||||
|
void visitWasmLoadI64(LWasmLoadI64* ins);
|
||||||
|
void visitWasmUnalignedLoadI64(LWasmUnalignedLoadI64* lir);
|
||||||
|
void visitWasmStoreI64(LWasmStoreI64* ins);
|
||||||
|
void visitWasmUnalignedStoreI64(LWasmUnalignedStoreI64* ins);
|
||||||
|
void visitWasmLoadGlobalVarI64(LWasmLoadGlobalVarI64* ins);
|
||||||
|
void visitWasmStoreGlobalVarI64(LWasmStoreGlobalVarI64* ins);
|
||||||
|
void visitAsmSelectI64(LAsmSelectI64* lir);
|
||||||
|
void visitAsmReinterpretFromI64(LAsmReinterpretFromI64* lir);
|
||||||
|
void visitAsmReinterpretToI64(LAsmReinterpretToI64* lir);
|
||||||
|
void visitExtendInt32ToInt64(LExtendInt32ToInt64* lir);
|
||||||
|
void visitWrapInt64ToInt32(LWrapInt64ToInt32* lir);
|
||||||
|
void visitClzI64(LClzI64* ins);
|
||||||
|
void visitCtzI64(LCtzI64* ins);
|
||||||
|
void visitNotI64(LNotI64* ins);
|
||||||
|
void visitWasmTruncateToInt64(LWasmTruncateToInt64* ins);
|
||||||
|
void visitInt64ToFloatingPoint(LInt64ToFloatingPoint* lir);
|
||||||
|
void visitTestI64AndBranch(LTestI64AndBranch* lir);
|
||||||
|
|
||||||
// Out of line visitors.
|
// Out of line visitors.
|
||||||
void visitOutOfLineBailout(OutOfLineBailout* ool);
|
void visitOutOfLineBailout(OutOfLineBailout* ool);
|
||||||
|
|
|
@ -78,6 +78,79 @@ class LUnboxFloatingPoint : public LInstructionHelper<1, 2, 0>
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class LDivOrModI64 : public LCallInstructionHelper<INT64_PIECES, INT64_PIECES*2, 0>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LIR_HEADER(DivOrModI64)
|
||||||
|
|
||||||
|
static const size_t Lhs = 0;
|
||||||
|
static const size_t Rhs = INT64_PIECES;
|
||||||
|
|
||||||
|
LDivOrModI64(const LInt64Allocation& lhs, const LInt64Allocation& rhs)
|
||||||
|
{
|
||||||
|
setInt64Operand(Lhs, lhs);
|
||||||
|
setInt64Operand(Rhs, rhs);
|
||||||
|
}
|
||||||
|
MBinaryArithInstruction* mir() const {
|
||||||
|
MOZ_ASSERT(mir_->isDiv() || mir_->isMod());
|
||||||
|
return static_cast<MBinaryArithInstruction*>(mir_);
|
||||||
|
}
|
||||||
|
bool canBeDivideByZero() const {
|
||||||
|
if (mir_->isMod())
|
||||||
|
return mir_->toMod()->canBeDivideByZero();
|
||||||
|
return mir_->toDiv()->canBeDivideByZero();
|
||||||
|
}
|
||||||
|
bool canBeNegativeOverflow() const {
|
||||||
|
if (mir_->isMod())
|
||||||
|
return mir_->toMod()->canBeNegativeDividend();
|
||||||
|
return mir_->toDiv()->canBeNegativeOverflow();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class LUDivOrModI64 : public LCallInstructionHelper<INT64_PIECES, INT64_PIECES*2, 0>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LIR_HEADER(UDivOrModI64)
|
||||||
|
|
||||||
|
static const size_t Lhs = 0;
|
||||||
|
static const size_t Rhs = INT64_PIECES;
|
||||||
|
|
||||||
|
LUDivOrModI64(const LInt64Allocation& lhs, const LInt64Allocation& rhs)
|
||||||
|
{
|
||||||
|
setInt64Operand(Lhs, lhs);
|
||||||
|
setInt64Operand(Rhs, rhs);
|
||||||
|
}
|
||||||
|
MBinaryArithInstruction* mir() const {
|
||||||
|
MOZ_ASSERT(mir_->isDiv() || mir_->isMod());
|
||||||
|
return static_cast<MBinaryArithInstruction*>(mir_);
|
||||||
|
}
|
||||||
|
bool canBeDivideByZero() const {
|
||||||
|
if (mir_->isMod())
|
||||||
|
return mir_->toMod()->canBeDivideByZero();
|
||||||
|
return mir_->toDiv()->canBeDivideByZero();
|
||||||
|
}
|
||||||
|
bool canBeNegativeOverflow() const {
|
||||||
|
if (mir_->isMod())
|
||||||
|
return mir_->toMod()->canBeNegativeDividend();
|
||||||
|
return mir_->toDiv()->canBeNegativeOverflow();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class LWasmTruncateToInt64 : public LCallInstructionHelper<INT64_PIECES, 1, 0>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LIR_HEADER(WasmTruncateToInt64);
|
||||||
|
|
||||||
|
explicit LWasmTruncateToInt64(const LAllocation& in)
|
||||||
|
{
|
||||||
|
setOperand(0, in);
|
||||||
|
}
|
||||||
|
|
||||||
|
MWasmTruncateToInt64* mir() const {
|
||||||
|
return mir_->toWasmTruncateToInt64();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace jit
|
} // namespace jit
|
||||||
} // namespace js
|
} // namespace js
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,14 @@
|
||||||
#define LIR_CPU_OPCODE_LIST(_) \
|
#define LIR_CPU_OPCODE_LIST(_) \
|
||||||
_(BoxFloatingPoint) \
|
_(BoxFloatingPoint) \
|
||||||
_(ModMaskI) \
|
_(ModMaskI) \
|
||||||
_(UDivOrMod)
|
_(UDivOrMod) \
|
||||||
|
_(DivOrModI64) \
|
||||||
|
_(UDivOrModI64) \
|
||||||
|
_(WasmUnalignedLoad) \
|
||||||
|
_(WasmUnalignedStore) \
|
||||||
|
_(WasmUnalignedLoadI64) \
|
||||||
|
_(WasmUnalignedStoreI64) \
|
||||||
|
_(WasmTruncateToInt64) \
|
||||||
|
_(Int64ToFloatingPoint)
|
||||||
|
|
||||||
#endif // jit_mips32_LOpcodes_mips32_h__
|
#endif // jit_mips32_LOpcodes_mips32_h__
|
||||||
|
|
|
@ -154,6 +154,36 @@ LIRGeneratorMIPS::lowerUntypedPhiInput(MPhi* phi, uint32_t inputPosition,
|
||||||
payload->setOperand(inputPosition, LUse(VirtualRegisterOfPayload(operand), LUse::ANY));
|
payload->setOperand(inputPosition, LUse(VirtualRegisterOfPayload(operand), LUse::ANY));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LIRGeneratorMIPS::defineInt64Phi(MPhi* phi, size_t lirIndex)
|
||||||
|
{
|
||||||
|
LPhi* low = current->getPhi(lirIndex + INT64LOW_INDEX);
|
||||||
|
LPhi* high = current->getPhi(lirIndex + INT64HIGH_INDEX);
|
||||||
|
|
||||||
|
uint32_t lowVreg = getVirtualRegister();
|
||||||
|
|
||||||
|
phi->setVirtualRegister(lowVreg);
|
||||||
|
|
||||||
|
uint32_t highVreg = getVirtualRegister();
|
||||||
|
MOZ_ASSERT(lowVreg + INT64HIGH_INDEX == highVreg + INT64LOW_INDEX);
|
||||||
|
|
||||||
|
low->setDef(0, LDefinition(lowVreg, LDefinition::INT32));
|
||||||
|
high->setDef(0, LDefinition(highVreg, LDefinition::INT32));
|
||||||
|
annotate(high);
|
||||||
|
annotate(low);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LIRGeneratorMIPS::lowerInt64PhiInput(MPhi* phi, uint32_t inputPosition,
|
||||||
|
LBlock* block, size_t lirIndex)
|
||||||
|
{
|
||||||
|
MDefinition* operand = phi->getOperand(inputPosition);
|
||||||
|
LPhi* low = block->getPhi(lirIndex + INT64LOW_INDEX);
|
||||||
|
LPhi* high = block->getPhi(lirIndex + INT64HIGH_INDEX);
|
||||||
|
low->setOperand(inputPosition, LUse(operand->virtualRegister() + INT64LOW_INDEX, LUse::ANY));
|
||||||
|
high->setOperand(inputPosition, LUse(operand->virtualRegister() + INT64HIGH_INDEX, LUse::ANY));
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
LIRGeneratorMIPS::lowerTruncateDToInt32(MTruncateToInt32* ins)
|
LIRGeneratorMIPS::lowerTruncateDToInt32(MTruncateToInt32* ins)
|
||||||
{
|
{
|
||||||
|
@ -172,6 +202,50 @@ LIRGeneratorMIPS::lowerTruncateFToInt32(MTruncateToInt32* ins)
|
||||||
define(new(alloc()) LTruncateFToInt32(useRegister(opd), LDefinition::BogusTemp()), ins);
|
define(new(alloc()) LTruncateFToInt32(useRegister(opd), LDefinition::BogusTemp()), ins);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LIRGeneratorMIPS::lowerDivI64(MDiv* div)
|
||||||
|
{
|
||||||
|
if (div->isUnsigned()) {
|
||||||
|
lowerUDivI64(div);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LDivOrModI64* lir = new(alloc()) LDivOrModI64(useInt64RegisterAtStart(div->lhs()),
|
||||||
|
useInt64RegisterAtStart(div->rhs()));
|
||||||
|
|
||||||
|
defineReturn(lir, div);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LIRGeneratorMIPS::lowerModI64(MMod* mod)
|
||||||
|
{
|
||||||
|
if (mod->isUnsigned()) {
|
||||||
|
lowerUModI64(mod);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LDivOrModI64* lir = new(alloc()) LDivOrModI64(useInt64RegisterAtStart(mod->lhs()),
|
||||||
|
useInt64RegisterAtStart(mod->rhs()));
|
||||||
|
|
||||||
|
defineReturn(lir, mod);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LIRGeneratorMIPS::lowerUDivI64(MDiv* div)
|
||||||
|
{
|
||||||
|
LUDivOrModI64* lir = new(alloc()) LUDivOrModI64(useInt64RegisterAtStart(div->lhs()),
|
||||||
|
useInt64RegisterAtStart(div->rhs()));
|
||||||
|
defineReturn(lir, div);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LIRGeneratorMIPS::lowerUModI64(MMod* mod)
|
||||||
|
{
|
||||||
|
LUDivOrModI64* lir = new(alloc()) LUDivOrModI64(useInt64RegisterAtStart(mod->lhs()),
|
||||||
|
useInt64RegisterAtStart(mod->rhs()));
|
||||||
|
defineReturn(lir, mod);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
LIRGeneratorMIPS::visitRandom(MRandom* ins)
|
LIRGeneratorMIPS::visitRandom(MRandom* ins)
|
||||||
{
|
{
|
||||||
|
|
|
@ -31,9 +31,17 @@ class LIRGeneratorMIPS : public LIRGeneratorMIPSShared
|
||||||
void lowerUntypedPhiInput(MPhi* phi, uint32_t inputPosition, LBlock* block, size_t lirIndex);
|
void lowerUntypedPhiInput(MPhi* phi, uint32_t inputPosition, LBlock* block, size_t lirIndex);
|
||||||
void defineUntypedPhi(MPhi* phi, size_t lirIndex);
|
void defineUntypedPhi(MPhi* phi, size_t lirIndex);
|
||||||
|
|
||||||
|
void lowerInt64PhiInput(MPhi* phi, uint32_t inputPosition, LBlock* block, size_t lirIndex);
|
||||||
|
void defineInt64Phi(MPhi* phi, size_t lirIndex);
|
||||||
|
|
||||||
void lowerTruncateDToInt32(MTruncateToInt32* ins);
|
void lowerTruncateDToInt32(MTruncateToInt32* ins);
|
||||||
void lowerTruncateFToInt32(MTruncateToInt32* ins);
|
void lowerTruncateFToInt32(MTruncateToInt32* ins);
|
||||||
|
|
||||||
|
void lowerDivI64(MDiv* div);
|
||||||
|
void lowerModI64(MMod* mod);
|
||||||
|
void lowerUDivI64(MDiv* div);
|
||||||
|
void lowerUModI64(MMod* mod);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void visitBox(MBox* box);
|
void visitBox(MBox* box);
|
||||||
void visitUnbox(MUnbox* unbox);
|
void visitUnbox(MUnbox* unbox);
|
||||||
|
|
|
@ -48,22 +48,35 @@ MacroAssembler::andPtr(Imm32 imm, Register dest)
|
||||||
void
|
void
|
||||||
MacroAssembler::and64(Imm64 imm, Register64 dest)
|
MacroAssembler::and64(Imm64 imm, Register64 dest)
|
||||||
{
|
{
|
||||||
and32(Imm32(imm.value & LOW_32_MASK), dest.low);
|
if (imm.low().value != int32_t(0xFFFFFFFF))
|
||||||
and32(Imm32((imm.value >> 32) & LOW_32_MASK), dest.high);
|
and32(imm.low(), dest.low);
|
||||||
|
if (imm.hi().value != int32_t(0xFFFFFFFF))
|
||||||
|
and32(imm.hi(), dest.high);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssembler::and64(Register64 src, Register64 dest)
|
||||||
|
{
|
||||||
|
and32(src.low, dest.low);
|
||||||
|
and32(src.high, dest.high);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MacroAssembler::or64(Imm64 imm, Register64 dest)
|
MacroAssembler::or64(Imm64 imm, Register64 dest)
|
||||||
{
|
{
|
||||||
or32(Imm32(imm.value & LOW_32_MASK), dest.low);
|
if (imm.low().value)
|
||||||
or32(Imm32((imm.value >> 32) & LOW_32_MASK), dest.high);
|
or32(imm.low(), dest.low);
|
||||||
|
if (imm.hi().value)
|
||||||
|
or32(imm.hi(), dest.high);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MacroAssembler::xor64(Imm64 imm, Register64 dest)
|
MacroAssembler::xor64(Imm64 imm, Register64 dest)
|
||||||
{
|
{
|
||||||
xor32(Imm32(imm.value & LOW_32_MASK), dest.low);
|
if (imm.low().value)
|
||||||
xor32(Imm32((imm.value >> 32) & LOW_32_MASK), dest.high);
|
xor32(imm.low(), dest.low);
|
||||||
|
if (imm.hi().value)
|
||||||
|
xor32(imm.hi(), dest.high);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -137,11 +150,19 @@ MacroAssembler::add64(Register64 src, Register64 dest)
|
||||||
void
|
void
|
||||||
MacroAssembler::add64(Imm32 imm, Register64 dest)
|
MacroAssembler::add64(Imm32 imm, Register64 dest)
|
||||||
{
|
{
|
||||||
as_addiu(dest.low, dest.low, imm.value);
|
ma_li(ScratchRegister, imm);
|
||||||
as_sltiu(ScratchRegister, dest.low, imm.value);
|
as_addu(dest.low, dest.low, ScratchRegister);
|
||||||
|
as_sltu(ScratchRegister, dest.low, ScratchRegister);
|
||||||
as_addu(dest.high, dest.high, ScratchRegister);
|
as_addu(dest.high, dest.high, ScratchRegister);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssembler::add64(Imm64 imm, Register64 dest)
|
||||||
|
{
|
||||||
|
add64(imm.low(), dest);
|
||||||
|
ma_addu(dest.high, dest.high, imm.hi());
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MacroAssembler::subPtr(Register src, Register dest)
|
MacroAssembler::subPtr(Register src, Register dest)
|
||||||
{
|
{
|
||||||
|
@ -154,6 +175,25 @@ MacroAssembler::subPtr(Imm32 imm, Register dest)
|
||||||
ma_subu(dest, dest, imm);
|
ma_subu(dest, dest, imm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssembler::sub64(Register64 src, Register64 dest)
|
||||||
|
{
|
||||||
|
as_sltu(ScratchRegister, dest.low, src.low);
|
||||||
|
as_subu(dest.high, dest.high, ScratchRegister);
|
||||||
|
as_subu(dest.low, dest.low, src.low);
|
||||||
|
as_subu(dest.high, dest.high, src.high);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssembler::sub64(Imm64 imm, Register64 dest)
|
||||||
|
{
|
||||||
|
ma_li(ScratchRegister, imm.low());
|
||||||
|
as_sltu(ScratchRegister, dest.low, ScratchRegister);
|
||||||
|
as_subu(dest.high, dest.high, ScratchRegister);
|
||||||
|
ma_subu(dest.low, dest.low, imm.low());
|
||||||
|
ma_subu(dest.high, dest.high, imm.hi());
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MacroAssembler::mul64(Imm64 imm, const Register64& dest)
|
MacroAssembler::mul64(Imm64 imm, const Register64& dest)
|
||||||
{
|
{
|
||||||
|
@ -199,6 +239,66 @@ MacroAssembler::mul64(Imm64 imm, const Register64& dest)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssembler::mul64(Imm64 imm, const Register64& dest, const Register temp)
|
||||||
|
{
|
||||||
|
// LOW32 = LOW(LOW(dest) * LOW(imm));
|
||||||
|
// HIGH32 = LOW(HIGH(dest) * LOW(imm)) [multiply imm into upper bits]
|
||||||
|
// + LOW(LOW(dest) * HIGH(imm)) [multiply dest into upper bits]
|
||||||
|
// + HIGH(LOW(dest) * LOW(imm)) [carry]
|
||||||
|
|
||||||
|
// HIGH(dest) = LOW(HIGH(dest) * LOW(imm));
|
||||||
|
MOZ_ASSERT(temp != dest.high && temp != dest.low);
|
||||||
|
|
||||||
|
ma_li(ScratchRegister, imm.firstHalf());
|
||||||
|
as_multu(dest.high, ScratchRegister);
|
||||||
|
as_mflo(dest.high);
|
||||||
|
|
||||||
|
ma_li(ScratchRegister, imm.secondHalf());
|
||||||
|
as_multu(dest.low, ScratchRegister);
|
||||||
|
as_mflo(temp);
|
||||||
|
as_addu(temp, dest.high, temp);
|
||||||
|
|
||||||
|
ma_li(ScratchRegister, imm.firstHalf());
|
||||||
|
as_multu(dest.low, ScratchRegister);
|
||||||
|
as_mfhi(dest.high);
|
||||||
|
as_mflo(dest.low);
|
||||||
|
as_addu(dest.high, dest.high, temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssembler::mul64(const Register64& src, const Register64& dest, const Register temp)
|
||||||
|
{
|
||||||
|
// LOW32 = LOW(LOW(dest) * LOW(imm));
|
||||||
|
// HIGH32 = LOW(HIGH(dest) * LOW(imm)) [multiply imm into upper bits]
|
||||||
|
// + LOW(LOW(dest) * HIGH(imm)) [multiply dest into upper bits]
|
||||||
|
// + HIGH(LOW(dest) * LOW(imm)) [carry]
|
||||||
|
|
||||||
|
// HIGH(dest) = LOW(HIGH(dest) * LOW(imm));
|
||||||
|
MOZ_ASSERT(dest != src);
|
||||||
|
MOZ_ASSERT(dest.low != src.high && dest.high != src.low);
|
||||||
|
|
||||||
|
as_multu(dest.high, src.low); // (2)
|
||||||
|
as_mflo(dest.high);
|
||||||
|
as_multu(dest.low, src.high); // (3)
|
||||||
|
as_mflo(temp);
|
||||||
|
as_addu(temp, dest.high, temp);
|
||||||
|
as_multu(dest.low, src.low); // (4) + (1)
|
||||||
|
as_mfhi(dest.high);
|
||||||
|
as_mflo(dest.low);
|
||||||
|
as_addu(dest.high, dest.high, temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssembler::neg64(Register64 reg)
|
||||||
|
{
|
||||||
|
ma_li(ScratchRegister, Imm32(1));
|
||||||
|
as_movz(ScratchRegister, zero, reg.low);
|
||||||
|
ma_negu(reg.low, reg.low);
|
||||||
|
as_addu(reg.high, reg.high, ScratchRegister);
|
||||||
|
ma_negu(reg.high, reg.high);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MacroAssembler::mulBy3(Register src, Register dest)
|
MacroAssembler::mulBy3(Register src, Register dest)
|
||||||
{
|
{
|
||||||
|
@ -239,10 +339,46 @@ MacroAssembler::lshift64(Imm32 imm, Register64 dest)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(0 <= imm.value && imm.value < 64);
|
MOZ_ASSERT(0 <= imm.value && imm.value < 64);
|
||||||
ScratchRegisterScope scratch(*this);
|
ScratchRegisterScope scratch(*this);
|
||||||
as_sll(dest.high, dest.high, imm.value);
|
|
||||||
as_srl(scratch, dest.low, 32 - imm.value);
|
if (imm.value == 0) {
|
||||||
as_or(dest.high, dest.high, scratch);
|
return;
|
||||||
as_sll(dest.low, dest.low, imm.value);
|
} else if (imm.value < 32) {
|
||||||
|
as_sll(dest.high, dest.high, imm.value);
|
||||||
|
as_srl(scratch, dest.low, 32 - imm.value);
|
||||||
|
as_or(dest.high, dest.high, scratch);
|
||||||
|
as_sll(dest.low, dest.low, imm.value);
|
||||||
|
} else {
|
||||||
|
as_sll(dest.high, dest.low, imm.value - 32);
|
||||||
|
move32(Imm32(0), dest.low);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssembler::lshift64(Register unmaskedShift, Register64 dest)
|
||||||
|
{
|
||||||
|
Label done, less;
|
||||||
|
ScratchRegisterScope shift(*this);
|
||||||
|
|
||||||
|
ma_and(shift, unmaskedShift, Imm32(0x3f));
|
||||||
|
ma_b(shift, Imm32(0), &done, Equal);
|
||||||
|
|
||||||
|
ma_sll(dest.high, dest.high, shift);
|
||||||
|
ma_subu(shift, shift, Imm32(32));
|
||||||
|
ma_b(shift, Imm32(0), &less, LessThan);
|
||||||
|
|
||||||
|
ma_sll(dest.high, dest.low, shift);
|
||||||
|
move32(Imm32(0), dest.low);
|
||||||
|
ma_b(&done);
|
||||||
|
|
||||||
|
bind(&less);
|
||||||
|
ma_li(SecondScratchReg, Imm32(0));
|
||||||
|
as_subu(shift, SecondScratchReg, shift);
|
||||||
|
ma_srl(SecondScratchReg, dest.low, shift);
|
||||||
|
as_or(dest.high, dest.high, SecondScratchReg);
|
||||||
|
ma_and(shift, unmaskedShift, Imm32(0x3f));
|
||||||
|
ma_sll(dest.low, dest.low, shift);
|
||||||
|
|
||||||
|
bind(&done);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -264,10 +400,323 @@ MacroAssembler::rshift64(Imm32 imm, Register64 dest)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(0 <= imm.value && imm.value < 64);
|
MOZ_ASSERT(0 <= imm.value && imm.value < 64);
|
||||||
ScratchRegisterScope scratch(*this);
|
ScratchRegisterScope scratch(*this);
|
||||||
as_srl(dest.low, dest.low, imm.value);
|
|
||||||
as_sll(scratch, dest.high, 32 - imm.value);
|
if (imm.value < 32) {
|
||||||
as_or(dest.low, dest.low, scratch);
|
as_srl(dest.low, dest.low, imm.value);
|
||||||
as_srl(dest.high, dest.high, imm.value);
|
as_sll(scratch, dest.high, 32 - imm.value);
|
||||||
|
as_or(dest.low, dest.low, scratch);
|
||||||
|
as_srl(dest.high, dest.high, imm.value);
|
||||||
|
} else if (imm.value == 32) {
|
||||||
|
ma_move(dest.low, dest.high);
|
||||||
|
move32(Imm32(0), dest.high);
|
||||||
|
} else {
|
||||||
|
ma_srl(dest.low, dest.high, Imm32(imm.value - 32));
|
||||||
|
move32(Imm32(0), dest.high);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssembler::rshift64(Register unmaskedShift, Register64 dest)
|
||||||
|
{
|
||||||
|
Label done, less;
|
||||||
|
ScratchRegisterScope shift(*this);
|
||||||
|
|
||||||
|
ma_and(shift, unmaskedShift, Imm32(0x3f));
|
||||||
|
ma_srl(dest.low, dest.low, shift);
|
||||||
|
ma_subu(shift, shift, Imm32(32));
|
||||||
|
ma_b(shift, Imm32(0), &less, LessThan);
|
||||||
|
|
||||||
|
ma_srl(dest.low, dest.high, shift);
|
||||||
|
move32(Imm32(0), dest.high);
|
||||||
|
ma_b(&done);
|
||||||
|
|
||||||
|
bind(&less);
|
||||||
|
ma_li(SecondScratchReg, Imm32(0));
|
||||||
|
as_subu(shift, SecondScratchReg, shift);
|
||||||
|
ma_sll(SecondScratchReg, dest.high, shift);
|
||||||
|
as_or(dest.low, dest.low, SecondScratchReg);
|
||||||
|
ma_and(shift, unmaskedShift, Imm32(0x3f));
|
||||||
|
ma_srl(dest.high, dest.high, shift);
|
||||||
|
|
||||||
|
bind(&done);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssembler::rshift64Arithmetic(Imm32 imm, Register64 dest)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(0 <= imm.value && imm.value < 64);
|
||||||
|
ScratchRegisterScope scratch(*this);
|
||||||
|
|
||||||
|
if (imm.value < 32) {
|
||||||
|
as_srl(dest.low, dest.low, imm.value);
|
||||||
|
as_sll(scratch, dest.high, 32 - imm.value);
|
||||||
|
as_or(dest.low, dest.low, scratch);
|
||||||
|
as_sra(dest.high, dest.high, imm.value);
|
||||||
|
} else if (imm.value == 32) {
|
||||||
|
ma_move(dest.low, dest.high);
|
||||||
|
as_sra(dest.high, dest.high, 31);
|
||||||
|
} else {
|
||||||
|
as_sra(dest.low, dest.high, imm.value - 32);
|
||||||
|
as_sra(dest.high, dest.high, 31);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssembler::rshift64Arithmetic(Register unmaskedShift, Register64 dest)
|
||||||
|
{
|
||||||
|
Label done, less;
|
||||||
|
|
||||||
|
ScratchRegisterScope shift(*this);
|
||||||
|
ma_and(shift, unmaskedShift, Imm32(0x3f));
|
||||||
|
|
||||||
|
ma_srl(dest.low, dest.low, shift);
|
||||||
|
ma_subu(shift, shift, Imm32(32));
|
||||||
|
ma_b(shift, Imm32(0), &less, LessThan);
|
||||||
|
|
||||||
|
ma_sra(dest.low, dest.high, shift);
|
||||||
|
as_sra(dest.high, dest.high, 31);
|
||||||
|
ma_b(&done);
|
||||||
|
|
||||||
|
bind(&less);
|
||||||
|
ma_li(SecondScratchReg, Imm32(0));
|
||||||
|
as_subu(shift, SecondScratchReg, shift);
|
||||||
|
ma_sll(SecondScratchReg, dest.high, shift);
|
||||||
|
as_or(dest.low, dest.low, SecondScratchReg);
|
||||||
|
ma_and(shift, unmaskedShift, Imm32(0x3f));
|
||||||
|
ma_sra(dest.high, dest.high, shift);
|
||||||
|
|
||||||
|
bind(&done);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===============================================================
|
||||||
|
// Rotation functions
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssembler::rotateLeft64(Imm32 count, Register64 input, Register64 dest, Register temp)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(temp == InvalidReg);
|
||||||
|
MOZ_ASSERT(input.low != dest.high && input.high != dest.low);
|
||||||
|
|
||||||
|
int32_t amount = count.value & 0x3f;
|
||||||
|
if (amount > 32) {
|
||||||
|
rotateRight64(Imm32(64 - amount), input, dest, temp);
|
||||||
|
} else {
|
||||||
|
ScratchRegisterScope scratch(*this);
|
||||||
|
if (amount == 0) {
|
||||||
|
ma_move(dest.low, input.low);
|
||||||
|
ma_move(dest.high, input.high);
|
||||||
|
} else if (amount == 32) {
|
||||||
|
ma_move(scratch, input.low);
|
||||||
|
ma_move(dest.low, input.high);
|
||||||
|
ma_move(dest.high, scratch);
|
||||||
|
} else {
|
||||||
|
MOZ_ASSERT(0 < amount && amount < 32);
|
||||||
|
ma_move(scratch, input.high);
|
||||||
|
ma_sll(dest.high, input.high, Imm32(amount));
|
||||||
|
ma_srl(SecondScratchReg, input.low, Imm32(32 - amount));
|
||||||
|
as_or(dest.high, dest.high, SecondScratchReg);
|
||||||
|
ma_sll(dest.low, input.low, Imm32(amount));
|
||||||
|
ma_srl(SecondScratchReg, scratch, Imm32(32 - amount));
|
||||||
|
as_or(dest.low, dest.low, SecondScratchReg);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssembler::rotateLeft64(Register shift, Register64 src, Register64 dest, Register temp)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(temp != src.low && temp != src.high);
|
||||||
|
MOZ_ASSERT(shift != src.low && shift != src.high);
|
||||||
|
MOZ_ASSERT(temp != InvalidReg);
|
||||||
|
|
||||||
|
ScratchRegisterScope shift_value(*this);
|
||||||
|
Label high, done, zero;
|
||||||
|
|
||||||
|
ma_and(temp, shift, Imm32(0x3f));
|
||||||
|
ma_b(temp, Imm32(32), &high, GreaterThanOrEqual);
|
||||||
|
|
||||||
|
// high = high << shift | low >> 32 - shift
|
||||||
|
// low = low << shift | high >> 32 - shift
|
||||||
|
ma_sll(dest.high, src.high, temp);
|
||||||
|
ma_b(temp, Imm32(0), &zero, Equal);
|
||||||
|
ma_li(SecondScratchReg, Imm32(32));
|
||||||
|
as_subu(shift_value, SecondScratchReg, temp);
|
||||||
|
|
||||||
|
ma_srl(SecondScratchReg, src.low, shift_value);
|
||||||
|
as_or(dest.high, dest.high, SecondScratchReg);
|
||||||
|
|
||||||
|
ma_sll(dest.low, src.low, temp);
|
||||||
|
ma_srl(SecondScratchReg, src.high, shift_value);
|
||||||
|
as_or(dest.low, dest.low, SecondScratchReg);
|
||||||
|
ma_b(&done);
|
||||||
|
|
||||||
|
bind(&zero);
|
||||||
|
ma_move(dest.low, src.low);
|
||||||
|
ma_move(dest.high, src.high);
|
||||||
|
ma_b(&done);
|
||||||
|
|
||||||
|
// A 32 - 64 shift is a 0 - 32 shift in the other direction.
|
||||||
|
bind(&high);
|
||||||
|
ma_and(shift, shift, Imm32(0x3f));
|
||||||
|
ma_li(SecondScratchReg, Imm32(64));
|
||||||
|
as_subu(temp, SecondScratchReg, shift);
|
||||||
|
|
||||||
|
ma_srl(dest.high, src.high, temp);
|
||||||
|
ma_li(SecondScratchReg, Imm32(32));
|
||||||
|
as_subu(shift_value, SecondScratchReg, temp);
|
||||||
|
ma_sll(SecondScratchReg, src.low, shift_value);
|
||||||
|
as_or(dest.high, dest.high, SecondScratchReg);
|
||||||
|
|
||||||
|
ma_srl(dest.low, src.low, temp);
|
||||||
|
ma_sll(SecondScratchReg, src.high, shift_value);
|
||||||
|
as_or(dest.low, dest.low, SecondScratchReg);
|
||||||
|
|
||||||
|
bind(&done);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssembler::rotateRight64(Imm32 count, Register64 input, Register64 dest, Register temp)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(temp == InvalidReg);
|
||||||
|
MOZ_ASSERT(input.low != dest.high && input.high != dest.low);
|
||||||
|
|
||||||
|
int32_t amount = count.value & 0x3f;
|
||||||
|
if (amount > 32) {
|
||||||
|
rotateLeft64(Imm32(64 - amount), input, dest, temp);
|
||||||
|
} else {
|
||||||
|
ScratchRegisterScope scratch(*this);
|
||||||
|
if (amount == 0) {
|
||||||
|
ma_move(dest.low, input.low);
|
||||||
|
ma_move(dest.high, input.high);
|
||||||
|
} else if (amount == 32) {
|
||||||
|
ma_move(scratch, input.low);
|
||||||
|
ma_move(dest.low, input.high);
|
||||||
|
ma_move(dest.high, scratch);
|
||||||
|
} else {
|
||||||
|
MOZ_ASSERT(0 < amount && amount < 32);
|
||||||
|
ma_move(scratch, input.high);
|
||||||
|
ma_srl(dest.high, input.high, Imm32(amount));
|
||||||
|
ma_sll(SecondScratchReg, input.low, Imm32(32 - amount));
|
||||||
|
as_or(dest.high, dest.high, SecondScratchReg);
|
||||||
|
ma_srl(dest.low, input.low, Imm32(amount));
|
||||||
|
ma_sll(SecondScratchReg, scratch, Imm32(32 - amount));
|
||||||
|
as_or(dest.low, dest.low, SecondScratchReg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssembler::rotateRight64(Register shift, Register64 src, Register64 dest, Register temp)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(temp != src.low && temp != src.high);
|
||||||
|
MOZ_ASSERT(shift != src.low && shift != src.high);
|
||||||
|
MOZ_ASSERT(temp != InvalidReg);
|
||||||
|
|
||||||
|
ScratchRegisterScope shift_value(*this);
|
||||||
|
Label high, done, zero;
|
||||||
|
|
||||||
|
ma_and(temp, shift, Imm32(0x3f));
|
||||||
|
ma_b(temp, Imm32(32), &high, GreaterThanOrEqual);
|
||||||
|
|
||||||
|
// high = high >> shift | low << 32 - shift
|
||||||
|
// low = low >> shift | high << 32 - shift
|
||||||
|
ma_srl(dest.high, src.high, temp);
|
||||||
|
ma_b(temp, Imm32(0), &zero, Equal);
|
||||||
|
ma_li(SecondScratchReg, Imm32(32));
|
||||||
|
as_subu(shift_value, SecondScratchReg, temp);
|
||||||
|
|
||||||
|
ma_sll(SecondScratchReg, src.low, shift_value);
|
||||||
|
as_or(dest.high, dest.high, SecondScratchReg);
|
||||||
|
|
||||||
|
ma_srl(dest.low, src.low, temp);
|
||||||
|
|
||||||
|
//ma_li(SecondScratchReg, Imm32(32));
|
||||||
|
//as_subu(shift_value, SecondScratchReg, shift_value);
|
||||||
|
ma_sll(SecondScratchReg, src.high, shift_value);
|
||||||
|
as_or(dest.low, dest.low, SecondScratchReg);
|
||||||
|
|
||||||
|
ma_b(&done);
|
||||||
|
|
||||||
|
bind(&zero);
|
||||||
|
ma_move(dest.low, src.low);
|
||||||
|
ma_move(dest.high, src.high);
|
||||||
|
ma_b(&done);
|
||||||
|
|
||||||
|
// A 32 - 64 shift is a 0 - 32 shift in the other direction.
|
||||||
|
bind(&high);
|
||||||
|
ma_and(shift, shift, Imm32(0x3f));
|
||||||
|
ma_li(SecondScratchReg, Imm32(64));
|
||||||
|
as_subu(temp, SecondScratchReg, shift);
|
||||||
|
|
||||||
|
ma_sll(dest.high, src.high, temp);
|
||||||
|
ma_li(SecondScratchReg, Imm32(32));
|
||||||
|
as_subu(shift_value, SecondScratchReg, temp);
|
||||||
|
|
||||||
|
ma_srl(SecondScratchReg, src.low, shift_value);
|
||||||
|
as_or(dest.high, dest.high, SecondScratchReg);
|
||||||
|
|
||||||
|
ma_sll(dest.low, src.low, temp);
|
||||||
|
ma_srl(SecondScratchReg, src.high, shift_value);
|
||||||
|
as_or(dest.low, dest.low, SecondScratchReg);
|
||||||
|
|
||||||
|
bind(&done);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===============================================================
|
||||||
|
// Bit counting functions
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssembler::clz64(Register64 src, Register dest)
|
||||||
|
{
|
||||||
|
Label done, low;
|
||||||
|
|
||||||
|
ma_b(src.high, Imm32(0), &low, Equal);
|
||||||
|
as_clz(dest, src.high);
|
||||||
|
ma_b(&done);
|
||||||
|
|
||||||
|
bind(&low);
|
||||||
|
as_clz(dest, src.low);
|
||||||
|
ma_addu(dest, Imm32(32));
|
||||||
|
|
||||||
|
bind(&done);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssembler::ctz64(Register64 src, Register dest)
|
||||||
|
{
|
||||||
|
Label done, high;
|
||||||
|
|
||||||
|
ma_b(src.low, Imm32(0), &high, Equal);
|
||||||
|
|
||||||
|
ma_ctz(dest, src.low);
|
||||||
|
ma_b(&done);
|
||||||
|
|
||||||
|
bind(&high);
|
||||||
|
ma_ctz(dest, src.high);
|
||||||
|
ma_addu(dest, Imm32(32));
|
||||||
|
|
||||||
|
bind(&done);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssembler::popcnt64(Register64 src, Register64 dest, Register tmp)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(dest.low != tmp);
|
||||||
|
MOZ_ASSERT(dest.high != tmp);
|
||||||
|
MOZ_ASSERT(dest.low != dest.high);
|
||||||
|
|
||||||
|
if (dest.low != src.high) {
|
||||||
|
popcnt32(src.low, dest.low, tmp);
|
||||||
|
popcnt32(src.high, dest.high, tmp);
|
||||||
|
} else {
|
||||||
|
MOZ_ASSERT(dest.high != src.high);
|
||||||
|
popcnt32(src.low, dest.high, tmp);
|
||||||
|
popcnt32(src.high, dest.low, tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
ma_addu(dest.low, dest.high);
|
||||||
|
move32(Imm32(0), dest.high);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===============================================================
|
// ===============================================================
|
||||||
|
@ -299,6 +748,112 @@ MacroAssembler::branch64(Condition cond, const Address& lhs, const Address& rhs,
|
||||||
branch32(cond, Address(lhs.base, lhs.offset + sizeof(uint32_t)), scratch, label);
|
branch32(cond, Address(lhs.base, lhs.offset + sizeof(uint32_t)), scratch, label);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssembler::branch64(Condition cond, Register64 lhs, Imm64 val, Label* success, Label* fail)
|
||||||
|
{
|
||||||
|
bool fallthrough = false;
|
||||||
|
Label fallthroughLabel;
|
||||||
|
|
||||||
|
if (!fail) {
|
||||||
|
fail = &fallthroughLabel;
|
||||||
|
fallthrough = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(cond) {
|
||||||
|
case Assembler::Equal:
|
||||||
|
branch32(Assembler::NotEqual, lhs.low, val.low(), fail);
|
||||||
|
branch32(Assembler::Equal, lhs.high, val.hi(), success);
|
||||||
|
if (!fallthrough)
|
||||||
|
jump(fail);
|
||||||
|
break;
|
||||||
|
case Assembler::NotEqual:
|
||||||
|
branch32(Assembler::NotEqual, lhs.low, val.low(), success);
|
||||||
|
branch32(Assembler::NotEqual, lhs.high, val.hi(), success);
|
||||||
|
if (!fallthrough)
|
||||||
|
jump(fail);
|
||||||
|
break;
|
||||||
|
case Assembler::LessThan:
|
||||||
|
case Assembler::LessThanOrEqual:
|
||||||
|
case Assembler::GreaterThan:
|
||||||
|
case Assembler::GreaterThanOrEqual:
|
||||||
|
case Assembler::Below:
|
||||||
|
case Assembler::BelowOrEqual:
|
||||||
|
case Assembler::Above:
|
||||||
|
case Assembler::AboveOrEqual: {
|
||||||
|
Assembler::Condition invert_cond = Assembler::InvertCondition(cond);
|
||||||
|
Assembler::Condition cond1 = Assembler::ConditionWithoutEqual(cond);
|
||||||
|
Assembler::Condition cond2 = Assembler::ConditionWithoutEqual(invert_cond);
|
||||||
|
Assembler::Condition cond3 = Assembler::UnsignedCondition(cond);
|
||||||
|
|
||||||
|
ma_b(lhs.high, val.hi(), success, cond1);
|
||||||
|
ma_b(lhs.high, val.hi(), fail, cond2);
|
||||||
|
ma_b(lhs.low, val.low(), success, cond3);
|
||||||
|
if (!fallthrough)
|
||||||
|
jump(fail);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
MOZ_CRASH("Condition code not supported");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fallthrough)
|
||||||
|
bind(fail);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssembler::branch64(Condition cond, Register64 lhs, Register64 rhs, Label* success, Label* fail)
|
||||||
|
{
|
||||||
|
bool fallthrough = false;
|
||||||
|
Label fallthroughLabel;
|
||||||
|
|
||||||
|
if (!fail) {
|
||||||
|
fail = &fallthroughLabel;
|
||||||
|
fallthrough = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(cond) {
|
||||||
|
case Assembler::Equal:
|
||||||
|
branch32(Assembler::NotEqual, lhs.low, rhs.low, fail);
|
||||||
|
branch32(Assembler::Equal, lhs.high, rhs.high, success);
|
||||||
|
if (!fallthrough)
|
||||||
|
jump(fail);
|
||||||
|
break;
|
||||||
|
case Assembler::NotEqual:
|
||||||
|
branch32(Assembler::NotEqual, lhs.low, rhs.low, success);
|
||||||
|
branch32(Assembler::NotEqual, lhs.high, rhs.high, success);
|
||||||
|
if (!fallthrough)
|
||||||
|
jump(fail);
|
||||||
|
break;
|
||||||
|
case Assembler::LessThan:
|
||||||
|
case Assembler::LessThanOrEqual:
|
||||||
|
case Assembler::GreaterThan:
|
||||||
|
case Assembler::GreaterThanOrEqual:
|
||||||
|
case Assembler::Below:
|
||||||
|
case Assembler::BelowOrEqual:
|
||||||
|
case Assembler::Above:
|
||||||
|
case Assembler::AboveOrEqual: {
|
||||||
|
Assembler::Condition invert_cond = Assembler::InvertCondition(cond);
|
||||||
|
Assembler::Condition cond1 = Assembler::ConditionWithoutEqual(cond);
|
||||||
|
Assembler::Condition cond2 = Assembler::ConditionWithoutEqual(invert_cond);
|
||||||
|
Assembler::Condition cond3 = Assembler::UnsignedCondition(cond);
|
||||||
|
|
||||||
|
ma_b(lhs.high, rhs.high, success, cond1);
|
||||||
|
ma_b(lhs.high, rhs.high, fail, cond2);
|
||||||
|
ma_b(lhs.low, rhs.low, success, cond3);
|
||||||
|
if (!fallthrough)
|
||||||
|
jump(fail);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
MOZ_CRASH("Condition code not supported");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fallthrough)
|
||||||
|
bind(fail);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MacroAssembler::branchPrivatePtr(Condition cond, const Address& lhs, Register rhs, Label* label)
|
MacroAssembler::branchPrivatePtr(Condition cond, const Address& lhs, Register rhs, Label* label)
|
||||||
{
|
{
|
||||||
|
|
|
@ -927,6 +927,31 @@ MacroAssemblerMIPSCompat::loadDouble(const BaseIndex& src, FloatRegister dest)
|
||||||
ma_ld(dest, Address(SecondScratchReg, src.offset));
|
ma_ld(dest, Address(SecondScratchReg, src.offset));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssemblerMIPSCompat::loadUnalignedDouble(const BaseIndex& src, Register temp,
|
||||||
|
FloatRegister dest)
|
||||||
|
{
|
||||||
|
computeScaledAddress(src, SecondScratchReg);
|
||||||
|
|
||||||
|
if (Imm16::IsInSignedRange(src.offset) && Imm16::IsInSignedRange(src.offset + 7)) {
|
||||||
|
as_lwl(temp, SecondScratchReg, src.offset + INT64LOW_OFFSET + 3);
|
||||||
|
as_lwr(temp, SecondScratchReg, src.offset + INT64LOW_OFFSET);
|
||||||
|
moveToDoubleLo(temp, dest);
|
||||||
|
as_lwl(temp, SecondScratchReg, src.offset + INT64HIGH_OFFSET + 3);
|
||||||
|
as_lwr(temp, SecondScratchReg, src.offset + INT64HIGH_OFFSET);
|
||||||
|
moveToDoubleHi(temp, dest);
|
||||||
|
} else {
|
||||||
|
ma_li(ScratchRegister, Imm32(src.offset));
|
||||||
|
as_daddu(ScratchRegister, SecondScratchReg, ScratchRegister);
|
||||||
|
as_lwl(temp, ScratchRegister, INT64LOW_OFFSET + 3);
|
||||||
|
as_lwr(temp, ScratchRegister, INT64LOW_OFFSET);
|
||||||
|
moveToDoubleLo(temp, dest);
|
||||||
|
as_lwl(temp, ScratchRegister, INT64HIGH_OFFSET + 3);
|
||||||
|
as_lwr(temp, ScratchRegister, INT64HIGH_OFFSET);
|
||||||
|
moveToDoubleHi(temp, dest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MacroAssemblerMIPSCompat::loadFloatAsDouble(const Address& address, FloatRegister dest)
|
MacroAssemblerMIPSCompat::loadFloatAsDouble(const Address& address, FloatRegister dest)
|
||||||
{
|
{
|
||||||
|
@ -954,6 +979,25 @@ MacroAssemblerMIPSCompat::loadFloat32(const BaseIndex& src, FloatRegister dest)
|
||||||
ma_ls(dest, Address(SecondScratchReg, src.offset));
|
ma_ls(dest, Address(SecondScratchReg, src.offset));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssemblerMIPSCompat::loadUnalignedFloat32(const BaseIndex& src, Register temp,
|
||||||
|
FloatRegister dest)
|
||||||
|
{
|
||||||
|
computeScaledAddress(src, SecondScratchReg);
|
||||||
|
|
||||||
|
if (Imm16::IsInSignedRange(src.offset) && Imm16::IsInSignedRange(src.offset + 3)) {
|
||||||
|
as_lwl(temp, SecondScratchReg, src.offset + 3);
|
||||||
|
as_lwr(temp, SecondScratchReg, src.offset);
|
||||||
|
} else {
|
||||||
|
ma_li(ScratchRegister, Imm32(src.offset));
|
||||||
|
as_daddu(ScratchRegister, SecondScratchReg, ScratchRegister);
|
||||||
|
as_lwl(temp, ScratchRegister, 3);
|
||||||
|
as_lwr(temp, ScratchRegister, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
moveToFloat32(temp, dest);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MacroAssemblerMIPSCompat::store8(Imm32 imm, const Address& address)
|
MacroAssemblerMIPSCompat::store8(Imm32 imm, const Address& address)
|
||||||
{
|
{
|
||||||
|
@ -1087,6 +1131,49 @@ MacroAssemblerMIPSCompat::storePtr(Register src, AbsoluteAddress dest)
|
||||||
storePtr(src, Address(ScratchRegister, 0));
|
storePtr(src, Address(ScratchRegister, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssemblerMIPSCompat::storeUnalignedFloat32(FloatRegister src, Register temp,
|
||||||
|
const BaseIndex& dest)
|
||||||
|
{
|
||||||
|
computeScaledAddress(dest, SecondScratchReg);
|
||||||
|
moveFromFloat32(src, temp);
|
||||||
|
|
||||||
|
if (Imm16::IsInSignedRange(dest.offset) && Imm16::IsInSignedRange(dest.offset + 3)) {
|
||||||
|
as_swl(temp, SecondScratchReg, dest.offset + 3);
|
||||||
|
as_swr(temp, SecondScratchReg, dest.offset);
|
||||||
|
} else {
|
||||||
|
ma_li(ScratchRegister, Imm32(dest.offset));
|
||||||
|
as_daddu(ScratchRegister, SecondScratchReg, ScratchRegister);
|
||||||
|
as_swl(temp, ScratchRegister, 3);
|
||||||
|
as_swr(temp, ScratchRegister, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssemblerMIPSCompat::storeUnalignedDouble(FloatRegister src, Register temp,
|
||||||
|
const BaseIndex& dest)
|
||||||
|
{
|
||||||
|
computeScaledAddress(dest, SecondScratchReg);
|
||||||
|
|
||||||
|
if (Imm16::IsInSignedRange(dest.offset) && Imm16::IsInSignedRange(dest.offset + 7)) {
|
||||||
|
moveFromDoubleLo(src, temp);
|
||||||
|
as_swl(temp, SecondScratchReg, dest.offset + INT64LOW_OFFSET + 3);
|
||||||
|
as_swr(temp, SecondScratchReg, dest.offset + INT64LOW_OFFSET);
|
||||||
|
moveFromDoubleHi(src, temp);
|
||||||
|
as_swl(temp, SecondScratchReg, dest.offset + INT64HIGH_OFFSET + 3);
|
||||||
|
as_swr(temp, SecondScratchReg, dest.offset + INT64HIGH_OFFSET);
|
||||||
|
} else {
|
||||||
|
ma_li(ScratchRegister, Imm32(dest.offset));
|
||||||
|
as_daddu(ScratchRegister, SecondScratchReg, ScratchRegister);
|
||||||
|
moveFromDoubleLo(src, temp);
|
||||||
|
as_swl(temp, ScratchRegister, INT64LOW_OFFSET + 3);
|
||||||
|
as_swr(temp, ScratchRegister, INT64LOW_OFFSET);
|
||||||
|
moveFromDoubleHi(src, temp);
|
||||||
|
as_swl(temp, ScratchRegister, INT64HIGH_OFFSET + 3);
|
||||||
|
as_swr(temp, ScratchRegister, INT64HIGH_OFFSET);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Note: this function clobbers the input register.
|
// Note: this function clobbers the input register.
|
||||||
void
|
void
|
||||||
MacroAssembler::clampDoubleToUint8(FloatRegister input, Register output)
|
MacroAssembler::clampDoubleToUint8(FloatRegister input, Register output)
|
||||||
|
@ -1325,6 +1412,12 @@ MacroAssemblerMIPSCompat::loadConstantFloat32(float f, FloatRegister dest)
|
||||||
ma_lis(dest, f);
|
ma_lis(dest, f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssemblerMIPSCompat::loadConstantFloat32(wasm::RawF32 f, FloatRegister dest)
|
||||||
|
{
|
||||||
|
ma_lis(dest, f);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MacroAssemblerMIPSCompat::loadInt32OrDouble(const Address& src, FloatRegister dest)
|
MacroAssemblerMIPSCompat::loadInt32OrDouble(const Address& src, FloatRegister dest)
|
||||||
{
|
{
|
||||||
|
@ -1375,6 +1468,34 @@ MacroAssemblerMIPSCompat::loadConstantDouble(double dp, FloatRegister dest)
|
||||||
ma_lid(dest, dp);
|
ma_lid(dest, dp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssemblerMIPSCompat::loadConstantDouble(wasm::RawF64 d, FloatRegister dest)
|
||||||
|
{
|
||||||
|
struct DoubleStruct {
|
||||||
|
uint32_t lo;
|
||||||
|
uint32_t hi;
|
||||||
|
} ;
|
||||||
|
DoubleStruct intStruct = mozilla::BitwiseCast<DoubleStruct>(d.bits());
|
||||||
|
|
||||||
|
// put hi part of 64 bit value into the odd register
|
||||||
|
if (intStruct.hi == 0) {
|
||||||
|
moveToDoubleHi(zero, dest);
|
||||||
|
} else {
|
||||||
|
ScratchRegisterScope scratch(asMasm());
|
||||||
|
ma_li(scratch, Imm32(intStruct.hi));
|
||||||
|
moveToDoubleHi(scratch, dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
// put low part of 64 bit value into the even register
|
||||||
|
if (intStruct.lo == 0) {
|
||||||
|
moveToDoubleLo(zero, dest);
|
||||||
|
} else {
|
||||||
|
ScratchRegisterScope scratch(asMasm());
|
||||||
|
ma_li(scratch, Imm32(intStruct.lo));
|
||||||
|
moveToDoubleLo(scratch, dest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Register
|
Register
|
||||||
MacroAssemblerMIPSCompat::extractObject(const Address& address, Register scratch)
|
MacroAssemblerMIPSCompat::extractObject(const Address& address, Register scratch)
|
||||||
{
|
{
|
||||||
|
|
|
@ -361,10 +361,12 @@ class MacroAssemblerMIPSCompat : public MacroAssemblerMIPS
|
||||||
void loadInt32OrDouble(Register base, Register index,
|
void loadInt32OrDouble(Register base, Register index,
|
||||||
FloatRegister dest, int32_t shift = defaultShift);
|
FloatRegister dest, int32_t shift = defaultShift);
|
||||||
void loadConstantDouble(double dp, FloatRegister dest);
|
void loadConstantDouble(double dp, FloatRegister dest);
|
||||||
|
void loadConstantDouble(wasm::RawF64 d, FloatRegister dest);
|
||||||
|
|
||||||
void boolValueToFloat32(const ValueOperand& operand, FloatRegister dest);
|
void boolValueToFloat32(const ValueOperand& operand, FloatRegister dest);
|
||||||
void int32ValueToFloat32(const ValueOperand& operand, FloatRegister dest);
|
void int32ValueToFloat32(const ValueOperand& operand, FloatRegister dest);
|
||||||
void loadConstantFloat32(float f, FloatRegister dest);
|
void loadConstantFloat32(float f, FloatRegister dest);
|
||||||
|
void loadConstantFloat32(wasm::RawF32 f, FloatRegister dest);
|
||||||
|
|
||||||
void testNullSet(Condition cond, const ValueOperand& value, Register dest);
|
void testNullSet(Condition cond, const ValueOperand& value, Register dest);
|
||||||
|
|
||||||
|
@ -892,6 +894,7 @@ class MacroAssemblerMIPSCompat : public MacroAssemblerMIPS
|
||||||
|
|
||||||
void loadDouble(const Address& addr, FloatRegister dest);
|
void loadDouble(const Address& addr, FloatRegister dest);
|
||||||
void loadDouble(const BaseIndex& src, FloatRegister dest);
|
void loadDouble(const BaseIndex& src, FloatRegister dest);
|
||||||
|
void loadUnalignedDouble(const BaseIndex& src, Register temp, FloatRegister dest);
|
||||||
|
|
||||||
// Load a float value into a register, then expand it to a double.
|
// Load a float value into a register, then expand it to a double.
|
||||||
void loadFloatAsDouble(const Address& addr, FloatRegister dest);
|
void loadFloatAsDouble(const Address& addr, FloatRegister dest);
|
||||||
|
@ -899,6 +902,7 @@ class MacroAssemblerMIPSCompat : public MacroAssemblerMIPS
|
||||||
|
|
||||||
void loadFloat32(const Address& addr, FloatRegister dest);
|
void loadFloat32(const Address& addr, FloatRegister dest);
|
||||||
void loadFloat32(const BaseIndex& src, FloatRegister dest);
|
void loadFloat32(const BaseIndex& src, FloatRegister dest);
|
||||||
|
void loadUnalignedFloat32(const BaseIndex& src, Register temp, FloatRegister dest);
|
||||||
|
|
||||||
void store8(Register src, const Address& address);
|
void store8(Register src, const Address& address);
|
||||||
void store8(Imm32 imm, const Address& address);
|
void store8(Imm32 imm, const Address& address);
|
||||||
|
@ -927,12 +931,21 @@ class MacroAssemblerMIPSCompat : public MacroAssemblerMIPS
|
||||||
store32(src.high, Address(address.base, address.offset + HIGH_32_OFFSET));
|
store32(src.high, Address(address.base, address.offset + HIGH_32_OFFSET));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void store64(Imm64 imm, Address address) {
|
||||||
|
store32(imm.low(), Address(address.base, address.offset + LOW_32_OFFSET));
|
||||||
|
store32(imm.hi(), Address(address.base, address.offset + HIGH_32_OFFSET));
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T> void storePtr(ImmWord imm, T address);
|
template <typename T> void storePtr(ImmWord imm, T address);
|
||||||
template <typename T> void storePtr(ImmPtr imm, T address);
|
template <typename T> void storePtr(ImmPtr imm, T address);
|
||||||
template <typename T> void storePtr(ImmGCPtr imm, T address);
|
template <typename T> void storePtr(ImmGCPtr imm, T address);
|
||||||
void storePtr(Register src, const Address& address);
|
void storePtr(Register src, const Address& address);
|
||||||
void storePtr(Register src, const BaseIndex& address);
|
void storePtr(Register src, const BaseIndex& address);
|
||||||
void storePtr(Register src, AbsoluteAddress dest);
|
void storePtr(Register src, AbsoluteAddress dest);
|
||||||
|
|
||||||
|
void storeUnalignedFloat32(FloatRegister src, Register temp, const BaseIndex& dest);
|
||||||
|
void storeUnalignedDouble(FloatRegister src, Register temp, const BaseIndex& dest);
|
||||||
|
|
||||||
void moveDouble(FloatRegister src, FloatRegister dest) {
|
void moveDouble(FloatRegister src, FloatRegister dest) {
|
||||||
as_movd(dest, src);
|
as_movd(dest, src);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1819,12 +1819,14 @@ typedef double (*Prototype_Double_None)();
|
||||||
typedef double (*Prototype_Double_Double)(double arg0);
|
typedef double (*Prototype_Double_Double)(double arg0);
|
||||||
typedef double (*Prototype_Double_Int)(int32_t arg0);
|
typedef double (*Prototype_Double_Int)(int32_t arg0);
|
||||||
typedef int32_t (*Prototype_Int_Double)(double arg0);
|
typedef int32_t (*Prototype_Int_Double)(double arg0);
|
||||||
|
typedef int64_t (*Prototype_Int64_Double)(double arg0);
|
||||||
typedef int32_t (*Prototype_Int_DoubleIntInt)(double arg0, int32_t arg1, int32_t arg2);
|
typedef int32_t (*Prototype_Int_DoubleIntInt)(double arg0, int32_t arg1, int32_t arg2);
|
||||||
typedef int32_t (*Prototype_Int_IntDoubleIntInt)(int32_t arg0, double arg1, int32_t arg2,
|
typedef int32_t (*Prototype_Int_IntDoubleIntInt)(int32_t arg0, double arg1, int32_t arg2,
|
||||||
int32_t arg3);
|
int32_t arg3);
|
||||||
typedef float (*Prototype_Float32_Float32)(float arg0);
|
typedef float (*Prototype_Float32_Float32)(float arg0);
|
||||||
|
|
||||||
typedef double (*Prototype_DoubleInt)(double arg0, int32_t arg1);
|
typedef double (*Prototype_DoubleInt)(double arg0, int32_t arg1);
|
||||||
|
typedef double (*Prototype_Double_IntInt)(int32_t arg0, int32_t arg1);
|
||||||
typedef double (*Prototype_Double_IntDouble)(int32_t arg0, double arg1);
|
typedef double (*Prototype_Double_IntDouble)(int32_t arg0, double arg1);
|
||||||
typedef double (*Prototype_Double_DoubleDouble)(double arg0, double arg1);
|
typedef double (*Prototype_Double_DoubleDouble)(double arg0, double arg1);
|
||||||
typedef int32_t (*Prototype_Int_IntDouble)(int32_t arg0, double arg1);
|
typedef int32_t (*Prototype_Int_IntDouble)(int32_t arg0, double arg1);
|
||||||
|
@ -1941,6 +1943,15 @@ Simulator::softwareInterrupt(SimInstruction* instr)
|
||||||
setRegister(v0, res);
|
setRegister(v0, res);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case Args_Int64_Double: {
|
||||||
|
double dval0, dval1;
|
||||||
|
int32_t ival;
|
||||||
|
getFpArgs(&dval0, &dval1, &ival);
|
||||||
|
Prototype_Int64_Double target = reinterpret_cast<Prototype_Int64_Double>(external);
|
||||||
|
int64_t result = target(dval0);
|
||||||
|
setCallResult(result);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case Args_Int_DoubleIntInt: {
|
case Args_Int_DoubleIntInt: {
|
||||||
double dval = getFpuRegisterDouble(12);
|
double dval = getFpuRegisterDouble(12);
|
||||||
Prototype_Int_DoubleIntInt target = reinterpret_cast<Prototype_Int_DoubleIntInt>(external);
|
Prototype_Int_DoubleIntInt target = reinterpret_cast<Prototype_Int_DoubleIntInt>(external);
|
||||||
|
@ -1978,6 +1989,12 @@ Simulator::softwareInterrupt(SimInstruction* instr)
|
||||||
setCallResultDouble(dresult);
|
setCallResultDouble(dresult);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case Args_Double_IntInt: {
|
||||||
|
Prototype_Double_IntInt target = reinterpret_cast<Prototype_Double_IntInt>(external);
|
||||||
|
double dresult = target(arg0, arg1);
|
||||||
|
setCallResultDouble(dresult);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case Args_Double_DoubleInt: {
|
case Args_Double_DoubleInt: {
|
||||||
double dval0, dval1;
|
double dval0, dval1;
|
||||||
int32_t ival;
|
int32_t ival;
|
||||||
|
|
|
@ -24,6 +24,7 @@ ABIArgGenerator::next(MIRType type)
|
||||||
{
|
{
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case MIRType::Int32:
|
case MIRType::Int32:
|
||||||
|
case MIRType::Int64:
|
||||||
case MIRType::Pointer: {
|
case MIRType::Pointer: {
|
||||||
Register destReg;
|
Register destReg;
|
||||||
if (GetIntArgReg(usedArgSlots_, &destReg))
|
if (GetIntArgReg(usedArgSlots_, &destReg))
|
||||||
|
|
|
@ -304,19 +304,122 @@ CodeGeneratorMIPS64::visitCompareBitwiseAndBranch(LCompareBitwiseAndBranch* lir)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CodeGeneratorMIPS64::visitWasmLoadI64(LWasmLoadI64* lir)
|
CodeGeneratorMIPS64::visitCompareI64(LCompareI64* lir)
|
||||||
|
{
|
||||||
|
MCompare* mir = lir->mir();
|
||||||
|
MOZ_ASSERT(mir->compareType() == MCompare::Compare_Int64 ||
|
||||||
|
mir->compareType() == MCompare::Compare_UInt64);
|
||||||
|
|
||||||
|
const LInt64Allocation lhs = lir->getInt64Operand(LCompareI64::Lhs);
|
||||||
|
const LInt64Allocation rhs = lir->getInt64Operand(LCompareI64::Rhs);
|
||||||
|
Register lhsReg = ToRegister64(lhs).reg;
|
||||||
|
Register output = ToRegister(lir->output());
|
||||||
|
Register rhsReg;
|
||||||
|
|
||||||
|
if (IsConstant(rhs)) {
|
||||||
|
rhsReg = ScratchRegister;
|
||||||
|
masm.ma_li(rhsReg, ImmWord(ToInt64(rhs)));
|
||||||
|
} else {
|
||||||
|
rhsReg = ToRegister64(rhs).reg;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isSigned = mir->compareType() == MCompare::Compare_Int64;
|
||||||
|
masm.cmpPtrSet(JSOpToCondition(lir->jsop(), isSigned), lhsReg, rhsReg, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGeneratorMIPS64::visitCompareI64AndBranch(LCompareI64AndBranch* lir)
|
||||||
|
{
|
||||||
|
MCompare* mir = lir->cmpMir();
|
||||||
|
MOZ_ASSERT(mir->compareType() == MCompare::Compare_Int64 ||
|
||||||
|
mir->compareType() == MCompare::Compare_UInt64);
|
||||||
|
|
||||||
|
const LInt64Allocation lhs = lir->getInt64Operand(LCompareI64::Lhs);
|
||||||
|
const LInt64Allocation rhs = lir->getInt64Operand(LCompareI64::Rhs);
|
||||||
|
Register lhsReg = ToRegister64(lhs).reg;
|
||||||
|
Register rhsReg;
|
||||||
|
|
||||||
|
if (IsConstant(rhs)) {
|
||||||
|
rhsReg = ScratchRegister;
|
||||||
|
masm.ma_li(rhsReg, ImmWord(ToInt64(rhs)));
|
||||||
|
} else {
|
||||||
|
rhsReg = ToRegister64(rhs).reg;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isSigned = mir->compareType() == MCompare::Compare_Int64;
|
||||||
|
Assembler::Condition cond = JSOpToCondition(lir->jsop(), isSigned);
|
||||||
|
emitBranch(lhsReg, rhsReg, cond, lir->ifTrue(), lir->ifFalse());
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGeneratorMIPS64::visitDivOrModI64(LDivOrModI64* lir)
|
||||||
|
{
|
||||||
|
Register lhs = ToRegister(lir->lhs());
|
||||||
|
Register rhs = ToRegister(lir->rhs());
|
||||||
|
Register output = ToRegister(lir->output());
|
||||||
|
|
||||||
|
Label done;
|
||||||
|
|
||||||
|
// Handle divide by zero.
|
||||||
|
if (lir->canBeDivideByZero())
|
||||||
|
masm.ma_b(rhs, rhs, wasm::JumpTarget::IntegerDivideByZero, Assembler::Zero);
|
||||||
|
|
||||||
|
// Handle an integer overflow exception from INT64_MIN / -1.
|
||||||
|
if (lir->canBeNegativeOverflow()) {
|
||||||
|
Label notmin;
|
||||||
|
masm.branchPtr(Assembler::NotEqual, lhs, ImmWord(INT64_MIN), ¬min);
|
||||||
|
masm.branchPtr(Assembler::NotEqual, rhs, ImmWord(-1), ¬min);
|
||||||
|
if (lir->mir()->isMod())
|
||||||
|
masm.ma_xor(output, output);
|
||||||
|
else
|
||||||
|
masm.jump(wasm::JumpTarget::IntegerOverflow);
|
||||||
|
masm.jump(&done);
|
||||||
|
masm.bind(¬min);
|
||||||
|
}
|
||||||
|
|
||||||
|
masm.as_ddiv(lhs, rhs);
|
||||||
|
|
||||||
|
if (lir->mir()->isMod())
|
||||||
|
masm.as_mfhi(output);
|
||||||
|
else
|
||||||
|
masm.as_mflo(output);
|
||||||
|
|
||||||
|
masm.bind(&done);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGeneratorMIPS64::visitUDivOrModI64(LUDivOrModI64* lir)
|
||||||
|
{
|
||||||
|
Register lhs = ToRegister(lir->lhs());
|
||||||
|
Register rhs = ToRegister(lir->rhs());
|
||||||
|
Register output = ToRegister(lir->output());
|
||||||
|
|
||||||
|
Label done;
|
||||||
|
|
||||||
|
// Prevent divide by zero.
|
||||||
|
if (lir->canBeDivideByZero())
|
||||||
|
masm.ma_b(rhs, rhs, wasm::JumpTarget::IntegerDivideByZero, Assembler::Zero);
|
||||||
|
|
||||||
|
masm.as_ddivu(lhs, rhs);
|
||||||
|
|
||||||
|
if (lir->mir()->isMod())
|
||||||
|
masm.as_mfhi(output);
|
||||||
|
else
|
||||||
|
masm.as_mflo(output);
|
||||||
|
|
||||||
|
masm.bind(&done);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void
|
||||||
|
CodeGeneratorMIPS64::emitWasmLoadI64(T* lir)
|
||||||
{
|
{
|
||||||
const MWasmLoad* mir = lir->mir();
|
const MWasmLoad* mir = lir->mir();
|
||||||
|
|
||||||
MOZ_ASSERT(lir->mir()->type() == MIRType::Int64);
|
MOZ_ASSERT(lir->mir()->type() == MIRType::Int64);
|
||||||
MOZ_ASSERT(!mir->barrierBefore() && !mir->barrierAfter(), "atomics NYI");
|
|
||||||
|
|
||||||
uint32_t offset = mir->offset();
|
uint32_t offset = mir->offset();
|
||||||
if (offset > INT32_MAX) {
|
MOZ_ASSERT(offset < wasm::OffsetGuardLimit);
|
||||||
// This is unreachable because of bounds checks.
|
|
||||||
masm.breakpoint();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Register ptr = ToRegister(lir->ptr());
|
Register ptr = ToRegister(lir->ptr());
|
||||||
|
|
||||||
|
@ -329,7 +432,129 @@ CodeGeneratorMIPS64::visitWasmLoadI64(LWasmLoadI64* lir)
|
||||||
MOZ_ASSERT(lir->ptrCopy()->isBogusTemp());
|
MOZ_ASSERT(lir->ptrCopy()->isBogusTemp());
|
||||||
}
|
}
|
||||||
|
|
||||||
masm.ma_load(ToRegister(lir->output()), BaseIndex(HeapReg, ptr, TimesOne), SizeDouble);
|
unsigned byteSize = mir->byteSize();
|
||||||
|
bool isSigned;
|
||||||
|
|
||||||
|
switch (mir->accessType()) {
|
||||||
|
case Scalar::Int8: isSigned = true; break;
|
||||||
|
case Scalar::Uint8: isSigned = false; break;
|
||||||
|
case Scalar::Int16: isSigned = true; break;
|
||||||
|
case Scalar::Uint16: isSigned = false; break;
|
||||||
|
case Scalar::Int32: isSigned = true; break;
|
||||||
|
case Scalar::Uint32: isSigned = false; break;
|
||||||
|
case Scalar::Int64: isSigned = true; break;
|
||||||
|
default: MOZ_CRASH("unexpected array type");
|
||||||
|
}
|
||||||
|
|
||||||
|
memoryBarrier(mir->barrierBefore());
|
||||||
|
|
||||||
|
if (mir->isUnaligned()) {
|
||||||
|
Register temp = ToRegister(lir->getTemp(1));
|
||||||
|
|
||||||
|
masm.ma_load_unaligned(ToOutRegister64(lir).reg, BaseIndex(HeapReg, ptr, TimesOne),
|
||||||
|
temp, static_cast<LoadStoreSize>(8 * byteSize),
|
||||||
|
isSigned ? SignExtend : ZeroExtend);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
masm.ma_load(ToOutRegister64(lir).reg, BaseIndex(HeapReg, ptr, TimesOne),
|
||||||
|
static_cast<LoadStoreSize>(8 * byteSize), isSigned ? SignExtend : ZeroExtend);
|
||||||
|
|
||||||
|
memoryBarrier(mir->barrierAfter());
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGeneratorMIPS64::visitWasmLoadI64(LWasmLoadI64* lir)
|
||||||
|
{
|
||||||
|
emitWasmLoadI64(lir);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGeneratorMIPS64::visitWasmUnalignedLoadI64(LWasmUnalignedLoadI64* lir)
|
||||||
|
{
|
||||||
|
emitWasmLoadI64(lir);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void
|
||||||
|
CodeGeneratorMIPS64::emitWasmStoreI64(T* lir)
|
||||||
|
{
|
||||||
|
const MWasmStore* mir = lir->mir();
|
||||||
|
|
||||||
|
MOZ_ASSERT(lir->mir()->type() == MIRType::Int64);
|
||||||
|
|
||||||
|
uint32_t offset = mir->offset();
|
||||||
|
MOZ_ASSERT(offset < wasm::OffsetGuardLimit);
|
||||||
|
|
||||||
|
Register ptr = ToRegister(lir->ptr());
|
||||||
|
|
||||||
|
// Maybe add the offset.
|
||||||
|
if (offset) {
|
||||||
|
Register ptrPlusOffset = ToRegister(lir->ptrCopy());
|
||||||
|
masm.addPtr(Imm32(offset), ptrPlusOffset);
|
||||||
|
ptr = ptrPlusOffset;
|
||||||
|
} else {
|
||||||
|
MOZ_ASSERT(lir->ptrCopy()->isBogusTemp());
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned byteSize = mir->byteSize();
|
||||||
|
bool isSigned;
|
||||||
|
|
||||||
|
switch (mir->accessType()) {
|
||||||
|
case Scalar::Int8: isSigned = true; break;
|
||||||
|
case Scalar::Uint8: isSigned = false; break;
|
||||||
|
case Scalar::Int16: isSigned = true; break;
|
||||||
|
case Scalar::Uint16: isSigned = false; break;
|
||||||
|
case Scalar::Int32: isSigned = true; break;
|
||||||
|
case Scalar::Uint32: isSigned = false; break;
|
||||||
|
case Scalar::Int64: isSigned = true; break;
|
||||||
|
default: MOZ_CRASH("unexpected array type");
|
||||||
|
}
|
||||||
|
|
||||||
|
memoryBarrier(mir->barrierBefore());
|
||||||
|
|
||||||
|
if (mir->isUnaligned()) {
|
||||||
|
Register temp = ToRegister(lir->getTemp(1));
|
||||||
|
|
||||||
|
masm.ma_store_unaligned(ToRegister64(lir->value()).reg, BaseIndex(HeapReg, ptr, TimesOne),
|
||||||
|
temp, static_cast<LoadStoreSize>(8 * byteSize),
|
||||||
|
isSigned ? SignExtend : ZeroExtend);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
masm.ma_store(ToRegister64(lir->value()).reg, BaseIndex(HeapReg, ptr, TimesOne),
|
||||||
|
static_cast<LoadStoreSize>(8 * byteSize), isSigned ? SignExtend : ZeroExtend);
|
||||||
|
|
||||||
|
memoryBarrier(mir->barrierAfter());
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGeneratorMIPS64::visitWasmStoreI64(LWasmStoreI64* lir)
|
||||||
|
{
|
||||||
|
emitWasmStoreI64(lir);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGeneratorMIPS64::visitWasmUnalignedStoreI64(LWasmUnalignedStoreI64* lir)
|
||||||
|
{
|
||||||
|
emitWasmStoreI64(lir);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGeneratorMIPS64::visitWasmLoadGlobalVarI64(LWasmLoadGlobalVarI64* ins)
|
||||||
|
{
|
||||||
|
const MWasmLoadGlobalVar* mir = ins->mir();
|
||||||
|
unsigned addr = mir->globalDataOffset() - AsmJSGlobalRegBias;
|
||||||
|
MOZ_ASSERT(mir->type() == MIRType::Int64);
|
||||||
|
masm.load64(Address(GlobalReg, addr), ToOutRegister64(ins));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGeneratorMIPS64::visitWasmStoreGlobalVarI64(LWasmStoreGlobalVarI64* ins)
|
||||||
|
{
|
||||||
|
const MWasmStoreGlobalVar* mir = ins->mir();
|
||||||
|
unsigned addr = mir->globalDataOffset() - AsmJSGlobalRegBias;
|
||||||
|
MOZ_ASSERT(mir->value()->type() == MIRType::Int64);
|
||||||
|
masm.store64(ToRegister64(ins->value()), Address(GlobalReg, addr));
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -369,6 +594,173 @@ CodeGeneratorMIPS64::visitAsmReinterpretToI64(LAsmReinterpretToI64* lir)
|
||||||
masm.as_dmfc1(ToRegister(lir->output()), ToFloatRegister(lir->input()));
|
masm.as_dmfc1(ToRegister(lir->output()), ToFloatRegister(lir->input()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGeneratorMIPS64::visitExtendInt32ToInt64(LExtendInt32ToInt64* lir)
|
||||||
|
{
|
||||||
|
const LAllocation* input = lir->getOperand(0);
|
||||||
|
Register output = ToRegister(lir->output());
|
||||||
|
|
||||||
|
if (lir->mir()->isUnsigned())
|
||||||
|
masm.ma_dext(output, ToRegister(input), Imm32(0), Imm32(32));
|
||||||
|
else
|
||||||
|
masm.ma_sll(output, ToRegister(input), Imm32(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGeneratorMIPS64::visitWrapInt64ToInt32(LWrapInt64ToInt32* lir)
|
||||||
|
{
|
||||||
|
const LAllocation* input = lir->getOperand(0);
|
||||||
|
Register output = ToRegister(lir->output());
|
||||||
|
|
||||||
|
if (lir->mir()->bottomHalf()) {
|
||||||
|
if (input->isMemory())
|
||||||
|
masm.load32(ToAddress(input), output);
|
||||||
|
else
|
||||||
|
masm.ma_sll(output, ToRegister(input), Imm32(0));
|
||||||
|
} else {
|
||||||
|
MOZ_CRASH("Not implemented.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGeneratorMIPS64::visitClzI64(LClzI64* lir)
|
||||||
|
{
|
||||||
|
Register64 input = ToRegister64(lir->getInt64Operand(0));
|
||||||
|
Register64 output = ToOutRegister64(lir);
|
||||||
|
masm.clz64(input, output.reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGeneratorMIPS64::visitCtzI64(LCtzI64* lir)
|
||||||
|
{
|
||||||
|
Register64 input = ToRegister64(lir->getInt64Operand(0));
|
||||||
|
Register64 output = ToOutRegister64(lir);
|
||||||
|
masm.ctz64(input, output.reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGeneratorMIPS64::visitNotI64(LNotI64* lir)
|
||||||
|
{
|
||||||
|
Register64 input = ToRegister64(lir->getInt64Operand(0));
|
||||||
|
Register output = ToRegister(lir->output());
|
||||||
|
|
||||||
|
masm.cmp64Set(Assembler::Equal, input.reg, Imm32(0), output);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGeneratorMIPS64::visitWasmTruncateToInt64(LWasmTruncateToInt64* lir)
|
||||||
|
{
|
||||||
|
FloatRegister input = ToFloatRegister(lir->input());
|
||||||
|
Register output = ToRegister(lir->output());
|
||||||
|
|
||||||
|
MWasmTruncateToInt64* mir = lir->mir();
|
||||||
|
MIRType fromType = mir->input()->type();
|
||||||
|
|
||||||
|
MOZ_ASSERT(fromType == MIRType::Double || fromType == MIRType::Float32);
|
||||||
|
|
||||||
|
auto* ool = new (alloc()) OutOfLineWasmTruncateCheck(mir, input);
|
||||||
|
addOutOfLineCode(ool, mir);
|
||||||
|
|
||||||
|
if (mir->isUnsigned()) {
|
||||||
|
Label isLarge, done;
|
||||||
|
|
||||||
|
if (fromType == MIRType::Double) {
|
||||||
|
masm.loadConstantDouble(double(INT64_MAX), ScratchDoubleReg);
|
||||||
|
masm.ma_bc1d(ScratchDoubleReg, input, &isLarge,
|
||||||
|
Assembler::DoubleLessThanOrEqual, ShortJump);
|
||||||
|
|
||||||
|
masm.as_truncld(ScratchDoubleReg, input);
|
||||||
|
} else {
|
||||||
|
masm.loadConstantFloat32(float(INT64_MAX), ScratchFloat32Reg);
|
||||||
|
masm.ma_bc1s(ScratchFloat32Reg, input, &isLarge,
|
||||||
|
Assembler::DoubleLessThanOrEqual, ShortJump);
|
||||||
|
|
||||||
|
masm.as_truncls(ScratchDoubleReg, input);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that the result is in the uint64_t range.
|
||||||
|
masm.moveFromDouble(ScratchDoubleReg, output);
|
||||||
|
masm.as_cfc1(ScratchRegister, Assembler::FCSR);
|
||||||
|
masm.as_ext(ScratchRegister, ScratchRegister, 16, 1);
|
||||||
|
masm.ma_dsrl(SecondScratchReg, output, Imm32(63));
|
||||||
|
masm.ma_or(SecondScratchReg, ScratchRegister);
|
||||||
|
masm.ma_b(SecondScratchReg, Imm32(0), ool->entry(), Assembler::NotEqual);
|
||||||
|
|
||||||
|
masm.ma_b(&done, ShortJump);
|
||||||
|
|
||||||
|
// The input is greater than double(INT64_MAX).
|
||||||
|
masm.bind(&isLarge);
|
||||||
|
if (fromType == MIRType::Double) {
|
||||||
|
masm.as_subd(ScratchDoubleReg, input, ScratchDoubleReg);
|
||||||
|
masm.as_truncld(ScratchDoubleReg, ScratchDoubleReg);
|
||||||
|
} else {
|
||||||
|
masm.as_subs(ScratchDoubleReg, input, ScratchDoubleReg);
|
||||||
|
masm.as_truncls(ScratchDoubleReg, ScratchDoubleReg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that the result is in the uint64_t range.
|
||||||
|
masm.moveFromDouble(ScratchDoubleReg, output);
|
||||||
|
masm.as_cfc1(ScratchRegister, Assembler::FCSR);
|
||||||
|
masm.as_ext(ScratchRegister, ScratchRegister, 16, 1);
|
||||||
|
masm.ma_dsrl(SecondScratchReg, output, Imm32(63));
|
||||||
|
masm.ma_or(SecondScratchReg, ScratchRegister);
|
||||||
|
masm.ma_b(SecondScratchReg, Imm32(0), ool->entry(), Assembler::NotEqual);
|
||||||
|
|
||||||
|
masm.ma_li(ScratchRegister, Imm32(1));
|
||||||
|
masm.ma_dins(output, ScratchRegister, Imm32(63), Imm32(1));
|
||||||
|
|
||||||
|
masm.bind(&done);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// When the input value is Infinity, NaN, or rounds to an integer outside the
|
||||||
|
// range [INT64_MIN; INT64_MAX + 1[, the Invalid Operation flag is set in the FCSR.
|
||||||
|
if (fromType == MIRType::Double)
|
||||||
|
masm.as_truncld(ScratchDoubleReg, input);
|
||||||
|
else
|
||||||
|
masm.as_truncls(ScratchDoubleReg, input);
|
||||||
|
|
||||||
|
// Check that the result is in the int64_t range.
|
||||||
|
masm.as_cfc1(output, Assembler::FCSR);
|
||||||
|
masm.as_ext(output, output, 16, 1);
|
||||||
|
masm.ma_b(output, Imm32(0), ool->entry(), Assembler::NotEqual);
|
||||||
|
|
||||||
|
masm.bind(ool->rejoin());
|
||||||
|
masm.moveFromDouble(ScratchDoubleReg, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGeneratorMIPS64::visitInt64ToFloatingPoint(LInt64ToFloatingPoint* lir)
|
||||||
|
{
|
||||||
|
Register input = ToRegister(lir->input());
|
||||||
|
FloatRegister output = ToFloatRegister(lir->output());
|
||||||
|
|
||||||
|
MIRType outputType = lir->mir()->type();
|
||||||
|
MOZ_ASSERT(outputType == MIRType::Double || outputType == MIRType::Float32);
|
||||||
|
|
||||||
|
if (outputType == MIRType::Double) {
|
||||||
|
if (lir->mir()->isUnsigned())
|
||||||
|
masm.convertUInt64ToDouble(input, output);
|
||||||
|
else
|
||||||
|
masm.convertInt64ToDouble(input, output);
|
||||||
|
} else {
|
||||||
|
if (lir->mir()->isUnsigned())
|
||||||
|
masm.convertUInt64ToFloat32(input, output);
|
||||||
|
else
|
||||||
|
masm.convertInt64ToFloat32(input, output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGeneratorMIPS64::visitTestI64AndBranch(LTestI64AndBranch* lir)
|
||||||
|
{
|
||||||
|
Register64 input = ToRegister64(lir->getInt64Operand(0));
|
||||||
|
MBasicBlock* ifTrue = lir->ifTrue();
|
||||||
|
MBasicBlock* ifFalse = lir->ifFalse();
|
||||||
|
|
||||||
|
emitBranch(input.reg, Imm32(0), Assembler::NonZero, ifTrue, ifFalse);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CodeGeneratorMIPS64::setReturnDoubleRegs(LiveRegisterSet* regs)
|
CodeGeneratorMIPS64::setReturnDoubleRegs(LiveRegisterSet* regs)
|
||||||
{
|
{
|
||||||
|
|
|
@ -39,15 +39,37 @@ class CodeGeneratorMIPS64 : public CodeGeneratorMIPSShared
|
||||||
|
|
||||||
void emitTableSwitchDispatch(MTableSwitch* mir, Register index, Register base);
|
void emitTableSwitchDispatch(MTableSwitch* mir, Register index, Register base);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void emitWasmLoadI64(T* ins);
|
||||||
|
template <typename T>
|
||||||
|
void emitWasmStoreI64(T* ins);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void visitCompareB(LCompareB* lir);
|
void visitCompareB(LCompareB* lir);
|
||||||
void visitCompareBAndBranch(LCompareBAndBranch* lir);
|
void visitCompareBAndBranch(LCompareBAndBranch* lir);
|
||||||
void visitCompareBitwise(LCompareBitwise* lir);
|
void visitCompareBitwise(LCompareBitwise* lir);
|
||||||
void visitCompareBitwiseAndBranch(LCompareBitwiseAndBranch* lir);
|
void visitCompareBitwiseAndBranch(LCompareBitwiseAndBranch* lir);
|
||||||
|
void visitCompareI64(LCompareI64* lir);
|
||||||
|
void visitCompareI64AndBranch(LCompareI64AndBranch* lir);
|
||||||
|
void visitDivOrModI64(LDivOrModI64* lir);
|
||||||
|
void visitUDivOrModI64(LUDivOrModI64* lir);
|
||||||
void visitWasmLoadI64(LWasmLoadI64* lir);
|
void visitWasmLoadI64(LWasmLoadI64* lir);
|
||||||
|
void visitWasmUnalignedLoadI64(LWasmUnalignedLoadI64* lir);
|
||||||
|
void visitWasmStoreI64(LWasmStoreI64* ins);
|
||||||
|
void visitWasmUnalignedStoreI64(LWasmUnalignedStoreI64* ins);
|
||||||
|
void visitWasmLoadGlobalVarI64(LWasmLoadGlobalVarI64* ins);
|
||||||
|
void visitWasmStoreGlobalVarI64(LWasmStoreGlobalVarI64* ins);
|
||||||
void visitAsmSelectI64(LAsmSelectI64* ins);
|
void visitAsmSelectI64(LAsmSelectI64* ins);
|
||||||
void visitAsmReinterpretFromI64(LAsmReinterpretFromI64* lir);
|
void visitAsmReinterpretFromI64(LAsmReinterpretFromI64* lir);
|
||||||
void visitAsmReinterpretToI64(LAsmReinterpretToI64* lir);
|
void visitAsmReinterpretToI64(LAsmReinterpretToI64* lir);
|
||||||
|
void visitExtendInt32ToInt64(LExtendInt32ToInt64* lir);
|
||||||
|
void visitWrapInt64ToInt32(LWrapInt64ToInt32* lir);
|
||||||
|
void visitClzI64(LClzI64* lir);
|
||||||
|
void visitCtzI64(LCtzI64* lir);
|
||||||
|
void visitNotI64(LNotI64* lir);
|
||||||
|
void visitWasmTruncateToInt64(LWasmTruncateToInt64* lir);
|
||||||
|
void visitInt64ToFloatingPoint(LInt64ToFloatingPoint* lir);
|
||||||
|
void visitTestI64AndBranch(LTestI64AndBranch* lir);
|
||||||
|
|
||||||
// Out of line visitors.
|
// Out of line visitors.
|
||||||
void visitOutOfLineBailout(OutOfLineBailout* ool);
|
void visitOutOfLineBailout(OutOfLineBailout* ool);
|
||||||
|
|
|
@ -46,6 +46,82 @@ class LUnboxFloatingPoint : public LUnbox
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class LDivOrModI64 : public LBinaryMath<1>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LIR_HEADER(DivOrModI64)
|
||||||
|
|
||||||
|
LDivOrModI64(const LAllocation& lhs, const LAllocation& rhs, const LDefinition& temp) {
|
||||||
|
setOperand(0, lhs);
|
||||||
|
setOperand(1, rhs);
|
||||||
|
setTemp(0, temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
const LDefinition* remainder() {
|
||||||
|
return getTemp(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
MBinaryArithInstruction* mir() const {
|
||||||
|
MOZ_ASSERT(mir_->isDiv() || mir_->isMod());
|
||||||
|
return static_cast<MBinaryArithInstruction*>(mir_);
|
||||||
|
}
|
||||||
|
bool canBeDivideByZero() const {
|
||||||
|
if (mir_->isMod())
|
||||||
|
return mir_->toMod()->canBeDivideByZero();
|
||||||
|
return mir_->toDiv()->canBeDivideByZero();
|
||||||
|
}
|
||||||
|
bool canBeNegativeOverflow() const {
|
||||||
|
if (mir_->isMod())
|
||||||
|
return mir_->toMod()->canBeNegativeDividend();
|
||||||
|
return mir_->toDiv()->canBeNegativeOverflow();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class LUDivOrModI64 : public LBinaryMath<1>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LIR_HEADER(UDivOrModI64);
|
||||||
|
|
||||||
|
LUDivOrModI64(const LAllocation& lhs, const LAllocation& rhs, const LDefinition& temp) {
|
||||||
|
setOperand(0, lhs);
|
||||||
|
setOperand(1, rhs);
|
||||||
|
setTemp(0, temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
const LDefinition* remainder() {
|
||||||
|
return getTemp(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* extraName() const {
|
||||||
|
return mir()->isTruncated() ? "Truncated" : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
MBinaryArithInstruction* mir() const {
|
||||||
|
MOZ_ASSERT(mir_->isDiv() || mir_->isMod());
|
||||||
|
return static_cast<MBinaryArithInstruction*>(mir_);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool canBeDivideByZero() const {
|
||||||
|
if (mir_->isMod())
|
||||||
|
return mir_->toMod()->canBeDivideByZero();
|
||||||
|
return mir_->toDiv()->canBeDivideByZero();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class LWasmTruncateToInt64 : public LInstructionHelper<1, 1, 0>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LIR_HEADER(WasmTruncateToInt64);
|
||||||
|
|
||||||
|
explicit LWasmTruncateToInt64(const LAllocation& in) {
|
||||||
|
setOperand(0, in);
|
||||||
|
}
|
||||||
|
|
||||||
|
MWasmTruncateToInt64* mir() const {
|
||||||
|
return mir_->toWasmTruncateToInt64();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace jit
|
} // namespace jit
|
||||||
} // namespace js
|
} // namespace js
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,14 @@
|
||||||
|
|
||||||
#define LIR_CPU_OPCODE_LIST(_) \
|
#define LIR_CPU_OPCODE_LIST(_) \
|
||||||
_(ModMaskI) \
|
_(ModMaskI) \
|
||||||
_(UDivOrMod)
|
_(DivOrModI64) \
|
||||||
|
_(UDivOrMod) \
|
||||||
|
_(UDivOrModI64) \
|
||||||
|
_(WasmUnalignedLoad) \
|
||||||
|
_(WasmUnalignedStore) \
|
||||||
|
_(WasmUnalignedLoadI64) \
|
||||||
|
_(WasmUnalignedStoreI64) \
|
||||||
|
_(WasmTruncateToInt64) \
|
||||||
|
_(Int64ToFloatingPoint)
|
||||||
|
|
||||||
#endif // jit_mips64_LOpcodes_mips64_h__
|
#endif // jit_mips64_LOpcodes_mips64_h__
|
||||||
|
|
|
@ -15,6 +15,19 @@
|
||||||
using namespace js;
|
using namespace js;
|
||||||
using namespace js::jit;
|
using namespace js::jit;
|
||||||
|
|
||||||
|
void
|
||||||
|
LIRGeneratorMIPS64::defineInt64Phi(MPhi* phi, size_t lirIndex)
|
||||||
|
{
|
||||||
|
defineTypedPhi(phi, lirIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LIRGeneratorMIPS64::lowerInt64PhiInput(MPhi* phi, uint32_t inputPosition,
|
||||||
|
LBlock* block, size_t lirIndex)
|
||||||
|
{
|
||||||
|
lowerTypedPhiInput(phi, inputPosition, block, lirIndex);
|
||||||
|
}
|
||||||
|
|
||||||
LBoxAllocation
|
LBoxAllocation
|
||||||
LIRGeneratorMIPS64::useBoxFixed(MDefinition* mir, Register reg1, Register reg2, bool useAtStart)
|
LIRGeneratorMIPS64::useBoxFixed(MDefinition* mir, Register reg1, Register reg2, bool useAtStart)
|
||||||
{
|
{
|
||||||
|
@ -24,6 +37,50 @@ LIRGeneratorMIPS64::useBoxFixed(MDefinition* mir, Register reg1, Register reg2,
|
||||||
return LBoxAllocation(LUse(reg1, mir->virtualRegister(), useAtStart));
|
return LBoxAllocation(LUse(reg1, mir->virtualRegister(), useAtStart));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LIRGeneratorMIPS64::lowerDivI64(MDiv* div)
|
||||||
|
{
|
||||||
|
if (div->isUnsigned()) {
|
||||||
|
lowerUDivI64(div);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LDivOrModI64* lir = new(alloc()) LDivOrModI64(useRegister(div->lhs()), useRegister(div->rhs()),
|
||||||
|
temp());
|
||||||
|
defineInt64(lir, div);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LIRGeneratorMIPS64::lowerModI64(MMod* mod)
|
||||||
|
{
|
||||||
|
if (mod->isUnsigned()) {
|
||||||
|
lowerUModI64(mod);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LDivOrModI64* lir = new(alloc()) LDivOrModI64(useRegister(mod->lhs()), useRegister(mod->rhs()),
|
||||||
|
temp());
|
||||||
|
defineInt64(lir, mod);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LIRGeneratorMIPS64::lowerUDivI64(MDiv* div)
|
||||||
|
{
|
||||||
|
LUDivOrModI64* lir = new(alloc()) LUDivOrModI64(useRegister(div->lhs()),
|
||||||
|
useRegister(div->rhs()),
|
||||||
|
temp());
|
||||||
|
defineInt64(lir, div);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LIRGeneratorMIPS64::lowerUModI64(MMod* mod)
|
||||||
|
{
|
||||||
|
LUDivOrModI64* lir = new(alloc()) LUDivOrModI64(useRegister(mod->lhs()),
|
||||||
|
useRegister(mod->rhs()),
|
||||||
|
temp());
|
||||||
|
defineInt64(lir, mod);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
LIRGeneratorMIPS64::visitBox(MBox* box)
|
LIRGeneratorMIPS64::visitBox(MBox* box)
|
||||||
{
|
{
|
||||||
|
|
|
@ -20,6 +20,9 @@ class LIRGeneratorMIPS64 : public LIRGeneratorMIPSShared
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
void lowerInt64PhiInput(MPhi*, uint32_t, LBlock*, size_t);
|
||||||
|
void defineInt64Phi(MPhi*, size_t);
|
||||||
|
|
||||||
// Returns a box allocation. reg2 is ignored on 64-bit platforms.
|
// Returns a box allocation. reg2 is ignored on 64-bit platforms.
|
||||||
LBoxAllocation useBoxFixed(MDefinition* mir, Register reg1, Register reg2,
|
LBoxAllocation useBoxFixed(MDefinition* mir, Register reg1, Register reg2,
|
||||||
bool useAtStart = false);
|
bool useAtStart = false);
|
||||||
|
@ -34,6 +37,11 @@ class LIRGeneratorMIPS64 : public LIRGeneratorMIPSShared
|
||||||
void lowerTruncateDToInt32(MTruncateToInt32* ins);
|
void lowerTruncateDToInt32(MTruncateToInt32* ins);
|
||||||
void lowerTruncateFToInt32(MTruncateToInt32* ins);
|
void lowerTruncateFToInt32(MTruncateToInt32* ins);
|
||||||
|
|
||||||
|
void lowerDivI64(MDiv* div);
|
||||||
|
void lowerModI64(MMod* mod);
|
||||||
|
void lowerUDivI64(MDiv* div);
|
||||||
|
void lowerUModI64(MMod* mod);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void visitBox(MBox* box);
|
void visitBox(MBox* box);
|
||||||
void visitUnbox(MUnbox* unbox);
|
void visitUnbox(MUnbox* unbox);
|
||||||
|
|
|
@ -50,6 +50,25 @@ MacroAssembler::and64(Imm64 imm, Register64 dest)
|
||||||
ma_and(dest.reg, ScratchRegister);
|
ma_and(dest.reg, ScratchRegister);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssembler::and64(Register64 src, Register64 dest)
|
||||||
|
{
|
||||||
|
ma_and(dest.reg, src.reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssembler::and64(const Operand& src, Register64 dest)
|
||||||
|
{
|
||||||
|
if (src.getTag() == Operand::MEM) {
|
||||||
|
Register64 scratch(ScratchRegister);
|
||||||
|
|
||||||
|
load64(src.toAddress(), scratch);
|
||||||
|
and64(scratch, dest);
|
||||||
|
} else {
|
||||||
|
and64(Register64(src.toReg()), dest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MacroAssembler::or64(Imm64 imm, Register64 dest)
|
MacroAssembler::or64(Imm64 imm, Register64 dest)
|
||||||
{
|
{
|
||||||
|
@ -82,12 +101,38 @@ MacroAssembler::or64(Register64 src, Register64 dest)
|
||||||
ma_or(dest.reg, src.reg);
|
ma_or(dest.reg, src.reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssembler::or64(const Operand& src, Register64 dest)
|
||||||
|
{
|
||||||
|
if (src.getTag() == Operand::MEM) {
|
||||||
|
Register64 scratch(ScratchRegister);
|
||||||
|
|
||||||
|
load64(src.toAddress(), scratch);
|
||||||
|
or64(scratch, dest);
|
||||||
|
} else {
|
||||||
|
or64(Register64(src.toReg()), dest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MacroAssembler::xor64(Register64 src, Register64 dest)
|
MacroAssembler::xor64(Register64 src, Register64 dest)
|
||||||
{
|
{
|
||||||
ma_xor(dest.reg, src.reg);
|
ma_xor(dest.reg, src.reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssembler::xor64(const Operand& src, Register64 dest)
|
||||||
|
{
|
||||||
|
if (src.getTag() == Operand::MEM) {
|
||||||
|
Register64 scratch(ScratchRegister);
|
||||||
|
|
||||||
|
load64(src.toAddress(), scratch);
|
||||||
|
xor64(scratch, dest);
|
||||||
|
} else {
|
||||||
|
xor64(Register64(src.toReg()), dest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MacroAssembler::xorPtr(Register src, Register dest)
|
MacroAssembler::xorPtr(Register src, Register dest)
|
||||||
{
|
{
|
||||||
|
@ -128,12 +173,33 @@ MacroAssembler::add64(Register64 src, Register64 dest)
|
||||||
addPtr(src.reg, dest.reg);
|
addPtr(src.reg, dest.reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssembler::add64(const Operand& src, Register64 dest)
|
||||||
|
{
|
||||||
|
if (src.getTag() == Operand::MEM) {
|
||||||
|
Register64 scratch(ScratchRegister);
|
||||||
|
|
||||||
|
load64(src.toAddress(), scratch);
|
||||||
|
add64(scratch, dest);
|
||||||
|
} else {
|
||||||
|
add64(Register64(src.toReg()), dest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MacroAssembler::add64(Imm32 imm, Register64 dest)
|
MacroAssembler::add64(Imm32 imm, Register64 dest)
|
||||||
{
|
{
|
||||||
ma_daddu(dest.reg, imm);
|
ma_daddu(dest.reg, imm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssembler::add64(Imm64 imm, Register64 dest)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(dest.reg != ScratchRegister);
|
||||||
|
mov(ImmWord(imm.value), ScratchRegister);
|
||||||
|
ma_daddu(dest.reg, ScratchRegister);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MacroAssembler::subPtr(Register src, Register dest)
|
MacroAssembler::subPtr(Register src, Register dest)
|
||||||
{
|
{
|
||||||
|
@ -146,6 +212,33 @@ MacroAssembler::subPtr(Imm32 imm, Register dest)
|
||||||
ma_dsubu(dest, dest, imm);
|
ma_dsubu(dest, dest, imm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssembler::sub64(Register64 src, Register64 dest)
|
||||||
|
{
|
||||||
|
as_dsubu(dest.reg, dest.reg, src.reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssembler::sub64(const Operand& src, Register64 dest)
|
||||||
|
{
|
||||||
|
if (src.getTag() == Operand::MEM) {
|
||||||
|
Register64 scratch(ScratchRegister);
|
||||||
|
|
||||||
|
load64(src.toAddress(), scratch);
|
||||||
|
sub64(scratch, dest);
|
||||||
|
} else {
|
||||||
|
sub64(Register64(src.toReg()), dest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssembler::sub64(Imm64 imm, Register64 dest)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(dest.reg != ScratchRegister);
|
||||||
|
mov(ImmWord(imm.value), ScratchRegister);
|
||||||
|
as_dsubu(dest.reg, dest.reg, ScratchRegister);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MacroAssembler::mul64(Imm64 imm, const Register64& dest)
|
MacroAssembler::mul64(Imm64 imm, const Register64& dest)
|
||||||
{
|
{
|
||||||
|
@ -155,6 +248,34 @@ MacroAssembler::mul64(Imm64 imm, const Register64& dest)
|
||||||
as_mflo(dest.reg);
|
as_mflo(dest.reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssembler::mul64(Imm64 imm, const Register64& dest, const Register temp)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(temp == InvalidReg);
|
||||||
|
mul64(imm, dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssembler::mul64(const Register64& src, const Register64& dest, const Register temp)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(temp == InvalidReg);
|
||||||
|
as_dmultu(dest.reg, src.reg);
|
||||||
|
as_mflo(dest.reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssembler::mul64(const Operand& src, const Register64& dest, const Register temp)
|
||||||
|
{
|
||||||
|
if (src.getTag() == Operand::MEM) {
|
||||||
|
Register64 scratch(ScratchRegister);
|
||||||
|
|
||||||
|
load64(src.toAddress(), scratch);
|
||||||
|
mul64(scratch, dest, temp);
|
||||||
|
} else {
|
||||||
|
mul64(Register64(src.toReg()), dest, temp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MacroAssembler::mulBy3(Register src, Register dest)
|
MacroAssembler::mulBy3(Register src, Register dest)
|
||||||
{
|
{
|
||||||
|
@ -171,6 +292,12 @@ MacroAssembler::inc64(AbsoluteAddress dest)
|
||||||
as_sd(SecondScratchReg, ScratchRegister, 0);
|
as_sd(SecondScratchReg, ScratchRegister, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssembler::neg64(Register64 reg)
|
||||||
|
{
|
||||||
|
as_dsubu(reg.reg, zero, reg.reg);
|
||||||
|
}
|
||||||
|
|
||||||
// ===============================================================
|
// ===============================================================
|
||||||
// Shift functions
|
// Shift functions
|
||||||
|
|
||||||
|
@ -188,6 +315,12 @@ MacroAssembler::lshift64(Imm32 imm, Register64 dest)
|
||||||
ma_dsll(dest.reg, dest.reg, imm);
|
ma_dsll(dest.reg, dest.reg, imm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssembler::lshift64(Register shift, Register64 dest)
|
||||||
|
{
|
||||||
|
ma_dsll(dest.reg, dest.reg, shift);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MacroAssembler::rshiftPtr(Imm32 imm, Register dest)
|
MacroAssembler::rshiftPtr(Imm32 imm, Register dest)
|
||||||
{
|
{
|
||||||
|
@ -195,6 +328,19 @@ MacroAssembler::rshiftPtr(Imm32 imm, Register dest)
|
||||||
ma_dsrl(dest, dest, imm);
|
ma_dsrl(dest, dest, imm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssembler::rshift64(Imm32 imm, Register64 dest)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(0 <= imm.value && imm.value < 64);
|
||||||
|
ma_dsrl(dest.reg, dest.reg, imm);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssembler::rshift64(Register shift, Register64 dest)
|
||||||
|
{
|
||||||
|
ma_dsrl(dest.reg, dest.reg, shift);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MacroAssembler::rshiftPtrArithmetic(Imm32 imm, Register dest)
|
MacroAssembler::rshiftPtrArithmetic(Imm32 imm, Register dest)
|
||||||
{
|
{
|
||||||
|
@ -203,15 +349,131 @@ MacroAssembler::rshiftPtrArithmetic(Imm32 imm, Register dest)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MacroAssembler::rshift64(Imm32 imm, Register64 dest)
|
MacroAssembler::rshift64Arithmetic(Imm32 imm, Register64 dest)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(0 <= imm.value && imm.value < 64);
|
MOZ_ASSERT(0 <= imm.value && imm.value < 64);
|
||||||
ma_dsrl(dest.reg, dest.reg, imm);
|
ma_dsra(dest.reg, dest.reg, imm);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssembler::rshift64Arithmetic(Register shift, Register64 dest)
|
||||||
|
{
|
||||||
|
ma_dsra(dest.reg, dest.reg, shift);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===============================================================
|
||||||
|
// Rotation functions
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssembler::rotateLeft64(Imm32 count, Register64 src, Register64 dest, Register temp)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(temp == InvalidReg);
|
||||||
|
|
||||||
|
if (count.value)
|
||||||
|
ma_drol(dest.reg, src.reg, count);
|
||||||
|
else
|
||||||
|
ma_move(dest.reg, src.reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssembler::rotateLeft64(Register count, Register64 src, Register64 dest, Register temp)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(temp == InvalidReg);
|
||||||
|
ma_drol(dest.reg, src.reg, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssembler::rotateRight64(Imm32 count, Register64 src, Register64 dest, Register temp)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(temp == InvalidReg);
|
||||||
|
|
||||||
|
if (count.value)
|
||||||
|
ma_dror(dest.reg, src.reg, count);
|
||||||
|
else
|
||||||
|
ma_move(dest.reg, src.reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssembler::rotateRight64(Register count, Register64 src, Register64 dest, Register temp)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(temp == InvalidReg);
|
||||||
|
ma_dror(dest.reg, src.reg, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===============================================================
|
||||||
|
// Bit counting functions
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssembler::clz64(Register64 src, Register dest)
|
||||||
|
{
|
||||||
|
as_dclz(dest, src.reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssembler::ctz64(Register64 src, Register dest)
|
||||||
|
{
|
||||||
|
ma_dctz(dest, src.reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssembler::popcnt64(Register64 input, Register64 output, Register tmp)
|
||||||
|
{
|
||||||
|
ma_move(output.reg, input.reg);
|
||||||
|
ma_dsra(tmp, input.reg, Imm32(1));
|
||||||
|
ma_li(ScratchRegister, ImmWord(0x5555555555555555UL));
|
||||||
|
ma_and(tmp, ScratchRegister);
|
||||||
|
ma_dsubu(output.reg, tmp);
|
||||||
|
ma_dsra(tmp, output.reg, Imm32(2));
|
||||||
|
ma_li(ScratchRegister, ImmWord(0x3333333333333333UL));
|
||||||
|
ma_and(output.reg, ScratchRegister);
|
||||||
|
ma_and(tmp, ScratchRegister);
|
||||||
|
ma_daddu(output.reg, tmp);
|
||||||
|
ma_dsrl(tmp, output.reg, Imm32(4));
|
||||||
|
ma_daddu(output.reg, tmp);
|
||||||
|
ma_li(ScratchRegister, ImmWord(0xF0F0F0F0F0F0F0FUL));
|
||||||
|
ma_and(output.reg, ScratchRegister);
|
||||||
|
ma_dsll(tmp, output.reg, Imm32(8));
|
||||||
|
ma_daddu(output.reg, tmp);
|
||||||
|
ma_dsll(tmp, output.reg, Imm32(16));
|
||||||
|
ma_daddu(output.reg, tmp);
|
||||||
|
ma_dsll(tmp, output.reg, Imm32(32));
|
||||||
|
ma_daddu(output.reg, tmp);
|
||||||
|
ma_dsra(output.reg, output.reg, Imm32(56));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===============================================================
|
// ===============================================================
|
||||||
// Branch functions
|
// Branch functions
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssembler::branch64(Condition cond, Register64 lhs, Imm64 val, Label* success, Label* fail)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(cond == Assembler::NotEqual || cond == Assembler::Equal ||
|
||||||
|
cond == Assembler::LessThan || cond == Assembler::LessThanOrEqual ||
|
||||||
|
cond == Assembler::GreaterThan || cond == Assembler::GreaterThanOrEqual ||
|
||||||
|
cond == Assembler::Below || cond == Assembler::BelowOrEqual ||
|
||||||
|
cond == Assembler::Above || cond == Assembler::AboveOrEqual,
|
||||||
|
"other condition codes not supported");
|
||||||
|
|
||||||
|
branchPtr(cond, lhs.reg, ImmWord(val.value), success);
|
||||||
|
if (fail)
|
||||||
|
jump(fail);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssembler::branch64(Condition cond, Register64 lhs, Register64 rhs, Label* success, Label* fail)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(cond == Assembler::NotEqual || cond == Assembler::Equal ||
|
||||||
|
cond == Assembler::LessThan || cond == Assembler::LessThanOrEqual ||
|
||||||
|
cond == Assembler::GreaterThan || cond == Assembler::GreaterThanOrEqual ||
|
||||||
|
cond == Assembler::Below || cond == Assembler::BelowOrEqual ||
|
||||||
|
cond == Assembler::Above || cond == Assembler::AboveOrEqual,
|
||||||
|
"other condition codes not supported");
|
||||||
|
|
||||||
|
branchPtr(cond, lhs.reg, rhs.reg, success);
|
||||||
|
if (fail)
|
||||||
|
jump(fail);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MacroAssembler::branch64(Condition cond, const Address& lhs, Imm64 val, Label* label)
|
MacroAssembler::branch64(Condition cond, const Address& lhs, Imm64 val, Label* label)
|
||||||
{
|
{
|
||||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче