зеркало из 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 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() {
|
||||
clearAllPluginPermissions();
|
||||
gTestBrowser.removeEventListener("load", pageLoad, true);
|
||||
gBrowser.removeCurrentTab();
|
||||
if (gPrivateWindow) {
|
||||
gPrivateWindow.close();
|
||||
}
|
||||
window.focus();
|
||||
finish();
|
||||
}
|
||||
|
||||
function createPrivateWindow(nextTest, url) {
|
||||
gPrivateWindow = OpenBrowserWindow({private: true});
|
||||
let createPrivateWindow = Task.async(function* createPrivateWindow(url) {
|
||||
gPrivateWindow = yield BrowserTestUtils.openNewBrowserWindow({private: true});
|
||||
ok(!!gPrivateWindow, "should have created a private window.");
|
||||
whenDelayedStartupFinished(gPrivateWindow, function() {
|
||||
gPrivateBrowser = gPrivateWindow.getBrowser().selectedBrowser;
|
||||
gPrivateBrowser.addEventListener("load", pageLoad, true);
|
||||
gNextTest = function() {
|
||||
prepareTest(nextTest, url, gPrivateBrowser);
|
||||
};
|
||||
});
|
||||
}
|
||||
gPrivateBrowser = gPrivateWindow.getBrowser().selectedBrowser;
|
||||
|
||||
function whenDelayedStartupFinished(aWindow, aCallback) {
|
||||
Services.obs.addObserver(function observer(aSubject, aTopic) {
|
||||
if (aWindow == aSubject) {
|
||||
Services.obs.removeObserver(observer, aTopic);
|
||||
executeSoon(aCallback);
|
||||
}
|
||||
}, "browser-delayed-startup-finished", false);
|
||||
}
|
||||
BrowserTestUtils.loadURI(gPrivateBrowser, url);
|
||||
yield BrowserTestUtils.browserLoaded(gPrivateBrowser);
|
||||
});
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
add_task(function* test() {
|
||||
registerCleanupFunction(function() {
|
||||
clearAllPluginPermissions();
|
||||
getTestPlugin().enabledState = Ci.nsIPluginTag.STATE_ENABLED;
|
||||
|
@ -71,136 +39,150 @@ function test() {
|
|||
let newTab = gBrowser.addTab();
|
||||
gBrowser.selectedTab = newTab;
|
||||
gTestBrowser = gBrowser.selectedBrowser;
|
||||
gTestBrowser.addEventListener("load", pageLoad, true);
|
||||
let promise = BrowserTestUtils.browserLoaded(gTestBrowser);
|
||||
|
||||
Services.prefs.setBoolPref("plugins.click_to_play", true);
|
||||
getTestPlugin().enabledState = Ci.nsIPluginTag.STATE_CLICKTOPLAY;
|
||||
getTestPlugin("Second Test Plug-in").enabledState = Ci.nsIPluginTag.STATE_CLICKTOPLAY;
|
||||
gNextTest = test1a;
|
||||
}
|
||||
yield promise;
|
||||
});
|
||||
|
||||
function test1a() {
|
||||
createPrivateWindow(test1b, gHttpTestRoot + "plugin_test.html");
|
||||
}
|
||||
add_task(function* test1a() {
|
||||
yield createPrivateWindow(gHttpTestRoot + "plugin_test.html");
|
||||
});
|
||||
|
||||
function test1b() {
|
||||
add_task(function* test1b() {
|
||||
let popupNotification = gPrivateWindow.PopupNotifications.getNotification("click-to-play-plugins", gPrivateBrowser);
|
||||
ok(popupNotification, "Test 1b, Should have a click-to-play notification");
|
||||
|
||||
let plugin = gPrivateBrowser.contentDocument.getElementById("test");
|
||||
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
|
||||
ok(!objLoadingContent.activated, "Test 1b, Plugin should not be activated");
|
||||
yield ContentTask.spawn(gPrivateBrowser, null, function() {
|
||||
let plugin = content.document.getElementById("test");
|
||||
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
|
||||
ok(!objLoadingContent.activated, "Test 1b, Plugin should not be activated");
|
||||
});
|
||||
|
||||
// Check the button status
|
||||
let promiseShown = BrowserTestUtils.waitForEvent(gPrivateWindow.PopupNotifications.panel,
|
||||
"Shown");
|
||||
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();
|
||||
prepareTest(test2a, gHttpTestRoot + "plugin_test.html");
|
||||
});
|
||||
}
|
||||
yield promiseShown;
|
||||
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
|
||||
let popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
|
||||
ok(popupNotification, "Test 2a, Should have a click-to-play notification");
|
||||
|
||||
let plugin = gTestBrowser.contentDocument.getElementById("test");
|
||||
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
|
||||
ok(!objLoadingContent.activated, "Test 2a, Plugin should not be activated");
|
||||
yield ContentTask.spawn(gTestBrowser, null, function() {
|
||||
let plugin = content.document.getElementById("test");
|
||||
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
|
||||
ok(!objLoadingContent.activated, "Test 2a, Plugin should not be activated");
|
||||
});
|
||||
|
||||
// Simulate clicking the "Allow Now" button.
|
||||
let promiseShown = BrowserTestUtils.waitForEvent(PopupNotifications.panel,
|
||||
"Shown");
|
||||
popupNotification.reshow();
|
||||
promiseShown.then(() => {
|
||||
PopupNotifications.panel.firstChild._secondaryButton.click();
|
||||
yield promiseShown;
|
||||
|
||||
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;
|
||||
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() {
|
||||
createPrivateWindow(test2c, gHttpTestRoot + "plugin_test.html");
|
||||
}
|
||||
add_task(function* test2b() {
|
||||
yield createPrivateWindow(gHttpTestRoot + "plugin_test.html");
|
||||
});
|
||||
|
||||
function test2c() {
|
||||
let promise = 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");
|
||||
add_task(function* test2c() {
|
||||
yield TestUtils.topicObserved("PopupNotifications-updateNotShowing");
|
||||
|
||||
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);
|
||||
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
|
||||
let popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
|
||||
ok(popupNotification, "Test 3a, Should have a click-to-play notification");
|
||||
|
||||
let plugin = gTestBrowser.contentDocument.getElementById("test");
|
||||
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
|
||||
ok(!objLoadingContent.activated, "Test 3a, Plugin should not be activated");
|
||||
yield ContentTask.spawn(gTestBrowser, null, function() {
|
||||
let plugin = content.document.getElementById("test");
|
||||
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
|
||||
ok(!objLoadingContent.activated, "Test 3a, Plugin should not be activated");
|
||||
});
|
||||
|
||||
// Simulate clicking the "Allow Always" button.
|
||||
let promiseShown = BrowserTestUtils.waitForEvent(PopupNotifications.panel,
|
||||
"Shown");
|
||||
popupNotification.reshow();
|
||||
promiseShown.then(() => {
|
||||
PopupNotifications.panel.firstChild._secondaryButton.click();
|
||||
yield promiseShown;
|
||||
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;
|
||||
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() {
|
||||
createPrivateWindow(test3c, gHttpTestRoot + "plugin_test.html");
|
||||
}
|
||||
add_task(function* test3b() {
|
||||
yield createPrivateWindow(gHttpTestRoot + "plugin_test.html");
|
||||
});
|
||||
|
||||
function test3c() {
|
||||
let promise = TestUtils.topicObserved("PopupNotifications-updateNotShowing");
|
||||
promise.then(() => {
|
||||
let popupNotification = gPrivateWindow.PopupNotifications.getNotification("click-to-play-plugins", gPrivateBrowser);
|
||||
ok(popupNotification, "Test 3c, Should have a click-to-play notification");
|
||||
add_task(function* test3c() {
|
||||
yield TestUtils.topicObserved("PopupNotifications-updateNotShowing");
|
||||
let popupNotification = gPrivateWindow.PopupNotifications.getNotification("click-to-play-plugins", gPrivateBrowser);
|
||||
ok(popupNotification, "Test 3c, Should have a click-to-play notification");
|
||||
|
||||
// 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 3c, Activated plugin in a private window should not have visible buttons");
|
||||
// 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 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);
|
||||
ok(popupNotification, "Test 3d, Should have a click-to-play notification");
|
||||
|
||||
|
@ -208,31 +190,30 @@ function test3d() {
|
|||
let promiseShown = BrowserTestUtils.waitForEvent(gPrivateWindow.PopupNotifications.panel,
|
||||
"Shown");
|
||||
popupNotification.reshow();
|
||||
promiseShown.then(() => {
|
||||
let doc = gPrivateWindow.document;
|
||||
for (let item of gPrivateWindow.PopupNotifications.panel.firstChild.childNodes) {
|
||||
let allowalways = doc.getAnonymousElementByAttribute(item, "anonid", "allowalways");
|
||||
ok(allowalways, "Test 3d, should have list item for allow always");
|
||||
let allownow = doc.getAnonymousElementByAttribute(item, "anonid", "allownow");
|
||||
ok(allownow, "Test 3d, should have list item for allow now");
|
||||
let block = doc.getAnonymousElementByAttribute(item, "anonid", "block");
|
||||
ok(block, "Test 3d, should have list item for block");
|
||||
yield promiseShown;
|
||||
let doc = gPrivateWindow.document;
|
||||
for (let item of gPrivateWindow.PopupNotifications.panel.firstChild.childNodes) {
|
||||
let allowalways = doc.getAnonymousElementByAttribute(item, "anonid", "allowalways");
|
||||
ok(allowalways, "Test 3d, should have list item for allow always");
|
||||
let allownow = doc.getAnonymousElementByAttribute(item, "anonid", "allownow");
|
||||
ok(allownow, "Test 3d, should have list item for allow now");
|
||||
let block = doc.getAnonymousElementByAttribute(item, "anonid", "block");
|
||||
ok(block, "Test 3d, should have list item for block");
|
||||
|
||||
if (item.action.pluginName === "Test") {
|
||||
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(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.");
|
||||
} else if (item.action.pluginName === "Second Test") {
|
||||
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(!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.");
|
||||
} else {
|
||||
ok(false, "Test 3d, Unexpected plugin '"+item.action.pluginName+"'");
|
||||
}
|
||||
if (item.action.pluginName === "Test") {
|
||||
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(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.");
|
||||
} else if (item.action.pluginName === "Second Test") {
|
||||
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(!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.");
|
||||
} else {
|
||||
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_downloads_panel_block.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)
|
||||
endif
|
||||
|
||||
SEARCHPLUGINS_NAMES = $(shell cat $(call MERGE_FILE,/searchplugins/list.txt)) ddg
|
||||
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_FILENAMES := $(shell $(PYTHON) $(srcdir)/searchplugins.py $(srcdir)/search/list.json $(AB_CD))
|
||||
SEARCHPLUGINS_PATH := .deps/generated_$(AB_CD)
|
||||
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))))
|
||||
|
@ -75,8 +70,8 @@ SEARCHPLUGINS := $(foreach plugin,$(addsuffix .xml,$(SEARCHPLUGINS_FILENAMES)),$
|
|||
SEARCHPLUGINS_FLAGS := --silence-missing-directive-warnings
|
||||
PP_TARGETS += SEARCHPLUGINS
|
||||
|
||||
list-txt = $(SEARCHPLUGINS_PATH)/list.txt
|
||||
GARBAGE += $(list-txt)
|
||||
list-json = $(SEARCHPLUGINS_PATH)/list.json
|
||||
GARBAGE += $(list-json)
|
||||
|
||||
libs:: searchplugins
|
||||
|
||||
|
@ -88,10 +83,9 @@ include $(topsrcdir)/config/rules.mk
|
|||
|
||||
include $(topsrcdir)/toolkit/locales/l10n.mk
|
||||
|
||||
$(list-txt): $(call mkdir_deps,$(SEARCHPLUGINS_PATH)) $(if $(IS_LANGUAGE_REPACK),FORCE)
|
||||
$(RM) $(list-txt)
|
||||
$(foreach plugin,$(SEARCHPLUGINS_NAMES),printf '$(plugin)\n' >> $(list-txt);)
|
||||
searchplugins:: $(list-txt)
|
||||
$(list-json): $(call mkdir_deps,$(SEARCHPLUGINS_PATH)) $(if $(IS_LANGUAGE_REPACK),FORCE)
|
||||
$(shell $(PYTHON) $(srcdir)/searchjson.py $(srcdir)/search/list.json $(AB_CD) $(list-json))
|
||||
searchplugins:: $(list-json)
|
||||
|
||||
$(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",
|
||||
"devtools/client", "devtools/shared",
|
||||
"browser",
|
||||
"extensions/reporter", "extensions/spellcheck",
|
||||
"extensions/spellcheck",
|
||||
"other-licenses/branding/firefox",
|
||||
"browser/branding/official",
|
||||
"services/sync",
|
||||
"browser/extensions/pocket"):
|
||||
"services/sync"):
|
||||
return "ignore"
|
||||
if mod not in ("browser", "extensions/spellcheck"):
|
||||
# we only have exceptions for browser and extensions/spellcheck
|
||||
|
|
|
@ -90,11 +90,11 @@
|
|||
locale/browser/syncQuota.properties (%chrome/browser/syncQuota.properties)
|
||||
% resource search-plugins chrome://browser/locale/searchplugins/
|
||||
#if BUILD_FASTER
|
||||
locale/browser/searchplugins/list.txt (%searchplugins/list.txt)
|
||||
locale/browser/searchplugins/ (%searchplugins/*.xml)
|
||||
locale/browser/searchplugins/list.json (search/list.json)
|
||||
#else
|
||||
locale/browser/searchplugins/list.txt (.deps/generated_@AB_CD@/list.txt)
|
||||
locale/browser/searchplugins/ (.deps/generated_@AB_CD@/*.xml)
|
||||
locale/browser/searchplugins/list.json (.deps/generated_@AB_CD@/list.json)
|
||||
#endif
|
||||
% locale browser-region @AB_CD@ %locale/browser-region/
|
||||
locale/browser-region/region.properties (%chrome/browser-region/region.properties)
|
||||
|
|
|
@ -8,11 +8,9 @@ all = browser/locales/all-locales
|
|||
|
||||
[compare]
|
||||
dirs = browser
|
||||
extensions/reporter
|
||||
other-licenses/branding/firefox
|
||||
browser/branding/official
|
||||
devtools/client
|
||||
browser/extensions/pocket
|
||||
|
||||
[includes]
|
||||
# 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 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(
|
||||
tool => tool.id != "command-button-noautohide");
|
||||
tool => tool.id != "command-button-noautohide" && tool.id != "command-button-pick");
|
||||
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,
|
||||
"All of the buttons are toggleable.");
|
||||
|
|
|
@ -71,10 +71,6 @@ loader.lazyGetter(this, "registerHarOverlay", () => {
|
|||
// addons that have manually inserted toolbarbuttons into DOM.
|
||||
// (By default, supported target is only local tab)
|
||||
const ToolboxButtons = exports.ToolboxButtons = [
|
||||
{ id: "command-button-pick",
|
||||
isTargetSupported: target =>
|
||||
target.getTrait("highlightable")
|
||||
},
|
||||
{ id: "command-button-frames",
|
||||
isTargetSupported: target => {
|
||||
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
|
||||
*/
|
||||
_buildButtons: function () {
|
||||
if (!this.target.isAddon || this.target.isWebExtension) {
|
||||
if (this.target.getTrait("highlightable")) {
|
||||
this._buildPickerButton();
|
||||
}
|
||||
|
||||
|
@ -1003,7 +999,6 @@ Toolbox.prototype = {
|
|||
this._pickerButton.className =
|
||||
"command-button command-button-invertable devtools-button";
|
||||
this._pickerButton.setAttribute("title", L10N.getStr("pickButton.tooltip"));
|
||||
this._pickerButton.setAttribute("hidden", "true");
|
||||
|
||||
let container = this.doc.querySelector("#toolbox-picker-container");
|
||||
container.appendChild(this._pickerButton);
|
||||
|
|
|
@ -37,7 +37,6 @@ pref("devtools.toolbox.splitconsoleEnabled", false);
|
|||
pref("devtools.toolbox.splitconsoleHeight", 100);
|
||||
|
||||
// Toolbox Button preferences
|
||||
pref("devtools.command-button-pick.enabled", true);
|
||||
pref("devtools.command-button-frames.enabled", true);
|
||||
pref("devtools.command-button-splitconsole.enabled", true);
|
||||
pref("devtools.command-button-paintflashing.enabled", false);
|
||||
|
|
|
@ -38,6 +38,8 @@
|
|||
#include "nsIDOMDocument.h"
|
||||
#include "nsIDOMElement.h"
|
||||
|
||||
#include "nsArray.h"
|
||||
#include "nsArrayUtils.h"
|
||||
#include "nsIDOMStorage.h"
|
||||
#include "nsIContentViewer.h"
|
||||
#include "nsIDocumentLoaderFactory.h"
|
||||
|
@ -205,7 +207,6 @@
|
|||
#include "nsISecureBrowserUI.h"
|
||||
#include "nsISocketProvider.h"
|
||||
#include "nsIStringBundle.h"
|
||||
#include "nsISupportsArray.h"
|
||||
#include "nsIURIFixup.h"
|
||||
#include "nsIURILoader.h"
|
||||
#include "nsIURL.h"
|
||||
|
@ -6684,21 +6685,20 @@ nsDocShell::RefreshURI(nsIURI* aURI, int32_t aDelay, bool aRepeat,
|
|||
refreshTimer->mMetaRefresh = aMetaRefresh;
|
||||
|
||||
if (!mRefreshURIList) {
|
||||
NS_ENSURE_SUCCESS(NS_NewISupportsArray(getter_AddRefs(mRefreshURIList)),
|
||||
NS_ERROR_FAILURE);
|
||||
mRefreshURIList = nsArray::Create();
|
||||
}
|
||||
|
||||
if (busyFlags & BUSY_FLAGS_BUSY || (!mIsActive && mDisableMetaRefreshWhenInactive)) {
|
||||
// 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.
|
||||
mRefreshURIList->AppendElement(refreshTimer);
|
||||
mRefreshURIList->AppendElement(refreshTimer, /*weak =*/ false);
|
||||
} else {
|
||||
// There is no page loading going on right now. Create the
|
||||
// timer and fire it right away.
|
||||
nsCOMPtr<nsITimer> timer = do_CreateInstance("@mozilla.org/timer;1");
|
||||
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);
|
||||
}
|
||||
return NS_OK;
|
||||
|
@ -6715,7 +6715,7 @@ nsDocShell::ForceRefreshURIFromTimer(nsIURI* aURI,
|
|||
// Remove aTimer from mRefreshURIList if needed
|
||||
if (mRefreshURIList) {
|
||||
uint32_t n = 0;
|
||||
mRefreshURIList->Count(&n);
|
||||
mRefreshURIList->GetLength(&n);
|
||||
|
||||
for (uint32_t i = 0; i < n; ++i) {
|
||||
nsCOMPtr<nsITimer> timer = do_QueryElementAt(mRefreshURIList, i);
|
||||
|
@ -7082,14 +7082,14 @@ nsDocShell::SetupRefreshURI(nsIChannel* aChannel)
|
|||
}
|
||||
|
||||
static void
|
||||
DoCancelRefreshURITimers(nsISupportsArray* aTimerList)
|
||||
DoCancelRefreshURITimers(nsIMutableArray* aTimerList)
|
||||
{
|
||||
if (!aTimerList) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t n = 0;
|
||||
aTimerList->Count(&n);
|
||||
aTimerList->GetLength(&n);
|
||||
|
||||
while (n) {
|
||||
nsCOMPtr<nsITimer> timer(do_QueryElementAt(aTimerList, --n));
|
||||
|
@ -7122,7 +7122,7 @@ nsDocShell::GetRefreshPending(bool* aResult)
|
|||
}
|
||||
|
||||
uint32_t count;
|
||||
nsresult rv = mRefreshURIList->Count(&count);
|
||||
nsresult rv = mRefreshURIList->GetLength(&count);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
*aResult = (count != 0);
|
||||
}
|
||||
|
@ -7134,7 +7134,7 @@ nsDocShell::SuspendRefreshURIs()
|
|||
{
|
||||
if (mRefreshURIList) {
|
||||
uint32_t n = 0;
|
||||
mRefreshURIList->Count(&n);
|
||||
mRefreshURIList->GetLength(&n);
|
||||
|
||||
for (uint32_t i = 0; i < n; ++i) {
|
||||
nsCOMPtr<nsITimer> timer = do_QueryElementAt(mRefreshURIList, i);
|
||||
|
@ -7152,7 +7152,7 @@ nsDocShell::SuspendRefreshURIs()
|
|||
NS_ASSERTION(rt,
|
||||
"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;
|
||||
}
|
||||
uint32_t n = 0;
|
||||
mRefreshURIList->Count(&n);
|
||||
mRefreshURIList->GetLength(&n);
|
||||
|
||||
while (n) {
|
||||
nsCOMPtr<nsISupports> element;
|
||||
mRefreshURIList->GetElementAt(--n, getter_AddRefs(element));
|
||||
nsCOMPtr<nsITimerCallback> refreshInfo(do_QueryInterface(element));
|
||||
nsCOMPtr<nsITimerCallback> refreshInfo =
|
||||
do_QueryElementAt(mRefreshURIList, --n);
|
||||
|
||||
if (refreshInfo) {
|
||||
// This is the nsRefreshTimer object, waiting to be
|
||||
|
@ -7212,7 +7211,7 @@ nsDocShell::RefreshURIFromQueue()
|
|||
// its corresponding timer object, so that in case another
|
||||
// load comes through before the timer can go off, the timer will
|
||||
// get cancelled in CancelRefreshURITimer()
|
||||
mRefreshURIList->ReplaceElementAt(timer, n);
|
||||
mRefreshURIList->ReplaceElementAt(timer, n, /*weak =*/ false);
|
||||
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
|
||||
// when EndPageLoad() is called.
|
||||
nsCOMPtr<nsISupportsArray> refreshURIList;
|
||||
nsCOMPtr<nsIMutableArray> refreshURIList;
|
||||
mLSHE->GetRefreshURIList(getter_AddRefs(refreshURIList));
|
||||
|
||||
// Reattach to the window object.
|
||||
|
@ -8755,7 +8754,7 @@ nsDocShell::RestoreFromHistory()
|
|||
|
||||
#ifdef DEBUG
|
||||
{
|
||||
nsCOMPtr<nsISupportsArray> refreshURIs;
|
||||
nsCOMPtr<nsIMutableArray> refreshURIs;
|
||||
mLSHE->GetRefreshURIList(getter_AddRefs(refreshURIs));
|
||||
nsCOMPtr<nsIDocShellTreeItem> childShell;
|
||||
mLSHE->ChildShellAt(0, getter_AddRefs(childShell));
|
||||
|
|
|
@ -84,11 +84,11 @@ class nsIDOMNode;
|
|||
class nsIDocShellTreeOwner;
|
||||
class nsIGlobalHistory2;
|
||||
class nsIHttpChannel;
|
||||
class nsIMutableArray;
|
||||
class nsIPrompt;
|
||||
class nsISHistory;
|
||||
class nsISecureBrowserUI;
|
||||
class nsIStringBundle;
|
||||
class nsISupportsArray;
|
||||
class nsIURIFixup;
|
||||
class nsIURILoader;
|
||||
class nsIWebBrowserFind;
|
||||
|
@ -810,8 +810,8 @@ protected:
|
|||
nsCString mContentTypeHint;
|
||||
nsIntPoint mDefaultScrollbarPref; // persistent across doc loads
|
||||
|
||||
nsCOMPtr<nsISupportsArray> mRefreshURIList;
|
||||
nsCOMPtr<nsISupportsArray> mSavedRefreshURIList;
|
||||
nsCOMPtr<nsIMutableArray> mRefreshURIList;
|
||||
nsCOMPtr<nsIMutableArray> mSavedRefreshURIList;
|
||||
RefPtr<nsDSURIContentListener> mContentListener;
|
||||
nsCOMPtr<nsIContentViewer> mContentViewer;
|
||||
nsCOMPtr<nsIWidget> mParentWidget;
|
||||
|
|
|
@ -12,12 +12,12 @@
|
|||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsIMutableArray;
|
||||
interface nsILayoutHistoryState;
|
||||
interface nsIContentViewer;
|
||||
interface nsIURI;
|
||||
interface nsIInputStream;
|
||||
interface nsIDocShellTreeItem;
|
||||
interface nsISupportsArray;
|
||||
interface nsIStructuredCloneContainer;
|
||||
interface nsIBFCacheEntry;
|
||||
interface nsIPrincipal;
|
||||
|
@ -119,7 +119,7 @@ interface nsISHEntry : nsISupports
|
|||
void clearChildShells();
|
||||
|
||||
/** Saved refresh URI list for the content viewer */
|
||||
attribute nsISupportsArray refreshURIList;
|
||||
attribute nsIMutableArray refreshURIList;
|
||||
|
||||
/**
|
||||
* Ensure that the cached presentation members are self-consistent.
|
||||
|
|
|
@ -824,14 +824,14 @@ nsSHEntry::ClearChildShells()
|
|||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsSHEntry::GetRefreshURIList(nsISupportsArray** aList)
|
||||
nsSHEntry::GetRefreshURIList(nsIMutableArray** aList)
|
||||
{
|
||||
NS_IF_ADDREF(*aList = mShared->mRefreshURIList);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsSHEntry::SetRefreshURIList(nsISupportsArray* aList)
|
||||
nsSHEntry::SetRefreshURIList(nsIMutableArray* aList)
|
||||
{
|
||||
mShared->mRefreshURIList = aList;
|
||||
return NS_OK;
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
#include "nsILayoutHistoryState.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "nsISupportsArray.h"
|
||||
#include "nsArray.h"
|
||||
|
||||
namespace dom = mozilla::dom;
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ class nsIContentViewer;
|
|||
class nsIDocShellTreeItem;
|
||||
class nsILayoutHistoryState;
|
||||
class nsDocShellEditorData;
|
||||
class nsISupportsArray;
|
||||
class nsIMutableArray;
|
||||
|
||||
// A document may have multiple SHEntries, either due to hash navigations or
|
||||
// calls to history.pushState. SHEntries corresponding to the same document
|
||||
|
@ -89,7 +89,7 @@ private:
|
|||
bool mExpired;
|
||||
nsCOMPtr<nsISupports> mWindowState;
|
||||
nsIntRect mViewerBounds;
|
||||
nsCOMPtr<nsISupportsArray> mRefreshURIList;
|
||||
nsCOMPtr<nsIMutableArray> mRefreshURIList;
|
||||
nsExpirationState mExpirationState;
|
||||
nsAutoPtr<nsDocShellEditorData> mEditorData;
|
||||
};
|
||||
|
|
|
@ -50,7 +50,6 @@
|
|||
#include "nsIXULWindow.h"
|
||||
#include "nsIEditor.h"
|
||||
#include "nsIMozBrowserFrame.h"
|
||||
#include "nsIPermissionManager.h"
|
||||
#include "nsISHistory.h"
|
||||
#include "nsNullPrincipal.h"
|
||||
#include "nsIScriptError.h"
|
||||
|
@ -146,7 +145,6 @@ NS_INTERFACE_MAP_END
|
|||
|
||||
nsFrameLoader::nsFrameLoader(Element* aOwner, bool aNetworkCreated)
|
||||
: mOwnerContent(aOwner)
|
||||
, mAppIdSentToPermissionManager(nsIScriptSecurityManager::NO_APP_ID)
|
||||
, mDetachedSubdocFrame(nullptr)
|
||||
, mRemoteBrowser(nullptr)
|
||||
, mChildID(0)
|
||||
|
@ -494,9 +492,6 @@ nsFrameLoader::ReallyStartLoadingInternal()
|
|||
mURIToLoad = nullptr;
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Track the appId's reference count if this frame is in-process
|
||||
ResetPermissionManagerStatus();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -1753,8 +1748,6 @@ nsFrameLoader::SetOwnerContent(Element* aContent)
|
|||
if (RenderFrameParent* rfp = GetCurrentRenderFrame()) {
|
||||
rfp->OwnerContentChanged(aContent);
|
||||
}
|
||||
|
||||
ResetPermissionManagerStatus();
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
|
|
|
@ -325,10 +325,6 @@ private:
|
|||
? 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 DestroyBrowserFrameScripts();
|
||||
|
||||
|
@ -354,9 +350,6 @@ private:
|
|||
// our <browser> element.
|
||||
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
|
||||
// reframed. Used to restore the presentation after reframing.
|
||||
nsWeakFrame mDetachedSubdocFrame;
|
||||
|
|
|
@ -3165,7 +3165,7 @@ nsGlobalWindow::SetOpenerWindow(nsPIDOMWindowOuter* aOpener,
|
|||
"Probably too late to call ComputeIsSecureContext again");
|
||||
mHadOriginalOpener = true;
|
||||
mOriginalOpenerWasSecureContext =
|
||||
nsGlobalWindow::Cast(aOpener->GetCurrentInnerWindow())->IsSecureContext();
|
||||
aOpener->GetCurrentInnerWindow()->IsSecureContext();
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
|
@ -3860,6 +3860,12 @@ nsPIDOMWindowInner::CreatePerformanceObjectIfNeeded()
|
|||
}
|
||||
}
|
||||
|
||||
bool
|
||||
nsPIDOMWindowInner::IsSecureContext() const
|
||||
{
|
||||
return nsGlobalWindow::Cast(this)->IsSecureContext();
|
||||
}
|
||||
|
||||
SuspendTypes
|
||||
nsPIDOMWindowOuter::GetMediaSuspend() const
|
||||
{
|
||||
|
|
|
@ -328,6 +328,10 @@ public:
|
|||
return static_cast<nsGlobalWindow*>(
|
||||
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) {
|
||||
return Cast(nsPIDOMWindowInner::From(aWin));
|
||||
}
|
||||
|
|
|
@ -811,6 +811,11 @@ public:
|
|||
return mInnerObjectsFreed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether this window is a secure context.
|
||||
*/
|
||||
bool IsSecureContext() const;
|
||||
|
||||
protected:
|
||||
void CreatePerformanceObjectIfNeeded();
|
||||
};
|
||||
|
|
|
@ -51,6 +51,7 @@
|
|||
#include "mozilla/dom/WebIDLGlobalNameHash.h"
|
||||
#include "mozilla/dom/WorkerPrivate.h"
|
||||
#include "mozilla/dom/WorkerScope.h"
|
||||
#include "mozilla/dom/XrayExpandoClass.h"
|
||||
#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
|
||||
#include "nsDOMClassInfo.h"
|
||||
#include "ipc/ErrorIPCUtils.h"
|
||||
|
@ -1892,6 +1893,37 @@ XrayOwnPropertyKeys(JSContext* cx, JS::Handle<JSObject*> wrapper,
|
|||
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 = {
|
||||
nullptr,
|
||||
nullptr,
|
||||
|
|
|
@ -2400,6 +2400,41 @@ XrayGetNativeProto(JSContext* cx, JS::Handle<JSObject*> obj,
|
|||
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 const js::ClassOps sBoringInterfaceObjectClassClassOps;
|
||||
|
|
|
@ -36,6 +36,18 @@ def memberReservedSlot(member, descriptor):
|
|||
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):
|
||||
return str(not not arg).lower()
|
||||
|
||||
|
@ -353,6 +365,11 @@ class CGNativePropertyHooks(CGThing):
|
|||
parentHooks = (toBindingNamespace(parentProtoName) + "::sNativePropertyHooks"
|
||||
if parentProtoName else 'nullptr')
|
||||
|
||||
if self.descriptor.wantsXrayExpandoClass:
|
||||
expandoClass = "&sXrayExpandoObjectClass"
|
||||
else:
|
||||
expandoClass = "&DefaultXrayExpandoObjectClass"
|
||||
|
||||
return fill(
|
||||
"""
|
||||
const NativePropertyHooks sNativePropertyHooks[] = { {
|
||||
|
@ -361,7 +378,8 @@ class CGNativePropertyHooks(CGThing):
|
|||
{ ${regular}, ${chrome} },
|
||||
${prototypeID},
|
||||
${constructorID},
|
||||
${parentHooks}
|
||||
${parentHooks},
|
||||
${expandoClass}
|
||||
} };
|
||||
""",
|
||||
resolveOwnProperty=resolveOwnProperty,
|
||||
|
@ -370,7 +388,8 @@ class CGNativePropertyHooks(CGThing):
|
|||
chrome=chrome,
|
||||
prototypeID=prototypeID,
|
||||
constructorID=constructorID,
|
||||
parentHooks=parentHooks)
|
||||
parentHooks=parentHooks,
|
||||
expandoClass=expandoClass)
|
||||
|
||||
|
||||
def NativePropertyHooks(descriptor):
|
||||
|
@ -529,6 +548,35 @@ class CGDOMProxyJSClass(CGThing):
|
|||
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):
|
||||
prototypeID = "prototypes::id::"
|
||||
if descriptor.interface.hasInterfacePrototypeObject():
|
||||
|
@ -3853,6 +3901,16 @@ class CGClearCachedValueMethod(CGAbstractMethod):
|
|||
saveMember = ""
|
||||
regetMember = ""
|
||||
|
||||
if self.descriptor.wantsXrays:
|
||||
clearXrayExpandoSlots = fill(
|
||||
"""
|
||||
xpc::ClearXrayExpandoSlots(obj, ${xraySlotIndex});
|
||||
""",
|
||||
xraySlotIndex=memberXrayExpandoReservedSlot(self.member,
|
||||
self.descriptor))
|
||||
else :
|
||||
clearXrayExpandoSlots = ""
|
||||
|
||||
return fill(
|
||||
"""
|
||||
$*{declObj}
|
||||
|
@ -3862,12 +3920,14 @@ class CGClearCachedValueMethod(CGAbstractMethod):
|
|||
}
|
||||
$*{saveMember}
|
||||
js::SetReservedSlot(obj, ${slotIndex}, JS::UndefinedValue());
|
||||
$*{clearXrayExpandoSlots}
|
||||
$*{regetMember}
|
||||
""",
|
||||
declObj=declObj,
|
||||
noopRetval=noopRetval,
|
||||
saveMember=saveMember,
|
||||
slotIndex=slotIndex,
|
||||
clearXrayExpandoSlots=clearXrayExpandoSlots,
|
||||
regetMember=regetMember)
|
||||
|
||||
|
||||
|
@ -7470,7 +7530,11 @@ class CGPerSignatureCall(CGThing):
|
|||
'returnsNewObject': returnsNewObject,
|
||||
'isConstructorRetval': self.isConstructor,
|
||||
'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:
|
||||
wrapCode += wrapForType(self.returnType, self.descriptor, resultTemplateValues)
|
||||
|
@ -7481,16 +7545,22 @@ class CGPerSignatureCall(CGThing):
|
|||
self.descriptor.interface.identifier.name,
|
||||
self.idlNode.identifier.name))
|
||||
if setSlot:
|
||||
# We need to make sure that our initial wrapping is done in the
|
||||
# reflector compartment, but that we finally set args.rval() in the
|
||||
# caller compartment. We also need to make sure that the actual
|
||||
# wrapping steps happen inside a do/while that they can break out
|
||||
# of.
|
||||
# When using a slot on the Xray expando, we need to make sure that
|
||||
# our initial conversion to a JS::Value is done in the caller
|
||||
# compartment. When using a slot on our reflector, we want to do
|
||||
# the conversion in the compartment of that reflector (that is,
|
||||
# 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
|
||||
# reflector compartment but after we've finished the initial
|
||||
# wrapping into args.rval().
|
||||
postSteps = ""
|
||||
# postConversionSteps are the steps that run while we're still in
|
||||
# the compartment we do our conversion in but after we've finished
|
||||
# the initial conversion into args.rval().
|
||||
postConversionSteps = ""
|
||||
if self.idlNode.getExtendedAttribute("Frozen"):
|
||||
assert self.idlNode.type.isSequence() or self.idlNode.type.isDictionary()
|
||||
freezeValue = CGGeneric(
|
||||
|
@ -7501,9 +7571,23 @@ class CGPerSignatureCall(CGThing):
|
|||
if self.idlNode.type.nullable():
|
||||
freezeValue = CGIfWrapper(freezeValue,
|
||||
"args.rval().isObject()")
|
||||
postSteps += freezeValue.define()
|
||||
postSteps += ("js::SetReservedSlot(reflector, %s, args.rval());\n" %
|
||||
memberReservedSlot(self.idlNode, self.descriptor))
|
||||
postConversionSteps += freezeValue.define()
|
||||
|
||||
# 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
|
||||
# wrapper if needed. We need to do this because otherwise the
|
||||
# wrapper could get garbage-collected and the cached value would
|
||||
|
@ -7514,22 +7598,48 @@ class CGPerSignatureCall(CGThing):
|
|||
# already-preserved wrapper.
|
||||
if (self.idlNode.getExtendedAttribute("Cached") and
|
||||
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(
|
||||
"""
|
||||
{ // 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
|
||||
$*{wrapCode}
|
||||
} 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
|
||||
return ${maybeWrap}(cx, args.rval());
|
||||
""",
|
||||
conversionScope=conversionScope,
|
||||
wrapCode=wrapCode,
|
||||
postSteps=postSteps,
|
||||
postConversionSteps=postConversionSteps,
|
||||
slotStorageSteps=slotStorageSteps,
|
||||
maybeWrap=getMaybeWrapValueFuncForType(self.idlNode.type))
|
||||
return wrapCode
|
||||
|
||||
|
@ -8023,7 +8133,13 @@ class CGNavigatorGetterCall(CGPerSignatureCall):
|
|||
True, descriptor, attr, getter=True)
|
||||
|
||||
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():
|
||||
|
@ -8637,27 +8753,64 @@ class CGSpecializedGetter(CGAbstractStaticMethod):
|
|||
"can't use our slot for property '%s'!" %
|
||||
(self.descriptor.interface.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.
|
||||
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);
|
||||
MOZ_ASSERT(JSCLASS_RESERVED_SLOTS(js::GetObjectClass(slotStorage)) > slotIndex);
|
||||
{
|
||||
// Scope for cachedVal
|
||||
JS::Value cachedVal = js::GetReservedSlot(reflector, ${slot});
|
||||
JS::Value cachedVal = js::GetReservedSlot(slotStorage, slotIndex);
|
||||
if (!cachedVal.isUndefined()) {
|
||||
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.
|
||||
return ${maybeWrap}(cx, args.rval());
|
||||
}
|
||||
}
|
||||
|
||||
""",
|
||||
slot=memberReservedSlot(self.attr, self.descriptor),
|
||||
maybeWrap=getMaybeWrapValueFuncForType(self.attr.type))
|
||||
else:
|
||||
prefix = ""
|
||||
|
@ -12073,6 +12226,8 @@ class CGDescriptor(CGThing):
|
|||
elif descriptor.needsXrayResolveHooks():
|
||||
cgThings.append(CGResolveOwnPropertyViaResolve(descriptor))
|
||||
cgThings.append(CGEnumerateOwnPropertiesViaGetOwnPropertyNames(descriptor))
|
||||
if descriptor.wantsXrayExpandoClass:
|
||||
cgThings.append(CGXrayExpandoJSClass(descriptor))
|
||||
|
||||
# Now that we have our ResolveOwnProperty/EnumerateOwnProperties stuff
|
||||
# done, set up our NativePropertyHooks.
|
||||
|
@ -13473,6 +13628,10 @@ class CGBindingRoot(CGThing):
|
|||
descriptorRequiresTelemetry(d) for d in descriptors)
|
||||
bindingHeaders["mozilla/dom/SimpleGlobalObject.h"] = any(
|
||||
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(unlinkMethods)
|
||||
|
|
|
@ -279,7 +279,18 @@ class Descriptor(DescriptorProvider):
|
|||
self.config = config
|
||||
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.
|
||||
ifaceName = self.interface.identifier.name
|
||||
|
|
|
@ -293,6 +293,10 @@ struct NativePropertyHooks
|
|||
// The NativePropertyHooks instance for the parent interface (for
|
||||
// ShimInterfaceInfo).
|
||||
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 {
|
||||
|
|
|
@ -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',
|
||||
'UnionMember.h',
|
||||
'WebIDLGlobalNameHash.h',
|
||||
'XrayExpandoClass.h',
|
||||
]
|
||||
|
||||
# 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, "
|
||||
"so it has to depend on something other than DOM state.",
|
||||
[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):
|
||||
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.
|
||||
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();
|
||||
}
|
||||
|
||||
|
|
|
@ -1907,33 +1907,9 @@ ContentParent::ActorDestroy(ActorDestroyReason why)
|
|||
// least until after the current task finishes running.
|
||||
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();
|
||||
nsTArray<ContentParentId> childIDArray =
|
||||
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
|
||||
for(uint32_t i = 0; i < childIDArray.Length(); i++) {
|
||||
|
@ -4616,10 +4592,6 @@ ContentParent::AllocateTabId(const TabId& aOpenerTabId,
|
|||
if (XRE_IsParentProcess()) {
|
||||
ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
|
||||
tabId = cpm->AllocateTabId(aOpenerTabId, aContext, aCpId);
|
||||
// Add appId's reference count in oop case
|
||||
if (tabId) {
|
||||
PermissionManagerAddref(aCpId, tabId);
|
||||
}
|
||||
}
|
||||
else {
|
||||
ContentChild::GetSingleton()->SendAllocateTabId(aOpenerTabId,
|
||||
|
@ -4636,11 +4608,6 @@ ContentParent::DeallocateTabId(const TabId& aTabId,
|
|||
bool aMarkedDestroying)
|
||||
{
|
||||
if (XRE_IsParentProcess()) {
|
||||
// Release appId's reference count in oop case
|
||||
if (aTabId) {
|
||||
PermissionManagerRelease(aCpId, aTabId);
|
||||
}
|
||||
|
||||
ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
|
||||
ContentParent* cp = cpm->GetContentProcessById(aCpId);
|
||||
|
||||
|
@ -5035,38 +5002,6 @@ ContentParent::RecvCreateWindow(PBrowserParent* aThisTab,
|
|||
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
|
||||
ContentParent::RecvProfile(const nsCString& aProfile)
|
||||
{
|
||||
|
|
|
@ -334,18 +334,6 @@ public:
|
|||
const ContentParentId& aCpId,
|
||||
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();
|
||||
|
||||
bool RequestRunToCompletion();
|
||||
|
|
|
@ -24,10 +24,3 @@ interface Body {
|
|||
[Throws]
|
||||
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();
|
||||
};
|
||||
|
||||
[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
|
||||
// Bug 1141979 - [FoxEye] Extend ImageBitmap with interfaces to access its
|
||||
// underlying image data
|
||||
|
@ -429,4 +409,4 @@ partial interface ImageBitmap {
|
|||
long mappedDataLength (ImageBitmapFormat aFormat);
|
||||
[Throws, Func="mozilla::dom::ImageBitmap::ExtensionsEnabled"]
|
||||
Promise<ImagePixelLayout> mapDataInto (ImageBitmapFormat aFormat, BufferSource aBuffer, long aOffset);
|
||||
};
|
||||
};
|
||||
|
|
|
@ -98,26 +98,6 @@ partial interface Window {
|
|||
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/
|
||||
[NoInterfaceObject]
|
||||
interface WindowSessionStorage {
|
||||
|
@ -425,11 +405,6 @@ partial interface Window {
|
|||
};
|
||||
#endif
|
||||
|
||||
// https://w3c.github.io/webappsec-secure-contexts/#monkey-patching-global-object
|
||||
partial interface Window {
|
||||
readonly attribute boolean isSecureContext;
|
||||
};
|
||||
|
||||
#ifdef HAVE_SIDEBAR
|
||||
// Mozilla extension
|
||||
partial interface Window {
|
||||
|
@ -517,5 +492,4 @@ partial interface Window {
|
|||
};
|
||||
|
||||
Window implements ChromeWindow;
|
||||
Window implements GlobalFetch;
|
||||
Window implements ImageBitmapFactories;
|
||||
Window implements WindowOrWorkerGlobalScope;
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
WorkerGlobalScope implements WindowTimers;
|
||||
WorkerGlobalScope implements WindowBase64;
|
||||
WorkerGlobalScope implements GlobalFetch;
|
||||
WorkerGlobalScope implements GlobalCrypto;
|
||||
WorkerGlobalScope implements IDBEnvironment;
|
||||
WorkerGlobalScope implements ImageBitmapFactories;
|
||||
WorkerGlobalScope implements WindowOrWorkerGlobalScope;
|
||||
|
||||
// Not implemented yet: bug 1072107.
|
||||
// WorkerGlobalScope implements FontFaceSource;
|
||||
|
|
|
@ -610,6 +610,7 @@ WEBIDL_FILES = [
|
|||
'WheelEvent.webidl',
|
||||
'WidevineCDMManifest.webidl',
|
||||
'WifiOptions.webidl',
|
||||
'WindowOrWorkerGlobalScope.webidl',
|
||||
'WindowRoot.webidl',
|
||||
'Worker.webidl',
|
||||
'WorkerDebuggerGlobalScope.webidl',
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "GeckoProfiler.h"
|
||||
#include "jsfriendapi.h"
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/AsyncEventDispatcher.h"
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "mozilla/CycleCollectedJSContext.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
|
@ -2446,6 +2447,11 @@ RuntimeService::CreateSharedWorkerFromLoadInfo(JSContext* aCx,
|
|||
// will reset the loadInfo's window.
|
||||
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;
|
||||
ErrorResult rv;
|
||||
if (!workerPrivate) {
|
||||
|
@ -2456,10 +2462,20 @@ RuntimeService::CreateSharedWorkerFromLoadInfo(JSContext* aCx,
|
|||
|
||||
created = true;
|
||||
} 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
|
||||
// must update the overriden load group to account for our document's
|
||||
// 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'
|
||||
|
@ -2473,6 +2489,16 @@ RuntimeService::CreateSharedWorkerFromLoadInfo(JSContext* aCx,
|
|||
RefPtr<SharedWorker> sharedWorker = new SharedWorker(window, workerPrivate,
|
||||
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())) {
|
||||
NS_WARNING("Worker is unreachable, this shouldn't happen!");
|
||||
sharedWorker->Close();
|
||||
|
|
|
@ -155,7 +155,6 @@ private:
|
|||
ServiceWorkerManagerParent::ServiceWorkerManagerParent()
|
||||
: mService(ServiceWorkerManagerService::GetOrCreate())
|
||||
, mID(++sServiceWorkerManagerParentID)
|
||||
, mActorDestroyed(false)
|
||||
{
|
||||
AssertIsOnBackgroundThread();
|
||||
mService->RegisterActor(this);
|
||||
|
@ -166,17 +165,6 @@ ServiceWorkerManagerParent::~ServiceWorkerManagerParent()
|
|||
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
|
||||
ServiceWorkerManagerParent::RecvRegister(
|
||||
const ServiceWorkerRegistrationData& aData)
|
||||
|
@ -320,8 +308,6 @@ ServiceWorkerManagerParent::ActorDestroy(ActorDestroyReason aWhy)
|
|||
{
|
||||
AssertIsOnBackgroundThread();
|
||||
|
||||
mActorDestroyed = true;
|
||||
|
||||
if (mService) {
|
||||
// This object is about to be released and with it, also mService will be
|
||||
// released too.
|
||||
|
|
|
@ -29,17 +29,11 @@ class ServiceWorkerManagerParent final : public PServiceWorkerManagerParent
|
|||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ServiceWorkerManagerParent)
|
||||
|
||||
bool ActorDestroyed() const
|
||||
{
|
||||
return mActorDestroyed;
|
||||
}
|
||||
|
||||
uint64_t ID() const
|
||||
{
|
||||
return mID;
|
||||
}
|
||||
|
||||
already_AddRefed<ContentParent> GetContentParent() const;
|
||||
private:
|
||||
ServiceWorkerManagerParent();
|
||||
~ServiceWorkerManagerParent();
|
||||
|
@ -69,8 +63,6 @@ private:
|
|||
// We use this ID in the Service in order to avoid the sending of messages to
|
||||
// ourself.
|
||||
uint64_t mID;
|
||||
|
||||
bool mActorDestroyed;
|
||||
};
|
||||
|
||||
} // namespace workers
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
#include "ServiceWorkerManagerParent.h"
|
||||
#include "ServiceWorkerRegistrar.h"
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
#include "mozilla/dom/TabParent.h"
|
||||
#include "mozilla/ipc/BackgroundParent.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "nsAutoPtr.h"
|
||||
|
@ -24,84 +23,6 @@ namespace {
|
|||
|
||||
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
|
||||
|
||||
ServiceWorkerManagerService::ServiceWorkerManagerService()
|
||||
|
@ -197,43 +118,22 @@ ServiceWorkerManagerService::PropagateSoftUpdate(
|
|||
{
|
||||
AssertIsOnBackgroundThread();
|
||||
|
||||
nsAutoPtr<nsTArray<NotifySoftUpdateData>> notifySoftUpdateDataArray(
|
||||
new nsTArray<NotifySoftUpdateData>());
|
||||
DebugOnly<bool> parentFound = false;
|
||||
for (auto iter = mAgents.Iter(); !iter.Done(); iter.Next()) {
|
||||
RefPtr<ServiceWorkerManagerParent> parent = iter.Get()->GetKey();
|
||||
MOZ_ASSERT(parent);
|
||||
|
||||
nsString scope(aScope);
|
||||
Unused << parent->SendNotifySoftUpdate(aOriginAttributes,
|
||||
scope);
|
||||
|
||||
#ifdef DEBUG
|
||||
if (parent->ID() == aParentID) {
|
||||
parentFound = true;
|
||||
}
|
||||
#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
|
||||
MOZ_ASSERT(parentFound);
|
||||
#endif
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include "nsIWorkerDebugger.h"
|
||||
#include "nsIXPConnect.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "nsGlobalWindow.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include "ImageContainer.h"
|
||||
|
@ -2182,7 +2183,8 @@ WorkerPrivateParent<Derived>::WorkerPrivateParent(
|
|||
mWorkerName(aWorkerName), mLoadingWorkerScript(false),
|
||||
mBusyCount(0), mParentStatus(Pending), mParentFrozen(false),
|
||||
mParentWindowPaused(false), mIsChromeWorker(aIsChromeWorker),
|
||||
mMainThreadObjectsForgotten(false), mWorkerType(aWorkerType),
|
||||
mMainThreadObjectsForgotten(false), mIsSecureContext(false),
|
||||
mWorkerType(aWorkerType),
|
||||
mCreationTimeStamp(TimeStamp::Now()),
|
||||
mCreationTimeHighRes((double)PR_Now() / PR_USEC_PER_MSEC)
|
||||
{
|
||||
|
@ -2202,8 +2204,14 @@ WorkerPrivateParent<Derived>::WorkerPrivateParent(
|
|||
if (aParent) {
|
||||
aParent->AssertIsOnWorkerThread();
|
||||
|
||||
// Note that this copies our parent's secure context state into 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());
|
||||
mNowBaseTimeStamp = aParent->NowBaseTimeStamp();
|
||||
mNowBaseTimeHighRes = aParent->NowBaseTime();
|
||||
|
@ -2213,6 +2221,26 @@ WorkerPrivateParent<Derived>::WorkerPrivateParent(
|
|||
|
||||
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 &&
|
||||
mLoadInfo.mWindow->GetPerformance()) {
|
||||
mNowBaseTimeStamp = mLoadInfo.mWindow->GetPerformance()->GetDOMTiming()->
|
||||
|
|
|
@ -202,6 +202,15 @@ private:
|
|||
bool mParentWindowPaused;
|
||||
bool mIsChromeWorker;
|
||||
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;
|
||||
TimeStamp mCreationTimeStamp;
|
||||
DOMHighResTimeStamp mCreationTimeHighRes;
|
||||
|
@ -823,6 +832,16 @@ public:
|
|||
IMPL_EVENT_HANDLER(message)
|
||||
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
|
||||
void
|
||||
AssertIsOnParentThread() const;
|
||||
|
|
|
@ -173,6 +173,15 @@ WorkerGlobalScope::GetCaches(ErrorResult& aRv)
|
|||
return ref.forget();
|
||||
}
|
||||
|
||||
bool
|
||||
WorkerGlobalScope::IsSecureContext() const
|
||||
{
|
||||
bool globalSecure =
|
||||
JS_GetIsSecureContext(js::GetObjectCompartment(GetWrapperPreserveColor()));
|
||||
MOZ_ASSERT(globalSecure == mWorkerPrivate->IsSecureContext());
|
||||
return globalSecure;
|
||||
}
|
||||
|
||||
already_AddRefed<WorkerLocation>
|
||||
WorkerGlobalScope::Location()
|
||||
{
|
||||
|
|
|
@ -163,6 +163,8 @@ public:
|
|||
already_AddRefed<cache::CacheStorage>
|
||||
GetCaches(ErrorResult& aRv);
|
||||
|
||||
bool IsSecureContext() const;
|
||||
|
||||
already_AddRefed<Promise>
|
||||
CreateImageBitmap(const ImageBitmapSource& aImage, ErrorResult& aRv);
|
||||
|
||||
|
|
|
@ -2434,63 +2434,6 @@ nsPermissionManager::RemovePermissionsWithAttributes(mozilla::OriginAttributesPa
|
|||
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
|
||||
//*****************************************************************************
|
||||
|
@ -2901,55 +2844,6 @@ nsPermissionManager::UpdateDB(OperationType aOp,
|
|||
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
|
||||
nsPermissionManager::UpdateExpireTime(nsIPrincipal* aPrincipal,
|
||||
const char* aType,
|
||||
|
|
|
@ -254,8 +254,6 @@ private:
|
|||
int64_t aExpireTime,
|
||||
int64_t aModificationTime);
|
||||
|
||||
nsresult RemoveExpiredPermissionsForApp(uint32_t aAppId);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
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
|
||||
// reopening the database.
|
||||
bool mIsShuttingDown;
|
||||
|
|
|
@ -274,6 +274,7 @@ const ClassOps MapObject::classOps_ = {
|
|||
const Class MapObject::class_ = {
|
||||
"Map",
|
||||
JSCLASS_HAS_PRIVATE |
|
||||
JSCLASS_HAS_RESERVED_SLOTS(MapObject::SlotCount) |
|
||||
JSCLASS_HAS_CACHED_PROTO(JSProto_Map) |
|
||||
JSCLASS_FOREGROUND_FINALIZE,
|
||||
&MapObject::classOps_
|
||||
|
@ -372,7 +373,7 @@ MapObject::mark(JSTracer* trc, JSObject* obj)
|
|||
}
|
||||
}
|
||||
|
||||
struct UnbarrieredHashPolicy {
|
||||
struct js::UnbarrieredHashPolicy {
|
||||
typedef Value Lookup;
|
||||
static HashNumber hash(const Lookup& v) { return v.asRawBits(); }
|
||||
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); }
|
||||
};
|
||||
|
||||
template <typename TableType>
|
||||
class OrderedHashTableRef : public gc::BufferableRef
|
||||
using NurseryKeysVector = Vector<JSObject*, 0, SystemAllocPolicy>;
|
||||
|
||||
template <typename TableObject>
|
||||
static NurseryKeysVector*
|
||||
GetNurseryKeys(TableObject* t)
|
||||
{
|
||||
TableType* table;
|
||||
Value key;
|
||||
Value value = t->getReservedSlot(TableObject::NurseryKeysSlot);
|
||||
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:
|
||||
explicit OrderedHashTableRef(TableType* t, const Value& k) : table(t), key(k) {}
|
||||
explicit OrderedHashTableRef(ObjectT* obj) : object(obj) {}
|
||||
|
||||
void trace(JSTracer* trc) override {
|
||||
MOZ_ASSERT(UnbarrieredHashPolicy::hash(key) ==
|
||||
HashableValue::Hasher::hash(*reinterpret_cast<HashableValue*>(&key)));
|
||||
Value prior = key;
|
||||
TraceManuallyBarrieredEdge(trc, &key, "ordered hash table key");
|
||||
table->rekeyOneEntry(prior, key);
|
||||
auto table = reinterpret_cast<typename ObjectT::UnbarrieredTable*>(object->getData());
|
||||
NurseryKeysVector* keys = GetNurseryKeys(object);
|
||||
MOZ_ASSERT(keys);
|
||||
for (JSObject* obj : *keys) {
|
||||
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
|
||||
WriteBarrierPost(JSRuntime* rt, ValueMap* map, const Value& key)
|
||||
template <typename ObjectT>
|
||||
inline static MOZ_MUST_USE bool
|
||||
WriteBarrierPostImpl(JSRuntime* rt, ObjectT* obj, const Value& keyValue)
|
||||
{
|
||||
typedef OrderedHashMap<Value, Value, UnbarrieredHashPolicy, RuntimeAllocPolicy> UnbarrieredMap;
|
||||
if (MOZ_UNLIKELY(key.isObject() && IsInsideNursery(&key.toObject()))) {
|
||||
rt->gc.storeBuffer.putGeneric(OrderedHashTableRef<UnbarrieredMap>(
|
||||
reinterpret_cast<UnbarrieredMap*>(map), key));
|
||||
if (MOZ_LIKELY(!keyValue.isObject()))
|
||||
return true;
|
||||
|
||||
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
|
||||
WriteBarrierPost(JSRuntime* rt, ValueSet* set, const Value& key)
|
||||
inline static MOZ_MUST_USE bool
|
||||
WriteBarrierPost(JSRuntime* rt, MapObject* map, const Value& key)
|
||||
{
|
||||
typedef OrderedHashSet<Value, UnbarrieredHashPolicy, RuntimeAllocPolicy> UnbarrieredSet;
|
||||
if (MOZ_UNLIKELY(key.isObject() && IsInsideNursery(&key.toObject()))) {
|
||||
rt->gc.storeBuffer.putGeneric(OrderedHashTableRef<UnbarrieredSet>(
|
||||
reinterpret_cast<UnbarrieredSet*>(set), key));
|
||||
}
|
||||
return WriteBarrierPostImpl(rt, map, key);
|
||||
}
|
||||
|
||||
inline static MOZ_MUST_USE bool
|
||||
WriteBarrierPost(JSRuntime* rt, SetObject* set, const Value& key)
|
||||
{
|
||||
return WriteBarrierPostImpl(rt, set, key);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -449,11 +510,13 @@ MapObject::set(JSContext* cx, HandleObject obj, HandleValue k, HandleValue v)
|
|||
return false;
|
||||
|
||||
HeapPtr<Value> rval(v);
|
||||
if (!map->put(key, rval)) {
|
||||
if (!WriteBarrierPost(cx->runtime(), &obj->as<MapObject>(), key.value()) ||
|
||||
!map->put(key, rval))
|
||||
{
|
||||
ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
WriteBarrierPost(cx->runtime(), map, key.value());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -471,6 +534,7 @@ MapObject::create(JSContext* cx, HandleObject proto /* = nullptr */)
|
|||
return nullptr;
|
||||
|
||||
mapObj->setPrivate(map.release());
|
||||
mapObj->setReservedSlot(NurseryKeysSlot, PrivateValue(nullptr));
|
||||
return mapObj;
|
||||
}
|
||||
|
||||
|
@ -550,11 +614,12 @@ MapObject::construct(JSContext* cx, unsigned argc, Value* vp)
|
|||
return false;
|
||||
|
||||
HeapPtr<Value> rval(val);
|
||||
if (!map->put(hkey, rval)) {
|
||||
if (!WriteBarrierPost(cx->runtime(), obj, key) ||
|
||||
!map->put(hkey, rval))
|
||||
{
|
||||
ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
WriteBarrierPost(cx->runtime(), map, key);
|
||||
} else {
|
||||
if (!args2.init(2))
|
||||
return false;
|
||||
|
@ -700,11 +765,13 @@ MapObject::set_impl(JSContext* cx, const CallArgs& args)
|
|||
ValueMap& map = extract(args);
|
||||
ARG0_KEY(cx, args, key);
|
||||
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);
|
||||
return false;
|
||||
}
|
||||
WriteBarrierPost(cx->runtime(), &map, key.value());
|
||||
|
||||
args.rval().set(args.thisv());
|
||||
return true;
|
||||
}
|
||||
|
@ -1017,6 +1084,7 @@ const ClassOps SetObject::classOps_ = {
|
|||
const Class SetObject::class_ = {
|
||||
"Set",
|
||||
JSCLASS_HAS_PRIVATE |
|
||||
JSCLASS_HAS_RESERVED_SLOTS(SetObject::SlotCount) |
|
||||
JSCLASS_HAS_CACHED_PROTO(JSProto_Set) |
|
||||
JSCLASS_FOREGROUND_FINALIZE,
|
||||
&SetObject::classOps_
|
||||
|
@ -1098,11 +1166,12 @@ SetObject::add(JSContext* cx, HandleObject obj, HandleValue k)
|
|||
if (!key.setValue(cx, k))
|
||||
return false;
|
||||
|
||||
if (!set->put(key)) {
|
||||
if (!WriteBarrierPost(cx->runtime(), &obj->as<SetObject>(), key.value()) ||
|
||||
!set->put(key))
|
||||
{
|
||||
ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
WriteBarrierPost(cx->runtime(), set, key.value());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1120,6 +1189,7 @@ SetObject::create(JSContext* cx, HandleObject proto /* = nullptr */)
|
|||
return nullptr;
|
||||
|
||||
obj->setPrivate(set.release());
|
||||
obj->setReservedSlot(NurseryKeysSlot, PrivateValue(nullptr));
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
@ -1189,11 +1259,12 @@ SetObject::construct(JSContext* cx, unsigned argc, Value* vp)
|
|||
if (isOriginalAdder) {
|
||||
if (!key.setValue(cx, keyVal))
|
||||
return false;
|
||||
if (!set->put(key)) {
|
||||
if (!WriteBarrierPost(cx->runtime(), obj, keyVal) ||
|
||||
!set->put(key))
|
||||
{
|
||||
ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
WriteBarrierPost(cx->runtime(), set, keyVal);
|
||||
} else {
|
||||
if (!args2.init(1))
|
||||
return false;
|
||||
|
@ -1306,11 +1377,12 @@ SetObject::add_impl(JSContext* cx, const CallArgs& args)
|
|||
|
||||
ValueSet& set = extract(args);
|
||||
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);
|
||||
return false;
|
||||
}
|
||||
WriteBarrierPost(cx->runtime(), &set, key.value());
|
||||
args.rval().set(args.thisv());
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -74,6 +74,11 @@ typedef OrderedHashSet<HashableValue,
|
|||
HashableValue::Hasher,
|
||||
RuntimeAllocPolicy> ValueSet;
|
||||
|
||||
template <typename ObjectT>
|
||||
class OrderedHashTableRef;
|
||||
|
||||
struct UnbarrieredHashPolicy;
|
||||
|
||||
class MapObject : public NativeObject {
|
||||
public:
|
||||
enum IteratorKind { Keys, Values, Entries };
|
||||
|
@ -88,6 +93,8 @@ class MapObject : public NativeObject {
|
|||
static JSObject* initClass(JSContext* cx, JSObject* obj);
|
||||
static const Class class_;
|
||||
|
||||
enum { NurseryKeysSlot, SlotCount };
|
||||
|
||||
static MOZ_MUST_USE bool getKeysAndValuesInterleaved(JSContext* cx, HandleObject obj,
|
||||
JS::MutableHandle<GCVector<JS::Value>> entries);
|
||||
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,
|
||||
MutableHandleValue iter);
|
||||
|
||||
using UnbarrieredTable = OrderedHashMap<Value, Value, UnbarrieredHashPolicy, RuntimeAllocPolicy>;
|
||||
friend class OrderedHashTableRef<MapObject>;
|
||||
|
||||
private:
|
||||
static const ClassOps classOps_;
|
||||
|
||||
|
@ -180,6 +190,8 @@ class SetObject : public NativeObject {
|
|||
static JSObject* initClass(JSContext* cx, JSObject* obj);
|
||||
static const Class class_;
|
||||
|
||||
enum { NurseryKeysSlot, SlotCount };
|
||||
|
||||
static MOZ_MUST_USE bool keys(JSContext *cx, HandleObject obj,
|
||||
JS::MutableHandle<GCVector<JS::Value>> keys);
|
||||
static MOZ_MUST_USE bool values(JSContext *cx, unsigned argc, Value *vp);
|
||||
|
@ -196,6 +208,9 @@ class SetObject : public NativeObject {
|
|||
MutableHandleValue iter);
|
||||
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:
|
||||
static const ClassOps classOps_;
|
||||
|
||||
|
|
|
@ -76,10 +76,6 @@ following forms:
|
|||
: The code completed normally, returning <i>value</i>. <i>Value</i> is a
|
||||
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>
|
||||
: The code threw <i>value</i> as an exception. <i>Value</i> is a debuggee
|
||||
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
|
||||
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>
|
||||
: Throw <i>value</i> as an exception from the current bytecode
|
||||
instruction. <i>Value</i> must be a debuggee value.
|
||||
|
|
|
@ -145,93 +145,9 @@ methods of other kinds of objects.
|
|||
[`Debugger.DebuggeeWouldRun`][wouldrun]
|
||||
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>
|
||||
: 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
|
||||
this environment, return `null`. <i>Name</i> must be a string whose
|
||||
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
|
||||
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
|
||||
|
||||
|
@ -132,9 +129,7 @@ its prototype:
|
|||
|
||||
`older`
|
||||
: 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
|
||||
suspended generator frame, the value of this property is `null`; see
|
||||
[Generator Frames][generator].)
|
||||
frame completes. If there is no older frame, this is `null`.
|
||||
|
||||
`depth`
|
||||
: The depth of this frame, counting from oldest to youngest; the oldest
|
||||
|
@ -142,8 +137,7 @@ its prototype:
|
|||
|
||||
`live`
|
||||
: 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
|
||||
finished its iteration); false if it has completed execution or been
|
||||
the stack; false if it has completed execution or been
|
||||
popped in some other way.
|
||||
|
||||
`script`
|
||||
|
@ -254,11 +248,6 @@ the compartment to which the handler method belongs.
|
|||
an `undefined` resumption value leaves the frame's throwing and
|
||||
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
|
||||
`Debugger.Frame` instances for a given stack frame with `onPop`
|
||||
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
|
||||
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
|
||||
|
||||
|
@ -291,7 +269,7 @@ methods of other kinds of objects.
|
|||
: 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
|
||||
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
|
||||
[invocation function conventions][inv fr].
|
||||
|
||||
|
@ -348,59 +326,3 @@ methods of other kinds of objects.
|
|||
|
||||
The <i>options</i> argument is as for
|
||||
[`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
|
||||
constructor, in which case SpiderMonkey provides an appropriate `this`
|
||||
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`.
|
||||
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
|
||||
array (in the debugger) of debuggee values, or `null` or `undefined`,
|
||||
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
|
||||
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
|
||||
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
|
||||
and so on remain active during the call. This function
|
||||
follows the [invocation function conventions][inv fr].
|
||||
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
|
||||
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()`
|
||||
: If the referent is a wrapper that this `Debugger.Object`'s compartment
|
||||
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`.
|
||||
|
||||
`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`
|
||||
: **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
|
||||
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()`
|
||||
: **If the instance refers to a `JSScript`**, return an array <i>L</i>
|
||||
describing the relationship between bytecode instruction offsets and
|
||||
|
@ -325,8 +305,8 @@ methods of other kinds of objects.
|
|||
|
||||
`getChildScripts()`
|
||||
: **If the instance refers to a `JSScript`**, return a new array whose
|
||||
elements are Debugger.Script objects for each function and each generator
|
||||
expression in this script. Only direct children are included; nested
|
||||
elements are Debugger.Script objects for each function
|
||||
in this script. Only direct children are included; nested
|
||||
children can be reached by walking the tree.
|
||||
|
||||
**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
|
||||
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`
|
||||
: **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
|
||||
|
@ -229,43 +218,3 @@ from its prototype:
|
|||
**If the instance refers to WebAssembly code**, `introductionScript` is
|
||||
the [`Debugger.Script`][script] instance referring to the same underlying
|
||||
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`
|
||||
: 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
|
||||
disables this `Debugger` instance; reading it produces true if the
|
||||
instance is enabled, or false otherwise. This property is initially
|
||||
|
@ -57,7 +57,7 @@ its prototype:
|
|||
|
||||
`uncaughtExceptionHook`
|
||||
: 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
|
||||
<i>debugger-exception</i> here. Exceptions thrown in the debugger are
|
||||
not propagated to debuggee code; instead, SpiderMonkey calls this
|
||||
|
@ -154,29 +154,6 @@ compartment.
|
|||
SpiderMonkey only calls `onEnterFrame` to report
|
||||
[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>
|
||||
: 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
|
||||
|
@ -468,9 +445,6 @@ other kinds of objects.
|
|||
`clearAllBreakpoints()`
|
||||
: Remove all breakpoints set using this `Debugger` instance.
|
||||
|
||||
`clearAllWatchpoints()` <i>(future plan)</i>
|
||||
: Clear all watchpoints owned by this `Debugger` instance.
|
||||
|
||||
`findAllGlobals()`
|
||||
: Return an array of [`Debugger.Object`][object] instances referring to all the
|
||||
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
|
||||
label 'frame' "Debugger.Frame"
|
||||
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 'fr eval' '#eval' "Debugger.Frame: Eval"
|
||||
|
||||
|
|
|
@ -4461,6 +4461,13 @@ BytecodeEmitter::emitDestructuringLHS(ParseNode* target, DestructuringFlavor fla
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeEmitter::emitConditionallyExecutedDestructuringLHS(ParseNode* target, DestructuringFlavor flav)
|
||||
{
|
||||
TDZCheckCache tdzCache(this);
|
||||
return emitDestructuringLHS(target, flav);
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeEmitter::emitIteratorNext(ParseNode* pn, bool allowSelfHosted)
|
||||
{
|
||||
|
@ -4513,6 +4520,76 @@ BytecodeEmitter::emitDestructuringOpsArrayHelper(ParseNode* pattern, Destructuri
|
|||
MOZ_ASSERT(pattern->isArity(PN_LIST));
|
||||
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
|
||||
* must leave the *original* value on the stack.
|
||||
|
@ -4524,20 +4601,36 @@ BytecodeEmitter::emitDestructuringOpsArrayHelper(ParseNode* pattern, Destructuri
|
|||
bool needToPopIterator = true;
|
||||
|
||||
for (ParseNode* member = pattern->pn_head; member; member = member->pn_next) {
|
||||
/*
|
||||
* Now push the property name currently being matched, which is the
|
||||
* current property name "label" on the left of a colon in the object
|
||||
* initializer.
|
||||
*/
|
||||
ParseNode* pndefault = nullptr;
|
||||
ParseNode* elem = member;
|
||||
if (elem->isKind(PNK_ASSIGN)) {
|
||||
pndefault = elem->pn_right;
|
||||
elem = elem->pn_left;
|
||||
}
|
||||
bool isHead = member == pattern->pn_head;
|
||||
if (member->isKind(PNK_SPREAD)) {
|
||||
JumpList beq;
|
||||
JumpList end;
|
||||
unsigned noteIndex = -1;
|
||||
if (!isHead) {
|
||||
// If spread is not the first element of the pattern,
|
||||
// iterator can already be completed.
|
||||
if (!newSrcNote(SRC_IF_ELSE, ¬eIndex))
|
||||
return false;
|
||||
if (!emitJump(JSOP_IFEQ, &beq)) // ... OBJ? ITER
|
||||
return false;
|
||||
|
||||
if (elem->isKind(PNK_SPREAD)) {
|
||||
/* Create a new array with the rest of the iterator */
|
||||
int32_t depth = stackDepth;
|
||||
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
|
||||
return false;
|
||||
if (!emitNumberOp(0)) // ... OBJ? ITER ARRAY INDEX
|
||||
|
@ -4546,68 +4639,136 @@ BytecodeEmitter::emitDestructuringOpsArrayHelper(ParseNode* pattern, Destructuri
|
|||
return false;
|
||||
if (!emit1(JSOP_POP)) // ... OBJ? ARRAY
|
||||
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;
|
||||
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 {
|
||||
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;
|
||||
if (!emitIteratorNext(pattern)) // ... OBJ? ITER RESULT
|
||||
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;
|
||||
|
||||
} else if (pndefault) {
|
||||
if (!emit1(JSOP_POP)) // ... OBJ? ITER
|
||||
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;
|
||||
if (!emit1(JSOP_NOP_DESTRUCTURING))
|
||||
return false;
|
||||
|
||||
/* Jump around else, fixup the branch, emit else, fixup jump. */
|
||||
JumpList jmp;
|
||||
if (!emitJump(JSOP_GOTO, &jmp))
|
||||
if (!emit1(JSOP_TRUE)) // ... OBJ? ITER RESULT DONE?
|
||||
return false;
|
||||
if (!emitJumpTargetAndPatch(beq))
|
||||
return false;
|
||||
|
||||
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))
|
||||
} else if (hasNextSpread) {
|
||||
if (!emit1(JSOP_TRUE)) // ... OBJ? ITER DONE?
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pndefault && !emitDefault(pndefault))
|
||||
JumpList end;
|
||||
if (!emitJump(JSOP_GOTO, &end))
|
||||
return false;
|
||||
if (!emitJumpTargetAndPatch(beq))
|
||||
return false;
|
||||
|
||||
// Destructure into the pattern the element contains.
|
||||
ParseNode* subpattern = elem;
|
||||
if (subpattern->isKind(PNK_ELISION)) {
|
||||
// The value destructuring into an elision just gets ignored.
|
||||
if (!emit1(JSOP_POP)) // ... OBJ? ITER
|
||||
stackDepth = depth;
|
||||
if (!emitAtomOp(cx->names().value, JSOP_GETPROP)) // ... OBJ? ITER VALUE
|
||||
return false;
|
||||
|
||||
if (pndefault) {
|
||||
if (!emitDefault(pndefault)) // ... OBJ? ITER VALUE
|
||||
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;
|
||||
}
|
||||
|
||||
if (needToPopIterator && !emit1(JSOP_POP))
|
||||
return false;
|
||||
if (needToPopIterator) {
|
||||
if (!emit1(JSOP_POP)) // ... OBJ?
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -6674,13 +6835,14 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto)
|
|||
RootedFunction fun(cx, funbox->function());
|
||||
RootedAtom name(cx, fun->name());
|
||||
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
|
||||
* emitted. Function definitions that need hoisting to the top of the
|
||||
* 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
|
||||
// block-scoped function to the top of their scope. When their
|
||||
// definitions are seen for the second time, we need to emit the
|
||||
|
@ -6804,7 +6966,7 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto)
|
|||
}
|
||||
|
||||
if (needsProto) {
|
||||
MOZ_ASSERT(pn->getOp() == JSOP_LAMBDA);
|
||||
MOZ_ASSERT(pn->getOp() == JSOP_FUNWITHPROTO || pn->getOp() == JSOP_LAMBDA);
|
||||
pn->setOp(JSOP_FUNWITHPROTO);
|
||||
}
|
||||
return emitIndex32(pn->getOp(), index);
|
||||
|
@ -9651,6 +9813,15 @@ CGConstList::finish(ConstArray* array)
|
|||
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.
|
||||
*
|
||||
|
@ -9662,9 +9833,15 @@ CGConstList::finish(ConstArray* array)
|
|||
unsigned
|
||||
CGObjectList::add(ObjectBox* objbox)
|
||||
{
|
||||
MOZ_ASSERT(!objbox->emitLink);
|
||||
if (isAdded(objbox))
|
||||
return indexOf(objbox->object);
|
||||
|
||||
objbox->emitLink = lastbox;
|
||||
lastbox = objbox;
|
||||
|
||||
// See the comment in CGObjectList::isAdded.
|
||||
if (!firstbox)
|
||||
firstbox = objbox;
|
||||
return length++;
|
||||
}
|
||||
|
||||
|
@ -9691,7 +9868,12 @@ CGObjectList::finish(ObjectArray* array)
|
|||
MOZ_ASSERT(!*cursor);
|
||||
MOZ_ASSERT(objbox->object->isTenured());
|
||||
*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);
|
||||
}
|
||||
|
||||
|
|
|
@ -43,10 +43,12 @@ class CGConstList {
|
|||
|
||||
struct CGObjectList {
|
||||
uint32_t length; /* number of emitted so far objects */
|
||||
ObjectBox* firstbox; /* first 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 indexOf(JSObject* obj);
|
||||
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
|
||||
// name or a compound []/{} expression).
|
||||
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 emitDestructuringOpsHelper(ParseNode* pattern, DestructuringFlavor flav);
|
||||
|
|
|
@ -635,12 +635,14 @@ class ParseNode
|
|||
MOZ_ASSERT(pn_arity == PN_CODE && getKind() == PNK_FUNCTION);
|
||||
MOZ_ASSERT(isOp(JSOP_LAMBDA) || // lambda, genexpr
|
||||
isOp(JSOP_LAMBDA_ARROW) || // arrow function
|
||||
isOp(JSOP_FUNWITHPROTO) || // already emitted lambda with needsProto
|
||||
isOp(JSOP_DEFFUN) || // non-body-level function statement
|
||||
isOp(JSOP_NOP) || // body-level function stmt in global code
|
||||
isOp(JSOP_GETLOCAL) || // body-level function stmt in function code
|
||||
isOp(JSOP_GETARG) || // body-level function redeclaring formal
|
||||
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
|
||||
* with the simple pointer-to-a-pointer scheme must derive from this class and
|
||||
* 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
|
||||
{
|
||||
|
|
|
@ -39,10 +39,10 @@ function assertIterable(expectCalls, fn, expectResult) {
|
|||
assertIterable([1,1,1,1],
|
||||
it => { var [a] = it; return [a]; },
|
||||
[1]);
|
||||
assertIterable([3,3,1,1],
|
||||
assertIterable([2,2,1,1],
|
||||
it => { var [a,b,c] = it; return [a,b,c]; },
|
||||
[1,undefined,undefined]);
|
||||
assertIterable([3,3,1,1],
|
||||
assertIterable([2,2,1,1],
|
||||
it => { var [a,b,...rest] = it; return [a,b,...rest]; },
|
||||
[1,undefined]);
|
||||
assertIterable([5,5,4,4],
|
||||
|
|
|
@ -31,18 +31,26 @@ function assertBuiltinFunction(o, name, arity) {
|
|||
|
||||
assertEq(typeof fn, "function");
|
||||
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"), {
|
||||
value: arity,
|
||||
writable: false,
|
||||
enumerable: false,
|
||||
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]())));
|
||||
|
||||
// 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
|
||||
assertBuiltinFunction(iterProto, "next", 0);
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
gczeal(0);
|
||||
|
||||
var values = {
|
||||
input1: null,
|
||||
input2: undefined,
|
||||
|
|
|
@ -728,7 +728,7 @@ class MacroAssembler : public MacroAssemblerSpecific
|
|||
inline void orPtr(Register src, 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 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(Imm32 imm, Register dest) PER_ARCH;
|
||||
|
||||
inline void and64(const Operand& src, Register64 dest) DEFINED_ON(x64);
|
||||
inline void or64(const Operand& src, Register64 dest) DEFINED_ON(x64);
|
||||
inline void xor64(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, mips64);
|
||||
inline void xor64(const Operand& src, Register64 dest) DEFINED_ON(x64, mips64);
|
||||
|
||||
// ===============================================================
|
||||
// Arithmetic functions
|
||||
|
@ -762,8 +762,8 @@ class MacroAssembler : public MacroAssemblerSpecific
|
|||
|
||||
inline void add64(Register64 src, 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(const Operand& src, Register64 dest) DEFINED_ON(x64);
|
||||
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, mips64);
|
||||
|
||||
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(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(Imm64 imm, Register64 dest) DEFINED_ON(x86, x64, arm);
|
||||
inline void sub64(const Operand& src, Register64 dest) DEFINED_ON(x64);
|
||||
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, mips32, mips64);
|
||||
inline void sub64(const Operand& src, Register64 dest) DEFINED_ON(x64, mips64);
|
||||
|
||||
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, 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, 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)
|
||||
DEFINED_ON(x86, x64, arm);
|
||||
DEFINED_ON(x86, x64, arm, mips32, mips64);
|
||||
|
||||
inline void mulBy3(Register src, Register dest) PER_ARCH;
|
||||
|
||||
|
@ -832,7 +832,7 @@ class MacroAssembler : public MacroAssemblerSpecific
|
|||
inline void dec32(RegisterOrInt32Constant* key);
|
||||
|
||||
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;
|
||||
|
||||
|
@ -875,16 +875,20 @@ class MacroAssembler : public MacroAssemblerSpecific
|
|||
|
||||
inline void lshift64(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.
|
||||
inline void lshift32(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 lshift64(Register shift, Register64 srcDest) DEFINED_ON(x86, x64, arm);
|
||||
inline void rshift64(Register shift, Register64 srcDest) DEFINED_ON(x86, x64, arm);
|
||||
inline void rshift64Arithmetic(Register shift, Register64 srcDest) DEFINED_ON(x86, x64, arm);
|
||||
inline void lshift64(Register shift, Register64 srcDest)
|
||||
DEFINED_ON(x86, x64, arm, mips32, mips64);
|
||||
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
|
||||
|
@ -896,18 +900,18 @@ class MacroAssembler : public MacroAssemblerSpecific
|
|||
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(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)
|
||||
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(Register count, Register input, Register dest) PER_SHARED_ARCH;
|
||||
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(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)
|
||||
DEFINED_ON(x86, x64, arm);
|
||||
DEFINED_ON(x86, x64, arm, mips32, mips64);
|
||||
|
||||
// ===============================================================
|
||||
// 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 ctz32(Register src, Register dest, bool knownNotZero) PER_SHARED_ARCH;
|
||||
|
||||
inline void clz64(Register64 src, Register dest) DEFINED_ON(x86, x64, arm);
|
||||
inline void ctz64(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, mips32, mips64);
|
||||
|
||||
// On x86_shared, temp may be Invalid only if the chip has the POPCNT instruction.
|
||||
// On ARM, temp may never be Invalid.
|
||||
|
@ -925,7 +929,8 @@ class MacroAssembler : public MacroAssemblerSpecific
|
|||
DEFINED_ON(arm, x86_shared, mips_shared);
|
||||
|
||||
// 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
|
||||
|
@ -962,9 +967,9 @@ class MacroAssembler : public MacroAssemblerSpecific
|
|||
// When a fail label is not defined it will fall through to next instruction,
|
||||
// else jump to the fail label.
|
||||
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,
|
||||
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
|
||||
// with Address as lhs. On others only the NotEqual condition.
|
||||
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());
|
||||
}
|
||||
|
||||
BufferOffset
|
||||
AssemblerMIPSShared::as_cvtsl(FloatRegister fd, FloatRegister fs)
|
||||
{
|
||||
return writeInst(InstReg(op_cop1, rs_l, zero, fs, fd, ff_cvt_s_fmt).encode());
|
||||
}
|
||||
|
||||
BufferOffset
|
||||
AssemblerMIPSShared::as_cvtsw(FloatRegister fd, FloatRegister fs)
|
||||
{
|
||||
|
|
|
@ -1226,7 +1226,7 @@ class AssemblerMIPSShared : public AssemblerShared
|
|||
#endif
|
||||
}
|
||||
static bool SupportsUnalignedAccesses() {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
static bool 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
|
||||
CodeGeneratorMIPSShared::branchToBlock(Assembler::FloatFormat fmt, FloatRegister lhs, FloatRegister rhs,
|
||||
MBasicBlock* mir, Assembler::DoubleCondition cond)
|
||||
|
@ -296,6 +332,22 @@ CodeGeneratorMIPSShared::visitAddI(LAddI* ins)
|
|||
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
|
||||
CodeGeneratorMIPSShared::visitSubI(LSubI* ins)
|
||||
{
|
||||
|
@ -323,6 +375,22 @@ CodeGeneratorMIPSShared::visitSubI(LSubI* ins)
|
|||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
CodeGeneratorMIPSShared::visitUrshD(LUrshD* ins)
|
||||
{
|
||||
|
@ -860,6 +1073,16 @@ CodeGeneratorMIPSShared::visitPopcntI(LPopcntI* ins)
|
|||
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
|
||||
CodeGeneratorMIPSShared::visitPowHalfD(LPowHalfD* ins)
|
||||
{
|
||||
|
@ -1309,7 +1532,29 @@ CodeGeneratorMIPSShared::visitOutOfLineWasmTruncateCheck(OutOfLineWasmTruncateCh
|
|||
masm.as_truncwd(ScratchFloat32Reg, ScratchDoubleReg);
|
||||
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.
|
||||
|
@ -1643,8 +1888,9 @@ CodeGeneratorMIPSShared::visitWasmCallI64(LWasmCallI64* ins)
|
|||
emitWasmCallBase(ins);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void
|
||||
CodeGeneratorMIPSShared::visitWasmLoad(LWasmLoad* lir)
|
||||
CodeGeneratorMIPSShared::emitWasmLoad(T* lir)
|
||||
{
|
||||
const MWasmLoad* mir = lir->mir();
|
||||
|
||||
|
@ -1680,21 +1926,55 @@ CodeGeneratorMIPSShared::visitWasmLoad(LWasmLoad* lir)
|
|||
|
||||
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 (byteSize == 4) {
|
||||
masm.loadFloat32(BaseIndex(HeapReg, ptr, TimesOne), ToFloatRegister(lir->output()));
|
||||
} else
|
||||
masm.loadDouble(BaseIndex(HeapReg, ptr, TimesOne), ToFloatRegister(lir->output()));
|
||||
if (byteSize == 4)
|
||||
masm.loadFloat32(address, ToFloatRegister(lir->output()));
|
||||
else
|
||||
masm.loadDouble(address, ToFloatRegister(lir->output()));
|
||||
} else {
|
||||
masm.ma_load(ToRegister(lir->output()), BaseIndex(HeapReg, ptr, TimesOne),
|
||||
static_cast<LoadStoreSize>(8 * byteSize), isSigned ? SignExtend : ZeroExtend);
|
||||
masm.ma_load(ToRegister(lir->output()), address,
|
||||
static_cast<LoadStoreSize>(8 * byteSize),
|
||||
isSigned ? SignExtend : ZeroExtend);
|
||||
}
|
||||
|
||||
memoryBarrier(mir->barrierAfter());
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
|
@ -1731,19 +2011,52 @@ CodeGeneratorMIPSShared::visitWasmStore(LWasmStore* lir)
|
|||
|
||||
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 (byteSize == 4) {
|
||||
masm.storeFloat32(ToFloatRegister(lir->value()), BaseIndex(HeapReg, ptr, TimesOne));
|
||||
masm.storeFloat32(ToFloatRegister(lir->value()), address);
|
||||
} else
|
||||
masm.storeDouble(ToFloatRegister(lir->value()), BaseIndex(HeapReg, ptr, TimesOne));
|
||||
masm.storeDouble(ToFloatRegister(lir->value()), address);
|
||||
} else {
|
||||
masm.ma_store(ToRegister(lir->value()), BaseIndex(HeapReg, ptr, TimesOne),
|
||||
static_cast<LoadStoreSize>(8 * byteSize), isSigned ? SignExtend : ZeroExtend);
|
||||
masm.ma_store(ToRegister(lir->value()), address,
|
||||
static_cast<LoadStoreSize>(8 * byteSize),
|
||||
isSigned ? SignExtend : ZeroExtend);
|
||||
}
|
||||
|
||||
memoryBarrier(mir->barrierAfter());
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorMIPSShared::visitWasmStore(LWasmStore* lir)
|
||||
{
|
||||
emitWasmStore(lir);
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorMIPSShared::visitWasmUnalignedStore(LWasmUnalignedStore* lir)
|
||||
{
|
||||
emitWasmStore(lir);
|
||||
}
|
||||
|
||||
void
|
||||
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
|
||||
CodeGeneratorMIPSShared::visitAsmSelect(LAsmSelect* ins)
|
||||
{
|
||||
|
|
|
@ -26,6 +26,16 @@ class CodeGeneratorMIPSShared : public CodeGeneratorShared
|
|||
protected:
|
||||
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;
|
||||
|
||||
template <typename T1, typename T2>
|
||||
|
@ -109,6 +119,11 @@ class CodeGeneratorMIPSShared : public CodeGeneratorShared
|
|||
emitBranch(reg, Imm32(0), cond, ifTrue, ifFalse);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void emitWasmLoad(T* ins);
|
||||
template <typename T>
|
||||
void emitWasmStore(T* ins);
|
||||
|
||||
public:
|
||||
// Instruction visitors.
|
||||
virtual void visitMinMaxD(LMinMaxD* ins);
|
||||
|
@ -118,11 +133,15 @@ class CodeGeneratorMIPSShared : public CodeGeneratorShared
|
|||
virtual void visitSqrtD(LSqrtD* ins);
|
||||
virtual void visitSqrtF(LSqrtF* ins);
|
||||
virtual void visitAddI(LAddI* ins);
|
||||
virtual void visitAddI64(LAddI64* ins);
|
||||
virtual void visitSubI(LSubI* ins);
|
||||
virtual void visitSubI64(LSubI64* ins);
|
||||
virtual void visitBitNotI(LBitNotI* ins);
|
||||
virtual void visitBitOpI(LBitOpI* ins);
|
||||
virtual void visitBitOpI64(LBitOpI64* ins);
|
||||
|
||||
virtual void visitMulI(LMulI* ins);
|
||||
virtual void visitMulI64(LMulI64* ins);
|
||||
|
||||
virtual void visitDivI(LDivI* ins);
|
||||
virtual void visitDivPowTwoI(LDivPowTwoI* ins);
|
||||
|
@ -131,11 +150,14 @@ class CodeGeneratorMIPSShared : public CodeGeneratorShared
|
|||
virtual void visitModMaskI(LModMaskI* ins);
|
||||
virtual void visitPowHalfD(LPowHalfD* ins);
|
||||
virtual void visitShiftI(LShiftI* ins);
|
||||
virtual void visitShiftI64(LShiftI64* ins);
|
||||
virtual void visitRotateI64(LRotateI64* lir);
|
||||
virtual void visitUrshD(LUrshD* ins);
|
||||
|
||||
virtual void visitClzI(LClzI* ins);
|
||||
virtual void visitCtzI(LCtzI* ins);
|
||||
virtual void visitPopcntI(LPopcntI* ins);
|
||||
virtual void visitPopcntI64(LPopcntI64* lir);
|
||||
|
||||
virtual void visitTestIAndBranch(LTestIAndBranch* test);
|
||||
virtual void visitCompare(LCompare* comp);
|
||||
|
@ -197,7 +219,9 @@ class CodeGeneratorMIPSShared : public CodeGeneratorShared
|
|||
void visitWasmCall(LWasmCall* ins);
|
||||
void visitWasmCallI64(LWasmCallI64* ins);
|
||||
void visitWasmLoad(LWasmLoad* ins);
|
||||
void visitWasmUnalignedLoad(LWasmUnalignedLoad* ins);
|
||||
void visitWasmStore(LWasmStore* ins);
|
||||
void visitWasmUnalignedStore(LWasmUnalignedStore* ins);
|
||||
void visitWasmAddOffset(LWasmAddOffset* ins);
|
||||
void visitAsmJSLoadHeap(LAsmJSLoadHeap* ins);
|
||||
void visitAsmJSStoreHeap(LAsmJSStoreHeap* ins);
|
||||
|
@ -207,6 +231,7 @@ class CodeGeneratorMIPSShared : public CodeGeneratorShared
|
|||
void visitAsmJSAtomicBinopHeapForEffect(LAsmJSAtomicBinopHeapForEffect* ins);
|
||||
|
||||
void visitAsmJSPassStackArg(LAsmJSPassStackArg* ins);
|
||||
void visitAsmJSPassStackArgI64(LAsmJSPassStackArgI64* ins);
|
||||
void visitAsmSelect(LAsmSelect* 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 js
|
||||
|
||||
|
|
|
@ -64,13 +64,37 @@ void
|
|||
LIRGeneratorMIPSShared::lowerForALUInt64(LInstructionHelper<INT64_PIECES, 2 * INT64_PIECES, 0>* ins,
|
||||
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
|
||||
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>
|
||||
|
@ -78,7 +102,18 @@ void
|
|||
LIRGeneratorMIPSShared::lowerForShiftInt64(LInstructionHelper<INT64_PIECES, INT64_PIECES + 1, Temps>* ins,
|
||||
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(
|
||||
|
@ -208,18 +243,6 @@ LIRGeneratorMIPSShared::lowerModI(MMod* mod)
|
|||
define(lir, mod);
|
||||
}
|
||||
|
||||
void
|
||||
LIRGeneratorMIPSShared::lowerDivI64(MDiv* div)
|
||||
{
|
||||
MOZ_CRASH("NYI");
|
||||
}
|
||||
|
||||
void
|
||||
LIRGeneratorMIPSShared::lowerModI64(MMod* mod)
|
||||
{
|
||||
MOZ_CRASH("NYI");
|
||||
}
|
||||
|
||||
void
|
||||
LIRGeneratorMIPSShared::visitPowHalf(MPowHalf* ins)
|
||||
{
|
||||
|
@ -299,18 +322,36 @@ LIRGeneratorMIPSShared::visitWasmLoad(MWasmLoad* ins)
|
|||
MDefinition* base = ins->base();
|
||||
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) {
|
||||
auto* lir = new(alloc()) LWasmLoadI64(useRegisterAtStart(base));
|
||||
auto* lir = new(alloc()) LWasmLoadI64(ptr);
|
||||
if (ins->offset())
|
||||
lir->setTemp(0, tempCopy(base, 0));
|
||||
|
||||
defineInt64(lir, ins);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
auto* lir = new(alloc()) LWasmLoad(useRegisterAtStart(base));
|
||||
auto* lir = new(alloc()) LWasmLoad(ptr);
|
||||
if (ins->offset())
|
||||
lir->setTemp(0, tempCopy(base, 0));
|
||||
|
||||
|
@ -324,10 +365,40 @@ LIRGeneratorMIPSShared::visitWasmStore(MWasmStore* ins)
|
|||
MOZ_ASSERT(base->type() == MIRType::Int32);
|
||||
|
||||
MDefinition* value = ins->value();
|
||||
LAllocation valueAlloc = useRegisterAtStart(value);
|
||||
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())
|
||||
lir->setTemp(0, tempCopy(base, 0));
|
||||
|
||||
|
@ -637,13 +708,20 @@ LIRGeneratorMIPSShared::visitAtomicTypedArrayElementBinop(MAtomicTypedArrayEleme
|
|||
void
|
||||
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
|
||||
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
|
||||
|
@ -673,5 +751,5 @@ LIRGeneratorMIPSShared::visitCopySign(MCopySign* ins)
|
|||
void
|
||||
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);
|
||||
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; }
|
||||
|
||||
void lowerForShift(LInstructionHelper<1, 2, 0>* ins, MDefinition* mir, MDefinition* lhs,
|
||||
|
@ -69,8 +66,6 @@ class LIRGeneratorMIPSShared : public LIRGeneratorShared
|
|||
MDefinition* lhs, MDefinition* rhs);
|
||||
void lowerDivI(MDiv* div);
|
||||
void lowerModI(MMod* mod);
|
||||
void lowerDivI64(MDiv* div);
|
||||
void lowerModI64(MMod* mod);
|
||||
void lowerMulI(MMul* mul, MDefinition* lhs, MDefinition* rhs);
|
||||
void lowerUDiv(MDiv* div);
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
MacroAssemblerMIPSShared::ma_store(Register data, const BaseIndex& dest,
|
||||
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);
|
||||
}
|
||||
|
||||
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.
|
||||
void
|
||||
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);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_lis(FloatRegister dest, wasm::RawF32 value)
|
||||
{
|
||||
Imm32 imm(value.bits());
|
||||
|
||||
ma_li(ScratchRegister, imm);
|
||||
moveToFloat32(ScratchRegister, dest);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_liNegZero(FloatRegister dest)
|
||||
{
|
||||
|
|
|
@ -104,12 +104,16 @@ class MacroAssemblerMIPSShared : public Assembler
|
|||
// load
|
||||
void ma_load(Register dest, const BaseIndex& src, LoadStoreSize size = SizeWord,
|
||||
LoadStoreExtension extension = SignExtend);
|
||||
void ma_load_unaligned(Register dest, const BaseIndex& src, Register temp,
|
||||
LoadStoreSize size, LoadStoreExtension extension);
|
||||
|
||||
// store
|
||||
void ma_store(Register data, const BaseIndex& dest, LoadStoreSize size = SizeWord,
|
||||
LoadStoreExtension extension = SignExtend);
|
||||
void ma_store(Imm32 imm, const BaseIndex& dest, LoadStoreSize size = SizeWord,
|
||||
LoadStoreExtension extension = SignExtend);
|
||||
void ma_store_unaligned(Register data, const BaseIndex& dest, Register temp,
|
||||
LoadStoreSize size, LoadStoreExtension extension);
|
||||
|
||||
// arithmetic based ops
|
||||
// add
|
||||
|
@ -159,6 +163,7 @@ class MacroAssemblerMIPSShared : public Assembler
|
|||
|
||||
// fp instructions
|
||||
void ma_lis(FloatRegister dest, float value);
|
||||
void ma_lis(FloatRegister dest, wasm::RawF32 value);
|
||||
void ma_liNegZero(FloatRegister dest);
|
||||
|
||||
void ma_sd(FloatRegister fd, BaseIndex address);
|
||||
|
|
|
@ -33,6 +33,21 @@ ABIArgGenerator::next(MIRType type)
|
|||
current_ = ABIArg(usedArgSlots_ * sizeof(intptr_t));
|
||||
usedArgSlots_++;
|
||||
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:
|
||||
if (!usedArgSlots_) {
|
||||
current_ = ABIArg(f12.asSingle());
|
||||
|
@ -223,6 +238,51 @@ Assembler::TraceDataRelocations(JSTracer* trc, JitCode* code, CompactBufferReade
|
|||
::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
|
||||
Assembler::trace(JSTracer* trc)
|
||||
{
|
||||
|
|
|
@ -127,6 +127,9 @@ class Assembler : public AssemblerMIPSShared
|
|||
: AssemblerMIPSShared()
|
||||
{ }
|
||||
|
||||
static Condition UnsignedCondition(Condition cond);
|
||||
static Condition ConditionWithoutEqual(Condition cond);
|
||||
|
||||
// MacroAssemblers hold onto gcthings, so they are traced by the GC.
|
||||
void trace(JSTracer* trc);
|
||||
|
||||
|
|
|
@ -300,6 +300,527 @@ CodeGeneratorMIPS::visitCompareBitwiseAndBranch(LCompareBitwiseAndBranch* lir)
|
|||
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
|
||||
CodeGeneratorMIPS::setReturnDoubleRegs(LiveRegisterSet* regs)
|
||||
{
|
||||
|
|
|
@ -33,11 +33,37 @@ class CodeGeneratorMIPS : public CodeGeneratorMIPSShared
|
|||
|
||||
void emitTableSwitchDispatch(MTableSwitch* mir, Register index, Register base);
|
||||
|
||||
template <typename T>
|
||||
void emitWasmLoadI64(T* ins);
|
||||
template <typename T>
|
||||
void emitWasmStoreI64(T* ins);
|
||||
|
||||
public:
|
||||
void visitCompareB(LCompareB* lir);
|
||||
void visitCompareBAndBranch(LCompareBAndBranch* lir);
|
||||
void visitCompareBitwise(LCompareBitwise* 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.
|
||||
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 js
|
||||
|
||||
|
|
|
@ -12,6 +12,14 @@
|
|||
#define LIR_CPU_OPCODE_LIST(_) \
|
||||
_(BoxFloatingPoint) \
|
||||
_(ModMaskI) \
|
||||
_(UDivOrMod)
|
||||
_(UDivOrMod) \
|
||||
_(DivOrModI64) \
|
||||
_(UDivOrModI64) \
|
||||
_(WasmUnalignedLoad) \
|
||||
_(WasmUnalignedStore) \
|
||||
_(WasmUnalignedLoadI64) \
|
||||
_(WasmUnalignedStoreI64) \
|
||||
_(WasmTruncateToInt64) \
|
||||
_(Int64ToFloatingPoint)
|
||||
|
||||
#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));
|
||||
}
|
||||
|
||||
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
|
||||
LIRGeneratorMIPS::lowerTruncateDToInt32(MTruncateToInt32* ins)
|
||||
{
|
||||
|
@ -172,6 +202,50 @@ LIRGeneratorMIPS::lowerTruncateFToInt32(MTruncateToInt32* 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
|
||||
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 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 lowerTruncateFToInt32(MTruncateToInt32* ins);
|
||||
|
||||
void lowerDivI64(MDiv* div);
|
||||
void lowerModI64(MMod* mod);
|
||||
void lowerUDivI64(MDiv* div);
|
||||
void lowerUModI64(MMod* mod);
|
||||
|
||||
public:
|
||||
void visitBox(MBox* box);
|
||||
void visitUnbox(MUnbox* unbox);
|
||||
|
|
|
@ -48,22 +48,35 @@ MacroAssembler::andPtr(Imm32 imm, Register dest)
|
|||
void
|
||||
MacroAssembler::and64(Imm64 imm, Register64 dest)
|
||||
{
|
||||
and32(Imm32(imm.value & LOW_32_MASK), dest.low);
|
||||
and32(Imm32((imm.value >> 32) & LOW_32_MASK), dest.high);
|
||||
if (imm.low().value != int32_t(0xFFFFFFFF))
|
||||
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
|
||||
MacroAssembler::or64(Imm64 imm, Register64 dest)
|
||||
{
|
||||
or32(Imm32(imm.value & LOW_32_MASK), dest.low);
|
||||
or32(Imm32((imm.value >> 32) & LOW_32_MASK), dest.high);
|
||||
if (imm.low().value)
|
||||
or32(imm.low(), dest.low);
|
||||
if (imm.hi().value)
|
||||
or32(imm.hi(), dest.high);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::xor64(Imm64 imm, Register64 dest)
|
||||
{
|
||||
xor32(Imm32(imm.value & LOW_32_MASK), dest.low);
|
||||
xor32(Imm32((imm.value >> 32) & LOW_32_MASK), dest.high);
|
||||
if (imm.low().value)
|
||||
xor32(imm.low(), dest.low);
|
||||
if (imm.hi().value)
|
||||
xor32(imm.hi(), dest.high);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -137,11 +150,19 @@ MacroAssembler::add64(Register64 src, Register64 dest)
|
|||
void
|
||||
MacroAssembler::add64(Imm32 imm, Register64 dest)
|
||||
{
|
||||
as_addiu(dest.low, dest.low, imm.value);
|
||||
as_sltiu(ScratchRegister, dest.low, imm.value);
|
||||
ma_li(ScratchRegister, imm);
|
||||
as_addu(dest.low, dest.low, ScratchRegister);
|
||||
as_sltu(ScratchRegister, dest.low, 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
|
||||
MacroAssembler::subPtr(Register src, Register dest)
|
||||
{
|
||||
|
@ -154,6 +175,25 @@ MacroAssembler::subPtr(Imm32 imm, Register dest)
|
|||
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
|
||||
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
|
||||
MacroAssembler::mulBy3(Register src, Register dest)
|
||||
{
|
||||
|
@ -239,10 +339,46 @@ MacroAssembler::lshift64(Imm32 imm, Register64 dest)
|
|||
{
|
||||
MOZ_ASSERT(0 <= imm.value && imm.value < 64);
|
||||
ScratchRegisterScope scratch(*this);
|
||||
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);
|
||||
|
||||
if (imm.value == 0) {
|
||||
return;
|
||||
} 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
|
||||
|
@ -264,10 +400,323 @@ MacroAssembler::rshift64(Imm32 imm, Register64 dest)
|
|||
{
|
||||
MOZ_ASSERT(0 <= imm.value && imm.value < 64);
|
||||
ScratchRegisterScope scratch(*this);
|
||||
as_srl(dest.low, dest.low, 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);
|
||||
|
||||
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_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);
|
||||
}
|
||||
|
||||
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
|
||||
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));
|
||||
}
|
||||
|
||||
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
|
||||
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));
|
||||
}
|
||||
|
||||
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
|
||||
MacroAssemblerMIPSCompat::store8(Imm32 imm, const Address& address)
|
||||
{
|
||||
|
@ -1087,6 +1131,49 @@ MacroAssemblerMIPSCompat::storePtr(Register src, AbsoluteAddress dest)
|
|||
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.
|
||||
void
|
||||
MacroAssembler::clampDoubleToUint8(FloatRegister input, Register output)
|
||||
|
@ -1325,6 +1412,12 @@ MacroAssemblerMIPSCompat::loadConstantFloat32(float f, FloatRegister dest)
|
|||
ma_lis(dest, f);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSCompat::loadConstantFloat32(wasm::RawF32 f, FloatRegister dest)
|
||||
{
|
||||
ma_lis(dest, f);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSCompat::loadInt32OrDouble(const Address& src, FloatRegister dest)
|
||||
{
|
||||
|
@ -1375,6 +1468,34 @@ MacroAssemblerMIPSCompat::loadConstantDouble(double dp, FloatRegister dest)
|
|||
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
|
||||
MacroAssemblerMIPSCompat::extractObject(const Address& address, Register scratch)
|
||||
{
|
||||
|
|
|
@ -361,10 +361,12 @@ class MacroAssemblerMIPSCompat : public MacroAssemblerMIPS
|
|||
void loadInt32OrDouble(Register base, Register index,
|
||||
FloatRegister dest, int32_t shift = defaultShift);
|
||||
void loadConstantDouble(double dp, FloatRegister dest);
|
||||
void loadConstantDouble(wasm::RawF64 d, FloatRegister dest);
|
||||
|
||||
void boolValueToFloat32(const ValueOperand& operand, FloatRegister dest);
|
||||
void int32ValueToFloat32(const ValueOperand& operand, FloatRegister dest);
|
||||
void loadConstantFloat32(float f, FloatRegister dest);
|
||||
void loadConstantFloat32(wasm::RawF32 f, FloatRegister 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 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.
|
||||
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 BaseIndex& src, FloatRegister dest);
|
||||
void loadUnalignedFloat32(const BaseIndex& src, Register temp, FloatRegister dest);
|
||||
|
||||
void store8(Register src, 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));
|
||||
}
|
||||
|
||||
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(ImmPtr imm, T address);
|
||||
template <typename T> void storePtr(ImmGCPtr imm, T address);
|
||||
void storePtr(Register src, const Address& address);
|
||||
void storePtr(Register src, const BaseIndex& address);
|
||||
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) {
|
||||
as_movd(dest, src);
|
||||
}
|
||||
|
|
|
@ -1819,12 +1819,14 @@ typedef double (*Prototype_Double_None)();
|
|||
typedef double (*Prototype_Double_Double)(double arg0);
|
||||
typedef double (*Prototype_Double_Int)(int32_t 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_IntDoubleIntInt)(int32_t arg0, double arg1, int32_t arg2,
|
||||
int32_t arg3);
|
||||
typedef float (*Prototype_Float32_Float32)(float arg0);
|
||||
|
||||
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_DoubleDouble)(double 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);
|
||||
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: {
|
||||
double dval = getFpuRegisterDouble(12);
|
||||
Prototype_Int_DoubleIntInt target = reinterpret_cast<Prototype_Int_DoubleIntInt>(external);
|
||||
|
@ -1978,6 +1989,12 @@ Simulator::softwareInterrupt(SimInstruction* instr)
|
|||
setCallResultDouble(dresult);
|
||||
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: {
|
||||
double dval0, dval1;
|
||||
int32_t ival;
|
||||
|
|
|
@ -24,6 +24,7 @@ ABIArgGenerator::next(MIRType type)
|
|||
{
|
||||
switch (type) {
|
||||
case MIRType::Int32:
|
||||
case MIRType::Int64:
|
||||
case MIRType::Pointer: {
|
||||
Register destReg;
|
||||
if (GetIntArgReg(usedArgSlots_, &destReg))
|
||||
|
|
|
@ -304,19 +304,122 @@ CodeGeneratorMIPS64::visitCompareBitwiseAndBranch(LCompareBitwiseAndBranch* lir)
|
|||
}
|
||||
|
||||
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();
|
||||
|
||||
MOZ_ASSERT(lir->mir()->type() == MIRType::Int64);
|
||||
MOZ_ASSERT(!mir->barrierBefore() && !mir->barrierAfter(), "atomics NYI");
|
||||
|
||||
uint32_t offset = mir->offset();
|
||||
if (offset > INT32_MAX) {
|
||||
// This is unreachable because of bounds checks.
|
||||
masm.breakpoint();
|
||||
return;
|
||||
}
|
||||
MOZ_ASSERT(offset < wasm::OffsetGuardLimit);
|
||||
|
||||
Register ptr = ToRegister(lir->ptr());
|
||||
|
||||
|
@ -329,7 +432,129 @@ CodeGeneratorMIPS64::visitWasmLoadI64(LWasmLoadI64* lir)
|
|||
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
|
||||
|
@ -369,6 +594,173 @@ CodeGeneratorMIPS64::visitAsmReinterpretToI64(LAsmReinterpretToI64* lir)
|
|||
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
|
||||
CodeGeneratorMIPS64::setReturnDoubleRegs(LiveRegisterSet* regs)
|
||||
{
|
||||
|
|
|
@ -39,15 +39,37 @@ class CodeGeneratorMIPS64 : public CodeGeneratorMIPSShared
|
|||
|
||||
void emitTableSwitchDispatch(MTableSwitch* mir, Register index, Register base);
|
||||
|
||||
template <typename T>
|
||||
void emitWasmLoadI64(T* ins);
|
||||
template <typename T>
|
||||
void emitWasmStoreI64(T* ins);
|
||||
|
||||
public:
|
||||
void visitCompareB(LCompareB* lir);
|
||||
void visitCompareBAndBranch(LCompareBAndBranch* lir);
|
||||
void visitCompareBitwise(LCompareBitwise* 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 visitWasmUnalignedLoadI64(LWasmUnalignedLoadI64* lir);
|
||||
void visitWasmStoreI64(LWasmStoreI64* ins);
|
||||
void visitWasmUnalignedStoreI64(LWasmUnalignedStoreI64* ins);
|
||||
void visitWasmLoadGlobalVarI64(LWasmLoadGlobalVarI64* ins);
|
||||
void visitWasmStoreGlobalVarI64(LWasmStoreGlobalVarI64* ins);
|
||||
void visitAsmSelectI64(LAsmSelectI64* ins);
|
||||
void visitAsmReinterpretFromI64(LAsmReinterpretFromI64* 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.
|
||||
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 js
|
||||
|
||||
|
|
|
@ -11,6 +11,14 @@
|
|||
|
||||
#define LIR_CPU_OPCODE_LIST(_) \
|
||||
_(ModMaskI) \
|
||||
_(UDivOrMod)
|
||||
_(DivOrModI64) \
|
||||
_(UDivOrMod) \
|
||||
_(UDivOrModI64) \
|
||||
_(WasmUnalignedLoad) \
|
||||
_(WasmUnalignedStore) \
|
||||
_(WasmUnalignedLoadI64) \
|
||||
_(WasmUnalignedStoreI64) \
|
||||
_(WasmTruncateToInt64) \
|
||||
_(Int64ToFloatingPoint)
|
||||
|
||||
#endif // jit_mips64_LOpcodes_mips64_h__
|
||||
|
|
|
@ -15,6 +15,19 @@
|
|||
using namespace js;
|
||||
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
|
||||
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));
|
||||
}
|
||||
|
||||
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
|
||||
LIRGeneratorMIPS64::visitBox(MBox* box)
|
||||
{
|
||||
|
|
|
@ -20,6 +20,9 @@ class LIRGeneratorMIPS64 : public LIRGeneratorMIPSShared
|
|||
{ }
|
||||
|
||||
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.
|
||||
LBoxAllocation useBoxFixed(MDefinition* mir, Register reg1, Register reg2,
|
||||
bool useAtStart = false);
|
||||
|
@ -34,6 +37,11 @@ class LIRGeneratorMIPS64 : public LIRGeneratorMIPSShared
|
|||
void lowerTruncateDToInt32(MTruncateToInt32* ins);
|
||||
void lowerTruncateFToInt32(MTruncateToInt32* ins);
|
||||
|
||||
void lowerDivI64(MDiv* div);
|
||||
void lowerModI64(MMod* mod);
|
||||
void lowerUDivI64(MDiv* div);
|
||||
void lowerUModI64(MMod* mod);
|
||||
|
||||
public:
|
||||
void visitBox(MBox* box);
|
||||
void visitUnbox(MUnbox* unbox);
|
||||
|
|
|
@ -50,6 +50,25 @@ MacroAssembler::and64(Imm64 imm, Register64 dest)
|
|||
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
|
||||
MacroAssembler::or64(Imm64 imm, Register64 dest)
|
||||
{
|
||||
|
@ -82,12 +101,38 @@ MacroAssembler::or64(Register64 src, Register64 dest)
|
|||
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
|
||||
MacroAssembler::xor64(Register64 src, Register64 dest)
|
||||
{
|
||||
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
|
||||
MacroAssembler::xorPtr(Register src, Register dest)
|
||||
{
|
||||
|
@ -128,12 +173,33 @@ MacroAssembler::add64(Register64 src, Register64 dest)
|
|||
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
|
||||
MacroAssembler::add64(Imm32 imm, Register64 dest)
|
||||
{
|
||||
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
|
||||
MacroAssembler::subPtr(Register src, Register dest)
|
||||
{
|
||||
|
@ -146,6 +212,33 @@ MacroAssembler::subPtr(Imm32 imm, Register dest)
|
|||
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
|
||||
MacroAssembler::mul64(Imm64 imm, const Register64& dest)
|
||||
{
|
||||
|
@ -155,6 +248,34 @@ MacroAssembler::mul64(Imm64 imm, const Register64& dest)
|
|||
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
|
||||
MacroAssembler::mulBy3(Register src, Register dest)
|
||||
{
|
||||
|
@ -171,6 +292,12 @@ MacroAssembler::inc64(AbsoluteAddress dest)
|
|||
as_sd(SecondScratchReg, ScratchRegister, 0);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::neg64(Register64 reg)
|
||||
{
|
||||
as_dsubu(reg.reg, zero, reg.reg);
|
||||
}
|
||||
|
||||
// ===============================================================
|
||||
// Shift functions
|
||||
|
||||
|
@ -188,6 +315,12 @@ MacroAssembler::lshift64(Imm32 imm, Register64 dest)
|
|||
ma_dsll(dest.reg, dest.reg, imm);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::lshift64(Register shift, Register64 dest)
|
||||
{
|
||||
ma_dsll(dest.reg, dest.reg, shift);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::rshiftPtr(Imm32 imm, Register dest)
|
||||
{
|
||||
|
@ -195,6 +328,19 @@ MacroAssembler::rshiftPtr(Imm32 imm, Register dest)
|
|||
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
|
||||
MacroAssembler::rshiftPtrArithmetic(Imm32 imm, Register dest)
|
||||
{
|
||||
|
@ -203,15 +349,131 @@ MacroAssembler::rshiftPtrArithmetic(Imm32 imm, Register dest)
|
|||
}
|
||||
|
||||
void
|
||||
MacroAssembler::rshift64(Imm32 imm, Register64 dest)
|
||||
MacroAssembler::rshift64Arithmetic(Imm32 imm, Register64 dest)
|
||||
{
|
||||
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
|
||||
|
||||
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
|
||||
MacroAssembler::branch64(Condition cond, const Address& lhs, Imm64 val, Label* label)
|
||||
{
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче