Merge inbound to central, a=merge

MozReview-Commit-ID: FNYBhhHr4dg

--HG--
rename : netwerk/base/security-prefs.js => security/manager/ssl/security-prefs.js
This commit is contained in:
Wes Kocher 2017-02-10 15:23:33 -08:00
Родитель 71842da0e8 5338290910
Коммит 4062cb56a0
147 изменённых файлов: 2369 добавлений и 1282 удалений

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

@ -545,6 +545,7 @@ DocAccessibleParent::SetCOMProxy(const RefPtr<IAccessible>& aCOMProxy)
IAccessibleHolder::COMPtrType ptr(rawNative);
IAccessibleHolder holder(Move(ptr));
IAccessibleHolder hWndAccHolder;
if (nsWinUtils::IsWindowEmulationStarted()) {
RootAccessible* rootDocument = outerDoc->RootAccessible();
MOZ_ASSERT(rootDocument);
@ -568,10 +569,16 @@ DocAccessibleParent::SetCOMProxy(const RefPtr<IAccessible>& aCOMProxy)
// Attach accessible document to the emulated native window
::SetPropW(hWnd, kPropNameDocAccParent, (HANDLE)this);
SetEmulatedWindowHandle(hWnd);
IAccessible* rawHWNDAcc = nullptr;
if (SUCCEEDED(::AccessibleObjectFromWindow(hWnd, OBJID_WINDOW,
IID_IAccessible,
(void**)&rawHWNDAcc))) {
hWndAccHolder.Set(IAccessibleHolder::COMPtrType(rawHWNDAcc));
}
}
}
Unused << SendParentCOMProxy(holder, reinterpret_cast<uintptr_t>(
mEmulatedWindowHandle));
mEmulatedWindowHandle), hWndAccHolder);
}
void

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

@ -47,12 +47,18 @@ DocAccessibleChild::Shutdown()
ipc::IPCResult
DocAccessibleChild::RecvParentCOMProxy(const IAccessibleHolder& aParentCOMProxy,
const WindowsHandle& aEmulatedWindowHandle)
const WindowsHandle& aEmulatedWindowHandle,
const IAccessibleHolder& aEmulatedWindowCOMProxy)
{
MOZ_ASSERT(!mParentProxy && !aParentCOMProxy.IsNull());
mParentProxy.reset(const_cast<IAccessibleHolder&>(aParentCOMProxy).Release());
SetConstructedInParentProcess();
mEmulatedWindowHandle = reinterpret_cast<HWND>(aEmulatedWindowHandle);
if (!aEmulatedWindowCOMProxy.IsNull()) {
MOZ_ASSERT(!mEmulatedWindowProxy);
mEmulatedWindowProxy.reset(
const_cast<IAccessibleHolder&>(aEmulatedWindowCOMProxy).Release());
}
for (uint32_t i = 0, l = mDeferredEvents.Length(); i < l; ++i) {
mDeferredEvents[i]->Dispatch();

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

@ -29,9 +29,11 @@ public:
virtual ipc::IPCResult
RecvParentCOMProxy(const IAccessibleHolder& aParentCOMProxy,
const WindowsHandle& aEmulatedWindowHandle) override;
const WindowsHandle& aEmulatedWindowHandle,
const IAccessibleHolder& aEmulatedWindowCOMProxy) override;
HWND GetEmulatedWindowHandle() const { return mEmulatedWindowHandle; }
IAccessible* GetEmulatedWindowIAccessible() const { return mEmulatedWindowProxy.get(); }
IAccessible* GetParentIAccessible() const { return mParentProxy.get(); }
@ -312,6 +314,7 @@ private:
bool mIsRemoteConstructed;
mscom::ProxyUniquePtr<IAccessible> mParentProxy;
mscom::ProxyUniquePtr<IAccessible> mEmulatedWindowProxy;
nsTArray<UniquePtr<DeferredEvent>> mDeferredEvents;
HWND mEmulatedWindowHandle;
};

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

@ -67,7 +67,8 @@ parent:
child:
async ParentCOMProxy(IAccessibleHolder aParentCOMProxy,
WindowsHandle aEmulatedWindowHandle);
WindowsHandle aEmulatedWindowHandle,
IAccessibleHolder aEmulatedWindowCOMProxy);
async __delete__();
};

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

@ -53,7 +53,14 @@ DocAccessibleWrap::get_accParent(
if (!ipcDoc) {
return DocAccessible::get_accParent(ppdispParent);
}
IAccessible* dispParent = ipcDoc->GetParentIAccessible();
// Emulated window proxy is only set for the top level content document when
// emulation is enabled.
IAccessible* dispParent = ipcDoc->GetEmulatedWindowIAccessible();
if (!dispParent) {
dispParent = ipcDoc->GetParentIAccessible();
}
if (!dispParent) {
return S_FALSE;
}

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

@ -151,9 +151,16 @@ XPCOMUtils.defineLazyGetter(this, "PopupNotifications", function() {
let tmp = {};
Cu.import("resource://gre/modules/PopupNotifications.jsm", tmp);
try {
// Hide all notifications while the URL is being edited and the address bar
// has focus, including the virtual focus in the results popup.
let shouldSuppress = () => {
return gURLBar.getAttribute("pageproxystate") != "valid" &&
gURLBar.focused;
};
return new tmp.PopupNotifications(gBrowser,
document.getElementById("notification-popup"),
document.getElementById("notification-popup-box"));
document.getElementById("notification-popup-box"),
{ shouldSuppress });
} catch (ex) {
Cu.reportError(ex);
return null;
@ -2441,17 +2448,8 @@ function BrowserPageInfo(documentURL, initialTab, imageElement, frameOuterWindow
*
* @param aURI [optional]
* nsIURI to set. If this is unspecified, the current URI will be used.
* @param aOptions [optional]
* An object with the following properties:
* {
* isForLocationChange:
* Set to true to indicate that the function was invoked to respond
* to a location change event, rather than to reset the current URI
* value. This is useful to avoid calling PopupNotifications.jsm
* multiple times.
* }
*/
function URLBarSetURI(aURI, aOptions = {}) {
function URLBarSetURI(aURI) {
var value = gBrowser.userTypedValue;
var valid = false;
@ -2484,7 +2482,7 @@ function URLBarSetURI(aURI, aOptions = {}) {
gURLBar.value = value;
gURLBar.valueIsTyped = !valid;
SetPageProxyState(valid ? "valid" : "invalid", aOptions);
SetPageProxyState(valid ? "valid" : "invalid");
}
function losslessDecodeURI(aURI) {
@ -2591,19 +2589,15 @@ function UpdatePageProxyState() {
* related user interface elments should be shown because the URI in the
* location bar matches the loaded page. The string "invalid" indicates
* that the URI in the location bar is different than the loaded page.
* @param aOptions [optional]
* An object with the following properties:
* {
* isForLocationChange:
* Set to true to indicate that the function was invoked to respond
* to a location change event. This is useful to avoid calling
* PopupNotifications.jsm multiple times.
* }
*/
function SetPageProxyState(aState, aOptions = {}) {
function SetPageProxyState(aState) {
if (!gURLBar)
return;
let oldPageProxyState = gURLBar.getAttribute("pageproxystate");
// The "browser_urlbar_stop_pending.js" test uses a MutationObserver to do
// some verifications at this point, and it breaks if we don't write the
// attribute, even if it hasn't changed (bug 1338115).
gURLBar.setAttribute("pageproxystate", aState);
// the page proxy state is set to valid via OnLocationChange, which
@ -2615,14 +2609,26 @@ function SetPageProxyState(aState, aOptions = {}) {
gURLBar.removeEventListener("input", UpdatePageProxyState);
}
// Only need to call anchorVisibilityChange if the PopupNotifications object
// for this window has already been initialized (i.e. its getter no
// longer exists). If this is the result of a locations change, then we will
// already invoke PopupNotifications.locationChange separately.
if (!Object.getOwnPropertyDescriptor(window, "PopupNotifications").get &&
!aOptions.isForLocationChange) {
PopupNotifications.anchorVisibilityChange();
// After we've ensured that we've applied the listeners and updated the value
// of gLastValidURLStr, return early if the actual state hasn't changed.
if (oldPageProxyState == aState) {
return;
}
UpdatePopupNotificationsVisibility();
}
function UpdatePopupNotificationsVisibility() {
// Only need to do something if the PopupNotifications object for this window
// has already been initialized (i.e. its getter no longer exists).
if (Object.getOwnPropertyDescriptor(window, "PopupNotifications").get) {
return;
}
// Notify PopupNotifications that the visible anchors may have changed. This
// also checks the suppression state according to the "shouldSuppress"
// function defined earlier in this file.
PopupNotifications.anchorVisibilityChange();
}
function PageProxyClickHandler(aEvent) {
@ -4541,7 +4547,7 @@ var XULBrowserWindow = {
this.reloadCommand.removeAttribute("disabled");
}
URLBarSetURI(aLocationURI, { isForLocationChange: true });
URLBarSetURI(aLocationURI);
BookmarkingUI.onLocationChange();

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

@ -102,49 +102,126 @@ var tests = [
gBrowser.selectedTab = this.oldSelectedTab;
}
},
// Test that popupnotifications are anchored to the identity icon while
// editing the URL in the location bar, and restored to their anchors when the
// URL is reverted.
// Test that popupnotifications are hidden while editing the URL in the
// location bar, anchored to the identity icon when the focus is moved away
// from the location bar, and restored when the URL is reverted.
{ id: "Test#4",
*run() {
this.oldSelectedTab = gBrowser.selectedTab;
yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com/");
for (let persistent of [false, true]) {
let shown = waitForNotificationPanel();
this.notifyObj = new BasicNotification(this.id);
this.notifyObj.anchorID = "geo-notification-icon";
this.notifyObj.addOptions({ persistent });
this.notification = showNotification(this.notifyObj);
yield shown;
let shownInitially = waitForNotificationPanel();
checkPopup(PopupNotifications.panel, this.notifyObj);
// Typing in the location bar should hide the notification.
let hidden = waitForNotificationPanelHidden();
gURLBar.select();
EventUtils.synthesizeKey("*", {});
yield hidden;
is(document.getElementById("geo-notification-icon").boxObject.width, 0,
"geo anchor shouldn't be visible");
// Moving focus to the next control should show the notifications again,
// anchored to the identity icon. We clear the URL bar before moving the
// focus so that the awesomebar popup doesn't get in the way.
shown = waitForNotificationPanel();
EventUtils.synthesizeKey("VK_BACK_SPACE", {});
EventUtils.synthesizeKey("VK_TAB", {});
yield shown;
is(PopupNotifications.panel.anchorNode.id, "identity-icon",
"notification anchored to identity icon");
// Moving focus to the location bar should hide the notification again.
hidden = waitForNotificationPanelHidden();
EventUtils.synthesizeKey("VK_TAB", { shiftKey: true });
yield hidden;
// Reverting the URL should show the notification again.
shown = waitForNotificationPanel();
EventUtils.synthesizeKey("VK_ESCAPE", {});
yield shown;
checkPopup(PopupNotifications.panel, this.notifyObj);
hidden = waitForNotificationPanelHidden();
this.notification.remove();
yield hidden;
}
goNext();
}
},
// Test that popupnotifications triggered while editing the URL in the
// location bar are only shown later when the URL is reverted.
{ id: "Test#5",
*run() {
for (let persistent of [false, true]) {
// Start editing the URL, ensuring that the awesomebar popup is hidden.
gURLBar.select();
EventUtils.synthesizeKey("*", {});
EventUtils.synthesizeKey("VK_BACK_SPACE", {});
// Trying to show a notification should display nothing.
let notShowing = promiseTopicObserved("PopupNotifications-updateNotShowing");
this.notifyObj = new BasicNotification(this.id);
this.notifyObj.anchorID = "geo-notification-icon";
this.notifyObj.addOptions({ persistent });
this.notification = showNotification(this.notifyObj);
yield notShowing;
// Reverting the URL should show the notification.
let shown = waitForNotificationPanel();
EventUtils.synthesizeKey("VK_ESCAPE", {});
yield shown;
checkPopup(PopupNotifications.panel, this.notifyObj);
let hidden = waitForNotificationPanelHidden();
this.notification.remove();
yield hidden;
}
goNext();
}
},
// Test that persistent panels are still open after switching to another tab
// and back, even while editing the URL in the new tab.
{ id: "Test#6",
*run() {
let shown = waitForNotificationPanel();
this.notifyObj = new BasicNotification(this.id);
this.notifyObj.anchorID = "geo-notification-icon";
this.notifyObj.addOptions({
persistent: true,
});
this.notification = showNotification(this.notifyObj);
yield shownInitially;
yield shown;
checkPopup(PopupNotifications.panel, this.notifyObj);
// Switching to a new tab should hide the notification.
let hidden = waitForNotificationPanelHidden();
this.oldSelectedTab = gBrowser.selectedTab;
yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com/");
yield hidden;
let shownAgain = waitForNotificationPanel();
// This will cause the popup to hide and show again.
// Start editing the URL.
gURLBar.select();
EventUtils.synthesizeKey("*", {});
// Keep the URL bar empty, so we don't show the awesomebar.
EventUtils.synthesizeKey("VK_BACK_SPACE", {});
yield shownAgain;
is(document.getElementById("geo-notification-icon").boxObject.width, 0,
"geo anchor shouldn't be visible");
is(PopupNotifications.panel.anchorNode.id, "identity-icon",
"notification anchored to identity icon");
let shownLastTime = waitForNotificationPanel();
// This will cause the popup to hide and show again.
EventUtils.synthesizeKey("VK_ESCAPE", {});
yield shownLastTime;
// Switching to the old tab should show the notification again.
shown = waitForNotificationPanel();
gBrowser.removeTab(gBrowser.selectedTab);
gBrowser.selectedTab = this.oldSelectedTab;
yield shown;
checkPopup(PopupNotifications.panel, this.notifyObj);
let hidden = new Promise(resolve => onPopupEvent("popuphidden", resolve));
hidden = waitForNotificationPanelHidden();
this.notification.remove();
gBrowser.removeTab(gBrowser.selectedTab);
gBrowser.selectedTab = this.oldSelectedTab;
yield hidden;
goNext();

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

@ -264,6 +264,14 @@ function waitForNotificationPanel() {
});
}
function waitForNotificationPanelHidden() {
return new Promise(resolve => {
onPopupEvent("popuphidden", function() {
resolve(this);
});
});
}
function triggerMainCommand(popup) {
let notifications = popup.childNodes;
ok(notifications.length > 0, "at least one notification displayed");

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

@ -1227,6 +1227,9 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
if (event.originalTarget == this.inputField) {
this._hideURLTooltip();
this.formatValue();
if (this.getAttribute("pageproxystate") != "valid") {
UpdatePopupNotificationsVisibility();
}
}
]]></handler>
@ -1234,6 +1237,9 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
if (event.originalTarget == this.inputField) {
this._clearNoActions();
this.formatValue();
if (this.getAttribute("pageproxystate") != "valid") {
UpdatePopupNotificationsVisibility();
}
}
if (ExtensionSearchHandler.hasActiveInputSession()) {
ExtensionSearchHandler.handleInputCancelled();

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

@ -1488,29 +1488,31 @@ var PlacesControllerDragHelper = {
continue;
let data = dt.mozGetDataAt(flavor, i);
let dragged;
let nodes;
try {
dragged = PlacesUtils.unwrapNodes(data, flavor)[0];
nodes = PlacesUtils.unwrapNodes(data, flavor);
} catch (e) {
return false;
}
// Only bookmarks and urls can be dropped into tag containers.
if (ip.isTag &&
dragged.type != PlacesUtils.TYPE_X_MOZ_URL &&
(dragged.type != PlacesUtils.TYPE_X_MOZ_PLACE ||
(dragged.uri && dragged.uri.startsWith("place:")) ))
return false;
for (let dragged of nodes) {
// Only bookmarks and urls can be dropped into tag containers.
if (ip.isTag &&
dragged.type != PlacesUtils.TYPE_X_MOZ_URL &&
(dragged.type != PlacesUtils.TYPE_X_MOZ_PLACE ||
(dragged.uri && dragged.uri.startsWith("place:")) ))
return false;
// The following loop disallows the dropping of a folder on itself or
// on any of its descendants.
if (dragged.type == PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER ||
(dragged.uri && dragged.uri.startsWith("place:")) ) {
let parentId = ip.itemId;
while (parentId != PlacesUtils.placesRootId) {
if (dragged.concreteId == parentId || dragged.id == parentId)
return false;
parentId = PlacesUtils.bookmarks.getFolderIdForItem(parentId);
// The following loop disallows the dropping of a folder on itself or
// on any of its descendants.
if (dragged.type == PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER ||
(dragged.uri && dragged.uri.startsWith("place:")) ) {
let parentId = ip.itemId;
while (parentId != PlacesUtils.placesRootId) {
if (dragged.concreteId == parentId || dragged.id == parentId)
return false;
parentId = PlacesUtils.bookmarks.getFolderIdForItem(parentId);
}
}
}
}
@ -1576,63 +1578,77 @@ var PlacesControllerDragHelper = {
let parentGuid = PlacesUIUtils.useAsyncTransactions ?
(yield insertionPoint.promiseGuid()) : null;
let tagName = insertionPoint.tagName;
// Following flavors may contain duplicated data.
let duplicable = new Map();
duplicable.set(PlacesUtils.TYPE_UNICODE, new Set());
duplicable.set(PlacesUtils.TYPE_X_MOZ_URL, new Set());
for (let i = 0; i < dropCount; ++i) {
let flavor = this.getFirstValidFlavor(dt.mozTypesAt(i));
if (!flavor)
return;
let data = dt.mozGetDataAt(flavor, i);
let unwrapped;
if (duplicable.has(flavor)) {
let handled = duplicable.get(flavor);
if (handled.has(data))
continue;
handled.add(data);
}
let nodes;
if (flavor != TAB_DROP_TYPE) {
// There's only ever one in the D&D case.
unwrapped = PlacesUtils.unwrapNodes(data, flavor)[0];
nodes = PlacesUtils.unwrapNodes(data, flavor);
} else if (data instanceof XULElement && data.localName == "tab" &&
data.ownerGlobal instanceof ChromeWindow) {
let uri = data.linkedBrowser.currentURI;
let spec = uri ? uri.spec : "about:blank";
unwrapped = { uri: spec,
title: data.label,
type: PlacesUtils.TYPE_X_MOZ_URL};
nodes = [{ uri: spec,
title: data.label,
type: PlacesUtils.TYPE_X_MOZ_URL}];
} else
throw new Error("bogus data was passed as a tab");
let index = insertionPoint.index;
for (let unwrapped of nodes) {
let index = insertionPoint.index;
// Adjust insertion index to prevent reversal of dragged items. When you
// drag multiple elts upward: need to increment index or each successive
// elt will be inserted at the same index, each above the previous.
let dragginUp = insertionPoint.itemId == unwrapped.parent &&
index < PlacesUtils.bookmarks.getItemIndex(unwrapped.id);
if (index != -1 && dragginUp)
index += movedCount++;
// Adjust insertion index to prevent reversal of dragged items. When you
// drag multiple elts upward: need to increment index or each successive
// elt will be inserted at the same index, each above the previous.
let dragginUp = insertionPoint.itemId == unwrapped.parent &&
index < PlacesUtils.bookmarks.getItemIndex(unwrapped.id);
if (index != -1 && dragginUp)
index += movedCount++;
// If dragging over a tag container we should tag the item.
if (insertionPoint.isTag) {
let uri = NetUtil.newURI(unwrapped.uri);
let tagItemId = insertionPoint.itemId;
if (PlacesUIUtils.useAsyncTransactions)
transactions.push(PlacesTransactions.Tag({ uri, tag: tagName }));
else
transactions.push(new PlacesTagURITransaction(uri, [tagItemId]));
} else {
// If this is not a copy, check for safety that we can move the source,
// otherwise report an error and fallback to a copy.
if (!doCopy && !PlacesControllerDragHelper.canMoveUnwrappedNode(unwrapped)) {
Components.utils.reportError("Tried to move an unmovable Places " +
"node, reverting to a copy operation.");
doCopy = true;
}
if (PlacesUIUtils.useAsyncTransactions) {
transactions.push(
PlacesUIUtils.getTransactionForData(unwrapped,
flavor,
parentGuid,
index,
doCopy));
// If dragging over a tag container we should tag the item.
if (insertionPoint.isTag) {
let uri = NetUtil.newURI(unwrapped.uri);
let tagItemId = insertionPoint.itemId;
if (PlacesUIUtils.useAsyncTransactions)
transactions.push(PlacesTransactions.Tag({ uri, tag: tagName }));
else
transactions.push(new PlacesTagURITransaction(uri, [tagItemId]));
} else {
transactions.push(PlacesUIUtils.makeTransaction(unwrapped,
flavor, insertionPoint.itemId,
index, doCopy));
// If this is not a copy, check for safety that we can move the
// source, otherwise report an error and fallback to a copy.
if (!doCopy && !PlacesControllerDragHelper.canMoveUnwrappedNode(unwrapped)) {
Components.utils.reportError("Tried to move an unmovable Places " +
"node, reverting to a copy operation.");
doCopy = true;
}
if (PlacesUIUtils.useAsyncTransactions) {
transactions.push(
PlacesUIUtils.getTransactionForData(unwrapped,
flavor,
parentGuid,
index,
doCopy));
} else {
transactions.push(PlacesUIUtils.makeTransaction(unwrapped,
flavor, insertionPoint.itemId,
index, doCopy));
}
}
}
}

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

@ -52,7 +52,46 @@ add_task(function* test() {
// Verify that we removed the bookmark successfully.
ok(!PlacesUtils.bookmarks.isBookmarked(uri), "URI should be removed");
}
};
/**
* Simulates a drop of multiple URIs onto the bookmarks bar.
*
* @param aEffect
* The effect to use for the drop operation: move, copy, or link.
* @param aMimeType
* The mime type to use for the drop operation.
*/
let simulateDragDropMultiple = function(aEffect, aMimeType) {
const uriSpecs = [
"http://www.mozilla.org/C54263C6-A484-46CF-8E2B-FE131586348A",
"http://www.mozilla.org/71381257-61E6-4376-AF7C-BF3C5FD8870D",
"http://www.mozilla.org/091A88BD-5743-4C16-A005-3D2EA3A3B71E"
];
let uris = uriSpecs.map(spec => makeURI(spec));
let data;
if (aMimeType == "text/x-moz-url")
data = uriSpecs.map(spec => spec + "\n" + spec).join("\n");
else
data = uriSpecs.join("\n");
EventUtils.synthesizeDrop(placesItems.childNodes[0],
placesItems,
[[{type: aMimeType,
data}]],
aEffect, window);
// Verify that the drop produces exactly one bookmark per each URL.
for (let uri of uris) {
let bookmarkIds = PlacesUtils.bookmarks
.getBookmarkIdsForURI(uri);
ok(bookmarkIds.length == 1, "There should be exactly one bookmark");
PlacesUtils.bookmarks.removeItem(bookmarkIds[0]);
// Verify that we removed the bookmark successfully.
ok(!PlacesUtils.bookmarks.isBookmarked(uri), "URI should be removed");
}
};
// Simulate a bookmark drop for all of the mime types and effects.
let mimeTypes = ["text/plain", "text/unicode", "text/x-moz-url"];
@ -60,6 +99,7 @@ add_task(function* test() {
effects.forEach(function(effect) {
mimeTypes.forEach(function(mimeType) {
simulateDragDrop(effect, mimeType);
simulateDragDropMultiple(effect, mimeType);
});
});
});

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

@ -6,25 +6,25 @@
Cu.import("resource:///modules/sessionstore/SessionStore.jsm");
function openAndCloseTab(window, url) {
async function openAndCloseTab(window, url) {
let tab = window.gBrowser.addTab(url);
yield promiseBrowserLoaded(tab.linkedBrowser, true, url);
yield TabStateFlusher.flush(tab.linkedBrowser);
yield promiseRemoveTab(tab);
await promiseBrowserLoaded(tab.linkedBrowser, true, url);
await TabStateFlusher.flush(tab.linkedBrowser);
await promiseRemoveTab(tab);
}
function* openWindow(url) {
let win = yield promiseNewWindowLoaded();
async function openWindow(url) {
let win = await promiseNewWindowLoaded();
let flags = Ci.nsIWebNavigation.LOAD_FLAGS_REPLACE_HISTORY;
win.gBrowser.selectedBrowser.loadURIWithFlags(url, flags);
yield promiseBrowserLoaded(win.gBrowser.selectedBrowser, true, url);
await promiseBrowserLoaded(win.gBrowser.selectedBrowser, true, url);
return win;
}
function closeWindow(win) {
yield BrowserTestUtils.closeWindow(win);
async function closeWindow(win) {
await BrowserTestUtils.closeWindow(win);
// Wait 20 ms to allow SessionStorage a chance to register the closed window.
yield new Promise(resolve => setTimeout(resolve, 20));
await new Promise(resolve => setTimeout(resolve, 20));
}
add_task(function* test_undoCloseById() {

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

@ -4,6 +4,8 @@
# 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/.
XPCSHELL_TESTS_MANIFESTS += ['test/unit/xpcshell.ini']
SOURCES += [
'host/rpc.cc',
]

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

@ -0,0 +1,59 @@
"use strict";
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
Components.utils.import("resource://gre/modules/Services.jsm");
// For the following 5 lines of codes, we redirect the
// path of the "ppapi.js" in addon to the exact file path.
Components.utils.import("resource://gre/modules/NetUtil.jsm");
let resHandler = Services.io.getProtocolHandler("resource")
.QueryInterface(Components.interfaces.nsISubstitutingProtocolHandler);
let dataURI = NetUtil.newURI(do_get_file("."));
resHandler.setSubstitution("ppapi.js", dataURI);
// Load the script
load("ppapi-runtime.jsm");
let instanceId = 1;
let url = "http://example.com";
let info = {
documentURL: "chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai",
url,
setupJSInstanceObject: false,
isFullFrame: false, //pluginElement.ownerDocument.mozSyntheticDocument,
arguments: {
keys: ["src", "full-frame", "top-level-url"],
values: [url, "", url],
},
};
// Head.js is a shared file for all the test_ppb***.js.
// Right now, window, process and MessageManager are base classes here.
// Fill in the classes when you need the functions of them
// and add more mocked classses if you need them in
// ppapi-runtime.jsm for your tests.
class Mock_Window {}
class Mock_Process {}
class Mock_MessageManager {
addMessageListener () {
}
}
// Here the new PPAPIRuntime, Call_PpbFunc and new PPAPIInstance are the
// core part to invoke codes in ppapi-runtime.jsm.
let rt = new PPAPIRuntime(new Mock_Process());
function Call_PpbFunc(obj) {
if (!obj || !obj.__interface || !obj.__version || !obj.__method) {
ok(false, 'invalid JSON');
}
let fn = obj.__interface + "_" + obj.__method;
return rt.table[fn](obj);
}
// PPAPIInstance constructor(id, rt, info, window, eventHandler, containerWindow, mm)
let instance = new PPAPIInstance(instanceId, rt, info, new Mock_Window(), null /*docShell.chromeEventHandler*/, null, new Mock_MessageManager());
do_register_cleanup(function () {
resHandler.setSubstitution("ppapi.js", null);
})

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

@ -0,0 +1,49 @@
"use strict";
const PPB_TESTCASES = [
{"__interface":"PPB_KeyboardInputEvent","__version":"1.2","__method":"IsKeyboardInputEvent","resource":0},
{"__interface":"PPB_KeyboardInputEvent","__version":"1.2","__method":"GetKeyCode","key_event":0},
{"__interface":"PPB_KeyboardInputEvent","__version":"1.2","__method":"GetCharacterText","character_event":0}
];
class Mock_DomEvent {
constructor(eventType) {
this.type = eventType;
this.timeStamp = 0;
}
}
class Mock_KeyboardInputEvent extends Mock_DomEvent {
constructor(eventType, keyCode, charCode) {
super(eventType);
this.keyCode = keyCode;
this.charCode = charCode;
}
}
function run_test() {
// We mock a "keydown" event to test "PPB_KeyboardInputEvent".
let event = new Mock_KeyboardInputEvent("keydown", 65, 0);
// To test PPB_KeyboardInputEvent we need to invoke event resource constructor
// in ppapi-runtime.jsm to get a resource id.
let eventType = EventTypes.get(event.type);
let resource = new eventType.resourceCtor(instance, event);
let PP_ResourceID = resource.toJSON();
PPB_TESTCASES[0].resource = PP_ResourceID;
PPB_TESTCASES[1].key_event = PP_ResourceID;
Assert.equal(Call_PpbFunc(PPB_TESTCASES[0]), PP_Bool.PP_TRUE);
Assert.equal(Call_PpbFunc(PPB_TESTCASES[1]), 65); // 65 is the keyCode when you press 'A'.
// We mock a "keypress" event to test "PPB_KeyboardInputEvent".
event = new Mock_KeyboardInputEvent("keypress", 0, 65);
eventType = EventTypes.get(event.type);
resource = new eventType.resourceCtor(instance, event);
PP_ResourceID = resource.toJSON();
PPB_TESTCASES[0].resource = PP_ResourceID;
PPB_TESTCASES[2].character_event = PP_ResourceID;
Assert.equal(Call_PpbFunc(PPB_TESTCASES[0]), PP_Bool.PP_TRUE);
Assert.equal(Call_PpbFunc(PPB_TESTCASES[2]).type, PP_VarType.PP_VARTYPE_STRING);
}

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

@ -0,0 +1,8 @@
[DEFAULT]
head = head.js
tail =
support-files =
../../host/common/opengles2-utils.jsm
../../host/common/ppapi-runtime.jsm
[test_ppbkeyboard.js]

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

@ -225,7 +225,7 @@ exports.MarkerDOMUtils = {
hbox.className = "marker-details-customcontainer";
let label = doc.createElement("label");
label.className = "custom-button devtools-button";
label.className = "custom-button";
label.setAttribute("value", "Show allocation triggers");
label.setAttribute("type", "show-allocations");
label.setAttribute("data-action", JSON.stringify({

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

@ -695,6 +695,12 @@
.marker-details-customcontainer .custom-button {
padding: 2px 5px;
border-width: 1px;
color: var(--theme-highlight-blue);
text-decoration: none;
}
.marker-details-customcontainer .custom-button:hover {
text-decoration: underline;
}
/**

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

@ -97,7 +97,7 @@ function eventExpected(msg) {
* enough, and we won't observe what should have been a failure of the test.
* But it shouldn't happen that good code will randomly *fail* this test.
*/
function run_test() {
function* run_test() {
/*
* TEST 1 tests that:
* <body onhashchange = ... > works,

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

@ -220,6 +220,17 @@ AudioChannelService::GetOrCreate()
return service.forget();
}
/* static */ already_AddRefed<AudioChannelService>
AudioChannelService::Get()
{
if (sXPCOMShuttingDown) {
return nullptr;
}
RefPtr<AudioChannelService> service = gAudioChannelService.get();
return service.forget();
}
/* static */ PRLogModuleInfo*
AudioChannelService::GetAudioChannelLog()
{
@ -933,6 +944,17 @@ AudioChannelService::IsAudioChannelActive(mozIDOMWindowProxy* aWindow,
*aActive = IsAudioChannelActive(window, (AudioChannel)aAudioChannel);
return NS_OK;
}
bool
AudioChannelService::IsWindowActive(nsPIDOMWindowOuter* aWindow)
{
MOZ_ASSERT(NS_IsMainThread());
auto* window = nsPIDOMWindowOuter::From(aWindow)->GetScriptableTop();
AudioChannelWindow* winData = GetOrCreateWindowData(window);
return !winData->mAudibleAgents.IsEmpty();
}
void
AudioChannelService::SetDefaultVolumeControlChannel(int32_t aChannel,
bool aVisible)

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

@ -89,11 +89,17 @@ public:
/**
* Returns the AudioChannelServce singleton.
* If AudioChannelServce is not exist, create and return new one.
* If AudioChannelService doesn't exist, create and return new one.
* Only to be called from main thread.
*/
static already_AddRefed<AudioChannelService> GetOrCreate();
/**
* Returns the AudioChannelService singleton if one exists.
* If AudioChannelService doesn't exist, returns null.
*/
static already_AddRefed<AudioChannelService> Get();
static bool IsAudioChannelMutedByDefault();
static PRLogModuleInfo* GetAudioChannelLog();
@ -150,6 +156,8 @@ public:
bool IsAudioChannelActive(nsPIDOMWindowOuter* aWindow, AudioChannel aChannel);
bool IsWindowActive(nsPIDOMWindowOuter* aWindow);
/**
* Return true if there is a telephony channel active in this process
* or one of its subprocesses.

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

@ -82,7 +82,10 @@ public:
}
private:
explicit DOMParser(nsISupports* aOwner) : mOwner(aOwner), mAttemptedInit(false)
explicit DOMParser(nsISupports* aOwner)
: mOwner(aOwner)
, mAttemptedInit(false)
, mOriginalPrincipalWasSystem(false)
{
MOZ_ASSERT(aOwner);
}

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

@ -34,9 +34,10 @@ int32_t
TimeoutManager::DOMMinTimeoutValue(bool aIsTracking) const {
// First apply any back pressure delay that might be in effect.
int32_t value = std::max(mBackPressureDelayMS, 0);
// Don't use the background timeout value when there are audio contexts
// present, so that background audio can keep running smoothly. (bug 1181073)
bool isBackground = !mWindow.AsInner()->HasAudioContexts() &&
// Don't use the background timeout value when the tab is playing audio.
// Until bug 1336484 we only used to do this for pages that use Web Audio.
// The original behavior was implemented in bug 11811073.
bool isBackground = !mWindow.AsInner()->IsPlayingAudio() &&
mWindow.IsBackgroundInternal();
auto minValue = aIsTracking ? (isBackground ? gMinTrackingBackgroundTimeoutValue
: gMinTrackingTimeoutValue)

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

@ -4152,9 +4152,13 @@ nsPIDOMWindowInner::SyncStateFromParentWindow()
}
bool
nsPIDOMWindowInner::HasAudioContexts() const
nsPIDOMWindowInner::IsPlayingAudio()
{
return !mAudioContexts.IsEmpty();
RefPtr<AudioChannelService> acs = AudioChannelService::Get();
if (!acs) {
return false;
}
return acs->IsWindowActive(GetOuterWindow());
}
mozilla::dom::TimeoutManager&

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

@ -878,7 +878,7 @@ public:
// window.
void SyncStateFromParentWindow();
bool HasAudioContexts() const;
bool IsPlayingAudio();
mozilla::dom::TimeoutManager& TimeoutManager();

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

@ -1,9 +1,13 @@
[DEFAULT]
support-files =
audio.ogg
empty.html
file_audioLoop.html
file_audioLoopInIframe.html
file_bug1011748_redirect.sjs
file_bug1011748_OK.sjs
file_messagemanager_unload.html
file_pluginAudio.html
file_use_counter_outer.html
file_use_counter_svg_getElementById.svg
file_use_counter_svg_currentScale.svg
@ -11,6 +15,8 @@ support-files =
file_use_counter_svg_fill_pattern.svg
file_use_counter_svg_fill_pattern_internal.svg
file_use_counter_svg_fill_pattern_data.svg
file_webaudioLoop.html
plugin.js
[browser_bug593387.js]
[browser_bug902350.js]
@ -27,3 +33,4 @@ skip-if = e10s # this tests non-e10s behavior. it's not expected to work in e10s
skip-if = true # Bug 1271028
[browser_use_counters.js]
[browser_bug1307747.js]
[browser_timeout_throttling_with_audio_playback.js]

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

@ -0,0 +1,61 @@
const kBaseURI = "http://mochi.test:8888/browser/dom/base/test/empty.html";
const kPluginJS = "chrome://mochitests/content/browser/dom/base/test/plugin.js";
var testURLs = [
"http://mochi.test:8888/browser/dom/base/test/file_audioLoop.html",
"http://mochi.test:8888/browser/dom/base/test/file_audioLoopInIframe.html",
"http://mochi.test:8888/browser/dom/base/test/file_pluginAudio.html",
"http://mochi.test:8888/browser/dom/base/test/file_webaudioLoop.html",
];
// We want to ensure that while audio is being played back, a background tab is
// treated the same as a foreground tab as far as timeout throttling is concerned.
// So we use a 10ms minimum timeout value for foreground tabs and a 100,000 second
// minimum timeout value for background tabs. This means that in case the test
// fails, it will time out in practice, but just for sanity the test condition
// ensures that the observed timeout delay falls in this range.
const kMinTimeoutForeground = 10;
const kMinTimeoutBackground = 100 * 1000 * 1000;
Services.scriptloader.loadSubScript(kPluginJS, this);
function* runTest(url) {
let currentTab = gBrowser.selectedTab;
let newTab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, kBaseURI);
let newBrowser = gBrowser.getBrowserForTab(newTab);
// Wait for the UI to indicate that audio is being played back.
let promise = BrowserTestUtils.waitForAttribute("soundplaying", newTab, "true");
newBrowser.loadURI(url);
yield promise;
// Put the tab in the background.
yield BrowserTestUtils.switchTab(gBrowser, currentTab);
let timeout = yield ContentTask.spawn(newBrowser, {}, function() {
return new Promise(resolve => {
let before = new Date();
content.window.setTimeout(function() {
let after = new Date();
resolve(after - before);
}, 0);
});
});
ok(timeout >= kMinTimeoutForeground &&
timeout <= kMinTimeoutBackground, `Got the correct timeout (${timeout})`);
// All done.
yield BrowserTestUtils.removeTab(newTab);
}
add_task(function* setup() {
yield SpecialPowers.pushPrefEnv({"set": [
["dom.min_timeout_value", kMinTimeoutForeground],
["dom.min_background_timeout_value", kMinTimeoutBackground],
]});
});
add_task(function* test() {
for (var url of testURLs) {
yield runTest(url);
}
});

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

@ -0,0 +1,2 @@
<!DOCTYPE html>
<iframe src="file_audioLoop.html"></iframe>

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

@ -189,6 +189,7 @@ support-files =
invalid_accesscontrol.resource^headers^
mutationobserver_dialog.html
orientationcommon.js
plugin.js
script-1_bug597345.sjs
script-2_bug597345.js
script_bug602838.sjs

32
dom/base/test/plugin.js Normal file
Просмотреть файл

@ -0,0 +1,32 @@
// Copied from /dom/plugins/test/mochitest/utils.js
function getTestPlugin(pluginName) {
var ph = SpecialPowers.Cc["@mozilla.org/plugin/host;1"]
.getService(SpecialPowers.Ci.nsIPluginHost);
var tags = ph.getPluginTags();
var name = pluginName || "Test Plug-in";
for (var tag of tags) {
if (tag.name == name) {
return tag;
}
}
ok(false, "Could not find plugin tag with plugin name '" + name + "'");
return null;
}
// Copied from /dom/plugins/test/mochitest/utils.js
function setTestPluginEnabledState(newEnabledState, pluginName) {
var oldEnabledState = SpecialPowers.setTestPluginEnabledState(newEnabledState, pluginName);
if (!oldEnabledState) {
return;
}
var plugin = getTestPlugin(pluginName);
while (plugin.enabledState != newEnabledState) {
// Run a nested event loop to wait for the preference change to
// propagate to the child. Yuck!
SpecialPowers.Services.tm.currentThread.processNextEvent(true);
}
SimpleTest.registerCleanupFunction(function() {
SpecialPowers.setTestPluginEnabledState(oldEnabledState, pluginName);
});
}
setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);

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

@ -3,6 +3,7 @@
<head>
<title>Test for audio controller in windows</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="plugin.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
@ -12,39 +13,6 @@
<script type="application/javascript">
// Copied from /dom/plugins/test/mochitest/utils.js
function getTestPlugin(pluginName) {
var ph = SpecialPowers.Cc["@mozilla.org/plugin/host;1"]
.getService(SpecialPowers.Ci.nsIPluginHost);
var tags = ph.getPluginTags();
var name = pluginName || "Test Plug-in";
for (var tag of tags) {
if (tag.name == name) {
return tag;
}
}
ok(false, "Could not find plugin tag with plugin name '" + name + "'");
return null;
}
// Copied from /dom/plugins/test/mochitest/utils.js
function setTestPluginEnabledState(newEnabledState, pluginName) {
var oldEnabledState = SpecialPowers.setTestPluginEnabledState(newEnabledState, pluginName);
if (!oldEnabledState) {
return;
}
var plugin = getTestPlugin(pluginName);
while (plugin.enabledState != newEnabledState) {
// Run a nested event loop to wait for the preference change to
// propagate to the child. Yuck!
SpecialPowers.Services.tm.currentThread.processNextEvent(true);
}
SimpleTest.registerCleanupFunction(function() {
SpecialPowers.setTestPluginEnabledState(oldEnabledState, pluginName);
});
}
setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
SimpleTest.waitForExplicitFinish();
var expectedNotification = null;

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

@ -259,7 +259,9 @@ MP3TrackDemuxer::ScanUntil(const TimeUnit& aTime)
}
if (Duration(mFrameIndex) > aTime) {
FastSeek(aTime);
// We've seeked past the target time, rewind back a little to correct it.
const int64_t rewind = aTime.ToMicroseconds() / 100;
FastSeek(aTime - TimeUnit::FromMicroseconds(rewind));
}
if (Duration(mFrameIndex + 1) > aTime) {

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

@ -16,10 +16,6 @@ MOCHITEST_MANIFESTS += [
'test/mochitest.ini',
]
BROWSER_CHROME_MANIFESTS += [
'test/browser.ini',
]
TEST_HARNESS_FILES.testing.mochitest.tests.dom.media.webaudio.test.blink += [
'test/blink/audio-testing.js',
'test/blink/convolution-testing.js',

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

@ -1 +0,0 @@
[browser_bug1181073.js]

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

@ -1,40 +0,0 @@
add_task(function*() {
// Make the min_background_timeout_value very high to avoid problems on slow machines
yield SpecialPowers.pushPrefEnv({
'set': [['dom.min_background_timeout_value', 3000]]
});
// Make a new tab, and put it in the background
yield BrowserTestUtils.withNewTab("about:blank", function*(browser) {
yield BrowserTestUtils.withNewTab("about:blank", function*() {
let time = yield ContentTask.spawn(browser, null, function () {
return new Promise(resolve => {
let start = content.performance.now();
let id = content.window.setInterval(function() {
let end = content.performance.now();
content.window.clearInterval(id);
resolve(end - start);
}, 0);
});
});
ok(time > 2000, "Interval is throttled with no webaudio (" + time + " ms)");
time = yield ContentTask.spawn(browser, null, function () {
return new Promise(resolve => {
// Create an audio context, and save it on the window so it doesn't get GCed
content.window._audioCtx = new content.window.AudioContext();
let start = content.performance.now();
let id = content.window.setInterval(function() {
let end = content.performance.now();
content.window.clearInterval(id);
resolve(end - start);
}, 0);
});
});
ok(time < 1000, "Interval is not throttled with an audio context present (" + time + " ms)");
});
});
});

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

@ -426,7 +426,7 @@ function build_test_uri(base_uri, host, test_id, type, timeout) {
}
// open a new tab, load the test, and wait for it to finish
function execute_test(test, mimetype) {
async function execute_test(test, mimetype) {
var src = build_test_uri(TOP_URI, test_servers[test].host,
test, test_settings[which_test].type,
test_settings[which_test].timeout);
@ -435,7 +435,7 @@ function execute_test(test, mimetype) {
test_servers[test]['tab'] = tab;
let browser = gBrowser.getBrowserForTab(tab);
yield BrowserTestUtils.browserLoaded(browser);
await BrowserTestUtils.browserLoaded(browser);
yield BrowserTestUtils.removeTab(tab);
await BrowserTestUtils.removeTab(tab);
}

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

@ -1247,7 +1247,8 @@ private:
MOZ_ASSERT(equal);
mWorkerPrivate->InitChannelInfo(aChannelInfo);
mWorkerPrivate->SetPrincipalOnMainThread(responsePrincipal, loadGroup);
rv = mWorkerPrivate->SetPrincipalOnMainThread(responsePrincipal, loadGroup);
MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
}
if (NS_SUCCEEDED(rv)) {

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

@ -1457,17 +1457,10 @@ public:
nsCOMPtr<nsIUploadChannel2> uploadChannel = do_QueryInterface(httpChannel);
if (uploadChannel) {
MOZ_ASSERT(!mUploadStream);
bool bodyHasHeaders = false;
rv = uploadChannel->GetUploadStreamHasHeaders(&bodyHasHeaders);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIInputStream> uploadStream;
rv = uploadChannel->CloneUploadStream(getter_AddRefs(uploadStream));
NS_ENSURE_SUCCESS(rv, rv);
if (bodyHasHeaders) {
HandleBodyWithHeaders(uploadStream);
} else {
mUploadStream = uploadStream;
}
mUploadStream = uploadStream;
}
return NS_OK;
@ -1600,52 +1593,6 @@ private:
return true;
}
nsresult
HandleBodyWithHeaders(nsIInputStream* aUploadStream)
{
// We are dealing with an nsMIMEInputStream which uses string input streams
// under the hood, so all of the data is available synchronously.
bool nonBlocking = false;
nsresult rv = aUploadStream->IsNonBlocking(&nonBlocking);
NS_ENSURE_SUCCESS(rv, rv);
if (NS_WARN_IF(!nonBlocking)) {
return NS_ERROR_NOT_AVAILABLE;
}
nsAutoCString body;
rv = NS_ConsumeStream(aUploadStream, UINT32_MAX, body);
NS_ENSURE_SUCCESS(rv, rv);
// Extract the headers in the beginning of the buffer
nsAutoCString::const_iterator begin, end;
body.BeginReading(begin);
body.EndReading(end);
const nsAutoCString::const_iterator body_end = end;
nsAutoCString headerName, headerValue;
bool emptyHeader = false;
while (FetchUtil::ExtractHeader(begin, end, headerName,
headerValue, &emptyHeader) &&
!emptyHeader) {
mHeaderNames.AppendElement(headerName);
mHeaderValues.AppendElement(headerValue);
headerName.Truncate();
headerValue.Truncate();
}
// Replace the upload stream with one only containing the body text.
nsCOMPtr<nsIStringInputStream> strStream =
do_CreateInstance(NS_STRINGINPUTSTREAM_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
// Skip past the "\r\n" that separates the headers and the body.
++begin;
++begin;
body.Assign(Substring(begin, body_end));
rv = strStream->SetData(body.BeginReading(), body.Length());
NS_ENSURE_SUCCESS(rv, rv);
mUploadStream = strStream;
return NS_OK;
}
};
NS_IMPL_ISUPPORTS_INHERITED(FetchEventRunnable, WorkerRunnable, nsIHttpHeaderVisitor)

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

@ -1835,7 +1835,7 @@ WorkerLoadInfo::StealFrom(WorkerLoadInfo& aOther)
mOriginAttributes = aOther.mOriginAttributes;
}
void
nsresult
WorkerLoadInfo::SetPrincipalOnMainThread(nsIPrincipal* aPrincipal,
nsILoadGroup* aLoadGroup)
{
@ -1846,7 +1846,8 @@ WorkerLoadInfo::SetPrincipalOnMainThread(nsIPrincipal* aPrincipal,
mPrincipal = aPrincipal;
mPrincipalIsSystem = nsContentUtils::IsSystemPrincipal(aPrincipal);
aPrincipal->GetCsp(getter_AddRefs(mCSP));
nsresult rv = aPrincipal->GetCsp(getter_AddRefs(mCSP));
NS_ENSURE_SUCCESS(rv, rv);
if (mCSP) {
mCSP->GetAllowsEval(&mReportCSPViolations, &mEvalAllowed);
@ -1854,8 +1855,8 @@ WorkerLoadInfo::SetPrincipalOnMainThread(nsIPrincipal* aPrincipal,
bool hasReferrerPolicy = false;
uint32_t rp = mozilla::net::RP_Unset;
nsresult rv = mCSP->GetReferrerPolicy(&rp, &hasReferrerPolicy);
NS_ENSURE_SUCCESS_VOID(rv);
rv = mCSP->GetReferrerPolicy(&rp, &hasReferrerPolicy);
NS_ENSURE_SUCCESS(rv, rv);
if (hasReferrerPolicy) {
mReferrerPolicy = static_cast<net::ReferrerPolicy>(rp);
@ -1870,8 +1871,10 @@ WorkerLoadInfo::SetPrincipalOnMainThread(nsIPrincipal* aPrincipal,
mPrincipalInfo = new PrincipalInfo();
mOriginAttributes = nsContentUtils::GetOriginAttributes(aLoadGroup);
MOZ_ALWAYS_SUCCEEDS(
PrincipalToPrincipalInfo(aPrincipal, mPrincipalInfo));
rv = PrincipalToPrincipalInfo(aPrincipal, mPrincipalInfo);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
nsresult
@ -1953,8 +1956,7 @@ WorkerLoadInfo::SetPrincipalFromChannel(nsIChannel* aChannel)
getter_AddRefs(loadGroup));
NS_ENSURE_SUCCESS(rv, rv);
SetPrincipalOnMainThread(principal, loadGroup);
return NS_OK;
return SetPrincipalOnMainThread(principal, loadGroup);
}
#if defined(DEBUG) || !defined(RELEASE_OR_BETA)
@ -3803,11 +3805,11 @@ WorkerPrivateParent<Derived>::SetBaseURI(nsIURI* aBaseURI)
}
template <class Derived>
void
nsresult
WorkerPrivateParent<Derived>::SetPrincipalOnMainThread(nsIPrincipal* aPrincipal,
nsILoadGroup* aLoadGroup)
{
mLoadInfo.SetPrincipalOnMainThread(aPrincipal, aLoadGroup);
return mLoadInfo.SetPrincipalOnMainThread(aPrincipal, aLoadGroup);
}
template <class Derived>

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

@ -607,7 +607,7 @@ public:
return mLoadInfo.mPrincipal;
}
void
nsresult
SetPrincipalOnMainThread(nsIPrincipal* aPrincipal, nsILoadGroup* aLoadGroup);
nsresult

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

@ -278,7 +278,7 @@ struct WorkerLoadInfo
void StealFrom(WorkerLoadInfo& aOther);
void
nsresult
SetPrincipalOnMainThread(nsIPrincipal* aPrincipal, nsILoadGroup* aLoadGroup);
nsresult

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

@ -2298,6 +2298,7 @@ XMLHttpRequestMainThread::OnStopRequest(nsIRequest *request, nsISupports *ctxt,
mProgressEventSink = nullptr;
mFlagSyncLooping = false;
mRequestSentTime = 0;
// update our charset and decoder to match mResponseXML,
// before it is possibly nulled out

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

@ -124,7 +124,7 @@ const tests = [
["warn", "iso-8859-1"]
];
function do_get_file_by_line(file, charset) {
function* do_get_file_by_line(file, charset) {
dump("getting file by line for file " + file.path + "\n");
dump("using charset " + charset +"\n");
let fis = Cc["@mozilla.org/network/file-input-stream;1"].
@ -163,7 +163,7 @@ function do_run_test(checker, name, charset, todo_good, todo_bad) {
if (good.exists()) {
var good_counter = 0;
for (val in do_get_file_by_line(good, charset)) {
for (val of do_get_file_by_line(good, charset)) {
let todo = false;
good_counter++;
if (todo_good && todo_good[good_counter]) {
@ -182,7 +182,7 @@ function do_run_test(checker, name, charset, todo_good, todo_bad) {
if (bad.exists()) {
var bad_counter = 0;
for (val in do_get_file_by_line(bad, charset)) {
for (val of do_get_file_by_line(bad, charset)) {
let todo = false;
bad_counter++;
if (todo_bad && todo_bad[bad_counter]) {

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

@ -3330,7 +3330,7 @@ FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(uint32_t aInde
}
switch (aIndex) {
case ATT_LIGHTING_SURFACE_SCALE:
mSurfaceScale = aValue;
mSurfaceScale = std::fpclassify(aValue) == FP_SUBNORMAL ? 0.0 : aValue;
break;
default:
MOZ_CRASH("GFX: FilterNodeLightingSoftware::SetAttribute float");

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

@ -397,7 +397,7 @@ ProcessOrDeferMessage(HWND hwnd,
case WM_GETOBJECT: {
if (!::GetPropW(hwnd, k3rdPartyWindowProp)) {
DWORD objId = static_cast<DWORD>(lParam);
if ((objId == OBJID_CLIENT || objId == MOZOBJID_UIAROOT)) {
if (objId == OBJID_CLIENT || objId == MOZOBJID_UIAROOT) {
WNDPROC oldWndProc = (WNDPROC)GetProp(hwnd, kOldWndProcProp);
if (oldWndProc) {
return CallWindowProcW(oldWndProc, hwnd, uMsg, wParam, lParam);
@ -1021,7 +1021,7 @@ MessageChannel::WaitForSyncNotify(bool aHandleWindowsMessages)
MOZ_ASSERT(gUIThreadId, "InitUIThread was not called!");
#if defined(ACCESSIBILITY)
if ((mFlags & REQUIRE_A11Y_REENTRY)) {
if (mFlags & REQUIRE_A11Y_REENTRY) {
MOZ_ASSERT(!(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION));
return WaitForSyncNotifyWithA11yReentry();
}

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

@ -629,8 +629,6 @@ BarriersAreAllowedOnCurrentThread();
static MOZ_ALWAYS_INLINE void
ExposeGCThingToActiveJS(JS::GCCellPtr thing)
{
MOZ_ASSERT(thing.kind() != JS::TraceKind::Shape);
// GC things residing in the nursery cannot be gray: they have no mark bits.
// All live objects in the nursery are moved to tenured at the beginning of
// each GC slice, so the gray marker never sees nursery things.

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

@ -47,7 +47,6 @@ class MOZ_RAII AutoTraceSession
JS::HeapState prevState;
AutoGeckoProfilerEntry pseudoFrame;
JSRuntime::AutoProhibitActiveContextChange prohibitActiveContextChange;
};
class MOZ_RAII AutoPrepareForTracing

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

@ -631,6 +631,8 @@ class GCRuntime
void startDebugGC(JSGCInvocationKind gckind, SliceBudget& budget);
void debugGCSlice(SliceBudget& budget);
bool canChangeActiveContext(JSContext* cx);
void triggerFullGCForAtoms() {
MOZ_ASSERT(fullGCForAtomsRequested_);
fullGCForAtomsRequested_ = false;
@ -691,8 +693,6 @@ class GCRuntime
allocTask.cancel(GCParallelTask::CancelAndWait);
}
void requestMinorGC(JS::gcreason::Reason reason);
#ifdef DEBUG
bool onBackgroundThread() { return helperState.onBackgroundThread(); }
#endif // DEBUG
@ -777,9 +777,7 @@ class GCRuntime
bool areGrayBitsValid() const { return grayBitsValid; }
void setGrayBitsInvalid() { grayBitsValid = false; }
bool minorGCRequested() const { return minorGCTriggerReason != JS::gcreason::NO_REASON; }
bool majorGCRequested() const { return majorGCTriggerReason != JS::gcreason::NO_REASON; }
bool isGcNeeded() { return minorGCRequested() || majorGCRequested(); }
bool fullGCForAtomsRequested() const { return fullGCForAtomsRequested_; }
@ -940,7 +938,8 @@ class GCRuntime
void endSweepingZoneGroup();
IncrementalProgress sweepPhase(SliceBudget& sliceBudget, AutoLockForExclusiveAccess& lock);
void endSweepPhase(bool lastGC, AutoLockForExclusiveAccess& lock);
void sweepZones(FreeOp* fop, bool lastGC);
void sweepZones(FreeOp* fop, ZoneGroup* group, bool lastGC);
void sweepZoneGroups(FreeOp* fop, bool destroyingRuntime);
void decommitAllWithoutUnlocking(const AutoLockGC& lock);
void startDecommit();
void queueZonesForBackgroundSweep(ZoneList& zones);
@ -1084,9 +1083,6 @@ class GCRuntime
mozilla::Atomic<JS::gcreason::Reason, mozilla::Relaxed> majorGCTriggerReason;
public:
ActiveThreadData<JS::gcreason::Reason> minorGCTriggerReason;
private:
/* Perform full GC if rt->keepAtoms() becomes false. */
ActiveThreadData<bool> fullGCForAtomsRequested_;

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

@ -2721,7 +2721,7 @@ IsMarkedInternal(JSRuntime* rt, JSObject** thingp)
if (IsInsideNursery(*thingp)) {
MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt));
return rt->zoneGroupFromMainThread()->nursery().getForwardedPointer(thingp);
return Nursery::getForwardedPointer(thingp);
}
return IsMarkedInternalCommon(thingp);
}

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

@ -80,6 +80,13 @@ ReallocateObjectBuffer(JSContext* cx, JSObject* obj, T* oldBuffer,
return obj->zone()->pod_realloc<T>(oldBuffer, oldCount, newCount);
}
static inline void
EvictAllNurseries(JSRuntime* rt, JS::gcreason::Reason reason = JS::gcreason::EVICT_NURSERY)
{
for (ZoneGroupsIter group(rt); !group.done(); group.next())
group->evictNursery(reason);
}
} // namespace js
#endif /* gc_Nursery_inl_h */

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

@ -84,16 +84,16 @@ struct js::Nursery::Canary
#endif
inline void
js::Nursery::NurseryChunk::poisonAndInit(JSRuntime* rt, uint8_t poison)
js::Nursery::NurseryChunk::poisonAndInit(ZoneGroup* group, uint8_t poison)
{
JS_POISON(this, poison, ChunkSize);
init(rt);
init(group);
}
inline void
js::Nursery::NurseryChunk::init(JSRuntime* rt)
js::Nursery::NurseryChunk::init(ZoneGroup* group)
{
new (&trailer) gc::ChunkTrailer(rt, &rt->zoneGroupFromMainThread()->storeBuffer());
new (&trailer) gc::ChunkTrailer(group->runtime, &group->storeBuffer());
}
/* static */ inline js::Nursery::NurseryChunk*
@ -122,6 +122,7 @@ js::Nursery::Nursery(ZoneGroup* group)
, profileThreshold_(0)
, enableProfiling_(false)
, reportTenurings_(0)
, minorGCTriggerReason_(JS::gcreason::NO_REASON)
, minorGcCount_(0)
, freeMallocedBuffersTask(nullptr)
, sweepActions_(nullptr)
@ -484,12 +485,9 @@ js::TenuringTracer::TenuringTracer(JSRuntime* rt, Nursery* nursery)
{
}
void
/* static */ void
js::Nursery::printProfileHeader()
{
if (!enableProfiling_)
return;
fprintf(stderr, "MinorGC: Reason PRate Size ");
#define PRINT_HEADER(name, text) \
fprintf(stderr, " %6s", text);
@ -515,6 +513,15 @@ js::Nursery::printTotalProfileTimes()
}
}
void
js::Nursery::maybeClearProfileDurations()
{
if (enableProfiling_) {
for (auto& duration : profileDurations_)
duration = mozilla::TimeDuration();
}
}
inline void
js::Nursery::startProfile(ProfileKey key)
{
@ -573,6 +580,7 @@ js::Nursery::collect(JS::gcreason::Reason reason)
rt->gc.stats().beginNurseryCollection(reason);
TraceMinorGCStart();
maybeClearProfileDurations();
startProfile(ProfileKey::Total);
// The hazard analysis thinks doCollection can invalidate pointers in
@ -642,7 +650,7 @@ js::Nursery::collect(JS::gcreason::Reason reason)
if (reportTenurings_) {
for (auto& entry : tenureCounts.entries) {
if (entry.count >= reportTenurings_) {
fprintf(stderr, "%d x ", entry.count);
fprintf(stderr, " %d x ", entry.count);
entry.group->print();
}
}
@ -703,7 +711,7 @@ js::Nursery::doCollection(JS::gcreason::Reason reason,
maybeStartProfile(ProfileKey::MarkDebugger);
{
gcstats::AutoPhase ap(rt->gc.stats(), gcstats::PHASE_MARK_ROOTS);
Debugger::traceAll(&mover);
Debugger::traceAllForMovingGC(&mover);
}
maybeEndProfile(ProfileKey::MarkDebugger);
@ -829,7 +837,7 @@ js::Nursery::sweep()
#ifdef JS_GC_ZEAL
/* Poison the nursery contents so touching a freed object will crash. */
for (unsigned i = 0; i < numChunks(); i++)
chunk(i).poisonAndInit(zoneGroup()->runtime, JS_SWEPT_NURSERY_PATTERN);
chunk(i).poisonAndInit(zoneGroup(), JS_SWEPT_NURSERY_PATTERN);
if (zoneGroup()->runtime->hasZealMode(ZealMode::GenerationalGC)) {
/* Only reset the alloc point when we are close to the end. */
@ -840,7 +848,7 @@ js::Nursery::sweep()
{
#ifdef JS_CRASH_DIAGNOSTICS
for (unsigned i = 0; i < numChunks(); ++i)
chunk(i).poisonAndInit(zoneGroup()->runtime, JS_SWEPT_NURSERY_PATTERN);
chunk(i).poisonAndInit(zoneGroup(), JS_SWEPT_NURSERY_PATTERN);
#endif
setCurrentChunk(0);
}
@ -874,7 +882,7 @@ js::Nursery::setCurrentChunk(unsigned chunkno)
currentChunk_ = chunkno;
position_ = chunk(chunkno).start();
currentEnd_ = chunk(chunkno).end();
chunk(chunkno).poisonAndInit(zoneGroup()->runtime, JS_FRESH_NURSERY_PATTERN);
chunk(chunkno).poisonAndInit(zoneGroup(), JS_FRESH_NURSERY_PATTERN);
}
MOZ_ALWAYS_INLINE void
@ -974,7 +982,7 @@ js::Nursery::updateNumChunksLocked(unsigned newCount,
}
chunks_[i] = NurseryChunk::fromChunk(newChunk);
chunk(i).poisonAndInit(zoneGroup()->runtime, JS_FRESH_NURSERY_PATTERN);
chunk(i).poisonAndInit(zoneGroup(), JS_FRESH_NURSERY_PATTERN);
}
}

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

@ -260,7 +260,7 @@ class Nursery
#endif
/* Print header line for profile times. */
void printProfileHeader();
static void printProfileHeader();
/* Print total profile times on shutdown. */
void printTotalProfileTimes();
@ -268,6 +268,14 @@ class Nursery
void* addressOfCurrentEnd() const { return (void*)&currentEnd_; }
void* addressOfPosition() const { return (void*)&position_; }
void requestMinorGC(JS::gcreason::Reason reason) const;
bool minorGCRequested() const { return minorGCTriggerReason_ != JS::gcreason::NO_REASON; }
JS::gcreason::Reason minorGCTriggerReason() const { return minorGCTriggerReason_; }
void clearMinorGCRequest() { minorGCTriggerReason_ = JS::gcreason::NO_REASON; }
bool enableProfiling() const { return enableProfiling_; }
private:
/* The amount of space in the mapped nursery available to allocations. */
static const size_t NurseryChunkUsableSize = gc::ChunkSize - sizeof(gc::ChunkTrailer);
@ -276,8 +284,8 @@ class Nursery
char data[NurseryChunkUsableSize];
gc::ChunkTrailer trailer;
static NurseryChunk* fromChunk(gc::Chunk* chunk);
void init(JSRuntime* rt);
void poisonAndInit(JSRuntime* rt, uint8_t poison);
void init(ZoneGroup* group);
void poisonAndInit(ZoneGroup* group, uint8_t poison);
uintptr_t start() const { return uintptr_t(&data); }
uintptr_t end() const { return uintptr_t(&trailer); }
gc::Chunk* toChunk(JSRuntime* rt);
@ -317,6 +325,13 @@ class Nursery
/* Report ObjectGroups with at lest this many instances tenured. */
int64_t reportTenurings_;
/*
* Whether and why a collection of this nursery has been requested. This is
* mutable as it is set by the store buffer, which otherwise cannot modify
* anything in the nursery.
*/
mutable JS::gcreason::Reason minorGCTriggerReason_;
/* Profiling data. */
enum class ProfileKey
@ -455,6 +470,7 @@ class Nursery
void minimizeAllocableSpace();
/* Profile recording and printing. */
void maybeClearProfileDurations();
void startProfile(ProfileKey key);
void endProfile(ProfileKey key);
void maybeStartProfile(ProfileKey key);

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

@ -86,9 +86,9 @@ JS::RootingContext::traceStackRoots(JSTracer* trc)
}
static void
TraceExactStackRoots(JSRuntime* rt, JSTracer* trc)
TraceExactStackRoots(const CooperatingContext& target, JSTracer* trc)
{
TlsContext.get()->traceStackRoots(trc);
target.context()->traceStackRoots(trc);
}
template <typename T, TraceFunction<T> TraceFn = TraceNullableRoot>
@ -201,18 +201,16 @@ AutoGCRooter::trace(JSTracer* trc)
}
/* static */ void
AutoGCRooter::traceAll(JSTracer* trc)
AutoGCRooter::traceAll(const CooperatingContext& target, JSTracer* trc)
{
for (AutoGCRooter* gcr = TlsContext.get()->autoGCRooters_; gcr; gcr = gcr->down)
for (AutoGCRooter* gcr = target.context()->autoGCRooters_; gcr; gcr = gcr->down)
gcr->trace(trc);
}
/* static */ void
AutoGCRooter::traceAllWrappers(JSTracer* trc)
AutoGCRooter::traceAllWrappers(const CooperatingContext& target, JSTracer* trc)
{
JSContext* cx = TlsContext.get();
for (AutoGCRooter* gcr = cx->autoGCRooters_; gcr; gcr = gcr->down) {
for (AutoGCRooter* gcr = target.context()->autoGCRooters_; gcr; gcr = gcr->down) {
if (gcr->tag_ == WRAPVECTOR || gcr->tag_ == WRAPPER)
gcr->trace(trc);
}
@ -290,7 +288,8 @@ js::TraceRuntime(JSTracer* trc)
MOZ_ASSERT(!trc->isMarkingTracer());
JSRuntime* rt = trc->runtime();
rt->zoneGroupFromMainThread()->evictNursery();
for (ZoneGroupsIter group(rt); !group.done(); group.next())
group->evictNursery();
AutoPrepareForTracing prep(TlsContext.get(), WithAtoms);
gcstats::AutoPhase ap(rt->gc.stats(), gcstats::PHASE_TRACE_HEAP);
rt->gc.traceRuntime(trc, prep.session().lock);
@ -330,18 +329,18 @@ js::gc::GCRuntime::traceRuntimeCommon(JSTracer* trc, TraceOrMarkRuntime traceOrM
// Trace active interpreter and JIT stack roots.
TraceInterpreterActivations(cx, target, trc);
jit::TraceJitActivations(cx, target, trc);
}
// Trace legacy C stack roots.
AutoGCRooter::traceAll(trc);
// Trace legacy C stack roots.
AutoGCRooter::traceAll(target, trc);
// Trace C stack roots.
TraceExactStackRoots(target, trc);
}
for (RootRange r = rootsHash.ref().all(); !r.empty(); r.popFront()) {
const RootEntry& entry = r.front();
TraceRoot(trc, entry.key(), entry.value());
}
// Trace C stack roots.
TraceExactStackRoots(rt, trc);
}
// Trace runtime global roots.
@ -353,9 +352,9 @@ js::gc::GCRuntime::traceRuntimeCommon(JSTracer* trc, TraceOrMarkRuntime traceOrM
// Trace the shared Intl data.
rt->traceSharedIntlData(trc);
// Trace anything in the current thread's context. Ignore other JSContexts,
// as these will only refer to ZoneGroups which we are not collecting/tracing.
TlsContext.get()->trace(trc);
// Trace anything in any of the cooperating threads.
for (const CooperatingContext& target : rt->cooperatingContexts())
target.context()->trace(trc);
// Trace all compartment roots, but not the compartment itself; it is
// traced via the parent pointer if traceRoots actually traces anything.

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

@ -1405,7 +1405,12 @@ Statistics::maybePrintProfileHeaders()
static int printedHeader = 0;
if ((printedHeader++ % 200) == 0) {
printProfileHeader();
runtime->zoneGroupFromMainThread()->nursery().printProfileHeader();
for (ZoneGroupsIter group(runtime); !group.done(); group.next()) {
if (group->nursery().enableProfiling()) {
Nursery::printProfileHeader();
break;
}
}
}
}

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

@ -91,7 +91,7 @@ StoreBuffer::setAboutToOverflow()
aboutToOverflow_ = true;
runtime_->gc.stats().count(gcstats::STAT_STOREBUFFER_OVERFLOW);
}
runtime_->gc.requestMinorGC(JS::gcreason::FULL_STORE_BUFFER);
nursery_.requestMinorGC(JS::gcreason::FULL_STORE_BUFFER);
}
void

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

@ -327,7 +327,7 @@ gc::GCRuntime::endVerifyPreBarriers()
MOZ_ASSERT(!JS::IsGenerationalGCEnabled(rt));
AutoPrepareForTracing prep(rt->contextFromMainThread(), SkipAtoms);
AutoPrepareForTracing prep(rt->activeContextFromOwnThread(), SkipAtoms);
bool compartmentCreated = false;

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

@ -337,7 +337,7 @@ Zone::notifyObservingDebuggers()
{
for (CompartmentsInZoneIter comps(this); !comps.done(); comps.next()) {
JSRuntime* rt = runtimeFromAnyThread();
RootedGlobalObject global(rt->contextFromMainThread(), comps->unsafeUnbarrieredMaybeGlobal());
RootedGlobalObject global(TlsContext.get(), comps->unsafeUnbarrieredMaybeGlobal());
if (!global)
continue;

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

@ -882,11 +882,11 @@ struct GCManagedDeletePolicy
{
void operator()(const T* ptr) {
if (ptr) {
JSRuntime* rt = TlsContext.get()->runtime();
if (CurrentThreadCanAccessRuntime(rt) && rt->zoneGroupFromMainThread()->nursery().isEnabled()) {
Zone* zone = ptr->zone();
if (zone && zone->group()->nursery().isEnabled()) {
// The object may contain nursery pointers and must only be
// destroyed after a minor GC.
rt->zoneGroupFromMainThread()->callAfterMinorGC(deletePtr, const_cast<T*>(ptr));
zone->group()->callAfterMinorGC(deletePtr, const_cast<T*>(ptr));
} else {
// The object cannot contain nursery pointers so can be
// destroyed immediately.

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

@ -748,8 +748,7 @@ BaselineCacheIRCompiler::emitStoreSlotShared(bool isFixed)
masm.storeValue(val, slot);
}
if (cx_->nursery().exists())
BaselineEmitPostWriteBarrierSlot(masm, obj, val, scratch1, LiveGeneralRegisterSet(), cx_);
BaselineEmitPostWriteBarrierSlot(masm, obj, val, scratch1, LiveGeneralRegisterSet(), cx_);
return true;
}
@ -862,8 +861,7 @@ BaselineCacheIRCompiler::emitAddAndStoreSlotShared(CacheOp op)
masm.storeValue(val, slot);
}
if (cx_->nursery().exists())
BaselineEmitPostWriteBarrierSlot(masm, obj, val, scratch1, LiveGeneralRegisterSet(), cx_);
BaselineEmitPostWriteBarrierSlot(masm, obj, val, scratch1, LiveGeneralRegisterSet(), cx_);
return true;
}
@ -1011,6 +1009,137 @@ BaselineCacheIRCompiler::emitStoreTypedObjectScalarProperty()
return true;
}
bool
BaselineCacheIRCompiler::emitStoreDenseElement()
{
ObjOperandId objId = reader.objOperandId();
Int32OperandId indexId = reader.int32OperandId();
// Allocate the fixed registers first. These need to be fixed for
// callTypeUpdateIC.
AutoScratchRegister scratch(allocator, masm, R1.scratchReg());
ValueOperand val = allocator.useFixedValueRegister(masm, reader.valOperandId(), R0);
Register obj = allocator.useRegister(masm, objId);
Register index = allocator.useRegister(masm, indexId);
FailurePath* failure;
if (!addFailurePath(&failure))
return false;
// Load obj->elements in scratch.
masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratch);
// Bounds check.
Address initLength(scratch, ObjectElements::offsetOfInitializedLength());
masm.branch32(Assembler::BelowOrEqual, initLength, index, failure->label());
// Hole check.
BaseObjectElementIndex element(scratch, index);
masm.branchTestMagic(Assembler::Equal, element, failure->label());
// Perform a single test to see if we either need to convert double
// elements, clone the copy on write elements in the object or fail
// due to a frozen element.
Label noSpecialHandling;
Address elementsFlags(scratch, ObjectElements::offsetOfFlags());
masm.branchTest32(Assembler::Zero, elementsFlags,
Imm32(ObjectElements::CONVERT_DOUBLE_ELEMENTS |
ObjectElements::COPY_ON_WRITE |
ObjectElements::FROZEN),
&noSpecialHandling);
// Fail if we need to clone copy on write elements or to throw due
// to a frozen element.
masm.branchTest32(Assembler::NonZero, elementsFlags,
Imm32(ObjectElements::COPY_ON_WRITE |
ObjectElements::FROZEN),
failure->label());
// We need to convert int32 values being stored into doubles. Note that
// double arrays are only created by IonMonkey, so if we have no FP support
// Ion is disabled and there should be no double arrays.
if (cx_->runtime()->jitSupportsFloatingPoint) {
// It's fine to convert the value in place in Baseline. We can't do
// this in Ion.
masm.convertInt32ValueToDouble(val);
} else {
masm.assumeUnreachable("There shouldn't be double arrays when there is no FP support.");
}
masm.bind(&noSpecialHandling);
// Call the type update IC. After this everything must be infallible as we
// don't save all registers here.
LiveGeneralRegisterSet saveRegs;
saveRegs.add(obj);
saveRegs.add(index);
saveRegs.add(val);
if (!callTypeUpdateIC(obj, val, scratch, saveRegs))
return false;
// Perform the store. Reload obj->elements because callTypeUpdateIC
// used the scratch register.
masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratch);
EmitPreBarrier(masm, element, MIRType::Value);
masm.storeValue(val, element);
BaselineEmitPostWriteBarrierSlot(masm, obj, val, scratch, LiveGeneralRegisterSet(), cx_);
return true;
}
bool
BaselineCacheIRCompiler::emitStoreUnboxedArrayElement()
{
ObjOperandId objId = reader.objOperandId();
Int32OperandId indexId = reader.int32OperandId();
// Allocate the fixed registers first. These need to be fixed for
// callTypeUpdateIC.
AutoScratchRegister scratch(allocator, masm, R1.scratchReg());
ValueOperand val = allocator.useFixedValueRegister(masm, reader.valOperandId(), R0);
JSValueType elementType = reader.valueType();
Register obj = allocator.useRegister(masm, objId);
Register index = allocator.useRegister(masm, indexId);
FailurePath* failure;
if (!addFailurePath(&failure))
return false;
// Bounds check.
Address initLength(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength());
masm.load32(initLength, scratch);
masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), scratch);
masm.branch32(Assembler::BelowOrEqual, scratch, index, failure->label());
// Call the type update IC. After this everything must be infallible as we
// don't save all registers here.
if (elementType == JSVAL_TYPE_OBJECT) {
LiveGeneralRegisterSet saveRegs;
saveRegs.add(obj);
saveRegs.add(index);
saveRegs.add(val);
if (!callTypeUpdateIC(obj, val, scratch, saveRegs))
return false;
}
// Load obj->elements.
masm.loadPtr(Address(obj, UnboxedArrayObject::offsetOfElements()), scratch);
// Note that the storeUnboxedProperty call here is infallible, as the
// IR emitter is responsible for guarding on |val|'s type.
BaseIndex element(scratch, index, ScaleFromElemWidth(UnboxedTypeSize(elementType)));
EmitUnboxedPreBarrierForBaseline(masm, element, elementType);
masm.storeUnboxedProperty(element, elementType,
ConstantOrRegister(TypedOrValueRegister(val)),
/* failure = */ nullptr);
if (UnboxedTypeNeedsPostBarrier(elementType))
BaselineEmitPostWriteBarrierSlot(masm, obj, val, scratch, LiveGeneralRegisterSet(), cx_);
return true;
}
typedef bool (*CallNativeSetterFn)(JSContext*, HandleFunction, HandleObject, HandleValue);
static const VMFunction CallNativeSetterInfo =
FunctionInfo<CallNativeSetterFn>(CallNativeSetter, "CallNativeSetter");

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

@ -325,7 +325,6 @@ DoTypeUpdateFallback(JSContext* cx, BaselineFrame* frame, ICUpdatedStub* stub, H
AddTypePropertyId(cx, group, maybeSingleton, id, value);
break;
}
case ICStub::SetElem_DenseOrUnboxedArray:
case ICStub::SetElem_DenseOrUnboxedArrayAdd: {
id = JSID_VOID;
AddTypePropertyId(cx, obj, id, value);
@ -953,20 +952,10 @@ static bool
DenseOrUnboxedArraySetElemStubExists(JSContext* cx, ICStub::Kind kind,
ICSetElem_Fallback* stub, HandleObject obj)
{
MOZ_ASSERT(kind == ICStub::SetElem_DenseOrUnboxedArray ||
kind == ICStub::SetElem_DenseOrUnboxedArrayAdd);
MOZ_ASSERT(kind == ICStub::SetElem_DenseOrUnboxedArrayAdd);
for (ICStubConstIterator iter = stub->beginChainConst(); !iter.atEnd(); iter++) {
if (kind == ICStub::SetElem_DenseOrUnboxedArray && iter->isSetElem_DenseOrUnboxedArray()) {
ICSetElem_DenseOrUnboxedArray* nstub = iter->toSetElem_DenseOrUnboxedArray();
if (obj->maybeShape() == nstub->shape() &&
JSObject::getGroup(cx, obj) == nstub->group())
{
return true;
}
}
if (kind == ICStub::SetElem_DenseOrUnboxedArrayAdd && iter->isSetElem_DenseOrUnboxedArrayAdd()) {
if (iter->isSetElem_DenseOrUnboxedArrayAdd()) {
ICSetElem_DenseOrUnboxedArrayAdd* nstub = iter->toSetElem_DenseOrUnboxedArrayAdd();
if (JSObject::getGroup(cx, obj) == nstub->group() &&
SetElemAddHasSameShapes(nstub, obj))
@ -1104,6 +1093,17 @@ DoSetElemFallback(JSContext* cx, BaselineFrame* frame, ICSetElem_Fallback* stub_
if (!obj)
return false;
RootedShape oldShape(cx, obj->maybeShape());
RootedObjectGroup oldGroup(cx, JSObject::getGroup(cx, obj));
if (!oldGroup)
return false;
if (obj->is<UnboxedPlainObject>()) {
MOZ_ASSERT(!oldShape);
if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando())
oldShape = expando->lastProperty();
}
bool isTemporarilyUnoptimizable = false;
bool attached = false;
@ -1132,17 +1132,6 @@ DoSetElemFallback(JSContext* cx, BaselineFrame* frame, ICSetElem_Fallback* stub_
}
}
RootedShape oldShape(cx, obj->maybeShape());
RootedObjectGroup oldGroup(cx, JSObject::getGroup(cx, obj));
if (!oldGroup)
return false;
if (obj->is<UnboxedPlainObject>()) {
MOZ_ASSERT(!oldShape);
if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando())
oldShape = expando->lastProperty();
}
// Check the old capacity
uint32_t oldCapacity = 0;
uint32_t oldInitLength = 0;
@ -1242,25 +1231,6 @@ DoSetElemFallback(JSContext* cx, BaselineFrame* frame, ICSetElem_Fallback* stub_
return false;
}
stub->addNewStub(newStub);
} else if (!addingCase &&
!DenseOrUnboxedArraySetElemStubExists(cx,
ICStub::SetElem_DenseOrUnboxedArray,
stub, obj))
{
JitSpew(JitSpew_BaselineIC,
" Generating SetElem_DenseOrUnboxedArray stub (shape=%p, group=%p)",
shape.get(), group.get());
ICSetElem_DenseOrUnboxedArray::Compiler compiler(cx, shape, group);
ICUpdatedStub* newStub = compiler.getStub(compiler.getStubSpace(outerScript));
if (!newStub)
return false;
if (compiler.needsUpdateStubs() &&
!newStub->addUpdateStubForValue(cx, outerScript, obj, JSID_VOIDHANDLE, rhs))
{
return false;
}
stub->addNewStub(newStub);
}
}
@ -1388,163 +1358,6 @@ EmitUnboxedPreBarrierForBaseline(MacroAssembler &masm, const BaseIndex& address,
MOZ_ASSERT(!UnboxedTypeNeedsPreBarrier(type));
}
bool
ICSetElem_DenseOrUnboxedArray::Compiler::generateStubCode(MacroAssembler& masm)
{
MOZ_ASSERT(engine_ == Engine::Baseline);
// R0 = object
// R1 = key
// Stack = { ... rhs-value, <return-addr>? }
Label failure, failurePopR0;
masm.branchTestObject(Assembler::NotEqual, R0, &failure);
masm.branchTestInt32(Assembler::NotEqual, R1, &failure);
AllocatableGeneralRegisterSet regs(availableGeneralRegs(2));
Register scratchReg = regs.takeAny();
// Unbox R0 and guard on its group and, if this is a native access, its shape.
Register obj = masm.extractObject(R0, ExtractTemp0);
masm.loadPtr(Address(ICStubReg, ICSetElem_DenseOrUnboxedArray::offsetOfGroup()),
scratchReg);
masm.branchTestObjGroup(Assembler::NotEqual, obj, scratchReg, &failure);
if (unboxedType_ == JSVAL_TYPE_MAGIC) {
masm.loadPtr(Address(ICStubReg, ICSetElem_DenseOrUnboxedArray::offsetOfShape()),
scratchReg);
masm.branchTestObjShape(Assembler::NotEqual, obj, scratchReg, &failure);
}
if (needsUpdateStubs()) {
// Stow both R0 and R1 (object and key)
// But R0 and R1 still hold their values.
EmitStowICValues(masm, 2);
// Stack is now: { ..., rhs-value, object-value, key-value, maybe?-RET-ADDR }
// Load rhs-value into R0
masm.loadValue(Address(masm.getStackPointer(), 2 * sizeof(Value) + ICStackValueOffset), R0);
// Call the type-update stub.
if (!callTypeUpdateIC(masm, sizeof(Value)))
return false;
// Unstow R0 and R1 (object and key)
EmitUnstowICValues(masm, 2);
// Restore object.
obj = masm.extractObject(R0, ExtractTemp0);
// Trigger post barriers here on the value being written. Fields which
// objects can be written to also need update stubs.
masm.Push(R1);
masm.loadValue(Address(masm.getStackPointer(), sizeof(Value) + ICStackValueOffset), R1);
LiveGeneralRegisterSet saveRegs;
saveRegs.add(R0);
saveRegs.addUnchecked(obj);
saveRegs.add(ICStubReg);
BaselineEmitPostWriteBarrierSlot(masm, obj, R1, scratchReg, saveRegs, cx);
masm.Pop(R1);
}
// Unbox key.
Register key = masm.extractInt32(R1, ExtractTemp1);
if (unboxedType_ == JSVAL_TYPE_MAGIC) {
// Set element on a native object.
// Load obj->elements in scratchReg.
masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratchReg);
// Bounds check.
Address initLength(scratchReg, ObjectElements::offsetOfInitializedLength());
masm.branch32(Assembler::BelowOrEqual, initLength, key, &failure);
// Hole check.
BaseIndex element(scratchReg, key, TimesEight);
masm.branchTestMagic(Assembler::Equal, element, &failure);
// Perform a single test to see if we either need to convert double
// elements, clone the copy on write elements in the object or fail
// due to a frozen element.
Label noSpecialHandling;
Address elementsFlags(scratchReg, ObjectElements::offsetOfFlags());
masm.branchTest32(Assembler::Zero, elementsFlags,
Imm32(ObjectElements::CONVERT_DOUBLE_ELEMENTS |
ObjectElements::COPY_ON_WRITE |
ObjectElements::FROZEN),
&noSpecialHandling);
// Fail if we need to clone copy on write elements or to throw due
// to a frozen element.
masm.branchTest32(Assembler::NonZero, elementsFlags,
Imm32(ObjectElements::COPY_ON_WRITE |
ObjectElements::FROZEN),
&failure);
// Failure is not possible now. Free up registers.
regs.add(R0);
regs.add(R1);
regs.takeUnchecked(obj);
regs.takeUnchecked(key);
Address valueAddr(masm.getStackPointer(), ICStackValueOffset);
// We need to convert int32 values being stored into doubles. In this case
// the heap typeset is guaranteed to contain both int32 and double, so it's
// okay to store a double. Note that double arrays are only created by
// IonMonkey, so if we have no floating-point support Ion is disabled and
// there should be no double arrays.
if (cx->runtime()->jitSupportsFloatingPoint)
masm.convertInt32ValueToDouble(valueAddr, regs.getAny(), &noSpecialHandling);
else
masm.assumeUnreachable("There shouldn't be double arrays when there is no FP support.");
masm.bind(&noSpecialHandling);
ValueOperand tmpVal = regs.takeAnyValue();
masm.loadValue(valueAddr, tmpVal);
EmitPreBarrier(masm, element, MIRType::Value);
masm.storeValue(tmpVal, element);
} else {
// Set element on an unboxed array.
// Bounds check.
Address initLength(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength());
masm.load32(initLength, scratchReg);
masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), scratchReg);
masm.branch32(Assembler::BelowOrEqual, scratchReg, key, &failure);
// Load obj->elements.
masm.loadPtr(Address(obj, UnboxedArrayObject::offsetOfElements()), scratchReg);
// Compute the address being written to.
BaseIndex address(scratchReg, key, ScaleFromElemWidth(UnboxedTypeSize(unboxedType_)));
EmitUnboxedPreBarrierForBaseline(masm, address, unboxedType_);
Address valueAddr(masm.getStackPointer(), ICStackValueOffset + sizeof(Value));
masm.Push(R0);
masm.loadValue(valueAddr, R0);
masm.storeUnboxedProperty(address, unboxedType_,
ConstantOrRegister(TypedOrValueRegister(R0)), &failurePopR0);
masm.Pop(R0);
}
EmitReturnFromIC(masm);
if (failurePopR0.used()) {
// Failure case: restore the value of R0
masm.bind(&failurePopR0);
masm.popValue(R0);
}
// Failure case - jump to next stub
masm.bind(&failure);
EmitStubGuardFailure(masm);
return true;
}
//
// SetElem_DenseOrUnboxedArrayAdd
//
@ -5279,12 +5092,6 @@ ICTypeUpdate_ObjectGroup::ICTypeUpdate_ObjectGroup(JitCode* stubCode, ObjectGrou
group_(group)
{ }
ICSetElem_DenseOrUnboxedArray::ICSetElem_DenseOrUnboxedArray(JitCode* stubCode, Shape* shape, ObjectGroup* group)
: ICUpdatedStub(SetElem_DenseOrUnboxedArray, stubCode),
shape_(shape),
group_(group)
{ }
ICSetElem_DenseOrUnboxedArrayAdd::ICSetElem_DenseOrUnboxedArrayAdd(JitCode* stubCode, ObjectGroup* group,
size_t protoChainDepth)
: ICUpdatedStub(SetElem_DenseOrUnboxedArrayAdd, stubCode),

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

@ -457,67 +457,6 @@ class ICSetElem_Fallback : public ICFallbackStub
};
};
class ICSetElem_DenseOrUnboxedArray : public ICUpdatedStub
{
friend class ICStubSpace;
GCPtrShape shape_; // null for unboxed arrays
GCPtrObjectGroup group_;
ICSetElem_DenseOrUnboxedArray(JitCode* stubCode, Shape* shape, ObjectGroup* group);
public:
static size_t offsetOfShape() {
return offsetof(ICSetElem_DenseOrUnboxedArray, shape_);
}
static size_t offsetOfGroup() {
return offsetof(ICSetElem_DenseOrUnboxedArray, group_);
}
GCPtrShape& shape() {
return shape_;
}
GCPtrObjectGroup& group() {
return group_;
}
class Compiler : public ICStubCompiler {
RootedShape shape_;
RootedObjectGroup group_;
JSValueType unboxedType_;
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
public:
virtual int32_t getKey() const {
return static_cast<int32_t>(engine_) |
(static_cast<int32_t>(kind) << 1) |
(static_cast<int32_t>(unboxedType_) << 17);
}
Compiler(JSContext* cx, Shape* shape, HandleObjectGroup group)
: ICStubCompiler(cx, ICStub::SetElem_DenseOrUnboxedArray, Engine::Baseline),
shape_(cx, shape),
group_(cx, group),
unboxedType_(shape
? JSVAL_TYPE_MAGIC
: group->unboxedLayoutDontCheckGeneration().elementType())
{}
ICUpdatedStub* getStub(ICStubSpace* space) {
ICSetElem_DenseOrUnboxedArray* stub =
newStub<ICSetElem_DenseOrUnboxedArray>(space, getStubCode(), shape_, group_);
if (!stub || !stub->initUpdatingChain(cx, space))
return nullptr;
return stub;
}
bool needsUpdateStubs() {
return unboxedType_ == JSVAL_TYPE_MAGIC || unboxedType_ == JSVAL_TYPE_OBJECT;
}
};
};
template <size_t ProtoChainDepth> class ICSetElem_DenseOrUnboxedArrayAddImpl;
class ICSetElem_DenseOrUnboxedArrayAdd : public ICUpdatedStub

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

@ -51,7 +51,6 @@ namespace jit {
_(GetElem_Fallback) \
\
_(SetElem_Fallback) \
_(SetElem_DenseOrUnboxedArray) \
_(SetElem_DenseOrUnboxedArrayAdd) \
_(SetElem_TypedArray) \
\

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

@ -57,20 +57,6 @@ SetElemICInspector::sawOOBTypedArrayWrite() const
return false;
}
bool
SetElemICInspector::sawDenseWrite() const
{
if (!icEntry_)
return false;
// Check for a SetElem_DenseAdd or SetElem_Dense stub.
for (ICStub* stub = icEntry_->firstStub(); stub; stub = stub->next()) {
if (stub->isSetElem_DenseOrUnboxedArrayAdd() || stub->isSetElem_DenseOrUnboxedArray())
return true;
}
return false;
}
bool
SetElemICInspector::sawTypedArrayWrite() const
{

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

@ -37,7 +37,6 @@ class SetElemICInspector : public ICInspector
bool sawOOBDenseWrite() const;
bool sawOOBTypedArrayWrite() const;
bool sawDenseWrite() const;
bool sawTypedArrayWrite() const;
};

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

@ -1906,6 +1906,17 @@ SetPropIRGenerator::tryAttachStub()
return true;
if (tryAttachSetArrayLength(obj, objId, id, rhsValId))
return true;
return false;
}
uint32_t index;
Int32OperandId indexId;
if (maybeGuardInt32Index(idVal_, setElemKeyValueId(), &index, &indexId)) {
if (tryAttachSetDenseElement(obj, objId, index, indexId, rhsValId))
return true;
if (tryAttachSetUnboxedArrayElement(obj, objId, index, indexId, rhsValId))
return true;
return false;
}
return false;
}
@ -2225,6 +2236,58 @@ SetPropIRGenerator::tryAttachSetArrayLength(HandleObject obj, ObjOperandId objId
return true;
}
bool
SetPropIRGenerator::tryAttachSetDenseElement(HandleObject obj, ObjOperandId objId, uint32_t index,
Int32OperandId indexId, ValOperandId rhsId)
{
if (!obj->isNative())
return false;
NativeObject* nobj = &obj->as<NativeObject>();
if (!nobj->containsDenseElement(index) || nobj->getElementsHeader()->isFrozen())
return false;
writer.guardGroup(objId, nobj->group());
writer.guardShape(objId, nobj->shape());
writer.storeDenseElement(objId, indexId, rhsId);
writer.returnFromIC();
// Type inference uses JSID_VOID for the element types.
setUpdateStubInfo(nobj->group(), JSID_VOID);
trackAttached("SetDenseElement");
return true;
}
bool
SetPropIRGenerator::tryAttachSetUnboxedArrayElement(HandleObject obj, ObjOperandId objId, uint32_t index,
Int32OperandId indexId, ValOperandId rhsId)
{
if (!obj->is<UnboxedArrayObject>())
return false;
if (!cx_->runtime()->jitSupportsFloatingPoint)
return false;
if (index >= obj->as<UnboxedArrayObject>().initializedLength())
return false;
writer.guardGroup(objId, obj->group());
JSValueType elementType = obj->group()->unboxedLayoutDontCheckGeneration().elementType();
EmitGuardUnboxedPropertyType(writer, elementType, rhsId);
writer.storeUnboxedArrayElement(objId, indexId, rhsId, elementType);
writer.returnFromIC();
// Type inference uses JSID_VOID for the element types.
setUpdateStubInfo(obj->group(), JSID_VOID);
trackAttached("SetUnboxedArrayElement");
return true;
}
bool
SetPropIRGenerator::tryAttachAddSlotStub(HandleObjectGroup oldGroup, HandleShape oldShape)
{

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

@ -187,6 +187,8 @@ extern const char* CacheKindNames[];
_(StoreTypedObjectReferenceProperty) \
_(StoreTypedObjectScalarProperty) \
_(StoreUnboxedProperty) \
_(StoreDenseElement) \
_(StoreUnboxedArrayElement) \
_(CallNativeSetter) \
_(CallScriptedSetter) \
_(CallSetArrayLength) \
@ -648,6 +650,19 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter
addStubField(offset, StubField::Type::RawWord);
writeOperandId(rhs);
}
void storeDenseElement(ObjOperandId obj, Int32OperandId index, ValOperandId rhs) {
writeOpWithOperandId(CacheOp::StoreDenseElement, obj);
writeOperandId(index);
writeOperandId(rhs);
}
void storeUnboxedArrayElement(ObjOperandId obj, Int32OperandId index, ValOperandId rhs,
JSValueType elementType)
{
writeOpWithOperandId(CacheOp::StoreUnboxedArrayElement, obj);
writeOperandId(index);
writeOperandId(rhs);
buffer_.writeByte(uint32_t(elementType));
}
void callScriptedSetter(ObjOperandId obj, JSFunction* setter, ValOperandId rhs) {
writeOpWithOperandId(CacheOp::CallScriptedSetter, obj);
addStubField(uintptr_t(setter), StubField::Type::JSObject);
@ -1021,6 +1036,11 @@ class MOZ_RAII SetPropIRGenerator : public IRGenerator
bool tryAttachSetArrayLength(HandleObject obj, ObjOperandId objId, HandleId id,
ValOperandId rhsId);
bool tryAttachSetDenseElement(HandleObject obj, ObjOperandId objId, uint32_t index,
Int32OperandId indexId, ValOperandId rhsId);
bool tryAttachSetUnboxedArrayElement(HandleObject obj, ObjOperandId objId, uint32_t index,
Int32OperandId indexId, ValOperandId rhsId);
void trackAttached(const char* name);
public:

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

@ -324,7 +324,7 @@ ExecutableAllocator::reprotectPool(JSRuntime* rt, ExecutablePool* pool, Protecti
{
// Don't race with reprotectAll called from the signal handler.
MOZ_ASSERT(rt->jitRuntime()->preventBackedgePatching() ||
rt->unsafeContextFromAnyThread()->handlingJitInterrupt());
rt->activeContext()->handlingJitInterrupt());
char* start = pool->m_allocation.pages;
if (!ReprotectRegion(start, pool->m_freePtr - start, protection))

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

@ -226,7 +226,7 @@ class ExecutableAllocator
#elif defined(JS_SIMULATOR_ARM) || defined(JS_SIMULATOR_MIPS32) || defined(JS_SIMULATOR_MIPS64)
static void cacheFlush(void* code, size_t size)
{
js::jit::Simulator::FlushICache(code, size);
js::jit::SimulatorProcess::FlushICache(code, size);
}
#elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
static void cacheFlush(void* code, size_t size)

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

@ -885,6 +885,18 @@ IonCacheIRCompiler::emitStoreTypedObjectScalarProperty()
MOZ_CRASH("Baseline-specific op");
}
bool
IonCacheIRCompiler::emitStoreDenseElement()
{
MOZ_CRASH("Baseline-specific op");
}
bool
IonCacheIRCompiler::emitStoreUnboxedArrayElement()
{
MOZ_CRASH("Baseline-specific op");
}
bool
IonCacheIRCompiler::emitCallNativeSetter()
{

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

@ -1821,6 +1821,17 @@ MacroAssembler::convertInt32ValueToDouble(const Address& address, Register scrat
storeDouble(ScratchDoubleReg, address);
}
void
MacroAssembler::convertInt32ValueToDouble(ValueOperand val)
{
Label done;
branchTestInt32(Assembler::NotEqual, val, &done);
unboxInt32(val, val.scratchReg());
convertInt32ToDouble(val.scratchReg(), ScratchDoubleReg);
boxDouble(ScratchDoubleReg, val);
bind(&done);
}
void
MacroAssembler::convertValueToFloatingPoint(ValueOperand value, FloatRegister output,
Label* fail, MIRType outputType)

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

@ -1960,6 +1960,8 @@ class MacroAssembler : public MacroAssemblerSpecific
bool compilingWasm);
void convertInt32ValueToDouble(const Address& address, Register scratch, Label* done);
void convertInt32ValueToDouble(ValueOperand val);
void convertValueToDouble(ValueOperand value, FloatRegister output, Label* fail) {
convertValueToFloatingPoint(value, output, fail, MIRType::Double);
}

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

@ -267,12 +267,6 @@ ICStub::trace(JSTracer* trc)
TraceEdge(trc, &callStub->expectedStr(), "baseline-callstringsplit-str");
break;
}
case ICStub::SetElem_DenseOrUnboxedArray: {
ICSetElem_DenseOrUnboxedArray* setElemStub = toSetElem_DenseOrUnboxedArray();
TraceNullableEdge(trc, &setElemStub->shape(), "baseline-getelem-dense-shape");
TraceEdge(trc, &setElemStub->group(), "baseline-setelem-dense-group");
break;
}
case ICStub::SetElem_DenseOrUnboxedArrayAdd: {
ICSetElem_DenseOrUnboxedArrayAdd* setElemStub = toSetElem_DenseOrUnboxedArrayAdd();
TraceEdge(trc, &setElemStub->group(), "baseline-setelem-denseadd-group");
@ -657,6 +651,9 @@ BaselineEmitPostWriteBarrierSlot(MacroAssembler& masm, Register obj, ValueOperan
Register scratch, LiveGeneralRegisterSet saveRegs,
JSContext* cx)
{
if (!cx->nursery().exists())
return;
Label skipBarrier;
masm.branchPtrInNurseryChunk(Assembler::Equal, obj, scratch, &skipBarrier);
masm.branchValueIsNurseryObject(Assembler::NotEqual, val, scratch, &skipBarrier);

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

@ -365,28 +365,16 @@ class AutoLockSimulatorCache : public LockGuard<Mutex>
using Base = LockGuard<Mutex>;
public:
explicit AutoLockSimulatorCache(Simulator* sim)
: Base(sim->cacheLock_)
, sim_(sim)
{
MOZ_ASSERT(sim_->cacheLockHolder_.isNothing());
#ifdef DEBUG
sim_->cacheLockHolder_ = mozilla::Some(ThisThread::GetId());
#endif
}
~AutoLockSimulatorCache() {
MOZ_ASSERT(sim_->cacheLockHolder_.isSome());
#ifdef DEBUG
sim_->cacheLockHolder_.reset();
#endif
}
private:
Simulator* const sim_;
explicit AutoLockSimulatorCache()
: Base(SimulatorProcess::singleton_->cacheLock_)
{}
};
bool Simulator::ICacheCheckingEnabled = false;
mozilla::Atomic<size_t, mozilla::ReleaseAcquire>
SimulatorProcess::ICacheCheckingDisableCount(1); // Checking is disabled by default.
mozilla::Atomic<bool, mozilla::ReleaseAcquire>
SimulatorProcess::cacheInvalidatedBySignalHandler_(false);
SimulatorProcess* SimulatorProcess::singleton_ = nullptr;
int64_t Simulator::StopSimAt = -1L;
@ -402,9 +390,6 @@ Simulator::Create(JSContext* cx)
return nullptr;
}
if (getenv("ARM_SIM_ICACHE_CHECKS"))
Simulator::ICacheCheckingEnabled = true;
char* stopAtStr = getenv("ARM_SIM_STOP_AT");
int64_t stopAt;
if (stopAtStr && sscanf(stopAtStr, "%lld", &stopAt) == 1) {
@ -998,9 +983,9 @@ AllOnOnePage(uintptr_t start, int size)
}
static CachePage*
GetCachePageLocked(Simulator::ICacheMap& i_cache, void* page)
GetCachePageLocked(SimulatorProcess::ICacheMap& i_cache, void* page)
{
Simulator::ICacheMap::AddPtr p = i_cache.lookupForAdd(page);
SimulatorProcess::ICacheMap::AddPtr p = i_cache.lookupForAdd(page);
if (p)
return p->value();
@ -1014,7 +999,7 @@ GetCachePageLocked(Simulator::ICacheMap& i_cache, void* page)
// Flush from start up to and not including start + size.
static void
FlushOnePageLocked(Simulator::ICacheMap& i_cache, intptr_t start, int size)
FlushOnePageLocked(SimulatorProcess::ICacheMap& i_cache, intptr_t start, int size)
{
MOZ_ASSERT(size <= CachePage::kPageSize);
MOZ_ASSERT(AllOnOnePage(start, size - 1));
@ -1029,7 +1014,7 @@ FlushOnePageLocked(Simulator::ICacheMap& i_cache, intptr_t start, int size)
}
static void
FlushICacheLocked(Simulator::ICacheMap& i_cache, void* start_addr, size_t size)
FlushICacheLocked(SimulatorProcess::ICacheMap& i_cache, void* start_addr, size_t size)
{
intptr_t start = reinterpret_cast<intptr_t>(start_addr);
int intra_line = (start & CachePage::kLineMask);
@ -1049,14 +1034,14 @@ FlushICacheLocked(Simulator::ICacheMap& i_cache, void* start_addr, size_t size)
FlushOnePageLocked(i_cache, start, size);
}
void
Simulator::checkICacheLocked(Simulator::ICacheMap& i_cache, SimInstruction* instr)
/* static */ void
SimulatorProcess::checkICacheLocked(SimInstruction* instr)
{
intptr_t address = reinterpret_cast<intptr_t>(instr);
void* page = reinterpret_cast<void*>(address & (~CachePage::kPageMask));
void* line = reinterpret_cast<void*>(address & (~CachePage::kLineMask));
int offset = (address & CachePage::kPageMask);
CachePage* cache_page = GetCachePageLocked(i_cache, page);
CachePage* cache_page = GetCachePageLocked(icache(), page);
char* cache_valid_byte = cache_page->validityByte(offset);
bool cache_hit = (*cache_valid_byte == CachePage::LINE_VALID);
char* cached_line = cache_page->cachedData(offset & ~CachePage::kLineMask);
@ -1074,7 +1059,7 @@ Simulator::checkICacheLocked(Simulator::ICacheMap& i_cache, SimInstruction* inst
// It is safe for the signal to arrive during the !cache_hit path, since it
// will be cleared the next time this function is called.
if (cacheInvalidatedBySignalHandler_) {
i_cache.clear();
icache().clear();
cacheInvalidatedBySignalHandler_ = false;
return;
}
@ -1089,13 +1074,13 @@ Simulator::checkICacheLocked(Simulator::ICacheMap& i_cache, SimInstruction* inst
}
HashNumber
Simulator::ICacheHasher::hash(const Lookup& l)
SimulatorProcess::ICacheHasher::hash(const Lookup& l)
{
return static_cast<uint32_t>(reinterpret_cast<uintptr_t>(l)) >> 2;
}
bool
Simulator::ICacheHasher::match(const Key& k, const Lookup& l)
SimulatorProcess::ICacheHasher::match(const Key& k, const Lookup& l)
{
MOZ_ASSERT((reinterpret_cast<intptr_t>(k) & CachePage::kPageMask) == 0);
MOZ_ASSERT((reinterpret_cast<intptr_t>(l) & CachePage::kPageMask) == 0);
@ -1109,22 +1094,18 @@ Simulator::setLastDebuggerInput(char* input)
lastDebuggerInput_ = input;
}
void
Simulator::FlushICache(void* start_addr, size_t size)
/* static */ void
SimulatorProcess::FlushICache(void* start_addr, size_t size)
{
JitSpewCont(JitSpew_CacheFlush, "[%p %" PRIxSIZE "]", start_addr, size);
if (Simulator::ICacheCheckingEnabled) {
Simulator* sim = Simulator::Current();
AutoLockSimulatorCache als(sim);
js::jit::FlushICacheLocked(sim->icache(), start_addr, size);
if (!ICacheCheckingDisableCount) {
AutoLockSimulatorCache als;
js::jit::FlushICacheLocked(icache(), start_addr, size);
}
}
Simulator::Simulator(JSContext* cx)
: cx_(cx),
cacheLock_(mutexid::SimulatorCacheLock)
: cx_(cx)
{
// Set up simulator support first. Some of this information is needed to
// setup the architecture state.
@ -1142,7 +1123,6 @@ Simulator::Simulator(JSContext* cx)
single_stepping_ = false;
single_step_callback_ = nullptr;
single_step_callback_arg_ = nullptr;
cacheInvalidatedBySignalHandler_ = false;
skipCalleeSavedRegsCheck = false;
// Set up architecture state.
@ -1178,7 +1158,6 @@ Simulator::Simulator(JSContext* cx)
lastDebuggerInput_ = nullptr;
redirection_ = nullptr;
exclusiveMonitorHeld_ = false;
exclusiveMonitor_ = 0;
}
@ -1186,9 +1165,6 @@ Simulator::Simulator(JSContext* cx)
bool
Simulator::init()
{
if (!icache_.init())
return false;
// Allocate 2MB for the stack. Note that we will only use 1MB, see below.
static const size_t stackSize = 2 * 1024*1024;
stack_ = reinterpret_cast<char*>(js_malloc(stackSize));
@ -1215,19 +1191,21 @@ Simulator::init()
// offset from the svc instruction so the simulator knows what to call.
class Redirection
{
friend class Simulator;
friend class SimulatorProcess;
// sim's lock must already be held.
Redirection(void* nativeFunction, ABIFunctionType type, Simulator* sim)
Redirection(void* nativeFunction, ABIFunctionType type)
: nativeFunction_(nativeFunction),
swiInstruction_(Assembler::AL | (0xf * (1 << 24)) | kCallRtRedirected),
type_(type),
next_(nullptr)
{
next_ = sim->redirection();
if (Simulator::ICacheCheckingEnabled)
FlushICacheLocked(sim->icache(), addressOfSwiInstruction(), SimInstruction::kInstrSize);
sim->setRedirection(this);
next_ = SimulatorProcess::redirection();
if (!SimulatorProcess::ICacheCheckingDisableCount) {
FlushICacheLocked(SimulatorProcess::icache(), addressOfSwiInstruction(),
SimInstruction::kInstrSize);
}
SimulatorProcess::setRedirection(this);
}
public:
@ -1236,11 +1214,9 @@ class Redirection
ABIFunctionType type() const { return type_; }
static Redirection* Get(void* nativeFunction, ABIFunctionType type) {
Simulator* sim = Simulator::Current();
AutoLockSimulatorCache als;
AutoLockSimulatorCache als(sim);
Redirection* current = sim->redirection();
Redirection* current = SimulatorProcess::redirection();
for (; current != nullptr; current = current->next_) {
if (current->nativeFunction_ == nativeFunction) {
MOZ_ASSERT(current->type() == type);
@ -1252,7 +1228,7 @@ class Redirection
Redirection* redir = (Redirection*)js_malloc(sizeof(Redirection));
if (!redir)
oomUnsafe.crash("Simulator redirection");
new(redir) Redirection(nativeFunction, type, sim);
new(redir) Redirection(nativeFunction, type);
return redir;
}
@ -1272,6 +1248,15 @@ class Redirection
Simulator::~Simulator()
{
js_free(stack_);
}
SimulatorProcess::SimulatorProcess()
: cacheLock_(mutexid::SimulatorCacheLock)
, redirection_(nullptr)
{}
SimulatorProcess::~SimulatorProcess()
{
Redirection* r = redirection_;
while (r) {
Redirection* next = r->next_;
@ -1280,6 +1265,15 @@ Simulator::~Simulator()
}
}
bool
SimulatorProcess::init()
{
if (getenv("ARM_SIM_ICACHE_CHECKS"))
ICacheCheckingDisableCount = 0;
return icache_.init();
}
/* static */ void*
Simulator::RedirectNativeFunction(void* nativeFunction, ABIFunctionType type)
{
@ -4660,9 +4654,9 @@ Simulator::decodeSpecialCondition(SimInstruction* instr)
void
Simulator::instructionDecode(SimInstruction* instr)
{
if (Simulator::ICacheCheckingEnabled) {
AutoLockSimulatorCache als(this);
checkICacheLocked(icache(), instr);
if (!SimulatorProcess::ICacheCheckingDisableCount) {
AutoLockSimulatorCache als;
SimulatorProcess::checkICacheLocked(instr);
}
pc_modified_ = false;
@ -4931,7 +4925,9 @@ Simulator::call(uint8_t* entry, int argument_count, ...)
Simulator*
Simulator::Current()
{
return TlsContext.get()->runtime()->unsafeContextFromAnyThread()->simulator();
JSContext* cx = TlsContext.get();
MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
return cx->simulator();
}
} // namespace jit

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

@ -71,11 +71,9 @@ const uint32_t kVFPRoundingModeMask = 3 << 22;
typedef int32_t Instr;
class SimInstruction;
// Per thread simulator state.
class Simulator
{
friend class Redirection;
friend class AutoLockSimulatorCache;
public:
friend class ArmDebugger;
enum Register {
@ -347,30 +345,7 @@ class Simulator
// Executes one instruction.
void instructionDecode(SimInstruction* instr);
private:
// ICache checking.
struct ICacheHasher {
typedef void* Key;
typedef void* Lookup;
static HashNumber hash(const Lookup& l);
static bool match(const Key& k, const Lookup& l);
};
public:
typedef HashMap<void*, CachePage*, ICacheHasher, SystemAllocPolicy> ICacheMap;
public:
static bool ICacheCheckingEnabled;
static void FlushICache(void* start, size_t size);
// Jitcode may be rewritten from a signal handler, but is prevented from
// calling FlushICache() because the signal may arrive within the critical
// area of an AutoLockSimulatorCache. This flag instructs the Simulator
// to remove all cache entries the next time it checks, avoiding false negatives.
mozilla::Atomic<bool, mozilla::ReleaseAcquire> cacheInvalidatedBySignalHandler_;
void checkICacheLocked(ICacheMap& i_cache, SimInstruction* instr);
static int64_t StopSimAt;
// For testing the MoveResolver code, a MoveResolver is set up, and
@ -472,37 +447,6 @@ class Simulator
return icount_;
}
private:
// This lock creates a critical section around 'redirection_' and
// 'icache_', which are referenced both by the execution engine
// and by the off-thread compiler (see Redirection::Get in the cpp file).
Mutex cacheLock_;
#ifdef DEBUG
mozilla::Maybe<Thread::Id> cacheLockHolder_;
#endif
Redirection* redirection_;
ICacheMap icache_;
public:
ICacheMap& icache() {
// Technically we need the lock to access the innards of the
// icache, not to take its address, but the latter condition
// serves as a useful complement to the former.
MOZ_ASSERT(cacheLockHolder_.isSome());
return icache_;
}
Redirection* redirection() const {
MOZ_ASSERT(cacheLockHolder_.isSome());
return redirection_;
}
void setRedirection(js::jit::Redirection* redirection) {
MOZ_ASSERT(cacheLockHolder_.isSome());
redirection_ = redirection;
}
private:
// Exclusive access monitor
void exclusiveMonitorSet(uint64_t value);
@ -513,7 +457,81 @@ class Simulator
uint64_t exclusiveMonitor_;
};
#define JS_CHECK_SIMULATOR_RECURSION_WITH_EXTRA(cx, extra, onerror) \
// Process wide simulator state.
class SimulatorProcess
{
friend class Redirection;
friend class AutoLockSimulatorCache;
private:
// ICache checking.
struct ICacheHasher {
typedef void* Key;
typedef void* Lookup;
static HashNumber hash(const Lookup& l);
static bool match(const Key& k, const Lookup& l);
};
public:
typedef HashMap<void*, CachePage*, ICacheHasher, SystemAllocPolicy> ICacheMap;
static mozilla::Atomic<size_t, mozilla::ReleaseAcquire> ICacheCheckingDisableCount;
static void FlushICache(void* start, size_t size);
// Jitcode may be rewritten from a signal handler, but is prevented from
// calling FlushICache() because the signal may arrive within the critical
// area of an AutoLockSimulatorCache. This flag instructs the Simulator
// to remove all cache entries the next time it checks, avoiding false negatives.
static mozilla::Atomic<bool, mozilla::ReleaseAcquire> cacheInvalidatedBySignalHandler_;
static void checkICacheLocked(SimInstruction* instr);
static bool initialize() {
singleton_ = js_new<SimulatorProcess>();
return singleton_ && singleton_->init();
}
static void destroy() {
js_delete(singleton_);
singleton_ = nullptr;
}
SimulatorProcess();
~SimulatorProcess();
private:
bool init();
static SimulatorProcess* singleton_;
// This lock creates a critical section around 'redirection_' and
// 'icache_', which are referenced both by the execution engine
// and by the off-thread compiler (see Redirection::Get in the cpp file).
Mutex cacheLock_;
Redirection* redirection_;
ICacheMap icache_;
public:
static ICacheMap& icache() {
// Technically we need the lock to access the innards of the
// icache, not to take its address, but the latter condition
// serves as a useful complement to the former.
MOZ_ASSERT(singleton_->cacheLock_.ownedByCurrentThread());
return singleton_->icache_;
}
static Redirection* redirection() {
MOZ_ASSERT(singleton_->cacheLock_.ownedByCurrentThread());
return singleton_->redirection_;
}
static void setRedirection(js::jit::Redirection* redirection) {
MOZ_ASSERT(singleton_->cacheLock_.ownedByCurrentThread());
singleton_->redirection_ = redirection;
}
};
#define JS_CHECK_SIMULATOR_RECURSION_WITH_EXTRA(cx, extra, onerror) \
JS_BEGIN_MACRO \
if (cx->simulator()->overRecursedWithExtra(extra)) { \
js::ReportOverRecursed(cx); \

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

@ -32,11 +32,14 @@
#include "threading/LockGuard.h"
#include "vm/Runtime.h"
js::jit::SimulatorProcess* js::jit::SimulatorProcess::singleton_ = nullptr;
namespace vixl {
using mozilla::DebugOnly;
using js::jit::ABIFunctionType;
using js::jit::SimulatorProcess;
Simulator::Simulator(Decoder* decoder, FILE* stream)
: stream_(nullptr)
@ -46,7 +49,6 @@ Simulator::Simulator(Decoder* decoder, FILE* stream)
, stack_limit_(nullptr)
, decoder_(nullptr)
, oom_(false)
, lock_(js::mutexid::Arm64SimulatorLock)
{
this->init(decoder, stream);
}
@ -144,13 +146,13 @@ void Simulator::init(Decoder* decoder, FILE* stream) {
// time they are encountered. This warning can be silenced using
// SilenceExclusiveAccessWarning().
print_exclusive_access_warning_ = true;
redirection_ = nullptr;
}
Simulator* Simulator::Current() {
return js::TlsContext.get()->simulator();
JSContext* cx = js::TlsContext.get();
MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(cx->runtime()));
return cx->simulator();
}
@ -294,8 +296,8 @@ class AutoLockSimulatorCache : public js::LockGuard<js::Mutex>
using Base = js::LockGuard<js::Mutex>;
public:
explicit AutoLockSimulatorCache(Simulator* sim)
: Base(sim->lock_)
explicit AutoLockSimulatorCache()
: Base(SimulatorProcess::singleton_->lock_)
{
}
};
@ -311,14 +313,14 @@ class Redirection
{
friend class Simulator;
Redirection(void* nativeFunction, ABIFunctionType type, Simulator* sim)
Redirection(void* nativeFunction, ABIFunctionType type)
: nativeFunction_(nativeFunction),
type_(type),
next_(nullptr)
{
next_ = sim->redirection();
next_ = SimulatorProcess::redirection();
// TODO: Flush ICache?
sim->setRedirection(this);
SimulatorProcess::setRedirection(this);
Instruction* instr = (Instruction*)(&svcInstruction_);
vixl::Assembler::svc(instr, kCallRtRedirected);
@ -330,13 +332,12 @@ class Redirection
ABIFunctionType type() const { return type_; }
static Redirection* Get(void* nativeFunction, ABIFunctionType type) {
Simulator* sim = Simulator::Current();
AutoLockSimulatorCache alsr(sim);
AutoLockSimulatorCache alsr;
// TODO: Store srt_ in the simulator for this assertion.
// VIXL_ASSERT_IF(pt->simulator(), pt->simulator()->srt_ == srt);
Redirection* current = sim->redirection();
Redirection* current = SimulatorProcess::redirection();
for (; current != nullptr; current = current->next_) {
if (current->nativeFunction_ == nativeFunction) {
VIXL_ASSERT(current->type() == type);
@ -348,7 +349,7 @@ class Redirection
Redirection* redir = (Redirection*)js_malloc(sizeof(Redirection));
if (!redir)
oomUnsafe.crash("Simulator redirection");
new(redir) Redirection(nativeFunction, type, sim);
new(redir) Redirection(nativeFunction, type);
return redir;
}
@ -366,14 +367,6 @@ class Redirection
};
void Simulator::setRedirection(Redirection* redirection) {
redirection_ = redirection;
}
Redirection* Simulator::redirection() const {
return redirection_;
}
void* Simulator::RedirectNativeFunction(void* nativeFunction, ABIFunctionType type) {

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

@ -704,8 +704,6 @@ class SimExclusiveGlobalMonitor {
class Redirection;
class Simulator : public DecoderVisitor {
friend class AutoLockSimulatorCache;
public:
explicit Simulator(Decoder* decoder, FILE* stream = stdout);
~Simulator();
@ -720,8 +718,6 @@ class Simulator : public DecoderVisitor {
bool overRecursed(uintptr_t newsp = 0) const;
bool overRecursedWithExtra(uint32_t extra) const;
int64_t call(uint8_t* entry, int argument_count, ...);
void setRedirection(Redirection* redirection);
Redirection* redirection() const;
static void* RedirectNativeFunction(void* nativeFunction, js::jit::ABIFunctionType type);
void setGPR32Result(int32_t result);
void setGPR64Result(int64_t result);
@ -2666,12 +2662,50 @@ class Simulator : public DecoderVisitor {
bool oom() const { return oom_; }
protected:
// Moz: Synchronizes access between main thread and compilation threads.
js::Mutex lock_;
Redirection* redirection_;
mozilla::Vector<int64_t, 0, js::SystemAllocPolicy> spStack_;
};
} // namespace vixl
namespace js {
namespace jit {
class SimulatorProcess
{
public:
static SimulatorProcess* singleton_;
SimulatorProcess()
: lock_(mutexid::Arm64SimulatorLock)
, redirection_(nullptr)
{}
// Synchronizes access between main thread and compilation threads.
js::Mutex lock_;
vixl::Redirection* redirection_;
static void setRedirection(vixl::Redirection* redirection) {
MOZ_ASSERT(singleton_->lock_.ownedByCurrentThread());
singleton_->redirection_ = redirection;
}
static vixl::Redirection* redirection() {
MOZ_ASSERT(singleton_->lock_.ownedByCurrentThread());
return singleton_->redirection_;
}
static bool initialize() {
singleton_ = js_new<SimulatorProcess>();
return !!singleton_;
}
static void destroy() {
js_delete(singleton_);
singleton_ = nullptr;
}
};
} // namespace jit
} // namespace js
#endif // JS_SIMULATOR_ARM64
#endif // VIXL_A64_SIMULATOR_A64_H_

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

@ -499,28 +499,16 @@ class AutoLockSimulatorCache : public LockGuard<Mutex>
using Base = LockGuard<Mutex>;
public:
explicit AutoLockSimulatorCache(Simulator* sim)
: Base(sim->cacheLock_)
, sim_(sim)
{
MOZ_ASSERT(sim_->cacheLockHolder_.isNothing());
#ifdef DEBUG
sim_->cacheLockHolder_ = mozilla::Some(ThisThread::GetId());
#endif
}
~AutoLockSimulatorCache() {
MOZ_ASSERT(sim_->cacheLockHolder_.isSome());
#ifdef DEBUG
sim_->cacheLockHolder_.reset();
#endif
}
private:
Simulator* const sim_;
AutoLockSimulatorCache()
: Base(SimulatorProcess::singleton_->cacheLock_)
{}
};
bool Simulator::ICacheCheckingEnabled = false;
mozilla::Atomic<size_t, mozilla::ReleaseAcquire>
SimulatorProcess::ICacheCheckingDisableCount(1); // Checking is disabled by default.
mozilla::Atomic<bool, mozilla::ReleaseAcquire>
SimulatorProcess::cacheInvalidatedBySignalHandler_(false);
SimulatorProcess* SimulatorProcess::singleton_ = nullptr;
int Simulator::StopSimAt = -1;
@ -536,9 +524,6 @@ Simulator::Create(JSContext* cx)
return nullptr;
}
if (getenv("MIPS_SIM_ICACHE_CHECKS"))
Simulator::ICacheCheckingEnabled = true;
char* stopAtStr = getenv("MIPS_SIM_STOP_AT");
int64_t stopAt;
if (stopAtStr && sscanf(stopAtStr, "%lld", &stopAt) == 1) {
@ -1151,9 +1136,9 @@ Simulator::setLastDebuggerInput(char* input)
}
static CachePage*
GetCachePageLocked(Simulator::ICacheMap& i_cache, void* page)
GetCachePageLocked(SimulatorProcess::ICacheMap& i_cache, void* page)
{
Simulator::ICacheMap::AddPtr p = i_cache.lookupForAdd(page);
SimulatorProcess::ICacheMap::AddPtr p = i_cache.lookupForAdd(page);
if (p)
return p->value();
@ -1165,7 +1150,7 @@ GetCachePageLocked(Simulator::ICacheMap& i_cache, void* page)
// Flush from start up to and not including start + size.
static void
FlushOnePageLocked(Simulator::ICacheMap& i_cache, intptr_t start, int size)
FlushOnePageLocked(SimulatorProcess::ICacheMap& i_cache, intptr_t start, int size)
{
MOZ_ASSERT(size <= CachePage::kPageSize);
MOZ_ASSERT(AllOnOnePage(start, size - 1));
@ -1179,7 +1164,7 @@ FlushOnePageLocked(Simulator::ICacheMap& i_cache, intptr_t start, int size)
}
static void
FlushICacheLocked(Simulator::ICacheMap& i_cache, void* start_addr, size_t size)
FlushICacheLocked(SimulatorProcess::ICacheMap& i_cache, void* start_addr, size_t size)
{
intptr_t start = reinterpret_cast<intptr_t>(start_addr);
int intra_line = (start & CachePage::kLineMask);
@ -1200,14 +1185,14 @@ FlushICacheLocked(Simulator::ICacheMap& i_cache, void* start_addr, size_t size)
}
}
void
Simulator::checkICacheLocked(Simulator::ICacheMap& i_cache, SimInstruction* instr)
/* static */ void
SimulatorProcess::checkICacheLocked(SimInstruction* instr)
{
intptr_t address = reinterpret_cast<intptr_t>(instr);
void* page = reinterpret_cast<void*>(address & (~CachePage::kPageMask));
void* line = reinterpret_cast<void*>(address & (~CachePage::kLineMask));
int offset = (address & CachePage::kPageMask);
CachePage* cache_page = GetCachePageLocked(i_cache, page);
CachePage* cache_page = GetCachePageLocked(icache(), page);
char* cache_valid_byte = cache_page->validityByte(offset);
bool cache_hit = (*cache_valid_byte == CachePage::LINE_VALID);
char* cached_line = cache_page->cachedData(offset & ~CachePage::kLineMask);
@ -1225,7 +1210,7 @@ Simulator::checkICacheLocked(Simulator::ICacheMap& i_cache, SimInstruction* inst
// It is safe for the signal to arrive during the !cache_hit path, since it
// will be cleared the next time this function is called.
if (cacheInvalidatedBySignalHandler_) {
i_cache.clear();
icache().clear();
cacheInvalidatedBySignalHandler_ = false;
return;
}
@ -1240,32 +1225,29 @@ Simulator::checkICacheLocked(Simulator::ICacheMap& i_cache, SimInstruction* inst
}
HashNumber
Simulator::ICacheHasher::hash(const Lookup& l)
SimulatorProcess::ICacheHasher::hash(const Lookup& l)
{
return static_cast<uint32_t>(reinterpret_cast<uintptr_t>(l)) >> 2;
}
bool
Simulator::ICacheHasher::match(const Key& k, const Lookup& l)
SimulatorProcess::ICacheHasher::match(const Key& k, const Lookup& l)
{
MOZ_ASSERT((reinterpret_cast<intptr_t>(k) & CachePage::kPageMask) == 0);
MOZ_ASSERT((reinterpret_cast<intptr_t>(l) & CachePage::kPageMask) == 0);
return k == l;
}
void
Simulator::FlushICache(void* start_addr, size_t size)
/* static */ void
SimulatorProcess::FlushICache(void* start_addr, size_t size)
{
if (Simulator::ICacheCheckingEnabled) {
Simulator* sim = Simulator::Current();
AutoLockSimulatorCache als(sim);
js::jit::FlushICacheLocked(sim->icache(), start_addr, size);
if (!ICacheCheckingDisableCount) {
AutoLockSimulatorCache als;
js::jit::FlushICacheLocked(icache(), start_addr, size);
}
}
Simulator::Simulator()
: cacheLock_(mutexid::SimulatorCacheLock),
cacheInvalidatedBySignalHandler_(false)
{
// Set up simulator support first. Some of this information is needed to
// setup the architecture state.
@ -1301,16 +1283,11 @@ Simulator::Simulator()
exceptions[i] = 0;
lastDebuggerInput_ = nullptr;
redirection_ = nullptr;
}
bool
Simulator::init()
{
if (!icache_.init())
return false;
// Allocate 2MB for the stack. Note that we will only use 1MB, see below.
static const size_t stackSize = 2 * 1024 * 1024;
stack_ = static_cast<char*>(js_malloc(stackSize));
@ -1338,19 +1315,21 @@ Simulator::init()
// offset from the swi instruction so the simulator knows what to call.
class Redirection
{
friend class Simulator;
friend class SimulatorProcess;
// sim's lock must already be held.
Redirection(void* nativeFunction, ABIFunctionType type, Simulator* sim)
Redirection(void* nativeFunction, ABIFunctionType type)
: nativeFunction_(nativeFunction),
swiInstruction_(kCallRedirInstr),
type_(type),
next_(nullptr)
{
next_ = sim->redirection();
if (Simulator::ICacheCheckingEnabled)
FlushICacheLocked(sim->icache(), addressOfSwiInstruction(), SimInstruction::kInstrSize);
sim->setRedirection(this);
next_ = SimulatorProcess::redirection();
if (!SimulatorProcess::ICacheCheckingDisableCount) {
FlushICacheLocked(SimulatorProcess::icache(), addressOfSwiInstruction(),
SimInstruction::kInstrSize);
}
SimulatorProcess::setRedirection(this);
}
public:
@ -1359,11 +1338,9 @@ class Redirection
ABIFunctionType type() const { return type_; }
static Redirection* Get(void* nativeFunction, ABIFunctionType type) {
Simulator* sim = Simulator::Current();
AutoLockSimulatorCache als;
AutoLockSimulatorCache als(sim);
Redirection* current = sim->redirection();
Redirection* current = SimulatorProcess::redirection();
for (; current != nullptr; current = current->next_) {
if (current->nativeFunction_ == nativeFunction) {
MOZ_ASSERT(current->type() == type);
@ -1377,7 +1354,7 @@ class Redirection
__FILE__, __LINE__);
MOZ_CRASH();
}
new(redir) Redirection(nativeFunction, type, sim);
new(redir) Redirection(nativeFunction, type);
return redir;
}
@ -1397,6 +1374,15 @@ class Redirection
Simulator::~Simulator()
{
js_free(stack_);
}
SimulatorProcess::SimulatorProcess()
: cacheLock_(mutexid::SimulatorCacheLock)
, redirection_(nullptr)
{}
SimulatorProcess::~SimulatorProcess()
{
Redirection* r = redirection_;
while (r) {
Redirection* next = r->next_;
@ -1405,6 +1391,15 @@ Simulator::~Simulator()
}
}
bool
SimulatorProcess::init()
{
if (getenv("MIPS_SIM_ICACHE_CHECKS"))
ICacheCheckingDisableCount = 0;
return icache_.init();
}
/* static */ void*
Simulator::RedirectNativeFunction(void* nativeFunction, ABIFunctionType type)
{
@ -1416,7 +1411,9 @@ Simulator::RedirectNativeFunction(void* nativeFunction, ABIFunctionType type)
Simulator*
Simulator::Current()
{
return TlsContext.get()->runtime()->unsafeContextFromAnyThread()->simulator();
JSContext* cx = TlsContext.get();
MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
return cx->simulator();
}
// Sets the register in the architecture state. It will also deal with updating
@ -3360,9 +3357,9 @@ Simulator::decodeTypeJump(SimInstruction* instr)
void
Simulator::instructionDecode(SimInstruction* instr)
{
if (Simulator::ICacheCheckingEnabled) {
AutoLockSimulatorCache als(this);
checkICacheLocked(icache(), instr);
if (!SimulatorProcess::ICacheCheckingDisableCount) {
AutoLockSimulatorCache als;
SimulatorProcess::checkICacheLocked(instr);
}
pc_modified_ = false;

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

@ -104,10 +104,9 @@ const uint32_t kMaxStopCode = 127;
typedef uint32_t Instr;
class SimInstruction;
// Per thread simulator state.
class Simulator {
friend class Redirection;
friend class MipsDebugger;
friend class AutoLockSimulatorCache;
public:
// Registers are declared in order. See "See MIPS Run Linux" chapter 2.
@ -214,8 +213,6 @@ class Simulator {
// Debugger input.
void setLastDebuggerInput(char* input);
char* lastDebuggerInput() { return lastDebuggerInput_; }
// ICache checking.
static void FlushICache(void* start, size_t size);
// Returns true if pc register contains one of the 'SpecialValues' defined
// below (bad_ra, end_sim_pc).
@ -365,6 +362,13 @@ class Simulator {
char* desc_;
};
StopCountAndDesc watchedStops_[kNumOfWatchedStops];
};
// Process wide simulator state.
class SimulatorProcess
{
friend class Redirection;
friend class AutoLockSimulatorCache;
private:
// ICache checking.
@ -378,44 +382,59 @@ class Simulator {
public:
typedef HashMap<void*, CachePage*, ICacheHasher, SystemAllocPolicy> ICacheMap;
private:
// This lock creates a critical section around 'redirection_' and
// 'icache_', which are referenced both by the execution engine
// and by the off-thread compiler (see Redirection::Get in the cpp file).
Mutex cacheLock_;
#ifdef DEBUG
mozilla::Maybe<Thread::Id> cacheLockHolder_;
#endif
static mozilla::Atomic<size_t, mozilla::ReleaseAcquire> ICacheCheckingDisableCount;
static void FlushICache(void* start, size_t size);
Redirection* redirection_;
ICacheMap icache_;
private:
// Jitcode may be rewritten from a signal handler, but is prevented from
// calling FlushICache() because the signal may arrive within the critical
// area of an AutoLockSimulatorCache. This flag instructs the Simulator
// to remove all cache entries the next time it checks, avoiding false negatives.
mozilla::Atomic<bool, mozilla::ReleaseAcquire> cacheInvalidatedBySignalHandler_;
static mozilla::Atomic<bool, mozilla::ReleaseAcquire> cacheInvalidatedBySignalHandler_;
void checkICacheLocked(ICacheMap& i_cache, SimInstruction* instr);
static void checkICacheLocked(SimInstruction* instr);
static bool initialize() {
singleton_ = js_new<SimulatorProcess>();
return singleton_ && singleton_->init();
}
static void destroy() {
js_delete(singleton_);
singleton_ = nullptr;
}
SimulatorProcess();
~SimulatorProcess();
private:
bool init();
static SimulatorProcess* singleton_;
// This lock creates a critical section around 'redirection_' and
// 'icache_', which are referenced both by the execution engine
// and by the off-thread compiler (see Redirection::Get in the cpp file).
Mutex cacheLock_;
Redirection* redirection_;
ICacheMap icache_;
public:
ICacheMap& icache() {
static ICacheMap& icache() {
// Technically we need the lock to access the innards of the
// icache, not to take its address, but the latter condition
// serves as a useful complement to the former.
MOZ_ASSERT(cacheLockHolder_.isSome());
return icache_;
MOZ_ASSERT(singleton_->cacheLock_.ownedByCurrentThread());
return singleton_->icache_;
}
Redirection* redirection() const {
MOZ_ASSERT(cacheLockHolder_.isSome());
return redirection_;
static Redirection* redirection() {
MOZ_ASSERT(singleton_->cacheLock_.ownedByCurrentThread());
return singleton_->redirection_;
}
void setRedirection(js::jit::Redirection* redirection) {
MOZ_ASSERT(cacheLockHolder_.isSome());
redirection_ = redirection;
static void setRedirection(js::jit::Redirection* redirection) {
MOZ_ASSERT(singleton_->cacheLock_.ownedByCurrentThread());
singleton_->redirection_ = redirection;
}
};

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

@ -531,28 +531,16 @@ class AutoLockSimulatorCache : public LockGuard<Mutex>
using Base = LockGuard<Mutex>;
public:
explicit AutoLockSimulatorCache(Simulator* sim)
: Base(sim->cacheLock_)
, sim_(sim)
{
MOZ_ASSERT(sim_->cacheLockHolder_.isNothing());
#ifdef DEBUG
sim_->cacheLockHolder_ = mozilla::Some(ThisThread::GetId());
#endif
}
~AutoLockSimulatorCache() {
MOZ_ASSERT(sim_->cacheLockHolder_.isSome());
#ifdef DEBUG
sim_->cacheLockHolder_.reset();
#endif
}
private:
Simulator* const sim_;
explicit AutoLockSimulatorCache()
: Base(SimulatorProcess::singleton_->cacheLock_)
{}
};
bool Simulator::ICacheCheckingEnabled = false;
mozilla::Atomic<size_t, mozilla::ReleaseAcquire>
SimulatorProcess::ICacheCheckingDisableCount(1); // Checking is disabled by default.
mozilla::Atomic<bool, mozilla::ReleaseAcquire>
SimulatorProcess::cacheInvalidatedBySignalHandler_(false);
SimulatorProcess* SimulatorProcess::singleton_ = nullptr;
int64_t Simulator::StopSimAt = -1;
@ -568,9 +556,6 @@ Simulator::Create(JSContext* cx)
return nullptr;
}
if (getenv("MIPS_SIM_ICACHE_CHECKS"))
Simulator::ICacheCheckingEnabled = true;
int64_t stopAt;
char* stopAtStr = getenv("MIPS_SIM_STOP_AT");
if (stopAtStr && sscanf(stopAtStr, "%" PRIi64, &stopAt) == 1) {
@ -1159,9 +1144,9 @@ Simulator::setLastDebuggerInput(char* input)
}
static CachePage*
GetCachePageLocked(Simulator::ICacheMap& i_cache, void* page)
GetCachePageLocked(SimulatorProcess::ICacheMap& i_cache, void* page)
{
Simulator::ICacheMap::AddPtr p = i_cache.lookupForAdd(page);
SimulatorProcess::ICacheMap::AddPtr p = i_cache.lookupForAdd(page);
if (p)
return p->value();
@ -1173,7 +1158,7 @@ GetCachePageLocked(Simulator::ICacheMap& i_cache, void* page)
// Flush from start up to and not including start + size.
static void
FlushOnePageLocked(Simulator::ICacheMap& i_cache, intptr_t start, int size)
FlushOnePageLocked(SimulatorProcess::ICacheMap& i_cache, intptr_t start, int size)
{
MOZ_ASSERT(size <= CachePage::kPageSize);
MOZ_ASSERT(AllOnOnePage(start, size - 1));
@ -1187,7 +1172,7 @@ FlushOnePageLocked(Simulator::ICacheMap& i_cache, intptr_t start, int size)
}
static void
FlushICacheLocked(Simulator::ICacheMap& i_cache, void* start_addr, size_t size)
FlushICacheLocked(SimulatorProcess::ICacheMap& i_cache, void* start_addr, size_t size)
{
intptr_t start = reinterpret_cast<intptr_t>(start_addr);
int intra_line = (start & CachePage::kLineMask);
@ -1207,14 +1192,14 @@ FlushICacheLocked(Simulator::ICacheMap& i_cache, void* start_addr, size_t size)
FlushOnePageLocked(i_cache, start, size);
}
void
Simulator::checkICacheLocked(Simulator::ICacheMap& i_cache, SimInstruction* instr)
/* static */ void
SimulatorProcess::checkICacheLocked(SimInstruction* instr)
{
intptr_t address = reinterpret_cast<intptr_t>(instr);
void* page = reinterpret_cast<void*>(address & (~CachePage::kPageMask));
void* line = reinterpret_cast<void*>(address & (~CachePage::kLineMask));
int offset = (address & CachePage::kPageMask);
CachePage* cache_page = GetCachePageLocked(i_cache, page);
CachePage* cache_page = GetCachePageLocked(icache(), page);
char* cache_valid_byte = cache_page->validityByte(offset);
bool cache_hit = (*cache_valid_byte == CachePage::LINE_VALID);
char* cached_line = cache_page->cachedData(offset & ~CachePage::kLineMask);
@ -1232,7 +1217,7 @@ Simulator::checkICacheLocked(Simulator::ICacheMap& i_cache, SimInstruction* inst
// It is safe for the signal to arrive during the !cache_hit path, since it
// will be cleared the next time this function is called.
if (cacheInvalidatedBySignalHandler_) {
i_cache.clear();
icache().clear();
cacheInvalidatedBySignalHandler_ = false;
return;
}
@ -1247,32 +1232,29 @@ Simulator::checkICacheLocked(Simulator::ICacheMap& i_cache, SimInstruction* inst
}
HashNumber
Simulator::ICacheHasher::hash(const Lookup& l)
SimulatorProcess::ICacheHasher::hash(const Lookup& l)
{
return U32(reinterpret_cast<uintptr_t>(l)) >> 2;
}
bool
Simulator::ICacheHasher::match(const Key& k, const Lookup& l)
SimulatorProcess::ICacheHasher::match(const Key& k, const Lookup& l)
{
MOZ_ASSERT((reinterpret_cast<intptr_t>(k) & CachePage::kPageMask) == 0);
MOZ_ASSERT((reinterpret_cast<intptr_t>(l) & CachePage::kPageMask) == 0);
return k == l;
}
void
Simulator::FlushICache(void* start_addr, size_t size)
/* static */ void
SimulatorProcess::FlushICache(void* start_addr, size_t size)
{
if (Simulator::ICacheCheckingEnabled) {
Simulator* sim = Simulator::Current();
AutoLockSimulatorCache als(sim);
js::jit::FlushICacheLocked(sim->icache(), start_addr, size);
if (!ICacheCheckingDisableCount) {
AutoLockSimulatorCache als;
js::jit::FlushICacheLocked(icache(), start_addr, size);
}
}
Simulator::Simulator()
: cacheLock_(mutexid::SimulatorCacheLock),
cacheInvalidatedBySignalHandler_(false)
{
// Set up simulator support first. Some of this information is needed to
// setup the architecture state.
@ -1309,16 +1291,11 @@ Simulator::Simulator()
exceptions[i] = 0;
lastDebuggerInput_ = nullptr;
redirection_ = nullptr;
}
bool
Simulator::init()
{
if (!icache_.init())
return false;
// Allocate 2MB for the stack. Note that we will only use 1MB, see below.
static const size_t stackSize = 2 * 1024 * 1024;
stack_ = static_cast<char*>(js_malloc(stackSize));
@ -1346,19 +1323,21 @@ Simulator::init()
// offset from the swi instruction so the simulator knows what to call.
class Redirection
{
friend class Simulator;
friend class SimulatorProcess;
// sim's lock must already be held.
Redirection(void* nativeFunction, ABIFunctionType type, Simulator* sim)
Redirection(void* nativeFunction, ABIFunctionType type)
: nativeFunction_(nativeFunction),
swiInstruction_(kCallRedirInstr),
type_(type),
next_(nullptr)
{
next_ = sim->redirection();
if (Simulator::ICacheCheckingEnabled)
FlushICacheLocked(sim->icache(), addressOfSwiInstruction(), SimInstruction::kInstrSize);
sim->setRedirection(this);
next_ = SimulatorProcess::redirection();
if (!SimulatorProcess::ICacheCheckingDisableCount) {
FlushICacheLocked(SimulatorProcess::icache(), addressOfSwiInstruction(),
SimInstruction::kInstrSize);
}
SimulatorProcess::setRedirection(this);
}
public:
@ -1367,11 +1346,9 @@ class Redirection
ABIFunctionType type() const { return type_; }
static Redirection* Get(void* nativeFunction, ABIFunctionType type) {
Simulator* sim = Simulator::Current();
AutoLockSimulatorCache als;
AutoLockSimulatorCache als(sim);
Redirection* current = sim->redirection();
Redirection* current = SimulatorProcess::redirection();
for (; current != nullptr; current = current->next_) {
if (current->nativeFunction_ == nativeFunction) {
MOZ_ASSERT(current->type() == type);
@ -1385,7 +1362,7 @@ class Redirection
__FILE__, __LINE__);
MOZ_CRASH();
}
new(redir) Redirection(nativeFunction, type, sim);
new(redir) Redirection(nativeFunction, type);
return redir;
}
@ -1405,6 +1382,15 @@ class Redirection
Simulator::~Simulator()
{
js_free(stack_);
}
SimulatorProcess::SimulatorProcess()
: cacheLock_(mutexid::SimulatorCacheLock)
, redirection_(nullptr)
{}
SimulatorProcess::~SimulatorProcess()
{
Redirection* r = redirection_;
while (r) {
Redirection* next = r->next_;
@ -1413,6 +1399,15 @@ Simulator::~Simulator()
}
}
bool
SimulatorProcess::init()
{
if (getenv("MIPS_SIM_ICACHE_CHECKS"))
ICacheCheckingDisableCount = 0;
return icache_.init();
}
/* static */ void*
Simulator::RedirectNativeFunction(void* nativeFunction, ABIFunctionType type)
{
@ -1424,7 +1419,9 @@ Simulator::RedirectNativeFunction(void* nativeFunction, ABIFunctionType type)
Simulator*
Simulator::Current()
{
return TlsContext.get()->runtime()->unsafeContextFromAnyThread()->simulator();
JSContext* cx = TlsContext.get();
MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
return cx->simulator();
}
// Sets the register in the architecture state. It will also deal with updating
@ -3654,9 +3651,9 @@ Simulator::decodeTypeJump(SimInstruction* instr)
void
Simulator::instructionDecode(SimInstruction* instr)
{
if (Simulator::ICacheCheckingEnabled) {
AutoLockSimulatorCache als(this);
checkICacheLocked(icache(), instr);
if (!SimulatorProcess::ICacheCheckingDisableCount) {
AutoLockSimulatorCache als;
SimulatorProcess::checkICacheLocked(instr);
}
pc_modified_ = false;

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

@ -110,10 +110,9 @@ const uint32_t kMaxStopCode = 127;
typedef uint32_t Instr;
class SimInstruction;
// Per thread simulator state.
class Simulator {
friend class Redirection;
friend class MipsDebugger;
friend class AutoLockSimulatorCache;
public:
// Registers are declared in order. See "See MIPS Run Linux" chapter 2.
@ -222,8 +221,6 @@ class Simulator {
// Debugger input.
void setLastDebuggerInput(char* input);
char* lastDebuggerInput() { return lastDebuggerInput_; }
// ICache checking.
static void FlushICache(void* start, size_t size);
// Returns true if pc register contains one of the 'SpecialValues' defined
// below (bad_ra, end_sim_pc).
@ -307,8 +304,6 @@ class Simulator {
void branchDelayInstructionDecode(SimInstruction* instr);
public:
static bool ICacheCheckingEnabled;
static int64_t StopSimAt;
// Runtime call support.
@ -381,6 +376,13 @@ class Simulator {
char* desc_;
};
StopCountAndDesc watchedStops_[kNumOfWatchedStops];
};
// Process wide simulator state.
class SimulatorProcess
{
friend class Redirection;
friend class AutoLockSimulatorCache;
private:
// ICache checking.
@ -394,48 +396,63 @@ class Simulator {
public:
typedef HashMap<void*, CachePage*, ICacheHasher, SystemAllocPolicy> ICacheMap;
private:
// This lock creates a critical section around 'redirection_' and
// 'icache_', which are referenced both by the execution engine
// and by the off-thread compiler (see Redirection::Get in the cpp file).
Mutex cacheLock_;
#ifdef DEBUG
mozilla::Maybe<Thread::Id> cacheLockHolder_;
#endif
static mozilla::Atomic<size_t, mozilla::ReleaseAcquire> ICacheCheckingDisableCount;
static void FlushICache(void* start, size_t size);
Redirection* redirection_;
ICacheMap icache_;
private:
// Jitcode may be rewritten from a signal handler, but is prevented from
// calling FlushICache() because the signal may arrive within the critical
// area of an AutoLockSimulatorCache. This flag instructs the Simulator
// to remove all cache entries the next time it checks, avoiding false negatives.
mozilla::Atomic<bool, mozilla::ReleaseAcquire> cacheInvalidatedBySignalHandler_;
static mozilla::Atomic<bool, mozilla::ReleaseAcquire> cacheInvalidatedBySignalHandler_;
void checkICacheLocked(ICacheMap& i_cache, SimInstruction* instr);
static void checkICacheLocked(SimInstruction* instr);
static bool initialize() {
singleton_ = js_new<SimulatorProcess>();
return singleton_ && singleton_->init();
}
static void destroy() {
js_delete(singleton_);
singleton_ = nullptr;
}
SimulatorProcess();
~SimulatorProcess();
private:
bool init();
static SimulatorProcess* singleton_;
// This lock creates a critical section around 'redirection_' and
// 'icache_', which are referenced both by the execution engine
// and by the off-thread compiler (see Redirection::Get in the cpp file).
Mutex cacheLock_;
Redirection* redirection_;
ICacheMap icache_;
public:
ICacheMap& icache() {
static ICacheMap& icache() {
// Technically we need the lock to access the innards of the
// icache, not to take its address, but the latter condition
// serves as a useful complement to the former.
MOZ_ASSERT(cacheLockHolder_.isSome());
return icache_;
MOZ_ASSERT(singleton_->cacheLock_.ownedByCurrentThread());
return singleton_->icache_;
}
Redirection* redirection() const {
MOZ_ASSERT(cacheLockHolder_.isSome());
return redirection_;
static Redirection* redirection() {
MOZ_ASSERT(singleton_->cacheLock_.ownedByCurrentThread());
return singleton_->redirection_;
}
void setRedirection(js::jit::Redirection* redirection) {
MOZ_ASSERT(cacheLockHolder_.isSome());
redirection_ = redirection;
static void setRedirection(js::jit::Redirection* redirection) {
MOZ_ASSERT(singleton_->cacheLock_.ownedByCurrentThread());
singleton_->redirection_ = redirection;
}
};
#define JS_CHECK_SIMULATOR_RECURSION_WITH_EXTRA(cx, extra, onerror) \
#define JS_CHECK_SIMULATOR_RECURSION_WITH_EXTRA(cx, extra, onerror) \
JS_BEGIN_MACRO \
if (cx->simulator()->overRecursedWithExtra(extra)) { \
js::ReportOverRecursed(cx); \

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

@ -28,7 +28,7 @@ struct TestStruct<js::GCPtr<T>>
{
js::GCPtr<T> wrapper;
JS::Zone* zone() { return wrapper->zone(); }
JS::Zone* zone() const { return wrapper->zone(); }
};
// Give the GCPtr version GCManagedDeletePolicy as required.

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

@ -4123,7 +4123,7 @@ JS::CancelOffThreadScript(JSContext* cx, void* token)
{
MOZ_ASSERT(cx);
MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
HelperThreadState().cancelParseTask(cx, ParseTaskKind::Script, token);
HelperThreadState().cancelParseTask(cx->runtime(), ParseTaskKind::Script, token);
}
JS_PUBLIC_API(bool)
@ -4148,7 +4148,7 @@ JS::CancelOffThreadModule(JSContext* cx, void* token)
{
MOZ_ASSERT(cx);
MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
HelperThreadState().cancelParseTask(cx, ParseTaskKind::Module, token);
HelperThreadState().cancelParseTask(cx->runtime(), ParseTaskKind::Module, token);
}
JS_PUBLIC_API(bool)
@ -4173,7 +4173,7 @@ JS::CancelOffThreadScriptDecoder(JSContext* cx, void* token)
{
MOZ_ASSERT(cx);
MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
HelperThreadState().cancelParseTask(cx, ParseTaskKind::ScriptDecode, token);
HelperThreadState().cancelParseTask(cx->runtime(), ParseTaskKind::ScriptDecode, token);
}
JS_PUBLIC_API(bool)

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

@ -886,6 +886,13 @@ JSContext::boolToResult(bool ok)
return JS::Result<>(reportedError);
}
inline JSContext*
JSRuntime::activeContextFromOwnThread()
{
MOZ_ASSERT(activeContext() == js::TlsContext.get());
return activeContext();
}
namespace js {
struct MOZ_RAII AutoResolving {

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

@ -485,7 +485,7 @@ JSContext::setCompartment(JSCompartment* comp,
comp->zone()->usedByExclusiveThread);
// Normal JSContexts cannot enter exclusive zones.
MOZ_ASSERT_IF(this == runtime()->unsafeContextFromAnyThread() && comp,
MOZ_ASSERT_IF(this == runtime()->activeContext() && comp,
!comp->zone()->usedByExclusiveThread);
// Only one thread can be in the atoms compartment at a time.

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

@ -1057,15 +1057,6 @@ AddLazyFunctionsForCompartment(JSContext* cx, AutoObjectVector& lazyFunctions, A
continue;
}
// This creates a new reference to an object that an ongoing incremental
// GC may find to be unreachable. Treat as if we're reading a weak
// reference and trigger the read barrier.
if (cx->zone()->needsIncrementalBarrier())
fun->readBarrier(fun);
// TODO: The above checks should be rolled into the cell iterator (see
// bug 1322971).
if (fun->isInterpretedLazy()) {
LazyScript* lazy = fun->lazyScriptOrNull();
if (lazy && lazy->sourceObject() && !lazy->hasUncompiledEnclosingScript()) {

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

@ -1164,8 +1164,10 @@ DumpHeapTracer::onChild(const JS::GCCellPtr& thing)
void
js::DumpHeap(JSContext* cx, FILE* fp, js::DumpHeapNurseryBehaviour nurseryBehaviour)
{
if (nurseryBehaviour == js::CollectNurseryBeforeDump)
cx->runtime()->zoneGroupFromMainThread()->evictNursery(JS::gcreason::API);
if (nurseryBehaviour == js::CollectNurseryBeforeDump) {
for (ZoneGroupsIter group(cx->runtime()); !group.done(); group.next())
group->evictNursery(JS::gcreason::API);
}
DumpHeapTracer dtrc(fp, cx);
@ -1222,7 +1224,7 @@ js::GetAnyCompartmentInZone(JS::Zone* zone)
void
JS::ObjectPtr::finalize(JSRuntime* rt)
{
if (IsIncrementalBarrierNeeded(rt->contextFromMainThread()))
if (IsIncrementalBarrierNeeded(rt->activeContextFromOwnThread()))
IncrementalObjectBarrier(value);
value = nullptr;
}

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

@ -243,6 +243,7 @@
#include "jsscriptinlines.h"
#include "gc/Heap-inl.h"
#include "gc/Nursery-inl.h"
#include "vm/GeckoProfiler-inl.h"
#include "vm/Stack-inl.h"
#include "vm/String-inl.h"
@ -826,7 +827,6 @@ GCRuntime::GCRuntime(JSRuntime* rt) :
grayBufferState(GCRuntime::GrayBufferState::Unused),
grayBitsValid(false),
majorGCTriggerReason(JS::gcreason::NO_REASON),
minorGCTriggerReason(JS::gcreason::NO_REASON),
fullGCForAtomsRequested_(false),
minorGCNumber(0),
majorGCNumber(0),
@ -920,13 +920,17 @@ GCRuntime::setZeal(uint8_t zeal, uint32_t frequency)
VerifyBarriers(rt, PreBarrierVerifier);
if (zeal == 0 && hasZealMode(ZealMode::GenerationalGC)) {
rt->zoneGroupFromMainThread()->evictNursery(JS::gcreason::DEBUG_GC);
rt->zoneGroupFromMainThread()->nursery().leaveZealMode();
for (ZoneGroupsIter group(rt); !group.done(); group.next()) {
group->evictNursery(JS::gcreason::DEBUG_GC);
group->nursery().leaveZealMode();
}
}
ZealMode zealMode = ZealMode(zeal);
if (zealMode == ZealMode::GenerationalGC)
rt->zoneGroupFromMainThread()->nursery().enterZealMode();
if (zealMode == ZealMode::GenerationalGC) {
for (ZoneGroupsIter group(rt); !group.done(); group.next())
group->nursery().enterZealMode();
}
// Zeal modes 8-10 are mutually exclusive. If we're setting one of those,
// we first reset all of them.
@ -1068,8 +1072,10 @@ void
GCRuntime::finish()
{
/* Wait for the nursery sweeping to end. */
if (rt->zoneGroupFromMainThread()->nursery().isEnabled())
rt->zoneGroupFromMainThread()->nursery().waitBackgroundFreeEnd();
for (ZoneGroupsIter group(rt); !group.done(); group.next()) {
if (group->nursery().isEnabled())
group->nursery().waitBackgroundFreeEnd();
}
/*
* Wait until the background finalization and allocation stops and the
@ -1103,7 +1109,8 @@ GCRuntime::finish()
FinishTrace();
rt->zoneGroupFromMainThread()->nursery().printTotalProfileTimes();
for (ZoneGroupsIter group(rt); !group.done(); group.next())
group->nursery().printTotalProfileTimes();
stats().printTotalProfileTimes();
}
@ -2549,7 +2556,7 @@ GCRuntime::updateRuntimePointersToRelocatedCells(AutoLockForExclusiveAccess& loc
// Mark roots to update them.
{
gcstats::AutoPhase ap2(stats(), gcstats::PHASE_MARK_ROOTS);
Debugger::traceAll(&trc);
Debugger::traceAllForMovingGC(&trc);
Debugger::traceIncomingCrossCompartmentEdges(&trc);
// Mark all gray roots, making sure we call the trace callback to get the
@ -2951,15 +2958,15 @@ GCRuntime::requestMajorGC(JS::gcreason::Reason reason)
}
void
GCRuntime::requestMinorGC(JS::gcreason::Reason reason)
Nursery::requestMinorGC(JS::gcreason::Reason reason) const
{
MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt));
MOZ_ASSERT(CurrentThreadCanAccessRuntime(zoneGroup()->runtime));
MOZ_ASSERT(!CurrentThreadIsPerformingGC());
if (minorGCRequested())
return;
minorGCTriggerReason = reason;
minorGCTriggerReason_ = reason;
// See comment in requestMajorGC.
TlsContext.get()->requestInterrupt(JSContext::RequestInterruptCanWait);
@ -2979,7 +2986,7 @@ GCRuntime::triggerGC(JS::gcreason::Reason reason)
if (JS::CurrentThreadIsHeapCollecting())
return false;
JS::PrepareForFullGC(rt->contextFromMainThread());
JS::PrepareForFullGC(rt->activeContextFromOwnThread());
requestMajorGC(reason);
return true;
}
@ -3060,7 +3067,7 @@ GCRuntime::maybeGC(Zone* zone)
#ifdef JS_GC_ZEAL
if (hasZealMode(ZealMode::Alloc) || hasZealMode(ZealMode::Poke)) {
JS::PrepareForFullGC(rt->contextFromMainThread());
JS::PrepareForFullGC(rt->activeContextFromOwnThread());
gc(GC_NORMAL, JS::gcreason::DEBUG_GC);
return;
}
@ -3478,7 +3485,7 @@ Zone::sweepCompartments(FreeOp* fop, bool keepAtleastOne, bool destroyingRuntime
}
void
GCRuntime::sweepZones(FreeOp* fop, bool destroyingRuntime)
GCRuntime::sweepZones(FreeOp* fop, ZoneGroup* group, bool destroyingRuntime)
{
MOZ_ASSERT_IF(destroyingRuntime, numActiveZoneIters == 0);
MOZ_ASSERT_IF(destroyingRuntime, arenasEmptyAtShutdown);
@ -3490,8 +3497,8 @@ GCRuntime::sweepZones(FreeOp* fop, bool destroyingRuntime)
JSZoneCallback callback = rt->destroyZoneCallback;
Zone** read = rt->zoneGroupFromMainThread()->zones().begin();
Zone** end = rt->zoneGroupFromMainThread()->zones().end();
Zone** read = group->zones().begin();
Zone** end = group->zones().end();
Zone** write = read;
while (read < end) {
@ -3529,7 +3536,30 @@ GCRuntime::sweepZones(FreeOp* fop, bool destroyingRuntime)
}
*write++ = zone;
}
rt->zoneGroupFromMainThread()->zones().shrinkTo(write - rt->zoneGroupFromMainThread()->zones().begin());
group->zones().shrinkTo(write - group->zones().begin());
}
void
GCRuntime::sweepZoneGroups(FreeOp* fop, bool destroyingRuntime)
{
ZoneGroup** read = groups.ref().begin();
ZoneGroup** end = groups.ref().end();
ZoneGroup** write = read;
while (read < end) {
ZoneGroup* group = *read++;
sweepZones(fop, group, destroyingRuntime);
// For now, the singleton zone group is not destroyed until the runtime
// itself is, bug 1323066.
if (group->zones().empty() && group != rt->zoneGroupFromMainThread()) {
MOZ_ASSERT(numActiveZoneIters == 0);
fop->delete_(group);
} else {
*write++ = group;
}
}
groups.ref().shrinkTo(write - groups.ref().begin());
}
#ifdef DEBUG
@ -3560,8 +3590,8 @@ ArenaLists::checkEmptyArenaList(AllocKind kind)
max_cells = atol(env);
for (Arena* current = arenaLists(kind).head(); current; current = current->next) {
for (ArenaCellIterUnderGC i(current); !i.done(); i.next()) {
Cell* t = i.get<Cell>();
MOZ_ASSERT(t->asTenured().isMarked(), "unmarked cells should have been finalized");
TenuredCell* t = i.getCell();
MOZ_ASSERT(t->isMarked(), "unmarked cells should have been finalized");
if (++num_live <= max_cells) {
fprintf(stderr, "ERROR: GC found live Cell %p of kind %s at shutdown\n",
t, AllocKindToAscii(kind));
@ -3580,19 +3610,21 @@ GCRuntime::purgeRuntime(AutoLockForExclusiveAccess& lock)
for (GCCompartmentsIter comp(rt); !comp.done(); comp.next())
comp->purge();
JSContext* cx = TlsContext.get();
for (const CooperatingContext& target : rt->cooperatingContexts()) {
freeUnusedLifoBlocksAfterSweeping(&target.context()->tempLifoAlloc());
target.context()->interpreterStack().purge(rt);
target.context()->frontendCollectionPool().purge();
}
freeUnusedLifoBlocksAfterSweeping(&cx->tempLifoAlloc());
cx->interpreterStack().purge(rt);
cx->frontendCollectionPool().purge();
rt->zoneGroupFromMainThread()->caches().gsnCache.purge();
rt->zoneGroupFromMainThread()->caches().envCoordinateNameCache.purge();
rt->zoneGroupFromMainThread()->caches().newObjectCache.purge();
rt->zoneGroupFromMainThread()->caches().nativeIterCache.purge();
rt->zoneGroupFromMainThread()->caches().uncompressedSourceCache.purge();
if (rt->zoneGroupFromMainThread()->caches().evalCache.initialized())
rt->zoneGroupFromMainThread()->caches().evalCache.clear();
for (ZoneGroupsIter group(rt); !group.done(); group.next()) {
group->caches().gsnCache.purge();
group->caches().envCoordinateNameCache.purge();
group->caches().newObjectCache.purge();
group->caches().nativeIterCache.purge();
group->caches().uncompressedSourceCache.purge();
if (group->caches().evalCache.initialized())
group->caches().evalCache.clear();
}
if (auto cache = rt->maybeThisRuntimeSharedImmutableStrings())
cache->purge();
@ -5508,7 +5540,6 @@ GCRuntime::IncrementalProgress
GCRuntime::compactPhase(JS::gcreason::Reason reason, SliceBudget& sliceBudget,
AutoLockForExclusiveAccess& lock)
{
MOZ_ASSERT(rt->zoneGroupFromMainThread()->nursery().isEmpty());
assertBackgroundSweepingFinished();
MOZ_ASSERT(startedCompacting);
@ -5527,6 +5558,7 @@ GCRuntime::compactPhase(JS::gcreason::Reason reason, SliceBudget& sliceBudget,
Zone* zone = zonesToMaybeCompact.ref().front();
zonesToMaybeCompact.ref().removeFront();
MOZ_ASSERT(zone->group()->nursery().isEmpty());
MOZ_ASSERT(zone->isGCFinished());
zone->setGCState(Zone::Compact);
@ -5558,10 +5590,12 @@ GCRuntime::compactPhase(JS::gcreason::Reason reason, SliceBudget& sliceBudget,
releaseRelocatedArenas(relocatedArenas);
// Clear caches that can contain cell pointers.
rt->zoneGroupFromMainThread()->caches().newObjectCache.purge();
rt->zoneGroupFromMainThread()->caches().nativeIterCache.purge();
if (rt->zoneGroupFromMainThread()->caches().evalCache.initialized())
rt->zoneGroupFromMainThread()->caches().evalCache.clear();
for (ZoneGroupsIter group(rt); !group.done(); group.next()) {
group->caches().newObjectCache.purge();
group->caches().nativeIterCache.purge();
if (group->caches().evalCache.initialized())
group->caches().evalCache.clear();
}
#ifdef DEBUG
CheckHashTablesAfterMovingGC(rt);
@ -5621,17 +5655,28 @@ HeapStateToLabel(JS::HeapState heapState)
return nullptr;
}
#ifdef DEBUG
static bool
AllNurseriesAreEmpty(JSRuntime* rt)
{
for (ZoneGroupsIter group(rt); !group.done(); group.next()) {
if (!group->nursery().isEmpty())
return false;
}
return true;
}
#endif
/* Start a new heap session. */
AutoTraceSession::AutoTraceSession(JSRuntime* rt, JS::HeapState heapState)
: lock(rt),
runtime(rt),
prevState(TlsContext.get()->heapState),
pseudoFrame(rt, HeapStateToLabel(heapState), ProfileEntry::Category::GC),
prohibitActiveContextChange(rt)
pseudoFrame(rt, HeapStateToLabel(heapState), ProfileEntry::Category::GC)
{
MOZ_ASSERT(prevState == JS::HeapState::Idle);
MOZ_ASSERT(heapState != JS::HeapState::Idle);
MOZ_ASSERT_IF(heapState == JS::HeapState::MajorCollecting, rt->zoneGroupFromMainThread()->nursery().isEmpty());
MOZ_ASSERT_IF(heapState == JS::HeapState::MajorCollecting, AllNurseriesAreEmpty(rt));
TlsContext.get()->heapState = heapState;
}
@ -5647,6 +5692,21 @@ JS::CurrentThreadHeapState()
return TlsContext.get()->heapState;
}
bool
GCRuntime::canChangeActiveContext(JSContext* cx)
{
// Threads cannot be in the middle of any operation that affects GC
// behavior when execution transfers to another thread for cooperative
// scheduling.
return cx->heapState == JS::HeapState::Idle
&& !cx->suppressGC
&& cx->allowGCBarriers
&& !cx->inUnsafeRegion
&& !cx->generationalDisabled
&& !cx->compactingDisabledCount
&& !cx->keepAtoms;
}
GCRuntime::IncrementalResult
GCRuntime::resetIncrementalGC(gc::AbortReason reason, AutoLockForExclusiveAccess& lock)
{
@ -5897,7 +5957,8 @@ GCRuntime::incrementalCollectSlice(SliceBudget& budget, JS::gcreason::Reason rea
MOZ_FALLTHROUGH;
case State::Mark:
AutoGCRooter::traceAllWrappers(&marker);
for (const CooperatingContext& target : rt->cooperatingContexts())
AutoGCRooter::traceAllWrappers(target, &marker);
/* If we needed delayed marking for gray roots, then collect until done. */
if (!hasBufferedGrayRoots()) {
@ -5982,7 +6043,7 @@ GCRuntime::incrementalCollectSlice(SliceBudget& budget, JS::gcreason::Reason rea
gcstats::AutoPhase ap2(stats(), gcstats::PHASE_DESTROY);
AutoSetThreadIsSweeping threadIsSweeping;
FreeOp fop(rt);
sweepZones(&fop, destroyingRuntime);
sweepZoneGroups(&fop, destroyingRuntime);
}
MOZ_ASSERT(!startedCompacting);
@ -6191,7 +6252,7 @@ GCRuntime::gcCycle(bool nonincrementalByAPI, SliceBudget& budget, JS::gcreason::
AutoExposeLiveCrossZoneEdges aelcze(&foundBlackGrayEdges.ref());
rt->zoneGroupFromMainThread()->evictNursery(reason);
EvictAllNurseries(rt, reason);
AutoTraceSession session(rt, JS::HeapState::MajorCollecting);
@ -6317,7 +6378,7 @@ GCRuntime::maybeDoCycleCollection()
}
double grayFraction = double(compartmentsGray) / double(compartmentsTotal);
if (grayFraction > ExcessiveGrayCompartments || compartmentsGray > LimitGrayCompartments)
callDoCycleCollectionCallback(rt->contextFromMainThread());
callDoCycleCollectionCallback(rt->activeContextFromOwnThread());
}
void
@ -6394,7 +6455,7 @@ GCRuntime::collect(bool nonincrementalByAPI, SliceBudget budget, JS::gcreason::R
bool repeatForDeadZone = false;
if (poked && cleanUpEverything) {
/* Need to re-schedule all zones for GC. */
JS::PrepareForFullGC(rt->contextFromMainThread());
JS::PrepareForFullGC(rt->activeContextFromOwnThread());
} else if (shouldRepeatForDeadZone(reason) && !wasReset) {
/*
* This code makes an extra effort to collect compartments that we
@ -6512,14 +6573,14 @@ GCRuntime::notifyDidPaint()
verifyPreBarriers();
if (hasZealMode(ZealMode::FrameGC)) {
JS::PrepareForFullGC(rt->contextFromMainThread());
JS::PrepareForFullGC(rt->activeContextFromOwnThread());
gc(GC_NORMAL, JS::gcreason::REFRESH_FRAME);
return;
}
#endif
if (isIncrementalGCInProgress() && !interFrameGC && tunables.areRefreshFrameSlicesEnabled()) {
JS::PrepareForIncrementalGC(rt->contextFromMainThread());
JS::PrepareForIncrementalGC(rt->activeContextFromOwnThread());
gcSlice(JS::gcreason::REFRESH_FRAME);
}
@ -6541,7 +6602,7 @@ GCRuntime::startDebugGC(JSGCInvocationKind gckind, SliceBudget& budget)
{
MOZ_ASSERT(!isIncrementalGCInProgress());
if (!ZonesSelected(rt))
JS::PrepareForFullGC(rt->contextFromMainThread());
JS::PrepareForFullGC(rt->activeContextFromOwnThread());
invocationKind = gckind;
collect(false, budget, JS::gcreason::DEBUG_GC);
}
@ -6551,7 +6612,7 @@ GCRuntime::debugGCSlice(SliceBudget& budget)
{
MOZ_ASSERT(isIncrementalGCInProgress());
if (!ZonesSelected(rt))
JS::PrepareForIncrementalGC(rt->contextFromMainThread());
JS::PrepareForIncrementalGC(rt->activeContextFromOwnThread());
collect(false, budget, JS::gcreason::DEBUG_GC);
}
@ -6560,7 +6621,7 @@ void
js::PrepareForDebugGC(JSRuntime* rt)
{
if (!ZonesSelected(rt))
JS::PrepareForFullGC(rt->contextFromMainThread());
JS::PrepareForFullGC(rt->activeContextFromOwnThread());
}
void
@ -6573,7 +6634,8 @@ GCRuntime::onOutOfMallocMemory()
decommitTask.join();
// Wait for background free of nursery huge slots to finish.
rt->zoneGroupFromMainThread()->nursery().waitBackgroundFreeEnd();
for (ZoneGroupsIter group(rt); !group.done(); group.next())
group->nursery().waitBackgroundFreeEnd();
AutoLockGC lock(rt);
onOutOfMallocMemory(lock);
@ -6605,7 +6667,7 @@ ZoneGroup::minorGC(JS::gcreason::Reason reason, gcstats::Phase phase)
gcstats::AutoPhase ap(runtime->gc.stats(), phase);
runtime->gc.minorGCTriggerReason = JS::gcreason::NO_REASON;
nursery().clearMinorGCRequest();
TraceLoggerThread* logger = TraceLoggerForCurrentThread();
AutoTraceLog logMinorGC(logger, TraceLogger_MinorGC);
nursery().collect(reason);
@ -6629,16 +6691,20 @@ JS::AutoDisableGenerationalGC::AutoDisableGenerationalGC(JSContext* cx)
: cx(cx)
{
if (!cx->generationalDisabled) {
cx->runtime()->zoneGroupFromMainThread()->evictNursery(JS::gcreason::API);
cx->runtime()->zoneGroupFromMainThread()->nursery().disable();
for (ZoneGroupsIter group(cx->runtime()); !group.done(); group.next()) {
group->evictNursery(JS::gcreason::API);
group->nursery().disable();
}
}
++cx->generationalDisabled;
}
JS::AutoDisableGenerationalGC::~AutoDisableGenerationalGC()
{
if (--cx->generationalDisabled == 0)
cx->runtime()->zoneGroupFromMainThread()->nursery().enable();
if (--cx->generationalDisabled == 0) {
for (ZoneGroupsIter group(cx->runtime()); !group.done(); group.next())
group->nursery().enable();
}
}
JS_PUBLIC_API(bool)
@ -6652,8 +6718,10 @@ GCRuntime::gcIfRequested()
{
// This method returns whether a major GC was performed.
if (minorGCRequested())
rt->zoneGroupFromMainThread()->minorGC(minorGCTriggerReason);
for (ZoneGroupsIter group(rt); !group.done(); group.next()) {
if (group->nursery().minorGCRequested())
group->minorGC(group->nursery().minorGCTriggerReason());
}
if (majorGCRequested()) {
if (!isIncrementalGCInProgress())
@ -6674,7 +6742,8 @@ js::gc::FinishGC(JSContext* cx)
JS::FinishIncrementalGC(cx, JS::gcreason::API);
}
cx->runtime()->zoneGroupFromMainThread()->nursery().waitBackgroundFreeEnd();
for (ZoneGroupsIter group(cx->runtime()); !group.done(); group.next())
group->nursery().waitBackgroundFreeEnd();
}
AutoPrepareForTracing::AutoPrepareForTracing(JSContext* cx, ZoneSelector selector)
@ -6740,7 +6809,7 @@ gc::MergeCompartments(JSCompartment* source, JSCompartment* target)
MOZ_ASSERT(source->creationOptions().addonIdOrNull() ==
target->creationOptions().addonIdOrNull());
JSContext* cx = source->contextFromMainThread();
JSContext* cx = source->runtimeFromMainThread()->activeContextFromOwnThread();
AutoPrepareForTracing prepare(cx, SkipAtoms);
@ -7637,7 +7706,7 @@ AutoAssertEmptyNursery::checkCondition(JSContext* cx) {
if (!noAlloc)
noAlloc.emplace();
this->cx = cx;
MOZ_ASSERT(cx->runtime()->zoneGroupFromMainThread()->nursery().isEmpty());
MOZ_ASSERT(AllNurseriesAreEmpty(cx->runtime()));
}
AutoEmptyNursery::AutoEmptyNursery(JSContext* cx)
@ -7645,7 +7714,7 @@ AutoEmptyNursery::AutoEmptyNursery(JSContext* cx)
{
MOZ_ASSERT(!cx->suppressGC);
cx->runtime()->gc.stats().suspendPhases();
cx->runtime()->zoneGroupFromMainThread()->evictNursery();
EvictAllNurseries(cx->runtime(), JS::gcreason::EVICT_NURSERY);
cx->runtime()->gc.stats().resumePhases();
checkCondition(cx);
}

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

@ -102,6 +102,12 @@ class ArenaIter
}
};
enum CellIterNeedsBarrier : uint8_t
{
CellIterDoesntNeedBarrier = 0,
CellIterMayNeedBarrier = 1
};
class ArenaCellIterImpl
{
size_t firstThingOffset;
@ -109,6 +115,8 @@ class ArenaCellIterImpl
Arena* arenaAddr;
FreeSpan span;
uint_fast16_t thing;
JS::TraceKind traceKind;
bool needsBarrier;
mozilla::DebugOnly<bool> initialized;
// Upon entry, |thing| points to any thing (free or used) and finds the
@ -128,17 +136,32 @@ class ArenaCellIterImpl
public:
ArenaCellIterImpl()
: firstThingOffset(0), thingSize(0), arenaAddr(nullptr), thing(0), initialized(false) {}
: firstThingOffset(0),
thingSize(0),
arenaAddr(nullptr),
thing(0),
traceKind(JS::TraceKind::Null),
needsBarrier(false),
initialized(false)
{}
explicit ArenaCellIterImpl(Arena* arena) : initialized(false) { init(arena); }
explicit ArenaCellIterImpl(Arena* arena, CellIterNeedsBarrier mayNeedBarrier)
: initialized(false)
{
init(arena, mayNeedBarrier);
}
void init(Arena* arena) {
void init(Arena* arena, CellIterNeedsBarrier mayNeedBarrier) {
MOZ_ASSERT(!initialized);
MOZ_ASSERT(arena);
MOZ_ASSERT_IF(!mayNeedBarrier,
CurrentThreadIsPerformingGC() || CurrentThreadIsGCSweeping());
initialized = true;
AllocKind kind = arena->getAllocKind();
firstThingOffset = Arena::firstThingOffset(kind);
thingSize = Arena::thingSize(kind);
traceKind = MapAllocToTraceKind(kind);
needsBarrier = mayNeedBarrier && !JS::CurrentThreadIsHeapCollecting();
reset(arena);
}
@ -161,11 +184,20 @@ class ArenaCellIterImpl
TenuredCell* getCell() const {
MOZ_ASSERT(!done());
return reinterpret_cast<TenuredCell*>(uintptr_t(arenaAddr) + thing);
TenuredCell* cell = reinterpret_cast<TenuredCell*>(uintptr_t(arenaAddr) + thing);
// This can result in a a new reference being created to an object that
// an ongoing incremental GC may find to be unreachable, so we may need
// a barrier here.
if (needsBarrier)
ExposeGCThingToActiveJS(JS::GCCellPtr(cell, traceKind));
return cell;
}
template<typename T> T* get() const {
MOZ_ASSERT(!done());
MOZ_ASSERT(JS::MapTypeToTraceKind<T>::kind == traceKind);
return static_cast<T*>(getCell());
}
@ -185,7 +217,7 @@ class ArenaCellIter : public ArenaCellIterImpl
{
public:
explicit ArenaCellIter(Arena* arena)
: ArenaCellIterImpl(arena)
: ArenaCellIterImpl(arena, CellIterMayNeedBarrier)
{
MOZ_ASSERT(JS::CurrentThreadIsHeapTracing());
}
@ -195,7 +227,7 @@ class ArenaCellIterUnderGC : public ArenaCellIterImpl
{
public:
explicit ArenaCellIterUnderGC(Arena* arena)
: ArenaCellIterImpl(arena)
: ArenaCellIterImpl(arena, CellIterDoesntNeedBarrier)
{
MOZ_ASSERT(CurrentThreadIsPerformingGC());
}
@ -205,7 +237,7 @@ class ArenaCellIterUnderFinalize : public ArenaCellIterImpl
{
public:
explicit ArenaCellIterUnderFinalize(Arena* arena)
: ArenaCellIterImpl(arena)
: ArenaCellIterImpl(arena, CellIterDoesntNeedBarrier)
{
MOZ_ASSERT(CurrentThreadIsGCSweeping());
}
@ -248,7 +280,7 @@ class ZoneCellIter<TenuredCell> {
rt->gc.waitBackgroundSweepEnd();
arenaIter.init(zone, kind);
if (!arenaIter.done())
cellIter.init(arenaIter.get());
cellIter.init(arenaIter.get(), CellIterMayNeedBarrier);
}
public:

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

@ -123,6 +123,8 @@ class AutoTraceSession;
class StoreBuffer;
} // namespace gc
class CooperatingContext;
inline JSCompartment* GetContextCompartment(const JSContext* cx);
inline JS::Zone* GetContextZone(const JSContext* cx);
@ -226,8 +228,8 @@ class JS_PUBLIC_API(AutoGCRooter)
/* Implemented in gc/RootMarking.cpp. */
inline void trace(JSTracer* trc);
static void traceAll(JSTracer* trc);
static void traceAllWrappers(JSTracer* trc);
static void traceAll(const js::CooperatingContext& target, JSTracer* trc);
static void traceAllWrappers(const js::CooperatingContext& target, JSTracer* trc);
protected:
AutoGCRooter * const down;

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

@ -24,7 +24,7 @@ using namespace js::gc;
WeakMapBase::WeakMapBase(JSObject* memOf, Zone* zone)
: memberOf(memOf),
zone(zone),
zone_(zone),
marked(false)
{
MOZ_ASSERT_IF(memberOf, memberOf->compartment()->zone() == zone);
@ -122,7 +122,7 @@ WeakMapBase::restoreMarkedWeakMaps(WeakMapSet& markedWeakMaps)
{
for (WeakMapSet::Range r = markedWeakMaps.all(); !r.empty(); r.popFront()) {
WeakMapBase* map = r.front();
MOZ_ASSERT(map->zone->isGCMarking());
MOZ_ASSERT(map->zone()->isGCMarking());
MOZ_ASSERT(!map->marked);
map->marked = true;
}
@ -145,7 +145,7 @@ ObjectValueMap::findZoneEdges()
if (!delegate)
continue;
Zone* delegateZone = delegate->zone();
if (delegateZone == zone || !delegateZone->isGCMarking())
if (delegateZone == zone() || !delegateZone->isGCMarking())
continue;
if (!delegateZone->gcZoneGroupEdges().put(key->zone()))
return false;

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

@ -49,6 +49,8 @@ class WeakMapBase : public mozilla::LinkedListElement<WeakMapBase>
WeakMapBase(JSObject* memOf, JS::Zone* zone);
virtual ~WeakMapBase();
Zone* zone() const { return zone_; }
// Garbage collector entry points.
// Unmark all weak maps in a zone.
@ -99,7 +101,7 @@ class WeakMapBase : public mozilla::LinkedListElement<WeakMapBase>
GCPtrObject memberOf;
// Zone containing this weak map.
JS::Zone* zone;
JS::Zone* zone_;
// Whether this object has been traced during garbage collection.
bool marked;
@ -136,7 +138,7 @@ class WeakMap : public HashMap<Key, Value, HashPolicy, RuntimeAllocPolicy>,
bool init(uint32_t len = 16) {
if (!Base::init(len))
return false;
zone->gcWeakMapList().insertFront(this);
zone()->gcWeakMapList().insertFront(this);
marked = JS::IsIncrementalGCInProgress(TlsContext.get());
return true;
}
@ -284,7 +286,7 @@ class WeakMap : public HashMap<Key, Value, HashPolicy, RuntimeAllocPolicy>,
if (!obj)
return nullptr;
MOZ_ASSERT(obj->runtimeFromMainThread() == zone->runtimeFromMainThread());
MOZ_ASSERT(obj->runtimeFromMainThread() == zone()->runtimeFromMainThread());
return obj;
}
@ -302,7 +304,7 @@ class WeakMap : public HashMap<Key, Value, HashPolicy, RuntimeAllocPolicy>,
* Check if the delegate is marked with any color to properly handle
* gray marking when the key's delegate is black and the map is gray.
*/
return delegate && gc::IsMarkedUnbarriered(zone->runtimeFromMainThread(), &delegate);
return delegate && gc::IsMarkedUnbarriered(zone()->runtimeFromMainThread(), &delegate);
}
bool keyNeedsMark(JSScript* script) const {

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

@ -13,6 +13,8 @@
#include "jscompartmentinlines.h"
#include "jsobjinlines.h"
#include "gc/Nursery-inl.h"
using namespace js;
#define PIERCE(cx, wrapper, pre, op, post) \
@ -516,7 +518,7 @@ js::NukeCrossCompartmentWrappers(JSContext* cx,
CHECK_REQUEST(cx);
JSRuntime* rt = cx->runtime();
rt->zoneGroupFromMainThread()->evictNursery(JS::gcreason::EVICT_NURSERY);
EvictAllNurseries(rt);
// Iterate through scopes looking for system cross compartment wrappers
// that point to an object that shares a global with obj.
@ -656,7 +658,7 @@ js::RecomputeWrappers(JSContext* cx, const CompartmentFilter& sourceFilter,
const CompartmentFilter& targetFilter)
{
// Drop any nursery-allocated wrappers.
cx->runtime()->zoneGroupFromMainThread()->evictNursery(JS::gcreason::EVICT_NURSERY);
EvictAllNurseries(cx->runtime());
AutoWrapperVector toRecompute(cx);
for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next()) {

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

@ -7661,7 +7661,7 @@ SetContextOptions(JSContext* cx, const OptionParser& op)
#if defined(JS_SIMULATOR_ARM)
if (op.getBoolOption("arm-sim-icache-checks"))
jit::Simulator::ICacheCheckingEnabled = true;
jit::SimulatorProcess::ICacheCheckingDisableCount = 0;
int32_t stopAt = op.getIntOption("arm-sim-stop-at");
if (stopAt >= 0)

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

@ -3044,10 +3044,12 @@ Debugger::traceIncomingCrossCompartmentEdges(JSTracer* trc)
gc::State state = rt->gc.state();
MOZ_ASSERT(state == gc::State::MarkRoots || state == gc::State::Compact);
for (Debugger* dbg : rt->zoneGroupFromMainThread()->debuggerList()) {
Zone* zone = MaybeForwarded(dbg->object.get())->zone();
if (!zone->isCollecting() || state == gc::State::Compact)
dbg->traceCrossCompartmentEdges(trc);
for (ZoneGroupsIter group(rt); !group.done(); group.next()) {
for (Debugger* dbg : group->debuggerList()) {
Zone* zone = MaybeForwarded(dbg->object.get())->zone();
if (!zone->isCollecting() || state == gc::State::Compact)
dbg->traceCrossCompartmentEdges(trc);
}
}
}
@ -3144,41 +3146,48 @@ Debugger::markIteratively(GCMarker* marker)
return markedAny;
}
/* static */ void
Debugger::traceAllForMovingGC(JSTracer* trc)
{
JSRuntime* rt = trc->runtime();
for (ZoneGroupsIter group(rt); !group.done(); group.next()) {
for (Debugger* dbg : group->debuggerList())
dbg->traceForMovingGC(trc);
}
}
/*
* Trace all debugger-owned GC things unconditionally. This is used during
* compacting GC and in minor GC: the minor GC cannot apply the weak constraints
* of the full GC because it visits only part of the heap.
*/
/* static */ void
Debugger::traceAll(JSTracer* trc)
void
Debugger::traceForMovingGC(JSTracer* trc)
{
JSRuntime* rt = trc->runtime();
for (Debugger* dbg : rt->zoneGroupFromMainThread()->debuggerList()) {
for (WeakGlobalObjectSet::Enum e(dbg->debuggees); !e.empty(); e.popFront())
TraceManuallyBarrieredEdge(trc, e.mutableFront().unsafeGet(), "Global Object");
for (WeakGlobalObjectSet::Enum e(debuggees); !e.empty(); e.popFront())
TraceManuallyBarrieredEdge(trc, e.mutableFront().unsafeGet(), "Global Object");
GCPtrNativeObject& dbgobj = dbg->toJSObjectRef();
TraceEdge(trc, &dbgobj, "Debugger Object");
GCPtrNativeObject& dbgobj = toJSObjectRef();
TraceEdge(trc, &dbgobj, "Debugger Object");
dbg->scripts.trace(trc);
dbg->sources.trace(trc);
dbg->objects.trace(trc);
dbg->environments.trace(trc);
dbg->wasmInstanceScripts.trace(trc);
dbg->wasmInstanceSources.trace(trc);
scripts.trace(trc);
sources.trace(trc);
objects.trace(trc);
environments.trace(trc);
wasmInstanceScripts.trace(trc);
wasmInstanceSources.trace(trc);
for (Breakpoint* bp = dbg->firstBreakpoint(); bp; bp = bp->nextInDebugger()) {
switch (bp->site->type()) {
case BreakpointSite::Type::JS:
TraceManuallyBarrieredEdge(trc, &bp->site->asJS()->script,
"breakpoint script");
break;
case BreakpointSite::Type::Wasm:
TraceManuallyBarrieredEdge(trc, &bp->asWasm()->wasmInstance, "breakpoint wasm instance");
break;
}
TraceEdge(trc, &bp->getHandlerRef(), "breakpoint handler");
for (Breakpoint* bp = firstBreakpoint(); bp; bp = bp->nextInDebugger()) {
switch (bp->site->type()) {
case BreakpointSite::Type::JS:
TraceManuallyBarrieredEdge(trc, &bp->site->asJS()->script,
"breakpoint script");
break;
case BreakpointSite::Type::Wasm:
TraceManuallyBarrieredEdge(trc, &bp->asWasm()->wasmInstance, "breakpoint wasm instance");
break;
}
TraceEdge(trc, &bp->getHandlerRef(), "breakpoint handler");
}
}
@ -3234,15 +3243,17 @@ Debugger::sweepAll(FreeOp* fop)
{
JSRuntime* rt = fop->runtime();
for (Debugger* dbg : rt->zoneGroupFromMainThread()->debuggerList()) {
if (IsAboutToBeFinalized(&dbg->object)) {
/*
* dbg is being GC'd. Detach it from its debuggees. The debuggee
* might be GC'd too. Since detaching requires access to both
* objects, this must be done before finalize time.
*/
for (WeakGlobalObjectSet::Enum e(dbg->debuggees); !e.empty(); e.popFront())
dbg->removeDebuggeeGlobal(fop, e.front().unbarrieredGet(), &e);
for (ZoneGroupsIter group(rt); !group.done(); group.next()) {
for (Debugger* dbg : group->debuggerList()) {
if (IsAboutToBeFinalized(&dbg->object)) {
/*
* dbg is being GC'd. Detach it from its debuggees. The debuggee
* might be GC'd too. Since detaching requires access to both
* objects, this must be done before finalize time.
*/
for (WeakGlobalObjectSet::Enum e(dbg->debuggees); !e.empty(); e.popFront())
dbg->removeDebuggeeGlobal(fop, e.front().unbarrieredGet(), &e);
}
}
}
}
@ -3263,9 +3274,12 @@ Debugger::findZoneEdges(Zone* zone, js::gc::ZoneComponentFinder& finder)
* For debugger cross compartment wrappers, add edges in the opposite
* direction to those already added by JSCompartment::findOutgoingEdges.
* This ensure that debuggers and their debuggees are finalized in the same
* group.
* group. We only need to look at the zone's ZoneGroup, as debuggers and
* debuggees are always in the same ZoneGroup.
*/
for (Debugger* dbg : zone->runtimeFromMainThread()->zoneGroupFromMainThread()->debuggerList()) {
if (zone->isAtomsZone())
return;
for (Debugger* dbg : zone->group()->debuggerList()) {
Zone* w = dbg->object->zone();
if (w == zone || !w->isGCMarking())
continue;
@ -11773,14 +11787,16 @@ FireOnGarbageCollectionHook(JSContext* cx, JS::dbg::GarbageCollectionEvent::Ptr&
// participated in this GC.
AutoCheckCannotGC noGC;
for (Debugger* dbg : cx->runtime()->zoneGroupFromMainThread()->debuggerList()) {
if (dbg->enabled &&
dbg->observedGC(data->majorGCNumber()) &&
dbg->getHook(Debugger::OnGarbageCollection))
{
if (!triggered.append(dbg->object)) {
JS_ReportOutOfMemory(cx);
return false;
for (ZoneGroupsIter group(cx->runtime()); !group.done(); group.next()) {
for (Debugger* dbg : group->debuggerList()) {
if (dbg->enabled &&
dbg->observedGC(data->majorGCNumber()) &&
dbg->getHook(Debugger::OnGarbageCollection))
{
if (!triggered.append(dbg->object)) {
JS_ReportOutOfMemory(cx);
return false;
}
}
}
}

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

@ -588,6 +588,7 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
static void traceObject(JSTracer* trc, JSObject* obj);
void trace(JSTracer* trc);
void traceForMovingGC(JSTracer* trc);
static void finalize(FreeOp* fop, JSObject* obj);
void traceCrossCompartmentEdges(JSTracer* tracer);
@ -805,6 +806,8 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
static inline Debugger* fromJSObject(const JSObject* obj);
static Debugger* fromChildJSObject(JSObject* obj);
Zone* zone() const { return toJSObject()->zone(); }
bool hasMemory() const;
DebuggerMemory& memory() const;
@ -829,7 +832,7 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
*/
static void traceIncomingCrossCompartmentEdges(JSTracer* tracer);
static MOZ_MUST_USE bool markIteratively(GCMarker* marker);
static void traceAll(JSTracer* trc);
static void traceAllForMovingGC(JSTracer* trc);
static void sweepAll(FreeOp* fop);
static void detachAllDebuggersFromGlobal(FreeOp* fop, GlobalObject* global);
static void findZoneEdges(JS::Zone* v, gc::ZoneComponentFinder& finder);

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

@ -2315,8 +2315,9 @@ DebugEnvironmentProxy::isOptimizedOut() const
/*****************************************************************************/
DebugEnvironments::DebugEnvironments(JSContext* cx)
: proxiedEnvs(cx),
DebugEnvironments::DebugEnvironments(JSContext* cx, Zone* zone)
: zone_(zone),
proxiedEnvs(cx),
missingEnvs(cx->runtime()),
liveEnvs(cx->runtime())
{}
@ -2430,7 +2431,7 @@ DebugEnvironments::ensureCompartmentData(JSContext* cx)
if (c->debugEnvs)
return c->debugEnvs;
auto debugEnvs = cx->make_unique<DebugEnvironments>(cx);
auto debugEnvs = cx->make_unique<DebugEnvironments>(cx, cx->zone());
if (!debugEnvs || !debugEnvs->init()) {
ReportOutOfMemory(cx);
return nullptr;

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

@ -911,6 +911,8 @@ class DebugEnvironmentProxy : public ProxyObject
/* Maintains per-compartment debug environment bookkeeping information. */
class DebugEnvironments
{
Zone* zone_;
/* The map from (non-debug) environments to debug environments. */
ObjectWeakMap proxiedEnvs;
@ -939,9 +941,11 @@ class DebugEnvironments
LiveEnvironmentMap liveEnvs;
public:
explicit DebugEnvironments(JSContext* cx);
DebugEnvironments(JSContext* cx, Zone* zone);
~DebugEnvironments();
Zone* zone() const { return zone_; }
private:
bool init();

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

@ -470,7 +470,7 @@ js::CancelOffThreadParses(JSRuntime* rt)
if (task->runtimeMatches(rt)) {
found = true;
AutoUnlockHelperThreadState unlock(lock);
HelperThreadState().cancelParseTask(rt->contextFromMainThread(), task->kind, task);
HelperThreadState().cancelParseTask(rt, task->kind, task);
}
}
if (!found)
@ -1352,10 +1352,10 @@ GlobalHelperThreadState::finishModuleParseTask(JSContext* cx, void* token)
}
void
GlobalHelperThreadState::cancelParseTask(JSContext* cx, ParseTaskKind kind, void* token)
GlobalHelperThreadState::cancelParseTask(JSRuntime* rt, ParseTaskKind kind, void* token)
{
ScopedJSDeletePtr<ParseTask> parseTask(removeFinishedParseTask(kind, token));
LeaveParseTaskZone(cx->runtime(), parseTask);
LeaveParseTaskZone(rt, parseTask);
}
JSObject*

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

@ -241,7 +241,7 @@ class GlobalHelperThreadState
}
JSScript* finishParseTask(JSContext* cx, ParseTaskKind kind, void* token);
void cancelParseTask(JSContext* cx, ParseTaskKind kind, void* token);
void cancelParseTask(JSRuntime* rt, ParseTaskKind kind, void* token);
void mergeParseTaskCompartment(JSContext* cx, ParseTask* parseTask,
Handle<GlobalObject*> global,

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

@ -19,6 +19,7 @@
#include "gc/Statistics.h"
#include "jit/ExecutableAllocator.h"
#include "jit/Ion.h"
#include "jit/JitCommon.h"
#include "js/Utility.h"
#if ENABLE_INTL_API
#include "unicode/uclean.h"
@ -123,6 +124,10 @@ JS::detail::InitWithFailureDiagnostic(bool isDebugBuild)
RETURN_IF_FAIL(FutexThread::initialize());
RETURN_IF_FAIL(js::gcstats::Statistics::initialize());
#ifdef JS_SIMULATOR
RETURN_IF_FAIL(js::jit::SimulatorProcess::initialize());
#endif
libraryInitState = InitState::Running;
return nullptr;
}
@ -148,6 +153,10 @@ JS_ShutDown(void)
js::DestroyHelperThreadsState();
#ifdef JS_SIMULATOR
js::jit::SimulatorProcess::destroy();
#endif
#ifdef JS_TRACE_LOGGING
js::DestroyTraceLoggerThreadState();
js::DestroyTraceLoggerGraphState();

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