This commit is contained in:
Phil Ringnalda 2016-10-10 19:12:47 -07:00
Родитель 19c81b094f e8dd21b167
Коммит 8bd6563760
175 изменённых файлов: 7058 добавлений и 1556 удалений

Просмотреть файл

@ -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, &noteIndex))
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, &noteIndex))
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, &noteIndex))
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), &notmin);
masm.branch64(Assembler::NotEqual, rhs, Imm64(-1), &notmin);
if (lir->mir()->isMod())
masm.xor64(output, output);
else
masm.jump(wasm::JumpTarget::IntegerOverflow);
masm.jump(&done);
masm.bind(&notmin);
}
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), &notmin);
masm.branchPtr(Assembler::NotEqual, rhs, ImmWord(-1), &notmin);
if (lir->mir()->isMod())
masm.ma_xor(output, output);
else
masm.jump(wasm::JumpTarget::IntegerOverflow);
masm.jump(&done);
masm.bind(&notmin);
}
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)
{

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше