зеркало из https://github.com/mozilla/gecko-dev.git
Merge inbound to central, a=merge
MozReview-Commit-ID: 5B5W6AHKcCb
This commit is contained in:
Коммит
3baa8a7e6e
|
@ -44,7 +44,6 @@ function openContextMenu(aMessage) {
|
|||
spellInfo,
|
||||
principal: data.principal,
|
||||
customMenuItems: data.customMenuItems,
|
||||
addonInfo: data.addonInfo,
|
||||
documentURIObject,
|
||||
docLocation: data.docLocation,
|
||||
charSet: data.charSet,
|
||||
|
|
|
@ -547,7 +547,7 @@ var DownloadsPanel = {
|
|||
// without any notification, and there would be no way to either open or
|
||||
// close the panel any more. To prevent this, check if the window is
|
||||
// minimized and in that case force the panel to the closed state.
|
||||
if (window.windowState == Ci.nsIDOMChromeWindow.STATE_MINIMIZED) {
|
||||
if (window.windowState == window.STATE_MINIMIZED) {
|
||||
DownloadsButton.releaseAnchor();
|
||||
this._state = this.kStateHidden;
|
||||
return;
|
||||
|
|
|
@ -1071,6 +1071,10 @@ var gPrivacyPane = {
|
|||
let blockUnwantedPref = document.getElementById("browser.safebrowsing.downloads.remote.block_potentially_unwanted");
|
||||
let blockUncommonPref = document.getElementById("browser.safebrowsing.downloads.remote.block_uncommon");
|
||||
|
||||
let learnMoreLink = document.getElementById("enableSafeBrowsingLearnMore");
|
||||
let phishingUrl = "https://support.mozilla.org/kb/how-does-phishing-and-malware-protection-work";
|
||||
learnMoreLink.setAttribute("href", phishingUrl);
|
||||
|
||||
enableSafeBrowsing.addEventListener("command", function() {
|
||||
safeBrowsingPhishingPref.value = enableSafeBrowsing.checked;
|
||||
safeBrowsingMalwarePref.value = enableSafeBrowsing.checked;
|
||||
|
|
|
@ -762,9 +762,13 @@
|
|||
<!-- addons, forgery (phishing) UI Security -->
|
||||
<groupbox id="browsingProtectionGroup" data-category="panePrivacy" hidden="true">
|
||||
<caption><label>&browsingProtection.label;</label></caption>
|
||||
<checkbox id="enableSafeBrowsing"
|
||||
label="&enableSafeBrowsing.label;"
|
||||
accesskey="&enableSafeBrowsing.accesskey;" />
|
||||
<hbox align = "center">
|
||||
<checkbox id="enableSafeBrowsing"
|
||||
label="&enableSafeBrowsing.label;"
|
||||
accesskey="&enableSafeBrowsing.accesskey;" />
|
||||
<label id="enableSafeBrowsingLearnMore"
|
||||
class="learnMore text-link">&enableSafeBrowsingLearnMore.label;</label>
|
||||
</hbox>
|
||||
<vbox class="indent">
|
||||
#ifdef MOZILLA_OFFICIAL
|
||||
<checkbox id="blockDownloads"
|
||||
|
|
|
@ -117,3 +117,4 @@
|
|||
<!ENTITY a11yPrivacy.checkbox.label "Prevent accessibility services from accessing your browser">
|
||||
<!ENTITY a11yPrivacy.checkbox.accesskey "a">
|
||||
<!ENTITY a11yPrivacy.learnmore.label "Learn more">
|
||||
<!ENTITY enableSafeBrowsingLearnMore.label "Learn more">
|
|
@ -510,15 +510,6 @@ class ContextMenu {
|
|||
return;
|
||||
}
|
||||
|
||||
let addonInfo = Object.create(null);
|
||||
let subject = {
|
||||
aEvent,
|
||||
addonInfo,
|
||||
};
|
||||
|
||||
subject.wrappedJSObject = subject;
|
||||
Services.obs.notifyObservers(subject, "content-contextmenu");
|
||||
|
||||
let doc = aEvent.target.ownerDocument;
|
||||
let {
|
||||
mozDocumentURIIfNotForErrorPages: docLocation,
|
||||
|
@ -612,7 +603,6 @@ class ContextMenu {
|
|||
baseURI,
|
||||
isRemote,
|
||||
referrer,
|
||||
addonInfo,
|
||||
editFlags,
|
||||
principal,
|
||||
spellInfo,
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "nsIDOMStorageManager.h"
|
||||
#include "nsDocLoader.h"
|
||||
#include "mozilla/BasePrincipal.h"
|
||||
#include "mozilla/Move.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "mozilla/WeakPtr.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
|
@ -291,6 +292,41 @@ public:
|
|||
|
||||
nsDOMNavigationTiming* GetNavigationTiming() const;
|
||||
|
||||
/**
|
||||
* Get the list of ancestor principals for this docshell. The list is meant
|
||||
* to be the list of principals of the documents this docshell is "nested
|
||||
* through" in the sense of
|
||||
* <https://html.spec.whatwg.org/multipage/browsers.html#browsing-context-nested-through>.
|
||||
* In practice, it is defined as follows:
|
||||
*
|
||||
* If this is an <iframe mozbrowser> or a toplevel content docshell
|
||||
* (i.e. toplevel document in spec terms), the list is empty.
|
||||
*
|
||||
* Otherwise the list is the list for the document we're nested through (again
|
||||
* in the spec sense), with the principal of that document prepended. Note
|
||||
* that this matches the ordering specified for Location.ancestorOrigins.
|
||||
*/
|
||||
const nsTArray<nsCOMPtr<nsIPrincipal>>& AncestorPrincipals() const
|
||||
{
|
||||
return mAncestorPrincipals;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the list of ancestor principals for this docshell. This is really only
|
||||
* needed for use by the frameloader. We can't do this ourselves, inside
|
||||
* docshell, because there's a bunch of state setup that frameloader does
|
||||
* (like telling us whether we're a mozbrowser), some of which comes after the
|
||||
* docshell is added to the docshell tree, which can affect what the ancestor
|
||||
* principals should look like.
|
||||
*
|
||||
* This method steals the data from the passed-in array.
|
||||
*/
|
||||
void SetAncestorPrincipals(
|
||||
nsTArray<nsCOMPtr<nsIPrincipal>>&& aAncestorPrincipals)
|
||||
{
|
||||
mAncestorPrincipals = mozilla::Move(aAncestorPrincipals);
|
||||
}
|
||||
|
||||
private:
|
||||
bool CanSetOriginAttributes();
|
||||
|
||||
|
@ -1094,6 +1130,9 @@ private:
|
|||
// as constants in the nsIDocShell.idl file.
|
||||
uint32_t mTouchEventsOverride;
|
||||
|
||||
// Our list of ancestor principals.
|
||||
nsTArray<nsCOMPtr<nsIPrincipal>> mAncestorPrincipals;
|
||||
|
||||
// Separate function to do the actual name (i.e. not _top, _self etc.)
|
||||
// searching for FindItemWithName.
|
||||
nsresult DoFindItemWithName(const nsAString& aName,
|
||||
|
|
|
@ -161,6 +161,10 @@ DOMIntersectionObserver::Observe(Element& aTarget)
|
|||
void
|
||||
DOMIntersectionObserver::Unobserve(Element& aTarget)
|
||||
{
|
||||
if (!mObservationTargets.Contains(&aTarget)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mObservationTargets.Length() == 1) {
|
||||
Disconnect();
|
||||
return;
|
||||
|
|
|
@ -50,3 +50,4 @@ DEPRECATED_OPERATION(ImageBitmapRenderingContext_TransferImageBitmap)
|
|||
DEPRECATED_OPERATION(URLCreateObjectURL_MediaStream)
|
||||
DEPRECATED_OPERATION(XMLBaseAttribute)
|
||||
DEPRECATED_OPERATION(XMLBaseAttributeForStyleAttr)
|
||||
DEPRECATED_OPERATION(WindowContentUntrusted)
|
||||
|
|
|
@ -4965,6 +4965,8 @@ nsIDocument::SetContainer(nsDocShell* aContainer)
|
|||
|
||||
static_cast<nsDocument*>(this)->SetIsContentDocument(true);
|
||||
}
|
||||
|
||||
mAncestorPrincipals = aContainer->AncestorPrincipals();
|
||||
}
|
||||
|
||||
nsISupports*
|
||||
|
|
|
@ -2532,7 +2532,7 @@ nsFrameLoader::MaybeCreateDocShell()
|
|||
// Note: This logic duplicates a lot of logic in
|
||||
// nsSubDocumentFrame::AttributeChanged. We should fix that.
|
||||
|
||||
int32_t parentType = docShell->ItemType();
|
||||
const int32_t parentType = docShell->ItemType();
|
||||
|
||||
// XXXbz why is this in content code, exactly? We should handle
|
||||
// this some other way..... Not sure how yet.
|
||||
|
@ -2711,6 +2711,16 @@ nsFrameLoader::MaybeCreateDocShell()
|
|||
|
||||
nsDocShell::Cast(mDocShell)->SetOriginAttributes(attrs);
|
||||
|
||||
if (!mDocShell->GetIsMozBrowser() &&
|
||||
parentType == mDocShell->ItemType()) {
|
||||
// Propagate through the ancestor principals.
|
||||
nsTArray<nsCOMPtr<nsIPrincipal>> ancestorPrincipals;
|
||||
// Make a copy, so we can modify it.
|
||||
ancestorPrincipals = doc->AncestorPrincipals();
|
||||
ancestorPrincipals.InsertElementAt(0, doc->NodePrincipal());
|
||||
nsDocShell::Cast(mDocShell)->SetAncestorPrincipals(Move(ancestorPrincipals));
|
||||
}
|
||||
|
||||
ReallyLoadFrameScripts();
|
||||
InitializeBrowserAPI();
|
||||
|
||||
|
|
|
@ -1144,6 +1144,7 @@ public:
|
|||
JS::Handle<JSObject*> wrapper) const override;
|
||||
|
||||
void finalize(JSFreeOp *fop, JSObject *proxy) const override;
|
||||
size_t objectMoved(JSObject* proxy, JSObject* old) const override;
|
||||
|
||||
bool isCallable(JSObject *obj) const override {
|
||||
return false;
|
||||
|
@ -1157,8 +1158,6 @@ public:
|
|||
bool unwatch(JSContext *cx, JS::Handle<JSObject*> proxy,
|
||||
JS::Handle<jsid> id) const override;
|
||||
|
||||
static void ObjectMoved(JSObject *obj, const JSObject *old);
|
||||
|
||||
static const nsOuterWindowProxy singleton;
|
||||
|
||||
protected:
|
||||
|
@ -1188,17 +1187,12 @@ protected:
|
|||
JS::AutoIdVector &props) const;
|
||||
};
|
||||
|
||||
static const js::ClassExtension OuterWindowProxyClassExtension = PROXY_MAKE_EXT(
|
||||
nsOuterWindowProxy::ObjectMoved
|
||||
);
|
||||
|
||||
// Give OuterWindowProxyClass 2 reserved slots, like the other wrappers, so
|
||||
// JSObject::swap can swap it with CrossCompartmentWrappers without requiring
|
||||
// malloc.
|
||||
const js::Class OuterWindowProxyClass = PROXY_CLASS_WITH_EXT(
|
||||
const js::Class OuterWindowProxyClass = PROXY_CLASS_DEF(
|
||||
"Proxy",
|
||||
JSCLASS_HAS_RESERVED_SLOTS(2), /* additional class flags */
|
||||
&OuterWindowProxyClassExtension);
|
||||
JSCLASS_HAS_RESERVED_SLOTS(2)); /* additional class flags */
|
||||
|
||||
const char *
|
||||
nsOuterWindowProxy::className(JSContext *cx, JS::Handle<JSObject*> proxy) const
|
||||
|
@ -1526,13 +1520,14 @@ nsOuterWindowProxy::unwatch(JSContext *cx, JS::Handle<JSObject*> proxy,
|
|||
return js::UnwatchGuts(cx, proxy, id);
|
||||
}
|
||||
|
||||
void
|
||||
nsOuterWindowProxy::ObjectMoved(JSObject *obj, const JSObject *old)
|
||||
size_t
|
||||
nsOuterWindowProxy::objectMoved(JSObject *obj, JSObject *old) const
|
||||
{
|
||||
nsGlobalWindow* outerWindow = GetOuterWindow(obj);
|
||||
if (outerWindow) {
|
||||
outerWindow->UpdateWrapper(obj, old);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const nsOuterWindowProxy
|
||||
|
@ -4989,6 +4984,9 @@ nsGlobalWindow::GetContentInternal(ErrorResult& aError, CallerType aCallerType)
|
|||
|
||||
nsCOMPtr<nsIDocShellTreeItem> primaryContent;
|
||||
if (aCallerType != CallerType::System) {
|
||||
if (mDoc) {
|
||||
mDoc->WarnOnceAbout(nsIDocument::eWindowContentUntrusted);
|
||||
}
|
||||
// If we're called by non-chrome code, make sure we don't return
|
||||
// the primary content window if the calling tab is hidden. In
|
||||
// such a case we return the same-type root in the hidden tab,
|
||||
|
@ -14067,14 +14065,13 @@ nsGlobalChromeWindow::Create(nsGlobalWindow *aOuterWindow)
|
|||
return window.forget();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsGlobalChromeWindow::GetWindowState(uint16_t* aWindowState)
|
||||
{
|
||||
FORWARD_TO_INNER_CHROME(GetWindowState, (aWindowState), NS_ERROR_UNEXPECTED);
|
||||
|
||||
*aWindowState = WindowState();
|
||||
return NS_OK;
|
||||
}
|
||||
enum WindowState {
|
||||
// These constants need to match the constants in Window.webidl
|
||||
STATE_MAXIMIZED = 1,
|
||||
STATE_MINIMIZED = 2,
|
||||
STATE_NORMAL = 3,
|
||||
STATE_FULLSCREEN = 4
|
||||
};
|
||||
|
||||
uint16_t
|
||||
nsGlobalWindow::WindowState()
|
||||
|
@ -14086,19 +14083,19 @@ nsGlobalWindow::WindowState()
|
|||
|
||||
switch (mode) {
|
||||
case nsSizeMode_Minimized:
|
||||
return nsIDOMChromeWindow::STATE_MINIMIZED;
|
||||
return STATE_MINIMIZED;
|
||||
case nsSizeMode_Maximized:
|
||||
return nsIDOMChromeWindow::STATE_MAXIMIZED;
|
||||
return STATE_MAXIMIZED;
|
||||
case nsSizeMode_Fullscreen:
|
||||
return nsIDOMChromeWindow::STATE_FULLSCREEN;
|
||||
return STATE_FULLSCREEN;
|
||||
case nsSizeMode_Normal:
|
||||
return nsIDOMChromeWindow::STATE_NORMAL;
|
||||
return STATE_NORMAL;
|
||||
default:
|
||||
NS_WARNING("Illegal window state for this chrome window");
|
||||
break;
|
||||
}
|
||||
|
||||
return nsIDOMChromeWindow::STATE_NORMAL;
|
||||
return STATE_NORMAL;
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -14110,15 +14107,6 @@ nsGlobalWindow::IsFullyOccluded()
|
|||
return widget && widget->IsFullyOccluded();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsGlobalChromeWindow::Maximize()
|
||||
{
|
||||
FORWARD_TO_INNER_CHROME(Maximize, (), NS_ERROR_UNEXPECTED);
|
||||
|
||||
nsGlobalWindow::Maximize();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsGlobalWindow::Maximize()
|
||||
{
|
||||
|
@ -14131,15 +14119,6 @@ nsGlobalWindow::Maximize()
|
|||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsGlobalChromeWindow::Minimize()
|
||||
{
|
||||
FORWARD_TO_INNER_CHROME(Minimize, (), NS_ERROR_UNEXPECTED);
|
||||
|
||||
nsGlobalWindow::Minimize();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsGlobalWindow::Minimize()
|
||||
{
|
||||
|
@ -14152,15 +14131,6 @@ nsGlobalWindow::Minimize()
|
|||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsGlobalChromeWindow::Restore()
|
||||
{
|
||||
FORWARD_TO_INNER_CHROME(Restore, (), NS_ERROR_UNEXPECTED);
|
||||
|
||||
nsGlobalWindow::Restore();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsGlobalWindow::Restore()
|
||||
{
|
||||
|
@ -14173,16 +14143,6 @@ nsGlobalWindow::Restore()
|
|||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsGlobalChromeWindow::GetAttention()
|
||||
{
|
||||
FORWARD_TO_INNER_CHROME(GetAttention, (), NS_ERROR_UNEXPECTED);
|
||||
|
||||
ErrorResult rv;
|
||||
GetAttention(rv);
|
||||
return rv.StealNSResult();
|
||||
}
|
||||
|
||||
void
|
||||
nsGlobalWindow::GetAttention(ErrorResult& aResult)
|
||||
{
|
||||
|
@ -14190,16 +14150,6 @@ nsGlobalWindow::GetAttention(ErrorResult& aResult)
|
|||
return GetAttentionWithCycleCount(-1, aResult);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsGlobalChromeWindow::GetAttentionWithCycleCount(int32_t aCycleCount)
|
||||
{
|
||||
FORWARD_TO_INNER_CHROME(GetAttentionWithCycleCount, (aCycleCount), NS_ERROR_UNEXPECTED);
|
||||
|
||||
ErrorResult rv;
|
||||
GetAttentionWithCycleCount(aCycleCount, rv);
|
||||
return rv.StealNSResult();
|
||||
}
|
||||
|
||||
void
|
||||
nsGlobalWindow::GetAttentionWithCycleCount(int32_t aCycleCount,
|
||||
ErrorResult& aError)
|
||||
|
@ -14213,23 +14163,6 @@ nsGlobalWindow::GetAttentionWithCycleCount(int32_t aCycleCount,
|
|||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsGlobalChromeWindow::BeginWindowMove(nsIDOMEvent *aMouseDownEvent, nsIDOMElement* aPanel)
|
||||
{
|
||||
FORWARD_TO_INNER_CHROME(BeginWindowMove, (aMouseDownEvent, aPanel), NS_ERROR_UNEXPECTED);
|
||||
|
||||
NS_ENSURE_TRUE(aMouseDownEvent, NS_ERROR_FAILURE);
|
||||
Event* mouseDownEvent = aMouseDownEvent->InternalDOMEvent();
|
||||
NS_ENSURE_TRUE(mouseDownEvent, NS_ERROR_FAILURE);
|
||||
|
||||
nsCOMPtr<Element> panel = do_QueryInterface(aPanel);
|
||||
NS_ENSURE_TRUE(panel || !aPanel, NS_ERROR_FAILURE);
|
||||
|
||||
ErrorResult rv;
|
||||
BeginWindowMove(*mouseDownEvent, panel, rv);
|
||||
return rv.StealNSResult();
|
||||
}
|
||||
|
||||
void
|
||||
nsGlobalWindow::BeginWindowMove(Event& aMouseDownEvent, Element* aPanel,
|
||||
ErrorResult& aError)
|
||||
|
@ -14285,16 +14218,6 @@ nsGlobalWindow::GetWindowRoot(mozilla::ErrorResult& aError)
|
|||
|
||||
//Note: This call will lock the cursor, it will not change as it moves.
|
||||
//To unlock, the cursor must be set back to CURSOR_AUTO.
|
||||
NS_IMETHODIMP
|
||||
nsGlobalChromeWindow::SetCursor(const nsAString& aCursor)
|
||||
{
|
||||
FORWARD_TO_INNER_CHROME(SetCursor, (aCursor), NS_ERROR_UNEXPECTED);
|
||||
|
||||
ErrorResult rv;
|
||||
SetCursor(aCursor, rv);
|
||||
return rv.StealNSResult();
|
||||
}
|
||||
|
||||
void
|
||||
nsGlobalWindow::SetCursorOuter(const nsAString& aCursor, ErrorResult& aError)
|
||||
{
|
||||
|
@ -14379,16 +14302,6 @@ nsGlobalWindow::GetBrowserDOMWindow(ErrorResult& aError)
|
|||
FORWARD_TO_OUTER_OR_THROW(GetBrowserDOMWindowOuter, (), aError, nullptr);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsGlobalChromeWindow::SetBrowserDOMWindow(nsIBrowserDOMWindow *aBrowserWindow)
|
||||
{
|
||||
FORWARD_TO_INNER_CHROME(SetBrowserDOMWindow, (aBrowserWindow), NS_ERROR_UNEXPECTED);
|
||||
|
||||
ErrorResult rv;
|
||||
SetBrowserDOMWindow(aBrowserWindow, rv);
|
||||
return rv.StealNSResult();
|
||||
}
|
||||
|
||||
void
|
||||
nsGlobalWindow::SetBrowserDOMWindowOuter(nsIBrowserDOMWindow* aBrowserWindow)
|
||||
{
|
||||
|
@ -14404,20 +14317,6 @@ nsGlobalWindow::SetBrowserDOMWindow(nsIBrowserDOMWindow* aBrowserWindow,
|
|||
FORWARD_TO_OUTER_OR_THROW(SetBrowserDOMWindowOuter, (aBrowserWindow), aError, );
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsGlobalChromeWindow::NotifyDefaultButtonLoaded(nsIDOMElement* aDefaultButton)
|
||||
{
|
||||
FORWARD_TO_INNER_CHROME(NotifyDefaultButtonLoaded,
|
||||
(aDefaultButton), NS_ERROR_UNEXPECTED);
|
||||
|
||||
nsCOMPtr<Element> defaultButton = do_QueryInterface(aDefaultButton);
|
||||
NS_ENSURE_ARG(defaultButton);
|
||||
|
||||
ErrorResult rv;
|
||||
NotifyDefaultButtonLoaded(*defaultButton, rv);
|
||||
return rv.StealNSResult();
|
||||
}
|
||||
|
||||
void
|
||||
nsGlobalWindow::NotifyDefaultButtonLoaded(Element& aDefaultButton,
|
||||
ErrorResult& aError)
|
||||
|
|
|
@ -446,10 +446,24 @@ public:
|
|||
}
|
||||
|
||||
/**
|
||||
* Set the principal responsible for this document.
|
||||
* Set the principal responsible for this document. Chances are,
|
||||
* you do not want to be using this.
|
||||
*/
|
||||
virtual void SetPrincipal(nsIPrincipal *aPrincipal) = 0;
|
||||
|
||||
/**
|
||||
* Get the list of ancestor principals for a document. This is the same as
|
||||
* the ancestor list for the document's docshell the last time SetContainer()
|
||||
* was called with a non-null argument. See the documentation for the
|
||||
* corresponding getter in docshell for how this list is determined. We store
|
||||
* a copy of the list, because we may lose the ability to reach our docshell
|
||||
* before people stop asking us for this information.
|
||||
*/
|
||||
const nsTArray<nsCOMPtr<nsIPrincipal>>& AncestorPrincipals() const
|
||||
{
|
||||
return mAncestorPrincipals;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the LoadGroup for the document. May return null.
|
||||
*/
|
||||
|
@ -3595,6 +3609,10 @@ protected:
|
|||
// StartBufferingCSPViolations.
|
||||
nsTArray<nsCOMPtr<nsIRunnable>> mBufferedCSPViolations;
|
||||
|
||||
// List of ancestor principals. This is set at the point a document
|
||||
// is connected to a docshell and not mutated thereafter.
|
||||
nsTArray<nsCOMPtr<nsIPrincipal>> mAncestorPrincipals;
|
||||
|
||||
// Restyle root for servo's style system.
|
||||
//
|
||||
// We store this as an nsINode, rather than as an Element, so that we can store
|
||||
|
|
|
@ -623,6 +623,7 @@ skip-if = toolkit == 'android'
|
|||
[test_bug1375050.html]
|
||||
[test_bug1381710.html]
|
||||
[test_bug1384658.html]
|
||||
[test_bug1399605.html]
|
||||
skip-if = toolkit == 'android'
|
||||
[test_caretPositionFromPoint.html]
|
||||
[test_change_policy.html]
|
||||
|
@ -814,6 +815,7 @@ skip-if = toolkit == 'android'
|
|||
[test_websocket5.html]
|
||||
skip-if = toolkit == 'android'
|
||||
[test_window_constructor.html]
|
||||
[test_window_content.html]
|
||||
[test_window_cross_origin_props.html]
|
||||
[test_window_define_nonconfigurable.html]
|
||||
[test_window_define_symbol.html]
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 1399605</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1399605">Mozilla Bug 1399605</a>
|
||||
<p id="display"></p>
|
||||
<div id="content">
|
||||
<div id="target"></div>
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
|
||||
let observer = new IntersectionObserver(function() {
|
||||
ok(true, "we are still observing");
|
||||
SimpleTest.finish();
|
||||
});
|
||||
observer.observe(document.getElementById('target'));
|
||||
observer.unobserve(document.getElementById('content'));
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
<div id="log">
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,28 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1400139
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 1400139</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="application/javascript">
|
||||
|
||||
/** Test for Bug 1400139 **/
|
||||
var props = [];
|
||||
for (var prop in window) props.push(prop);
|
||||
is(props.indexOf("content"), -1, "Should not have a property named 'content'");
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1400139">Mozilla Bug 1400139</a>
|
||||
<p id="display"></p>
|
||||
<div id="notcontent" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -544,23 +544,16 @@ class CGDOMProxyJSClass(CGThing):
|
|||
# HTMLAllCollection. So just hardcode it here.
|
||||
if self.descriptor.interface.identifier.name == "HTMLAllCollection":
|
||||
flags.append("JSCLASS_EMULATES_UNDEFINED")
|
||||
objectMovedHook = OBJECT_MOVED_HOOK_NAME if self.descriptor.wrapperCache else 'nullptr'
|
||||
return fill(
|
||||
"""
|
||||
static const js::ClassExtension sClassExtension = PROXY_MAKE_EXT(
|
||||
${objectMoved}
|
||||
);
|
||||
|
||||
static const DOMJSClass sClass = {
|
||||
PROXY_CLASS_WITH_EXT("${name}",
|
||||
${flags},
|
||||
&sClassExtension),
|
||||
PROXY_CLASS_DEF("${name}",
|
||||
${flags}),
|
||||
$*{descriptor}
|
||||
};
|
||||
""",
|
||||
name=self.descriptor.interface.identifier.name,
|
||||
flags=" | ".join(flags),
|
||||
objectMoved=objectMovedHook,
|
||||
descriptor=DOMClass(self.descriptor))
|
||||
|
||||
|
||||
|
@ -1731,20 +1724,32 @@ class CGClassFinalizeHook(CGAbstractClassHook):
|
|||
self.args[0].name, self.args[1].name).define()
|
||||
|
||||
|
||||
def objectMovedHook(descriptor, hookName, obj, old):
|
||||
assert descriptor.wrapperCache
|
||||
return fill("""
|
||||
if (self) {
|
||||
UpdateWrapper(self, self, ${obj}, ${old});
|
||||
}
|
||||
|
||||
return 0;
|
||||
""",
|
||||
obj=obj,
|
||||
old=old)
|
||||
|
||||
|
||||
class CGClassObjectMovedHook(CGAbstractClassHook):
|
||||
"""
|
||||
A hook for objectMovedOp, used to update the wrapper cache when an object it
|
||||
is holding moves.
|
||||
"""
|
||||
def __init__(self, descriptor):
|
||||
args = [Argument('JSObject*', 'obj'), Argument('const JSObject*', 'old')]
|
||||
args = [Argument('JSObject*', 'obj'), Argument('JSObject*', 'old')]
|
||||
CGAbstractClassHook.__init__(self, descriptor, OBJECT_MOVED_HOOK_NAME,
|
||||
'void', args)
|
||||
'size_t', args)
|
||||
|
||||
def generate_code(self):
|
||||
assert self.descriptor.wrapperCache
|
||||
return CGIfWrapper(CGGeneric("UpdateWrapper(self, self, obj, old);\n"),
|
||||
"self").define()
|
||||
return objectMovedHook(self.descriptor, self.name,
|
||||
self.args[0].name, self.args[1].name)
|
||||
|
||||
|
||||
def JSNativeArguments():
|
||||
|
@ -2361,6 +2366,12 @@ def IDLToCIdentifier(name):
|
|||
return name.replace("-", "_")
|
||||
|
||||
|
||||
def EnumerabilityFlags(member):
|
||||
if member.getExtendedAttribute("NonEnumerable"):
|
||||
return "0"
|
||||
return "JSPROP_ENUMERATE"
|
||||
|
||||
|
||||
class MethodDefiner(PropertyDefiner):
|
||||
"""
|
||||
A class for defining methods on a prototype object.
|
||||
|
@ -2421,18 +2432,11 @@ class MethodDefiner(PropertyDefiner):
|
|||
})
|
||||
continue
|
||||
|
||||
# Iterable methods should be enumerable, maplike/setlike methods
|
||||
# should not.
|
||||
isMaplikeOrSetlikeMethod = (m.isMaplikeOrSetlikeOrIterableMethod() and
|
||||
(m.maplikeOrSetlikeOrIterable.isMaplike() or
|
||||
m.maplikeOrSetlikeOrIterable.isSetlike()))
|
||||
method = {
|
||||
"name": m.identifier.name,
|
||||
"methodInfo": not m.isStatic(),
|
||||
"length": methodLength(m),
|
||||
# Methods generated for a maplike/setlike declaration are not
|
||||
# enumerable.
|
||||
"flags": "JSPROP_ENUMERATE" if not isMaplikeOrSetlikeMethod else "0",
|
||||
"flags": EnumerabilityFlags(m),
|
||||
"condition": PropertyDefiner.getControllingCondition(m, descriptor),
|
||||
"allowCrossOriginThis": m.getExtendedAttribute("CrossOriginCallable"),
|
||||
"returnsPromise": m.returnsPromise(),
|
||||
|
@ -2728,9 +2732,7 @@ class AttrDefiner(PropertyDefiner):
|
|||
|
||||
def flags(attr):
|
||||
unforgeable = " | JSPROP_PERMANENT" if self.unforgeable else ""
|
||||
# Attributes generated as part of a maplike/setlike declaration are
|
||||
# not enumerable.
|
||||
enumerable = " | JSPROP_ENUMERATE" if not attr.isMaplikeOrSetlikeAttr() else ""
|
||||
enumerable = " | %s" % EnumerabilityFlags(attr)
|
||||
return ("JSPROP_SHARED" + enumerable + unforgeable)
|
||||
|
||||
def getter(attr):
|
||||
|
@ -6851,8 +6853,7 @@ def getWrapTemplateForType(type, descriptorProvider, result, successCode,
|
|||
if not descriptor.hasXPConnectImpls:
|
||||
# Can only fail to wrap as a new-binding object
|
||||
# if they already threw an exception.
|
||||
# XXX Assertion disabled for now, see bug 991271.
|
||||
failed = ("MOZ_ASSERT(true || JS_IsExceptionPending(cx));\n" +
|
||||
failed = ("MOZ_ASSERT(JS_IsExceptionPending(cx));\n" +
|
||||
exceptionCode)
|
||||
else:
|
||||
if descriptor.notflattened:
|
||||
|
@ -12408,6 +12409,20 @@ class CGDOMJSProxyHandler_finalize(ClassMethod):
|
|||
self.args[0].name, self.args[1].name).define())
|
||||
|
||||
|
||||
class CGDOMJSProxyHandler_objectMoved(ClassMethod):
|
||||
def __init__(self, descriptor):
|
||||
args = [Argument('JSObject*', 'obj'), Argument('JSObject*', 'old')]
|
||||
ClassMethod.__init__(self, "objectMoved", "size_t", args,
|
||||
virtual=True, override=True, const=True)
|
||||
self.descriptor = descriptor
|
||||
|
||||
def getBody(self):
|
||||
return (("%s* self = UnwrapPossiblyNotInitializedDOMObject<%s>(obj);\n" %
|
||||
(self.descriptor.nativeType, self.descriptor.nativeType)) +
|
||||
objectMovedHook(self.descriptor, OBJECT_MOVED_HOOK_NAME,
|
||||
self.args[0].name, self.args[1].name))
|
||||
|
||||
|
||||
class CGDOMJSProxyHandler_getElements(ClassMethod):
|
||||
def __init__(self, descriptor):
|
||||
assert descriptor.supportsIndexedProperties()
|
||||
|
@ -12583,6 +12598,8 @@ class CGDOMJSProxyHandler(CGClass):
|
|||
raise TypeError("Need a wrapper cache to support nursery "
|
||||
"allocation of DOM objects")
|
||||
methods.append(CGDOMJSProxyHandler_canNurseryAllocate())
|
||||
if descriptor.wrapperCache:
|
||||
methods.append(CGDOMJSProxyHandler_objectMoved(descriptor))
|
||||
|
||||
if descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
|
||||
parentClass = 'ShadowingDOMProxyHandler'
|
||||
|
@ -12839,7 +12856,7 @@ class CGDescriptor(CGThing):
|
|||
# wants a custom hook.
|
||||
cgThings.append(CGClassFinalizeHook(descriptor))
|
||||
|
||||
if descriptor.concrete and descriptor.wrapperCache:
|
||||
if descriptor.concrete and descriptor.wrapperCache and not descriptor.proxy:
|
||||
cgThings.append(CGClassObjectMovedHook(descriptor))
|
||||
|
||||
# Generate the _ClearCachedFooValue methods before the property arrays that use them.
|
||||
|
@ -15371,8 +15388,7 @@ class CGJSImplMethod(CGJSImplMember):
|
|||
MOZ_ASSERT(js::IsObjectInContextCompartment(scopeObj, cx));
|
||||
JS::Rooted<JS::Value> wrappedVal(cx);
|
||||
if (!GetOrCreateDOMReflector(cx, impl, &wrappedVal, aGivenProto)) {
|
||||
//XXX Assertion disabled for now, see bug 991271.
|
||||
MOZ_ASSERT(true || JS_IsExceptionPending(cx));
|
||||
MOZ_ASSERT(JS_IsExceptionPending(cx));
|
||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
@ -50,12 +50,13 @@ SimpleGlobal_finalize(js::FreeOp *fop, JSObject *obj)
|
|||
NS_RELEASE(globalObject);
|
||||
}
|
||||
|
||||
static void
|
||||
SimpleGlobal_moved(JSObject *obj, const JSObject *old)
|
||||
static size_t
|
||||
SimpleGlobal_moved(JSObject *obj, JSObject *old)
|
||||
{
|
||||
SimpleGlobalObject* globalObject =
|
||||
static_cast<SimpleGlobalObject*>(JS_GetPrivate(obj));
|
||||
globalObject->UpdateWrapper(obj, old);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const js::ClassOps SimpleGlobalClassOps = {
|
||||
|
|
|
@ -3703,6 +3703,11 @@ class IDLMaplikeOrSetlikeOrIterableBase(IDLInterfaceMember):
|
|||
if isIteratorAlias:
|
||||
method.addExtendedAttributes(
|
||||
[IDLExtendedAttribute(self.location, ("Alias", "@@iterator"))])
|
||||
# Methods generated for iterables should be enumerable, but the ones for
|
||||
# maplike/setlike should not be.
|
||||
if not self.isIterable():
|
||||
method.addExtendedAttributes(
|
||||
[IDLExtendedAttribute(self.location, ("NonEnumerable",))])
|
||||
members.append(method)
|
||||
|
||||
def resolve(self, parentScope):
|
||||
|
@ -3826,11 +3831,15 @@ class IDLMaplikeOrSetlike(IDLMaplikeOrSetlikeOrIterableBase):
|
|||
specification during parsing.
|
||||
"""
|
||||
# Both maplike and setlike have a size attribute
|
||||
members.append(IDLAttribute(self.location,
|
||||
IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"), "size"),
|
||||
BuiltinTypes[IDLBuiltinType.Types.unsigned_long],
|
||||
True,
|
||||
maplikeOrSetlike=self))
|
||||
sizeAttr = IDLAttribute(self.location,
|
||||
IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"), "size"),
|
||||
BuiltinTypes[IDLBuiltinType.Types.unsigned_long],
|
||||
True,
|
||||
maplikeOrSetlike=self)
|
||||
# This should be non-enumerable.
|
||||
sizeAttr.addExtendedAttributes(
|
||||
[IDLExtendedAttribute(self.location, ("NonEnumerable",))])
|
||||
members.append(sizeAttr)
|
||||
self.reserved_ro_names = ["size"]
|
||||
self.disallowedMemberNames.append("size")
|
||||
|
||||
|
@ -3966,7 +3975,8 @@ class IDLConst(IDLInterfaceMember):
|
|||
elif (identifier == "Pref" or
|
||||
identifier == "ChromeOnly" or
|
||||
identifier == "Func" or
|
||||
identifier == "SecureContext"):
|
||||
identifier == "SecureContext" or
|
||||
identifier == "NonEnumerable"):
|
||||
# Known attributes that we don't need to do anything with here
|
||||
pass
|
||||
else:
|
||||
|
@ -4344,7 +4354,8 @@ class IDLAttribute(IDLInterfaceMember):
|
|||
identifier == "NeedsSubjectPrincipal" or
|
||||
identifier == "NeedsCallerType" or
|
||||
identifier == "ReturnValueNeedsContainsHack" or
|
||||
identifier == "BinaryName"):
|
||||
identifier == "BinaryName" or
|
||||
identifier == "NonEnumerable"):
|
||||
# Known attributes that we don't need to do anything with here
|
||||
pass
|
||||
else:
|
||||
|
@ -5081,7 +5092,8 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
|
|||
identifier == "BinaryName" or
|
||||
identifier == "NeedsSubjectPrincipal" or
|
||||
identifier == "NeedsCallerType" or
|
||||
identifier == "StaticClassOverride"):
|
||||
identifier == "StaticClassOverride" or
|
||||
identifier == "NonEnumerable"):
|
||||
# Known attributes that we don't need to do anything with here
|
||||
pass
|
||||
else:
|
||||
|
|
|
@ -978,6 +978,10 @@ public:
|
|||
int8_t Dashed_attribute();
|
||||
void Dashed_method();
|
||||
|
||||
bool NonEnumerableAttr() const;
|
||||
void SetNonEnumerableAttr(bool);
|
||||
void NonEnumerableMethod();
|
||||
|
||||
// Methods and properties imported via "implements"
|
||||
bool ImplementedProperty();
|
||||
void SetImplementedProperty(bool);
|
||||
|
|
|
@ -975,6 +975,14 @@ interface TestInterface {
|
|||
attribute byte dashed-attribute;
|
||||
void dashed-method();
|
||||
|
||||
// [NonEnumerable] tests
|
||||
[NonEnumerable]
|
||||
attribute boolean nonEnumerableAttr;
|
||||
[NonEnumerable]
|
||||
const boolean nonEnumerableConst = true;
|
||||
[NonEnumerable]
|
||||
void nonEnumerableMethod();
|
||||
|
||||
// If you add things here, add them to TestExampleGen and TestJSImplGen as well
|
||||
};
|
||||
|
||||
|
|
|
@ -802,6 +802,14 @@ interface TestExampleInterface {
|
|||
attribute byte dashed-attribute;
|
||||
void dashed-method();
|
||||
|
||||
// [NonEnumerable] tests
|
||||
[NonEnumerable]
|
||||
attribute boolean nonEnumerableAttr;
|
||||
[NonEnumerable]
|
||||
const boolean nonEnumerableConst = true;
|
||||
[NonEnumerable]
|
||||
void nonEnumerableMethod();
|
||||
|
||||
// If you add things here, add them to TestCodeGen and TestJSImplGen as well
|
||||
};
|
||||
|
||||
|
|
|
@ -822,6 +822,14 @@ interface TestJSImplInterface {
|
|||
attribute byte dashed-attribute;
|
||||
void dashed-method();
|
||||
|
||||
// [NonEnumerable] tests
|
||||
[NonEnumerable]
|
||||
attribute boolean nonEnumerableAttr;
|
||||
[NonEnumerable]
|
||||
const boolean nonEnumerableConst = true;
|
||||
[NonEnumerable]
|
||||
void nonEnumerableMethod();
|
||||
|
||||
// If you add things here, add them to TestCodeGen as well
|
||||
};
|
||||
|
||||
|
|
|
@ -85,7 +85,7 @@ MultipartBlobImpl::GetInternalStream(nsIInputStream** aStream,
|
|||
}
|
||||
}
|
||||
|
||||
stream.forget(aStream);
|
||||
CallQueryInterface(stream, aStream);
|
||||
}
|
||||
|
||||
already_AddRefed<BlobImpl>
|
||||
|
|
|
@ -394,8 +394,13 @@ FSMultipartFormData::FSMultipartFormData(NotNull<const Encoding*> aEncoding,
|
|||
nsIContent* aOriginatingElement)
|
||||
: EncodingFormSubmission(aEncoding, aOriginatingElement)
|
||||
{
|
||||
mPostDataStream =
|
||||
mPostData =
|
||||
do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1");
|
||||
|
||||
nsCOMPtr<nsIInputStream> inputStream = do_QueryInterface(mPostData);
|
||||
MOZ_ASSERT(SameCOMIdentity(mPostData, inputStream));
|
||||
mPostDataStream = inputStream;
|
||||
|
||||
mTotalLength = 0;
|
||||
|
||||
mBoundary.AssignLiteral("---------------------------");
|
||||
|
@ -600,7 +605,7 @@ FSMultipartFormData::AddDataChunk(const nsACString& aName,
|
|||
// here, since we're about to add the file input stream
|
||||
AddPostDataStream();
|
||||
|
||||
mPostDataStream->AppendStream(aInputStream);
|
||||
mPostData->AppendStream(aInputStream);
|
||||
mTotalLength += aInputStreamSize;
|
||||
}
|
||||
|
||||
|
@ -640,7 +645,7 @@ FSMultipartFormData::AddPostDataStream()
|
|||
mPostDataChunk);
|
||||
NS_ASSERTION(postDataChunkStream, "Could not open a stream for POST!");
|
||||
if (postDataChunkStream) {
|
||||
mPostDataStream->AppendStream(postDataChunkStream);
|
||||
mPostData->AppendStream(postDataChunkStream);
|
||||
mTotalLength += mPostDataChunk.Length();
|
||||
}
|
||||
|
||||
|
|
|
@ -193,7 +193,13 @@ private:
|
|||
* chunks--string streams and file streams interleaved to make one big POST
|
||||
* stream.
|
||||
*/
|
||||
nsCOMPtr<nsIMultiplexInputStream> mPostDataStream;
|
||||
nsCOMPtr<nsIMultiplexInputStream> mPostData;
|
||||
|
||||
/**
|
||||
* The same stream, but as an nsIInputStream.
|
||||
* Raw pointers because it is just QI of mInputStream.
|
||||
*/
|
||||
nsIInputStream* mPostDataStream;
|
||||
|
||||
/**
|
||||
* The current string chunk. When a file is hit, the string chunk gets
|
||||
|
|
|
@ -11,60 +11,28 @@ interface nsIDOMEvent;
|
|||
interface nsIMessageBroadcaster;
|
||||
interface mozIDOMWindowProxy;
|
||||
|
||||
[scriptable, uuid(78bdcb41-1efa-409f-aaba-70842213f80f)]
|
||||
// Scriptable only so Components.interfaces.nsIDOMChromeWindow works.
|
||||
[scriptable, builtinclass, uuid(78bdcb41-1efa-409f-aaba-70842213f80f)]
|
||||
interface nsIDOMChromeWindow : nsISupports
|
||||
{
|
||||
const unsigned short STATE_MAXIMIZED = 1;
|
||||
const unsigned short STATE_MINIMIZED = 2;
|
||||
const unsigned short STATE_NORMAL = 3;
|
||||
const unsigned short STATE_FULLSCREEN = 4;
|
||||
|
||||
readonly attribute unsigned short windowState;
|
||||
|
||||
/**
|
||||
* browserDOMWindow provides access to yet another layer of
|
||||
* utility functions implemented by chrome script. It will be null
|
||||
* for DOMWindows not corresponding to browsers.
|
||||
*/
|
||||
attribute nsIBrowserDOMWindow browserDOMWindow;
|
||||
|
||||
void getAttention();
|
||||
|
||||
void getAttentionWithCycleCount(in long aCycleCount);
|
||||
|
||||
void setCursor(in DOMString cursor);
|
||||
|
||||
void maximize();
|
||||
void minimize();
|
||||
void restore();
|
||||
|
||||
/**
|
||||
* Notify a default button is loaded on a dialog or a wizard.
|
||||
* defaultButton is the default button.
|
||||
*/
|
||||
void notifyDefaultButtonLoaded(in nsIDOMElement defaultButton);
|
||||
[noscript]
|
||||
readonly attribute nsIBrowserDOMWindow browserDOMWindow;
|
||||
|
||||
[noscript]
|
||||
readonly attribute nsIMessageBroadcaster messageManager;
|
||||
|
||||
/**
|
||||
* Returns the message manager identified by the given group name that
|
||||
* manages all frame loaders belonging to that group.
|
||||
*/
|
||||
[noscript]
|
||||
nsIMessageBroadcaster getGroupMessageManager(in AString group);
|
||||
|
||||
/**
|
||||
* On some operating systems, we must allow the window manager to
|
||||
* handle window dragging. This function tells the window manager to
|
||||
* start dragging the window. This function will fail unless called
|
||||
* while the left mouse button is held down, callers must check this.
|
||||
*
|
||||
* The optional panel argument should be set when moving a panel.
|
||||
*
|
||||
* Returns NS_ERROR_NOT_IMPLEMENTED (and thus throws in JS) if the OS
|
||||
* doesn't support this.
|
||||
*/
|
||||
void beginWindowMove(in nsIDOMEvent mouseDownEvent, [optional] in nsIDOMElement panel);
|
||||
|
||||
/**
|
||||
* These methods provide a way to specify the opener value for the content in
|
||||
* the window before the content itself is created. This is important in order
|
||||
|
@ -76,6 +44,8 @@ interface nsIDOMChromeWindow : nsISupports
|
|||
* take the value set earlier, and null out the value in the
|
||||
* nsIDOMChromeWindow.
|
||||
*/
|
||||
[noscript]
|
||||
void setOpenerForInitialContentBrowser(in mozIDOMWindowProxy aOpener);
|
||||
[noscript]
|
||||
mozIDOMWindowProxy takeOpenerForInitialContentBrowser();
|
||||
};
|
||||
|
|
|
@ -330,6 +330,8 @@ MozAutoGainControlWarning=mozAutoGainControl is deprecated. Use autoGainControl
|
|||
MozNoiseSuppressionWarning=mozNoiseSuppression is deprecated. Use noiseSuppression instead.
|
||||
# LOCALIZATION NOTE: Do not translate xml:base.
|
||||
XMLBaseAttributeWarning=Use of xml:base attribute is deprecated and will be removed soon. Please remove any use of it.
|
||||
# LOCALIZATION NOTE: Do not translate "content", "Window", and "window.top"
|
||||
WindowContentUntrustedWarning=The ‘content’ attribute of Window objects is deprecated. Please use ‘window.top’ instead.
|
||||
# LOCALIZATION NOTE: %S is the tag name of the element that starts the loop
|
||||
SVGReferenceLoopWarning=There is an SVG <%S> reference loop in this document, which will prevent the document rendering correctly.
|
||||
# LOCALIZATION NOTE: %S is the tag name of the element that starts the chain
|
||||
|
|
|
@ -210,6 +210,7 @@ TCPSocket::CreateStream()
|
|||
|
||||
mMultiplexStream = do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1", &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsCOMPtr<nsIInputStream> stream = do_QueryInterface(mMultiplexStream);
|
||||
|
||||
mMultiplexStreamCopier = do_CreateInstance("@mozilla.org/network/async-stream-copier;1", &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
@ -218,7 +219,7 @@ TCPSocket::CreateStream()
|
|||
do_GetService("@mozilla.org/network/socket-transport-service;1");
|
||||
|
||||
nsCOMPtr<nsIEventTarget> target = do_QueryInterface(sts);
|
||||
rv = mMultiplexStreamCopier->Init(mMultiplexStream,
|
||||
rv = mMultiplexStreamCopier->Init(stream,
|
||||
mSocketOutputStream,
|
||||
target,
|
||||
true, /* source buffered */
|
||||
|
@ -596,7 +597,8 @@ TCPSocket::BufferedAmount()
|
|||
}
|
||||
if (mMultiplexStream) {
|
||||
uint64_t available = 0;
|
||||
mMultiplexStream->Available(&available);
|
||||
nsCOMPtr<nsIInputStream> stream(do_QueryInterface(mMultiplexStream));
|
||||
stream->Available(&available);
|
||||
return available;
|
||||
}
|
||||
return 0;
|
||||
|
|
|
@ -232,6 +232,8 @@ public:
|
|||
return false;
|
||||
}
|
||||
void finalize(JSFreeOp* fop, JSObject* proxy) const override;
|
||||
|
||||
size_t objectMoved(JSObject* obj, JSObject* old) const override;
|
||||
};
|
||||
|
||||
const char NPObjWrapperProxyHandler::family = 0;
|
||||
|
@ -241,9 +243,6 @@ static bool
|
|||
NPObjWrapper_Resolve(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
|
||||
bool* resolved, JS::MutableHandle<JSObject*> method);
|
||||
|
||||
static void
|
||||
NPObjWrapper_ObjectMoved(JSObject *obj, const JSObject *old);
|
||||
|
||||
static bool
|
||||
NPObjWrapper_toPrimitive(JSContext *cx, unsigned argc, JS::Value *vp);
|
||||
|
||||
|
@ -253,14 +252,9 @@ CreateNPObjectMember(NPP npp, JSContext *cx,
|
|||
JS::Handle<jsid> id, NPVariant* getPropertyResult,
|
||||
JS::MutableHandle<JS::Value> vp);
|
||||
|
||||
static const js::ClassExtension sNPObjWrapperProxyClassExtension = PROXY_MAKE_EXT(
|
||||
NPObjWrapper_ObjectMoved
|
||||
);
|
||||
|
||||
const js::Class sNPObjWrapperProxyClass = PROXY_CLASS_WITH_EXT(
|
||||
const js::Class sNPObjWrapperProxyClass = PROXY_CLASS_DEF(
|
||||
NPRUNTIME_JSCLASS_NAME,
|
||||
JSCLASS_HAS_RESERVED_SLOTS(1),
|
||||
&sNPObjWrapperProxyClassExtension);
|
||||
JSCLASS_HAS_RESERVED_SLOTS(1));
|
||||
|
||||
typedef struct NPObjectMemberPrivate {
|
||||
JS::Heap<JSObject *> npobjWrapper;
|
||||
|
@ -1787,19 +1781,19 @@ NPObjWrapperProxyHandler::finalize(JSFreeOp* fop, JSObject* proxy) const
|
|||
sDelayedReleases->AppendElement(npobj);
|
||||
}
|
||||
|
||||
static void
|
||||
NPObjWrapper_ObjectMoved(JSObject *obj, const JSObject *old)
|
||||
size_t
|
||||
NPObjWrapperProxyHandler::objectMoved(JSObject *obj, JSObject *old) const
|
||||
{
|
||||
// The wrapper JSObject has been moved, so we need to update the entry in the
|
||||
// sNPObjWrappers hash table, if present.
|
||||
|
||||
if (!sNPObjWrappers) {
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
NPObject *npobj = (NPObject *)js::GetProxyPrivate(obj).toPrivate();
|
||||
if (!npobj) {
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Calling PLDHashTable::Search() will not result in GC.
|
||||
|
@ -1810,6 +1804,7 @@ NPObjWrapper_ObjectMoved(JSObject *obj, const JSObject *old)
|
|||
MOZ_ASSERT(entry && entry->mJSObj);
|
||||
MOZ_ASSERT(entry->mJSObj == old);
|
||||
entry->mJSObj = obj;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -240,6 +240,7 @@ PresentationTCPSessionTransport::CreateStream()
|
|||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
nsCOMPtr<nsIInputStream> stream = do_QueryInterface(mMultiplexStream);
|
||||
|
||||
mMultiplexStreamCopier = do_CreateInstance("@mozilla.org/network/async-stream-copier;1", &rv);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
|
@ -253,7 +254,7 @@ PresentationTCPSessionTransport::CreateStream()
|
|||
}
|
||||
|
||||
nsCOMPtr<nsIEventTarget> target = do_QueryInterface(sts);
|
||||
rv = mMultiplexStreamCopier->Init(mMultiplexStream,
|
||||
rv = mMultiplexStreamCopier->Init(stream,
|
||||
mSocketOutputStream,
|
||||
target,
|
||||
true, /* source buffered */
|
||||
|
|
|
@ -223,7 +223,7 @@ U2F::Register(const nsAString& aAppId,
|
|||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!mPromiseHolder.Exists());
|
||||
Cancel();
|
||||
MOZ_ASSERT(mRegisterCallback.isNothing());
|
||||
mRegisterCallback = Some(nsMainThreadPtrHandle<U2FRegisterCallback>(
|
||||
new nsMainThreadPtrHolder<U2FRegisterCallback>(
|
||||
|
@ -320,7 +320,7 @@ U2F::Sign(const nsAString& aAppId,
|
|||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!mPromiseHolder.Exists());
|
||||
Cancel();
|
||||
MOZ_ASSERT(mSignCallback.isNothing());
|
||||
mSignCallback = Some(nsMainThreadPtrHandle<U2FSignCallback>(
|
||||
new nsMainThreadPtrHolder<U2FSignCallback>(
|
||||
|
@ -385,5 +385,32 @@ U2F::Sign(const nsAString& aAppId,
|
|||
->Track(mPromiseHolder);
|
||||
}
|
||||
|
||||
void
|
||||
U2F::Cancel()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
const ErrorCode errorCode = ErrorCode::OTHER_ERROR;
|
||||
|
||||
if (mRegisterCallback.isSome()) {
|
||||
RegisterResponse response;
|
||||
response.mErrorCode.Construct(static_cast<uint32_t>(errorCode));
|
||||
ExecuteCallback(response, mRegisterCallback);
|
||||
}
|
||||
|
||||
if (mSignCallback.isSome()) {
|
||||
SignResponse response;
|
||||
response.mErrorCode.Construct(static_cast<uint32_t>(errorCode));
|
||||
ExecuteCallback(response, mSignCallback);
|
||||
}
|
||||
|
||||
RefPtr<U2FManager> mgr = U2FManager::Get();
|
||||
if (mgr) {
|
||||
mgr->Cancel(NS_ERROR_DOM_OPERATION_ERR);
|
||||
}
|
||||
|
||||
mPromiseHolder.DisconnectIfExists();
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -70,6 +70,9 @@ public:
|
|||
ErrorResult& aRv);
|
||||
|
||||
private:
|
||||
void
|
||||
Cancel();
|
||||
|
||||
nsString mOrigin;
|
||||
nsCOMPtr<nsPIDOMWindowInner> mParent;
|
||||
nsCOMPtr<nsISerialEventTarget> mEventTarget;
|
||||
|
|
|
@ -104,17 +104,20 @@ async function doTests() {
|
|||
var signedData = assembleRegistrationSignedData(state.appParam, state.challengeParam, state.keyHandleBytes, state.publicKeyBytes);
|
||||
return verifySignature(attestationPublicKey, signedData, state.attestationSig);
|
||||
}).then(function(verified) {
|
||||
local_ok(verified, "Attestation Certificate signature verified")
|
||||
local_ok(verified, "Attestation Certificate signature verified");
|
||||
// Import the public key of the U2F token into WebCrypto
|
||||
return importPublicKey(state.publicKeyBytes)
|
||||
return importPublicKey(state.publicKeyBytes);
|
||||
}).then(function(key) {
|
||||
state.publicKey = key;
|
||||
local_ok(true, "Imported public key")
|
||||
local_ok(true, "Imported public key");
|
||||
|
||||
// Ensure the attestation certificate is properly self-signed
|
||||
return state.attestationCert.verify()
|
||||
return state.attestationCert.verify();
|
||||
}).then(function(verified) {
|
||||
local_ok(verified, "Register attestation signature verified")
|
||||
if (!verified) {
|
||||
local_ok(verified, "Cert problem: " + bytesToBase64UrlSafe(state.attestation));
|
||||
}
|
||||
local_ok(verified, "Register attestation signature verified");
|
||||
});
|
||||
|
||||
state.regKey = {
|
||||
|
|
|
@ -18,7 +18,7 @@ static void
|
|||
u2f_register_callback(uint64_t aTransactionId, rust_u2f_result* aResult)
|
||||
{
|
||||
StaticMutexAutoLock lock(gInstanceMutex);
|
||||
if (NS_WARN_IF(!gInstance || !gPBackgroundThread)) {
|
||||
if (!gInstance || NS_WARN_IF(!gPBackgroundThread)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -35,7 +35,7 @@ static void
|
|||
u2f_sign_callback(uint64_t aTransactionId, rust_u2f_result* aResult)
|
||||
{
|
||||
StaticMutexAutoLock lock(gInstanceMutex);
|
||||
if (NS_WARN_IF(!gInstance || !gPBackgroundThread)) {
|
||||
if (!gInstance || NS_WARN_IF(!gPBackgroundThread)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -63,15 +63,21 @@ U2FHIDTokenManager::U2FHIDTokenManager() : mTransactionId(0)
|
|||
|
||||
U2FHIDTokenManager::~U2FHIDTokenManager()
|
||||
{
|
||||
StaticMutexAutoLock lock(gInstanceMutex);
|
||||
MOZ_ASSERT(NS_GetCurrentThread() == gPBackgroundThread);
|
||||
{
|
||||
StaticMutexAutoLock lock(gInstanceMutex);
|
||||
MOZ_ASSERT(NS_GetCurrentThread() == gPBackgroundThread);
|
||||
|
||||
mRegisterPromise.RejectIfExists(NS_ERROR_DOM_UNKNOWN_ERR, __func__);
|
||||
mSignPromise.RejectIfExists(NS_ERROR_DOM_UNKNOWN_ERR, __func__);
|
||||
mRegisterPromise.RejectIfExists(NS_ERROR_DOM_UNKNOWN_ERR, __func__);
|
||||
mSignPromise.RejectIfExists(NS_ERROR_DOM_UNKNOWN_ERR, __func__);
|
||||
|
||||
gInstance = nullptr;
|
||||
}
|
||||
|
||||
// Release gInstanceMutex before we call U2FManager::drop(). It will wait
|
||||
// for the work queue thread to join, and that requires the
|
||||
// u2f_{register,sign}_callback to lock and return.
|
||||
rust_u2f_mgr_free(mU2FManager);
|
||||
mU2FManager = nullptr;
|
||||
gInstance = nullptr;
|
||||
}
|
||||
|
||||
// A U2F Register operation causes a new key pair to be generated by the token.
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
name = "u2fhid"
|
||||
version = "0.1.0"
|
||||
authors = ["Kyle Machulis <kyle@nonpolynomial.com>", "J.C. Jones <jc@mozilla.com>", "Tim Taubert <ttaubert@mozilla.com>"]
|
||||
build = "build.rs"
|
||||
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
libudev = "^0.2"
|
||||
|
@ -19,6 +18,7 @@ log = "0.3"
|
|||
env_logger = "0.4.1"
|
||||
libc = "^0.2"
|
||||
boxfnonce = "0.0.3"
|
||||
runloop = "0.1.0"
|
||||
|
||||
[dev-dependencies]
|
||||
rust-crypto = "^0.2"
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
fn main() {
|
||||
#[cfg(any(target_os = "macos"))]
|
||||
println!("cargo:rustc-link-lib=framework=IOKit");
|
||||
}
|
|
@ -32,9 +32,9 @@ extern crate log;
|
|||
extern crate rand;
|
||||
extern crate libc;
|
||||
extern crate boxfnonce;
|
||||
extern crate runloop;
|
||||
|
||||
mod consts;
|
||||
mod runloop;
|
||||
mod u2ftypes;
|
||||
mod u2fprotocol;
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ impl PlatformManager {
|
|||
|
||||
let cbc = callback.clone();
|
||||
|
||||
let thread = RunLoop::new(
|
||||
let thread = RunLoop::new_with_timeout(
|
||||
move |alive| {
|
||||
let mut devices = DeviceMap::new();
|
||||
let monitor = try_or!(Monitor::new(), |e| { callback.call(Err(e)); });
|
||||
|
@ -87,7 +87,7 @@ impl PlatformManager {
|
|||
|
||||
let cbc = callback.clone();
|
||||
|
||||
let thread = RunLoop::new(
|
||||
let thread = RunLoop::new_with_timeout(
|
||||
move |alive| {
|
||||
let mut devices = DeviceMap::new();
|
||||
let monitor = try_or!(Monitor::new(), |e| { callback.call(Err(e)); });
|
||||
|
|
|
@ -61,48 +61,45 @@ impl Monitor {
|
|||
pub fn new() -> io::Result<Self> {
|
||||
let (tx, rx) = channel();
|
||||
|
||||
let thread = RunLoop::new(
|
||||
move |alive| -> io::Result<()> {
|
||||
let ctx = libudev::Context::new()?;
|
||||
let mut enumerator = libudev::Enumerator::new(&ctx)?;
|
||||
enumerator.match_subsystem(UDEV_SUBSYSTEM)?;
|
||||
let thread = RunLoop::new(move |alive| -> io::Result<()> {
|
||||
let ctx = libudev::Context::new()?;
|
||||
let mut enumerator = libudev::Enumerator::new(&ctx)?;
|
||||
enumerator.match_subsystem(UDEV_SUBSYSTEM)?;
|
||||
|
||||
// Iterate all existing devices.
|
||||
for dev in enumerator.scan_devices()? {
|
||||
if let Some(path) = dev.devnode().map(|p| p.to_owned().into_os_string()) {
|
||||
tx.send(Event::Add(path)).map_err(to_io_err)?;
|
||||
}
|
||||
// Iterate all existing devices.
|
||||
for dev in enumerator.scan_devices()? {
|
||||
if let Some(path) = dev.devnode().map(|p| p.to_owned().into_os_string()) {
|
||||
tx.send(Event::Add(path)).map_err(to_io_err)?;
|
||||
}
|
||||
}
|
||||
|
||||
let mut monitor = libudev::Monitor::new(&ctx)?;
|
||||
monitor.match_subsystem(UDEV_SUBSYSTEM)?;
|
||||
let mut monitor = libudev::Monitor::new(&ctx)?;
|
||||
monitor.match_subsystem(UDEV_SUBSYSTEM)?;
|
||||
|
||||
// Start listening for new devices.
|
||||
let mut socket = monitor.listen()?;
|
||||
let mut fds = vec![
|
||||
::libc::pollfd {
|
||||
fd: socket.as_raw_fd(),
|
||||
events: POLLIN,
|
||||
revents: 0,
|
||||
},
|
||||
];
|
||||
// Start listening for new devices.
|
||||
let mut socket = monitor.listen()?;
|
||||
let mut fds = vec![
|
||||
::libc::pollfd {
|
||||
fd: socket.as_raw_fd(),
|
||||
events: POLLIN,
|
||||
revents: 0,
|
||||
},
|
||||
];
|
||||
|
||||
// Loop until we're stopped by the controlling thread, or fail.
|
||||
while alive() {
|
||||
// Wait for new events, break on failure.
|
||||
poll(&mut fds)?;
|
||||
// Loop until we're stopped by the controlling thread, or fail.
|
||||
while alive() {
|
||||
// Wait for new events, break on failure.
|
||||
poll(&mut fds)?;
|
||||
|
||||
// Send the event over.
|
||||
let udev_event = socket.receive_event();
|
||||
if let Some(event) = udev_event.and_then(Event::from_udev) {
|
||||
tx.send(event).map_err(to_io_err)?;
|
||||
}
|
||||
// Send the event over.
|
||||
let udev_event = socket.receive_event();
|
||||
if let Some(event) = udev_event.and_then(Event::from_udev) {
|
||||
tx.send(event).map_err(to_io_err)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
},
|
||||
0, /* no timeout */
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
Ok(Self { rx, thread })
|
||||
}
|
||||
|
|
|
@ -50,6 +50,7 @@ pub struct IOHIDDeviceRef(*const c_void);
|
|||
unsafe impl Send for IOHIDDeviceRef {}
|
||||
unsafe impl Sync for IOHIDDeviceRef {}
|
||||
|
||||
#[link(name = "IOKit", kind = "framework")]
|
||||
extern "C" {
|
||||
// IOHIDManager
|
||||
pub fn IOHIDManagerCreate(
|
||||
|
|
|
@ -45,7 +45,7 @@ impl PlatformManager {
|
|||
|
||||
let cbc = callback.clone();
|
||||
|
||||
let thread = RunLoop::new(
|
||||
let thread = RunLoop::new_with_timeout(
|
||||
move |alive| {
|
||||
let mut devices = DeviceMap::new();
|
||||
let monitor = try_or!(Monitor::new(), |e| { callback.call(Err(e)); });
|
||||
|
@ -98,7 +98,7 @@ impl PlatformManager {
|
|||
|
||||
let cbc = callback.clone();
|
||||
|
||||
let thread = RunLoop::new(
|
||||
let thread = RunLoop::new_with_timeout(
|
||||
move |alive| {
|
||||
let mut devices = DeviceMap::new();
|
||||
let monitor = try_or!(Monitor::new(), |e| { callback.call(Err(e)); });
|
||||
|
|
|
@ -31,53 +31,50 @@ impl Monitor {
|
|||
pub fn new() -> io::Result<Self> {
|
||||
let (tx, rx) = channel();
|
||||
|
||||
let thread = RunLoop::new(
|
||||
move |alive| -> io::Result<()> {
|
||||
let tx_box = Box::new(tx);
|
||||
let tx_ptr = Box::into_raw(tx_box) as *mut libc::c_void;
|
||||
let thread = RunLoop::new(move |alive| -> io::Result<()> {
|
||||
let tx_box = Box::new(tx);
|
||||
let tx_ptr = Box::into_raw(tx_box) as *mut libc::c_void;
|
||||
|
||||
// This will keep `tx` alive only for the scope.
|
||||
let _tx = unsafe { Box::from_raw(tx_ptr as *mut Sender<Event>) };
|
||||
// This will keep `tx` alive only for the scope.
|
||||
let _tx = unsafe { Box::from_raw(tx_ptr as *mut Sender<Event>) };
|
||||
|
||||
// Create and initialize a scoped HID manager.
|
||||
let manager = IOHIDManager::new()?;
|
||||
// Create and initialize a scoped HID manager.
|
||||
let manager = IOHIDManager::new()?;
|
||||
|
||||
// Match only U2F devices.
|
||||
let dict = IOHIDDeviceMatcher::new();
|
||||
unsafe { IOHIDManagerSetDeviceMatching(manager.get(), dict.get()) };
|
||||
// Match only U2F devices.
|
||||
let dict = IOHIDDeviceMatcher::new();
|
||||
unsafe { IOHIDManagerSetDeviceMatching(manager.get(), dict.get()) };
|
||||
|
||||
// Register callbacks.
|
||||
unsafe {
|
||||
IOHIDManagerRegisterDeviceMatchingCallback(
|
||||
manager.get(),
|
||||
Monitor::device_add_cb,
|
||||
tx_ptr,
|
||||
);
|
||||
IOHIDManagerRegisterDeviceRemovalCallback(
|
||||
manager.get(),
|
||||
Monitor::device_remove_cb,
|
||||
tx_ptr,
|
||||
);
|
||||
// Register callbacks.
|
||||
unsafe {
|
||||
IOHIDManagerRegisterDeviceMatchingCallback(
|
||||
manager.get(),
|
||||
Monitor::device_add_cb,
|
||||
tx_ptr,
|
||||
);
|
||||
IOHIDManagerRegisterDeviceRemovalCallback(
|
||||
manager.get(),
|
||||
Monitor::device_remove_cb,
|
||||
tx_ptr,
|
||||
);
|
||||
}
|
||||
|
||||
// Run the Event Loop. CFRunLoopRunInMode() will dispatch HID
|
||||
// input reports into the various callbacks
|
||||
while alive() {
|
||||
trace!("OSX Runloop running, handle={:?}", thread::current());
|
||||
|
||||
if unsafe { CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, 0) } ==
|
||||
kCFRunLoopRunStopped
|
||||
{
|
||||
debug!("OSX Runloop device stopped.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
debug!("OSX Runloop completed, handle={:?}", thread::current());
|
||||
|
||||
// Run the Event Loop. CFRunLoopRunInMode() will dispatch HID
|
||||
// input reports into the various callbacks
|
||||
while alive() {
|
||||
trace!("OSX Runloop running, handle={:?}", thread::current());
|
||||
|
||||
if unsafe { CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, 0) } ==
|
||||
kCFRunLoopRunStopped
|
||||
{
|
||||
debug!("OSX Runloop device stopped.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
debug!("OSX Runloop completed, handle={:?}", thread::current());
|
||||
|
||||
Ok(())
|
||||
},
|
||||
0, /* no timeout */
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
Ok(Self { rx, thread })
|
||||
}
|
||||
|
|
|
@ -38,48 +38,45 @@ impl U2FManager {
|
|||
let (tx, rx) = channel();
|
||||
|
||||
// Start a new work queue thread.
|
||||
let queue = try!(RunLoop::new(
|
||||
move |alive| {
|
||||
let mut pm = PlatformManager::new();
|
||||
let queue = RunLoop::new(move |alive| {
|
||||
let mut pm = PlatformManager::new();
|
||||
|
||||
while alive() {
|
||||
match rx.recv_timeout(Duration::from_millis(50)) {
|
||||
Ok(QueueAction::Register {
|
||||
timeout,
|
||||
challenge,
|
||||
application,
|
||||
callback,
|
||||
}) => {
|
||||
// This must not block, otherwise we can't cancel.
|
||||
pm.register(timeout, challenge, application, callback);
|
||||
}
|
||||
Ok(QueueAction::Sign {
|
||||
timeout,
|
||||
challenge,
|
||||
application,
|
||||
key_handles,
|
||||
callback,
|
||||
}) => {
|
||||
// This must not block, otherwise we can't cancel.
|
||||
pm.sign(timeout, challenge, application, key_handles, callback);
|
||||
}
|
||||
Ok(QueueAction::Cancel) => {
|
||||
// Cancelling must block so that we don't start a new
|
||||
// polling thread before the old one has shut down.
|
||||
pm.cancel();
|
||||
}
|
||||
Err(RecvTimeoutError::Disconnected) => {
|
||||
break;
|
||||
}
|
||||
_ => { /* continue */ }
|
||||
while alive() {
|
||||
match rx.recv_timeout(Duration::from_millis(50)) {
|
||||
Ok(QueueAction::Register {
|
||||
timeout,
|
||||
challenge,
|
||||
application,
|
||||
callback,
|
||||
}) => {
|
||||
// This must not block, otherwise we can't cancel.
|
||||
pm.register(timeout, challenge, application, callback);
|
||||
}
|
||||
Ok(QueueAction::Sign {
|
||||
timeout,
|
||||
challenge,
|
||||
application,
|
||||
key_handles,
|
||||
callback,
|
||||
}) => {
|
||||
// This must not block, otherwise we can't cancel.
|
||||
pm.sign(timeout, challenge, application, key_handles, callback);
|
||||
}
|
||||
Ok(QueueAction::Cancel) => {
|
||||
// Cancelling must block so that we don't start a new
|
||||
// polling thread before the old one has shut down.
|
||||
pm.cancel();
|
||||
}
|
||||
Err(RecvTimeoutError::Disconnected) => {
|
||||
break;
|
||||
}
|
||||
_ => { /* continue */ }
|
||||
}
|
||||
}
|
||||
|
||||
// Cancel any ongoing activity.
|
||||
pm.cancel();
|
||||
},
|
||||
0, /* no timeout */
|
||||
));
|
||||
// Cancel any ongoing activity.
|
||||
pm.cancel();
|
||||
})?;
|
||||
|
||||
Ok(Self {
|
||||
queue: queue,
|
||||
|
|
|
@ -40,7 +40,7 @@ impl PlatformManager {
|
|||
|
||||
let cbc = callback.clone();
|
||||
|
||||
let thread = RunLoop::new(
|
||||
let thread = RunLoop::new_with_timeout(
|
||||
move |alive| {
|
||||
let mut devices = DeviceMap::new();
|
||||
let monitor = try_or!(Monitor::new(), |e| { callback.call(Err(e)); });
|
||||
|
@ -86,7 +86,7 @@ impl PlatformManager {
|
|||
|
||||
let cbc = callback.clone();
|
||||
|
||||
let thread = RunLoop::new(
|
||||
let thread = RunLoop::new_with_timeout(
|
||||
move |alive| {
|
||||
let mut devices = DeviceMap::new();
|
||||
let monitor = try_or!(Monitor::new(), |e| { callback.call(Err(e)); });
|
||||
|
|
|
@ -37,35 +37,32 @@ impl Monitor {
|
|||
pub fn new() -> io::Result<Self> {
|
||||
let (tx, rx) = channel();
|
||||
|
||||
let thread = RunLoop::new(
|
||||
move |alive| -> io::Result<()> {
|
||||
let mut stored = HashSet::new();
|
||||
let thread = RunLoop::new(move |alive| -> io::Result<()> {
|
||||
let mut stored = HashSet::new();
|
||||
|
||||
while alive() {
|
||||
let device_info_set = DeviceInfoSet::new()?;
|
||||
let devices = HashSet::from_iter(device_info_set.devices());
|
||||
while alive() {
|
||||
let device_info_set = DeviceInfoSet::new()?;
|
||||
let devices = HashSet::from_iter(device_info_set.devices());
|
||||
|
||||
// Remove devices that are gone.
|
||||
for path in stored.difference(&devices) {
|
||||
tx.send(Event::Remove(path.clone())).map_err(to_io_err)?;
|
||||
}
|
||||
|
||||
// Add devices that were plugged in.
|
||||
for path in devices.difference(&stored) {
|
||||
tx.send(Event::Add(path.clone())).map_err(to_io_err)?;
|
||||
}
|
||||
|
||||
// Remember the new set.
|
||||
stored = devices;
|
||||
|
||||
// Wait a little before looking for devices again.
|
||||
thread::sleep(Duration::from_millis(100));
|
||||
// Remove devices that are gone.
|
||||
for path in stored.difference(&devices) {
|
||||
tx.send(Event::Remove(path.clone())).map_err(to_io_err)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
},
|
||||
0, /* no timeout */
|
||||
)?;
|
||||
// Add devices that were plugged in.
|
||||
for path in devices.difference(&stored) {
|
||||
tx.send(Event::Add(path.clone())).map_err(to_io_err)?;
|
||||
}
|
||||
|
||||
// Remember the new set.
|
||||
stored = devices;
|
||||
|
||||
// Wait a little before looking for devices again.
|
||||
thread::sleep(Duration::from_millis(100));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
Ok(Self {
|
||||
rx: rx,
|
||||
|
|
|
@ -340,7 +340,8 @@ partial interface Window {
|
|||
#ifdef NIGHTLY_BUILD
|
||||
ChromeOnly,
|
||||
#endif
|
||||
Replaceable, Throws, NeedsCallerType] readonly attribute object? content;
|
||||
NonEnumerable, Replaceable, Throws, NeedsCallerType]
|
||||
readonly attribute object? content;
|
||||
|
||||
[Throws, ChromeOnly] any getInterface(IID iid);
|
||||
|
||||
|
@ -374,6 +375,7 @@ partial interface Window {
|
|||
|
||||
[Func="IsChromeOrXBL"]
|
||||
interface ChromeWindow {
|
||||
// The STATE_* constants need to match the corresponding enum in nsGlobalWindow.cpp.
|
||||
[Func="nsGlobalWindow::IsPrivilegedChromeWindow"]
|
||||
const unsigned short STATE_MAXIMIZED = 1;
|
||||
[Func="nsGlobalWindow::IsPrivilegedChromeWindow"]
|
||||
|
|
|
@ -232,7 +232,9 @@ FileReaderSync::ReadAsText(Blob& aBlob,
|
|||
|
||||
nsAutoCString charset;
|
||||
encoding->Name(charset);
|
||||
aRv = ConvertStream(multiplexStream, charset.get(), aResult);
|
||||
|
||||
nsCOMPtr<nsIInputStream> multiplex(do_QueryInterface(multiplexStream));
|
||||
aRv = ConvertStream(multiplex, charset.get(), aResult);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
52365
|
||||
52366
|
||||
0/nm
|
||||
0th/pt
|
||||
1/n1
|
||||
|
@ -50434,6 +50434,7 @@ vane/MS
|
|||
vanguard/MS
|
||||
vanilla/SM
|
||||
vanish/JDSG
|
||||
vanishingly
|
||||
vanity/SM
|
||||
vanned
|
||||
vanning
|
||||
|
|
|
@ -53,6 +53,7 @@
|
|||
#include "GLContextEGL.h"
|
||||
#include "GLContextProvider.h"
|
||||
#include "GLLibraryEGL.h"
|
||||
#include "LayersLogging.h"
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/gfx/gfxVars.h"
|
||||
|
@ -169,7 +170,7 @@ GLContextEGLFactory::Create(EGLNativeWindowType aWindow,
|
|||
MOZ_ASSERT(aWindow);
|
||||
nsCString discardFailureId;
|
||||
if (!sEGLLibrary.EnsureInitialized(false, &discardFailureId)) {
|
||||
MOZ_CRASH("GFX: Failed to load EGL library 3!\n");
|
||||
gfxCriticalNote << "Failed to load EGL library 3!";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -177,14 +178,14 @@ GLContextEGLFactory::Create(EGLNativeWindowType aWindow,
|
|||
|
||||
EGLConfig config;
|
||||
if (!CreateConfig(&config, aWebRender)) {
|
||||
MOZ_CRASH("GFX: Failed to create EGLConfig!\n");
|
||||
gfxCriticalNote << "Failed to create EGLConfig!";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
EGLSurface surface = mozilla::gl::CreateSurfaceFromNativeWindow(aWindow, config);
|
||||
|
||||
if (!surface) {
|
||||
MOZ_CRASH("GFX: Failed to create EGLSurface!\n");
|
||||
gfxCriticalNote << "Failed to create EGLSurface!";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -196,7 +197,7 @@ GLContextEGLFactory::Create(EGLNativeWindowType aWindow,
|
|||
RefPtr<GLContextEGL> gl = GLContextEGL::CreateGLContext(flags, caps, false, config,
|
||||
surface, &discardFailureId);
|
||||
if (!gl) {
|
||||
MOZ_CRASH("GFX: Failed to create EGLContext!\n");
|
||||
gfxCriticalNote << "Failed to create EGLContext!";
|
||||
mozilla::gl::DestroySurface(surface);
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -717,7 +718,7 @@ GLContextProviderEGL::CreateWrappingExisting(void* aContext, void* aSurface)
|
|||
{
|
||||
nsCString discardFailureId;
|
||||
if (!sEGLLibrary.EnsureInitialized(false, &discardFailureId)) {
|
||||
MOZ_CRASH("GFX: Failed to load EGL library 2!\n");
|
||||
MOZ_CRASH("GFX: Failed to load EGL library 2!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -758,7 +759,7 @@ EGLSurface
|
|||
GLContextEGL::CreateCompatibleSurface(void* aWindow)
|
||||
{
|
||||
if (mConfig == EGL_NO_CONFIG) {
|
||||
MOZ_CRASH("GFX: Failed with invalid EGLConfig 2!\n");
|
||||
MOZ_CRASH("GFX: Failed with invalid EGLConfig 2!");
|
||||
}
|
||||
|
||||
return GLContextProviderEGL::CreateEGLSurface(aWindow, mConfig);
|
||||
|
@ -770,11 +771,11 @@ GLContextProviderEGL::CreateEGLSurface(void* aWindow, EGLConfig aConfig)
|
|||
// NOTE: aWindow is an ANativeWindow
|
||||
nsCString discardFailureId;
|
||||
if (!sEGLLibrary.EnsureInitialized(false, &discardFailureId)) {
|
||||
MOZ_CRASH("GFX: Failed to load EGL library 4!\n");
|
||||
MOZ_CRASH("GFX: Failed to load EGL library 4!");
|
||||
}
|
||||
EGLConfig config = aConfig;
|
||||
if (!config && !CreateConfig(&config, /* aEnableDepthBuffer */ false)) {
|
||||
MOZ_CRASH("GFX: Failed to create EGLConfig 2!\n");
|
||||
MOZ_CRASH("GFX: Failed to create EGLConfig 2!");
|
||||
}
|
||||
|
||||
MOZ_ASSERT(aWindow);
|
||||
|
@ -782,7 +783,7 @@ GLContextProviderEGL::CreateEGLSurface(void* aWindow, EGLConfig aConfig)
|
|||
EGLSurface surface = sEGLLibrary.fCreateWindowSurface(EGL_DISPLAY(), config, aWindow,
|
||||
0);
|
||||
if (surface == EGL_NO_SURFACE) {
|
||||
MOZ_CRASH("GFX: Failed to create EGLSurface 2!\n");
|
||||
MOZ_CRASH("GFX: Failed to create EGLSurface 2!");
|
||||
}
|
||||
|
||||
return surface;
|
||||
|
@ -793,7 +794,7 @@ GLContextProviderEGL::DestroyEGLSurface(EGLSurface surface)
|
|||
{
|
||||
nsCString discardFailureId;
|
||||
if (!sEGLLibrary.EnsureInitialized(false, &discardFailureId)) {
|
||||
MOZ_CRASH("GFX: Failed to load EGL library 5!\n");
|
||||
MOZ_CRASH("GFX: Failed to load EGL library 5!");
|
||||
}
|
||||
|
||||
sEGLLibrary.fDestroySurface(EGL_DISPLAY(), surface);
|
||||
|
|
|
@ -32,6 +32,7 @@ public:
|
|||
, mStride(0)
|
||||
, mMapCount(0)
|
||||
, mFormat(SurfaceFormat::UNKNOWN)
|
||||
, mWasPurged(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -66,10 +67,14 @@ public:
|
|||
bool Map(MapType, MappedSurface *aMappedSurface) override
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
if (mWasPurged) {
|
||||
return false;
|
||||
}
|
||||
if (mMapCount == 0) {
|
||||
mVBufPtr = mVBuf;
|
||||
}
|
||||
if (mVBufPtr.WasBufferPurged()) {
|
||||
mWasPurged = true;
|
||||
return false;
|
||||
}
|
||||
aMappedSurface->mData = mVBufPtr;
|
||||
|
@ -82,6 +87,7 @@ public:
|
|||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
MOZ_ASSERT(mMapCount > 0);
|
||||
MOZ_ASSERT(!mWasPurged);
|
||||
if (--mMapCount == 0) {
|
||||
mVBufPtr = nullptr;
|
||||
}
|
||||
|
@ -100,6 +106,7 @@ private:
|
|||
RefPtr<VolatileBuffer> mVBuf;
|
||||
VolatileBufferPtr<uint8_t> mVBufPtr;
|
||||
SurfaceFormat mFormat;
|
||||
bool mWasPurged;
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
|
|
|
@ -102,6 +102,7 @@ WebRenderLayerManager::DoDestroy(bool aIsSync)
|
|||
|
||||
if (WrBridge()) {
|
||||
// Just clear ImageKeys, they are deleted during WebRenderAPI destruction.
|
||||
mImageKeysToDeleteLater.Clear();
|
||||
mImageKeysToDelete.Clear();
|
||||
// CompositorAnimations are cleared by WebRenderBridgeParent.
|
||||
mDiscardedCompositorAnimationsIds.Clear();
|
||||
|
@ -377,7 +378,6 @@ WebRenderLayerManager::EndTransactionWithoutLayer(nsDisplayList* aDisplayList,
|
|||
{
|
||||
MOZ_ASSERT(aDisplayList && aDisplayListBuilder);
|
||||
mEndTransactionWithoutLayers = true;
|
||||
DiscardImages();
|
||||
WrBridge()->RemoveExpiredFontKeys();
|
||||
EndTransactionInternal(nullptr,
|
||||
nullptr,
|
||||
|
@ -687,7 +687,6 @@ WebRenderLayerManager::EndTransaction(DrawPaintedLayerCallback aCallback,
|
|||
EndTransactionFlags aFlags)
|
||||
{
|
||||
mEndTransactionWithoutLayers = false;
|
||||
DiscardImages();
|
||||
WrBridge()->RemoveExpiredFontKeys();
|
||||
EndTransactionInternal(aCallback, aCallbackData, aFlags);
|
||||
}
|
||||
|
@ -818,6 +817,12 @@ WebRenderLayerManager::EndTransactionInternal(DrawPaintedLayerCallback aCallback
|
|||
mLatestTransactionId = mTransactionIdAllocator->GetTransactionId(/*aThrottle*/ true);
|
||||
TimeStamp transactionStart = mTransactionIdAllocator->GetTransactionStart();
|
||||
|
||||
for (const auto& key : mImageKeysToDelete) {
|
||||
resourceUpdates.DeleteImage(key);
|
||||
}
|
||||
mImageKeysToDelete.Clear();
|
||||
mImageKeysToDelete.SwapElements(mImageKeysToDeleteLater);
|
||||
|
||||
// Skip the synchronization for buffer since we also skip the painting during
|
||||
// device-reset status.
|
||||
if (!gfxPlatform::GetPlatform()->DidRenderingDeviceReset()) {
|
||||
|
@ -923,16 +928,20 @@ WebRenderLayerManager::MakeSnapshotIfRequired(LayoutDeviceIntSize aSize)
|
|||
void
|
||||
WebRenderLayerManager::AddImageKeyForDiscard(wr::ImageKey key)
|
||||
{
|
||||
mImageKeysToDelete.AppendElement(key);
|
||||
mImageKeysToDeleteLater.AppendElement(key);
|
||||
}
|
||||
|
||||
void
|
||||
WebRenderLayerManager::DiscardImages()
|
||||
{
|
||||
wr::IpcResourceUpdateQueue resources(WrBridge()->GetShmemAllocator());
|
||||
for (const auto& key : mImageKeysToDeleteLater) {
|
||||
resources.DeleteImage(key);
|
||||
}
|
||||
for (const auto& key : mImageKeysToDelete) {
|
||||
resources.DeleteImage(key);
|
||||
}
|
||||
mImageKeysToDeleteLater.Clear();
|
||||
mImageKeysToDelete.Clear();
|
||||
WrBridge()->UpdateResources(resources);
|
||||
}
|
||||
|
@ -959,6 +968,7 @@ WebRenderLayerManager::DiscardLocalImages()
|
|||
// Removes images but doesn't tell the parent side about them
|
||||
// This is useful in empty / failed transactions where we created
|
||||
// image keys but didn't tell the parent about them yet.
|
||||
mImageKeysToDeleteLater.Clear();
|
||||
mImageKeysToDelete.Clear();
|
||||
}
|
||||
|
||||
|
|
|
@ -250,6 +250,11 @@ private:
|
|||
private:
|
||||
nsIWidget* MOZ_NON_OWNING_REF mWidget;
|
||||
nsTArray<wr::ImageKey> mImageKeysToDelete;
|
||||
// TODO - This is needed because we have some code that creates image keys
|
||||
// and enqueues them for deletion right away which is bad not only because
|
||||
// of poor texture cache usage, but also because images end up deleted before
|
||||
// they are used. This should hopfully be temporary.
|
||||
nsTArray<wr::ImageKey> mImageKeysToDeleteLater;
|
||||
nsTArray<uint64_t> mDiscardedCompositorAnimationsIds;
|
||||
|
||||
/* PaintedLayer callbacks; valid at the end of a transaciton,
|
||||
|
|
|
@ -614,12 +614,29 @@ public:
|
|||
return false;
|
||||
}
|
||||
|
||||
template<typename Function>
|
||||
void CollectSizeOfSurfaces(nsTArray<SurfaceMemoryCounter>& aCounters,
|
||||
MallocSizeOf aMallocSizeOf)
|
||||
MallocSizeOf aMallocSizeOf,
|
||||
Function&& aRemoveCallback)
|
||||
{
|
||||
CachedSurface::SurfaceMemoryReport report(aCounters, aMallocSizeOf);
|
||||
for (auto iter = ConstIter(); !iter.Done(); iter.Next()) {
|
||||
for (auto iter = mSurfaces.Iter(); !iter.Done(); iter.Next()) {
|
||||
NotNull<CachedSurface*> surface = WrapNotNull(iter.UserData());
|
||||
|
||||
// We don't need the drawable surface for ourselves, but adding a surface
|
||||
// to the report will trigger this indirectly. If the surface was
|
||||
// discarded by the OS because it was in volatile memory, we should remove
|
||||
// it from the cache immediately rather than include it in the report.
|
||||
DrawableSurface drawableSurface;
|
||||
if (!surface->IsPlaceholder()) {
|
||||
drawableSurface = surface->GetDrawableSurface();
|
||||
if (!drawableSurface) {
|
||||
aRemoveCallback(surface);
|
||||
iter.Remove();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
const IntSize& size = surface->GetSurfaceKey().Size();
|
||||
bool factor2Size = false;
|
||||
if (mFactor2Mode) {
|
||||
|
@ -1073,6 +1090,8 @@ public:
|
|||
|
||||
cache->Prune([this, &aAutoLock](NotNull<CachedSurface*> aSurface) -> void {
|
||||
StopTracking(aSurface, /* aIsTracked */ true, aAutoLock);
|
||||
// Individual surfaces must be freed outside the lock.
|
||||
mCachedSurfacesDiscard.AppendElement(aSurface);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1165,7 +1184,8 @@ public:
|
|||
|
||||
void CollectSizeOfSurfaces(const ImageKey aImageKey,
|
||||
nsTArray<SurfaceMemoryCounter>& aCounters,
|
||||
MallocSizeOf aMallocSizeOf)
|
||||
MallocSizeOf aMallocSizeOf,
|
||||
const StaticMutexAutoLock& aAutoLock)
|
||||
{
|
||||
RefPtr<ImageSurfaceCache> cache = GetImageCache(aImageKey);
|
||||
if (!cache) {
|
||||
|
@ -1173,7 +1193,12 @@ public:
|
|||
}
|
||||
|
||||
// Report all surfaces in the per-image cache.
|
||||
cache->CollectSizeOfSurfaces(aCounters, aMallocSizeOf);
|
||||
cache->CollectSizeOfSurfaces(aCounters, aMallocSizeOf,
|
||||
[this, &aAutoLock](NotNull<CachedSurface*> aSurface) -> void {
|
||||
StopTracking(aSurface, /* aIsTracked */ true, aAutoLock);
|
||||
// Individual surfaces must be freed outside the lock.
|
||||
mCachedSurfacesDiscard.AppendElement(aSurface);
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -1544,9 +1569,13 @@ SurfaceCache::RemoveImage(const ImageKey aImageKey)
|
|||
/* static */ void
|
||||
SurfaceCache::PruneImage(const ImageKey aImageKey)
|
||||
{
|
||||
StaticMutexAutoLock lock(sInstanceMutex);
|
||||
if (sInstance) {
|
||||
sInstance->PruneImage(aImageKey, lock);
|
||||
nsTArray<RefPtr<CachedSurface>> discard;
|
||||
{
|
||||
StaticMutexAutoLock lock(sInstanceMutex);
|
||||
if (sInstance) {
|
||||
sInstance->PruneImage(aImageKey, lock);
|
||||
sInstance->TakeDiscard(discard, lock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1568,12 +1597,16 @@ SurfaceCache::CollectSizeOfSurfaces(const ImageKey aImageKey,
|
|||
nsTArray<SurfaceMemoryCounter>& aCounters,
|
||||
MallocSizeOf aMallocSizeOf)
|
||||
{
|
||||
StaticMutexAutoLock lock(sInstanceMutex);
|
||||
if (!sInstance) {
|
||||
return;
|
||||
}
|
||||
nsTArray<RefPtr<CachedSurface>> discard;
|
||||
{
|
||||
StaticMutexAutoLock lock(sInstanceMutex);
|
||||
if (!sInstance) {
|
||||
return;
|
||||
}
|
||||
|
||||
return sInstance->CollectSizeOfSurfaces(aImageKey, aCounters, aMallocSizeOf);
|
||||
sInstance->CollectSizeOfSurfaces(aImageKey, aCounters, aMallocSizeOf, lock);
|
||||
sInstance->TakeDiscard(discard, lock);
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ size_t
|
||||
|
|
|
@ -131,7 +131,7 @@ class CPOWProxyHandler : public BaseProxyHandler
|
|||
virtual const char* className(JSContext* cx, HandleObject proxy) const override;
|
||||
virtual RegExpShared* regexp_toShared(JSContext* cx, HandleObject proxy) const override;
|
||||
virtual void finalize(JSFreeOp* fop, JSObject* proxy) const override;
|
||||
virtual void objectMoved(JSObject* proxy, const JSObject* old) const override;
|
||||
virtual size_t objectMoved(JSObject* proxy, JSObject* old) const override;
|
||||
virtual bool isCallable(JSObject* obj) const override;
|
||||
virtual bool isConstructor(JSObject* obj) const override;
|
||||
virtual bool getPrototype(JSContext* cx, HandleObject proxy, MutableHandleObject protop) const override;
|
||||
|
@ -895,10 +895,11 @@ CPOWProxyHandler::finalize(JSFreeOp* fop, JSObject* proxy) const
|
|||
delete aux;
|
||||
}
|
||||
|
||||
void
|
||||
CPOWProxyHandler::objectMoved(JSObject* proxy, const JSObject* old) const
|
||||
size_t
|
||||
CPOWProxyHandler::objectMoved(JSObject* proxy, JSObject* old) const
|
||||
{
|
||||
OwnerOf(proxy)->updatePointer(proxy, old);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -510,8 +510,8 @@ typedef void
|
|||
typedef JSObject*
|
||||
(* JSWeakmapKeyDelegateOp)(JSObject* obj);
|
||||
|
||||
typedef void
|
||||
(* JSObjectMovedOp)(JSObject* obj, const JSObject* old);
|
||||
typedef size_t
|
||||
(* JSObjectMovedOp)(JSObject* obj, JSObject* old);
|
||||
|
||||
/* js::Class operation signatures. */
|
||||
|
||||
|
@ -711,7 +711,8 @@ struct JS_STATIC_CLASS ClassExtension
|
|||
JSWeakmapKeyDelegateOp weakmapKeyDelegateOp;
|
||||
|
||||
/**
|
||||
* Optional hook called when an object is moved by a compacting GC.
|
||||
* Optional hook called when an object is moved by generational or
|
||||
* compacting GC.
|
||||
*
|
||||
* There may exist weak pointers to an object that are not traced through
|
||||
* when the normal trace APIs are used, for example objects in the wrapper
|
||||
|
@ -720,6 +721,12 @@ struct JS_STATIC_CLASS ClassExtension
|
|||
* Note that this hook can be called before JS_NewObject() returns if a GC
|
||||
* is triggered during construction of the object. This can happen for
|
||||
* global objects for example.
|
||||
*
|
||||
* The function should return the difference between nursery bytes used and
|
||||
* tenured bytes used, which may be nonzero e.g. if some nursery-allocated
|
||||
* data beyond the actual GC thing is moved into malloced memory.
|
||||
*
|
||||
* This is used to compute the nursery promotion rate.
|
||||
*/
|
||||
JSObjectMovedOp objectMovedOp;
|
||||
};
|
||||
|
|
|
@ -94,6 +94,10 @@ struct StructGCPolicy
|
|||
static bool needsSweep(T* tp) {
|
||||
return tp->needsSweep();
|
||||
}
|
||||
|
||||
static bool isValid(const T& tp) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// The default GC policy attempts to defer to methods on the underlying type.
|
||||
|
@ -108,6 +112,7 @@ struct IgnoreGCPolicy {
|
|||
static T initial() { return T(); }
|
||||
static void trace(JSTracer* trc, T* t, const char* name) {}
|
||||
static bool needsSweep(T* v) { return false; }
|
||||
static bool isValid(const T& v) { return true; }
|
||||
};
|
||||
template <> struct GCPolicy<uint32_t> : public IgnoreGCPolicy<uint32_t> {};
|
||||
template <> struct GCPolicy<uint64_t> : public IgnoreGCPolicy<uint64_t> {};
|
||||
|
@ -125,6 +130,9 @@ struct GCPointerPolicy
|
|||
return js::gc::IsAboutToBeFinalizedUnbarriered(vp);
|
||||
return false;
|
||||
}
|
||||
static bool isValid(T v) {
|
||||
return js::gc::IsCellPointerValid(v);
|
||||
}
|
||||
};
|
||||
template <> struct GCPolicy<JS::Symbol*> : public GCPointerPolicy<JS::Symbol*> {};
|
||||
template <> struct GCPolicy<JSAtom*> : public GCPointerPolicy<JSAtom*> {};
|
||||
|
@ -133,6 +141,24 @@ template <> struct GCPolicy<JSObject*> : public GCPointerPolicy<JSObject*> {};
|
|||
template <> struct GCPolicy<JSScript*> : public GCPointerPolicy<JSScript*> {};
|
||||
template <> struct GCPolicy<JSString*> : public GCPointerPolicy<JSString*> {};
|
||||
|
||||
template <typename T>
|
||||
struct NonGCPointerPolicy
|
||||
{
|
||||
static T initial() { return nullptr; }
|
||||
static void trace(JSTracer* trc, T* vp, const char* name) {
|
||||
if (*vp)
|
||||
(*vp)->trace(trc);
|
||||
}
|
||||
static bool needsSweep(T* vp) {
|
||||
if (*vp)
|
||||
return (*vp)->needsSweep();
|
||||
return false;
|
||||
}
|
||||
static bool isValid(T v) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct GCPolicy<JS::Heap<T>>
|
||||
{
|
||||
|
@ -158,6 +184,11 @@ struct GCPolicy<mozilla::UniquePtr<T, D>>
|
|||
return GCPolicy<T>::needsSweep(tp->get());
|
||||
return false;
|
||||
}
|
||||
static bool isValid(const mozilla::UniquePtr<T,D>& t) {
|
||||
if (t.get())
|
||||
return GCPolicy<T>::isValid(*t.get());
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// GCPolicy<Maybe<T>> forwards tracing/sweeping to GCPolicy<T*> if
|
||||
|
@ -175,6 +206,11 @@ struct GCPolicy<mozilla::Maybe<T>>
|
|||
return GCPolicy<T>::needsSweep(tp->ptr());
|
||||
return false;
|
||||
}
|
||||
static bool isValid(const mozilla::Maybe<T>& t) {
|
||||
if (t.isSome())
|
||||
return GCPolicy<T>::isValid(t.ref());
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template <> struct GCPolicy<JS::Realm*>; // see Realm.h
|
||||
|
|
|
@ -118,6 +118,19 @@ struct GCPolicy<mozilla::Variant<Ts...>>
|
|||
static void trace(JSTracer* trc, mozilla::Variant<Ts...>* v, const char* name) {
|
||||
Impl::trace(trc, v, name);
|
||||
}
|
||||
|
||||
static bool isValid(const mozilla::Variant<Ts...>& v) {
|
||||
return v.match(IsValidMatcher());
|
||||
}
|
||||
|
||||
private:
|
||||
struct IsValidMatcher
|
||||
{
|
||||
template<typename T>
|
||||
bool match(T& v) {
|
||||
return GCPolicy<T>::isValid(v);
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace JS
|
||||
|
|
|
@ -53,6 +53,7 @@ const size_t ChunkMarkBitmapBits = 129024;
|
|||
const size_t ChunkRuntimeOffset = ChunkSize - sizeof(void*);
|
||||
const size_t ChunkTrailerSize = 2 * sizeof(uintptr_t) + sizeof(uint64_t);
|
||||
const size_t ChunkLocationOffset = ChunkSize - ChunkTrailerSize;
|
||||
const size_t ChunkStoreBufferOffset = ChunkLocationOffset + sizeof(uint64_t);
|
||||
const size_t ArenaZoneOffset = sizeof(size_t);
|
||||
const size_t ArenaHeaderSize = sizeof(size_t) + 2 * sizeof(uintptr_t) +
|
||||
sizeof(size_t) + sizeof(uintptr_t);
|
||||
|
@ -372,6 +373,24 @@ extern JS_PUBLIC_API(bool)
|
|||
CellIsNotGray(const Cell* cell);
|
||||
#endif
|
||||
|
||||
MOZ_ALWAYS_INLINE ChunkLocation
|
||||
GetCellLocation(const void* cell)
|
||||
{
|
||||
uintptr_t addr = uintptr_t(cell);
|
||||
addr &= ~js::gc::ChunkMask;
|
||||
addr |= js::gc::ChunkLocationOffset;
|
||||
return *reinterpret_cast<ChunkLocation*>(addr);
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE bool
|
||||
NurseryCellHasStoreBuffer(const void* cell)
|
||||
{
|
||||
uintptr_t addr = uintptr_t(cell);
|
||||
addr &= ~js::gc::ChunkMask;
|
||||
addr |= js::gc::ChunkStoreBufferOffset;
|
||||
return *reinterpret_cast<void**>(addr) != nullptr;
|
||||
}
|
||||
|
||||
} /* namespace detail */
|
||||
|
||||
MOZ_ALWAYS_INLINE bool
|
||||
|
@ -379,14 +398,27 @@ IsInsideNursery(const js::gc::Cell* cell)
|
|||
{
|
||||
if (!cell)
|
||||
return false;
|
||||
uintptr_t addr = uintptr_t(cell);
|
||||
addr &= ~js::gc::ChunkMask;
|
||||
addr |= js::gc::ChunkLocationOffset;
|
||||
auto location = *reinterpret_cast<ChunkLocation*>(addr);
|
||||
auto location = detail::GetCellLocation(cell);
|
||||
MOZ_ASSERT(location == ChunkLocation::Nursery || location == ChunkLocation::TenuredHeap);
|
||||
return location == ChunkLocation::Nursery;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE bool
|
||||
IsCellPointerValid(const void* cell)
|
||||
{
|
||||
if (!cell)
|
||||
return true;
|
||||
auto addr = uintptr_t(cell);
|
||||
if (addr < ChunkSize || addr % CellAlignBytes != 0)
|
||||
return false;
|
||||
auto location = detail::GetCellLocation(cell);
|
||||
if (location == ChunkLocation::TenuredHeap)
|
||||
return !!detail::GetGCThingZone(addr);
|
||||
if (location == ChunkLocation::Nursery)
|
||||
return detail::NurseryCellHasStoreBuffer(cell);
|
||||
return false;
|
||||
}
|
||||
|
||||
} /* namespace gc */
|
||||
} /* namespace js */
|
||||
|
||||
|
|
|
@ -171,6 +171,9 @@ struct GCPolicy<jsid>
|
|||
static void trace(JSTracer* trc, jsid* idp, const char* name) {
|
||||
js::UnsafeTraceManuallyBarrieredEdge(trc, idp, name);
|
||||
}
|
||||
static bool isValid(jsid id) {
|
||||
return !JSID_IS_GCTHING(id) || js::gc::IsCellPointerValid(JSID_TO_GCTHING(id).asCell());
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace JS
|
||||
|
|
|
@ -335,7 +335,7 @@ class JS_FRIEND_API(BaseProxyHandler)
|
|||
virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const;
|
||||
virtual void trace(JSTracer* trc, JSObject* proxy) const;
|
||||
virtual void finalize(JSFreeOp* fop, JSObject* proxy) const;
|
||||
virtual void objectMoved(JSObject* proxy, const JSObject* old) const;
|
||||
virtual size_t objectMoved(JSObject* proxy, JSObject* old) const;
|
||||
|
||||
// Allow proxies, wrappers in particular, to specify callability at runtime.
|
||||
// Note: These do not take const JSObject*, but they do in spirit.
|
||||
|
@ -684,18 +684,6 @@ extern JS_FRIEND_DATA(const js::ClassOps) ProxyClassOps;
|
|||
extern JS_FRIEND_DATA(const js::ClassExtension) ProxyClassExtension;
|
||||
extern JS_FRIEND_DATA(const js::ObjectOps) ProxyObjectOps;
|
||||
|
||||
/*
|
||||
* Helper Macros for creating JSClasses that function as proxies.
|
||||
*
|
||||
* NB: The macro invocation must be surrounded by braces, so as to
|
||||
* allow for potential JSClass extensions.
|
||||
*/
|
||||
#define PROXY_MAKE_EXT(objectMoved) \
|
||||
{ \
|
||||
js::proxy_WeakmapKeyDelegate, \
|
||||
objectMoved \
|
||||
}
|
||||
|
||||
template <unsigned Flags>
|
||||
constexpr unsigned
|
||||
CheckProxyFlags()
|
||||
|
@ -722,7 +710,7 @@ CheckProxyFlags()
|
|||
return Flags;
|
||||
}
|
||||
|
||||
#define PROXY_CLASS_WITH_EXT(name, flags, extPtr) \
|
||||
#define PROXY_CLASS_DEF(name, flags) \
|
||||
{ \
|
||||
name, \
|
||||
js::Class::NON_NATIVE | \
|
||||
|
@ -731,13 +719,10 @@ CheckProxyFlags()
|
|||
js::CheckProxyFlags<flags>(), \
|
||||
&js::ProxyClassOps, \
|
||||
JS_NULL_CLASS_SPEC, \
|
||||
extPtr, \
|
||||
&js::ProxyClassExtension, \
|
||||
&js::ProxyObjectOps \
|
||||
}
|
||||
|
||||
#define PROXY_CLASS_DEF(name, flags) \
|
||||
PROXY_CLASS_WITH_EXT(name, flags, &js::ProxyClassExtension)
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
#endif /* js_Proxy_h */
|
||||
|
|
|
@ -28,9 +28,8 @@ namespace JS {
|
|||
|
||||
// Each Realm holds a strong reference to its GlobalObject, and vice versa.
|
||||
template <>
|
||||
struct GCPolicy<Realm*>
|
||||
struct GCPolicy<Realm*> : public NonGCPointerPolicy<Realm*>
|
||||
{
|
||||
static Realm* initial() { return nullptr; }
|
||||
static void trace(JSTracer* trc, Realm** vp, const char* name) {
|
||||
if (*vp)
|
||||
::js::gc::TraceRealm(trc, *vp, name);
|
||||
|
|
|
@ -396,6 +396,7 @@ class TenuredHeap : public js::HeapBase<T, TenuredHeap<T>>
|
|||
|
||||
void setPtr(T newPtr) {
|
||||
MOZ_ASSERT((reinterpret_cast<uintptr_t>(newPtr) & flagsMask) == 0);
|
||||
MOZ_ASSERT(js::gc::IsCellPointerValid(newPtr));
|
||||
if (newPtr)
|
||||
AssertGCThingMustBeTenured(newPtr);
|
||||
bits = (bits & flagsMask) | reinterpret_cast<uintptr_t>(newPtr);
|
||||
|
@ -571,9 +572,11 @@ class MOZ_STACK_CLASS MutableHandle : public js::MutableHandleBase<T, MutableHan
|
|||
public:
|
||||
void set(const T& v) {
|
||||
*ptr = v;
|
||||
MOZ_ASSERT(GCPolicy<T>::isValid(*ptr));
|
||||
}
|
||||
void set(T&& v) {
|
||||
*ptr = mozilla::Move(v);
|
||||
MOZ_ASSERT(GCPolicy<T>::isValid(*ptr));
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -818,6 +821,7 @@ class MOZ_RAII Rooted : public js::RootedBase<T, Rooted<T>>
|
|||
Rooted(const RootingContext& cx, S&& initial)
|
||||
: ptr(mozilla::Forward<S>(initial))
|
||||
{
|
||||
MOZ_ASSERT(GCPolicy<T>::isValid(ptr));
|
||||
registerWithRootLists(rootLists(cx));
|
||||
}
|
||||
|
||||
|
@ -834,9 +838,11 @@ class MOZ_RAII Rooted : public js::RootedBase<T, Rooted<T>>
|
|||
*/
|
||||
void set(const T& value) {
|
||||
ptr = value;
|
||||
MOZ_ASSERT(GCPolicy<T>::isValid(ptr));
|
||||
}
|
||||
void set(T&& value) {
|
||||
ptr = mozilla::Move(value);
|
||||
MOZ_ASSERT(GCPolicy<T>::isValid(ptr));
|
||||
}
|
||||
|
||||
DECLARE_POINTER_CONSTREF_OPS(T);
|
||||
|
|
|
@ -354,17 +354,17 @@ class MOZ_NON_PARAM alignas(8) Value
|
|||
}
|
||||
|
||||
void setString(JSString* str) {
|
||||
MOZ_ASSERT(uintptr_t(str) > 0x1000);
|
||||
MOZ_ASSERT(js::gc::IsCellPointerValid(str));
|
||||
data.asBits = bitsFromTagAndPayload(JSVAL_TAG_STRING, PayloadType(str));
|
||||
}
|
||||
|
||||
void setSymbol(JS::Symbol* sym) {
|
||||
MOZ_ASSERT(uintptr_t(sym) > 0x1000);
|
||||
MOZ_ASSERT(js::gc::IsCellPointerValid(sym));
|
||||
data.asBits = bitsFromTagAndPayload(JSVAL_TAG_SYMBOL, PayloadType(sym));
|
||||
}
|
||||
|
||||
void setObject(JSObject& obj) {
|
||||
MOZ_ASSERT(uintptr_t(&obj) >= 0x1000);
|
||||
MOZ_ASSERT(js::gc::IsCellPointerValid(&obj));
|
||||
#if defined(JS_PUNBOX64)
|
||||
// VisualStudio cannot contain parenthesized C++ style cast and shift
|
||||
// inside decltype in template parameter:
|
||||
|
@ -756,7 +756,7 @@ class MOZ_NON_PARAM alignas(8) Value
|
|||
MOZ_ASSERT(JS::GCThingTraceKind(cell) != JS::TraceKind::Object,
|
||||
"Private GC thing Values must not be objects. Make an ObjectValue instead.");
|
||||
|
||||
MOZ_ASSERT(uintptr_t(cell) > 0x1000);
|
||||
MOZ_ASSERT(js::gc::IsCellPointerValid(cell));
|
||||
#if defined(JS_PUNBOX64)
|
||||
// VisualStudio cannot contain parenthesized C++ style cast and shift
|
||||
// inside decltype in template parameter:
|
||||
|
@ -1259,6 +1259,9 @@ struct GCPolicy<JS::Value>
|
|||
static bool isTenured(const Value& thing) {
|
||||
return !thing.isGCThing() || !IsInsideNursery(thing.toGCThing());
|
||||
}
|
||||
static bool isValid(const Value& value) {
|
||||
return !value.isGCThing() || js::gc::IsCellPointerValid(value.toGCThing());
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace JS
|
||||
|
|
|
@ -133,7 +133,8 @@ pub struct ProxyTraps {
|
|||
pub finalize:
|
||||
::std::option::Option<unsafe extern "C" fn(fop: *mut JSFreeOp, proxy: *mut JSObject)>,
|
||||
pub objectMoved:
|
||||
::std::option::Option<unsafe extern "C" fn(proxy: *mut JSObject, old: *const JSObject)>,
|
||||
::std::option::Option<unsafe extern "C" fn(proxy: *mut JSObject,
|
||||
old: *mut JSObject) -> usize>,
|
||||
pub isCallable: ::std::option::Option<unsafe extern "C" fn(obj: *mut JSObject) -> bool>,
|
||||
pub isConstructor: ::std::option::Option<unsafe extern "C" fn(obj: *mut JSObject) -> bool>,
|
||||
}
|
||||
|
|
|
@ -85,7 +85,7 @@ struct ProxyTraps {
|
|||
bool (*defaultValue)(JSContext *cx, JS::HandleObject obj, JSType hint, JS::MutableHandleValue vp);
|
||||
void (*trace)(JSTracer *trc, JSObject *proxy);
|
||||
void (*finalize)(JSFreeOp *fop, JSObject *proxy);
|
||||
void (*objectMoved)(JSObject *proxy, const JSObject *old);
|
||||
size_t (*objectMoved)(JSObject *proxy, JSObject *old);
|
||||
|
||||
bool (*isCallable)(JSObject *obj);
|
||||
bool (*isConstructor)(JSObject *obj);
|
||||
|
@ -226,12 +226,11 @@ static int HandlerFamily;
|
|||
: _base::finalize(fop, proxy); \
|
||||
} \
|
||||
\
|
||||
virtual void objectMoved(JSObject* proxy, \
|
||||
const JSObject *old) const override \
|
||||
virtual size_t objectMoved(JSObject* proxy, JSObject *old) const override \
|
||||
{ \
|
||||
mTraps.objectMoved \
|
||||
? mTraps.objectMoved(proxy, old) \
|
||||
: _base::objectMoved(proxy, old); \
|
||||
return mTraps.objectMoved \
|
||||
? mTraps.objectMoved(proxy, old) \
|
||||
: _base::objectMoved(proxy, old); \
|
||||
} \
|
||||
\
|
||||
virtual bool isCallable(JSObject* obj) const override \
|
||||
|
@ -568,19 +567,9 @@ WrapperNew(JSContext* aCx, JS::HandleObject aObj, const void* aHandler,
|
|||
return js::Wrapper::New(aCx, aObj, (const js::Wrapper*)aHandler, options);
|
||||
}
|
||||
|
||||
void WindowProxyObjectMoved(JSObject*, const JSObject*)
|
||||
{
|
||||
abort();
|
||||
}
|
||||
|
||||
static const js::ClassExtension WindowProxyClassExtension = PROXY_MAKE_EXT(
|
||||
WindowProxyObjectMoved
|
||||
);
|
||||
|
||||
const js::Class WindowProxyClass = PROXY_CLASS_WITH_EXT(
|
||||
const js::Class WindowProxyClass = PROXY_CLASS_DEF(
|
||||
"Proxy",
|
||||
JSCLASS_HAS_RESERVED_SLOTS(1), /* additional class flags */
|
||||
&WindowProxyClassExtension);
|
||||
JSCLASS_HAS_RESERVED_SLOTS(1)); /* additional class flags */
|
||||
|
||||
const js::Class*
|
||||
GetWindowProxyClass()
|
||||
|
|
|
@ -275,21 +275,21 @@ MapIteratorObject::finalize(FreeOp* fop, JSObject* obj)
|
|||
fop->delete_(range);
|
||||
}
|
||||
|
||||
void
|
||||
MapIteratorObject::objectMoved(JSObject* obj, const JSObject* old)
|
||||
size_t
|
||||
MapIteratorObject::objectMoved(JSObject* obj, JSObject* old)
|
||||
{
|
||||
if (!IsInsideNursery(old))
|
||||
return;
|
||||
return 0;
|
||||
|
||||
MapIteratorObject* iter = &obj->as<MapIteratorObject>();
|
||||
ValueMap::Range* range = MapIteratorObjectRange(iter);
|
||||
if (!range)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
Nursery& nursery = iter->zone()->group()->nursery();
|
||||
if (!nursery.isInside(range)) {
|
||||
nursery.removeMallocedBuffer(range);
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
AutoEnterOOMUnsafeRegion oomUnsafe;
|
||||
|
@ -300,6 +300,7 @@ MapIteratorObject::objectMoved(JSObject* obj, const JSObject* old)
|
|||
new (newRange) ValueMap::Range(*range);
|
||||
range->~Range();
|
||||
iter->setReservedSlot(MapIteratorObject::RangeSlot, PrivateValue(newRange));
|
||||
return sizeof(ValueMap::Range);
|
||||
}
|
||||
|
||||
template <typename Range>
|
||||
|
@ -1124,21 +1125,21 @@ SetIteratorObject::finalize(FreeOp* fop, JSObject* obj)
|
|||
fop->delete_(range);
|
||||
}
|
||||
|
||||
void
|
||||
SetIteratorObject::objectMoved(JSObject* obj, const JSObject* old)
|
||||
size_t
|
||||
SetIteratorObject::objectMoved(JSObject* obj, JSObject* old)
|
||||
{
|
||||
if (!IsInsideNursery(old))
|
||||
return;
|
||||
return 0;
|
||||
|
||||
SetIteratorObject* iter = &obj->as<SetIteratorObject>();
|
||||
ValueSet::Range* range = SetIteratorObjectRange(iter);
|
||||
if (!range)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
Nursery& nursery = iter->zone()->group()->nursery();
|
||||
if (!nursery.isInside(range)) {
|
||||
nursery.removeMallocedBuffer(range);
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
AutoEnterOOMUnsafeRegion oomUnsafe;
|
||||
|
@ -1149,6 +1150,7 @@ SetIteratorObject::objectMoved(JSObject* obj, const JSObject* old)
|
|||
new (newRange) ValueSet::Range(*range);
|
||||
range->~Range();
|
||||
iter->setReservedSlot(SetIteratorObject::RangeSlot, PrivateValue(newRange));
|
||||
return sizeof(ValueSet::Range);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -190,7 +190,7 @@ class MapIteratorObject : public NativeObject
|
|||
static MapIteratorObject* create(JSContext* cx, HandleObject mapobj, ValueMap* data,
|
||||
MapObject::IteratorKind kind);
|
||||
static void finalize(FreeOp* fop, JSObject* obj);
|
||||
static void objectMoved(JSObject* obj, const JSObject* old);
|
||||
static size_t objectMoved(JSObject* obj, JSObject* old);
|
||||
|
||||
static MOZ_MUST_USE bool next(Handle<MapIteratorObject*> mapIterator,
|
||||
HandleArrayObject resultPairObj, JSContext* cx);
|
||||
|
@ -293,7 +293,7 @@ class SetIteratorObject : public NativeObject
|
|||
static SetIteratorObject* create(JSContext* cx, HandleObject setobj, ValueSet* data,
|
||||
SetObject::IteratorKind kind);
|
||||
static void finalize(FreeOp* fop, JSObject* obj);
|
||||
static void objectMoved(JSObject* obj, const JSObject* old);
|
||||
static size_t objectMoved(JSObject* obj, JSObject* old);
|
||||
|
||||
static MOZ_MUST_USE bool next(Handle<SetIteratorObject*> setIterator,
|
||||
HandleArrayObject resultObj, JSContext* cx);
|
||||
|
|
|
@ -1573,7 +1573,7 @@ js::RegExpPrototypeOptimizable(JSContext* cx, unsigned argc, Value* vp)
|
|||
bool
|
||||
js::RegExpPrototypeOptimizableRaw(JSContext* cx, JSObject* proto)
|
||||
{
|
||||
JS::AutoCheckCannotGC nogc;
|
||||
AutoUnsafeCallWithABI unsafe;
|
||||
AutoAssertNoPendingException aanpe(cx);
|
||||
if (!proto->isNative())
|
||||
return false;
|
||||
|
@ -1666,7 +1666,7 @@ js::RegExpInstanceOptimizable(JSContext* cx, unsigned argc, Value* vp)
|
|||
bool
|
||||
js::RegExpInstanceOptimizableRaw(JSContext* cx, JSObject* obj, JSObject* proto)
|
||||
{
|
||||
JS::AutoCheckCannotGC nogc;
|
||||
AutoUnsafeCallWithABI unsafe;
|
||||
AutoAssertNoPendingException aanpe(cx);
|
||||
|
||||
RegExpObject* rx = &obj->as<RegExpObject>();
|
||||
|
|
|
@ -2358,7 +2358,7 @@ SetIonCheckGraphCoherency(JSContext* cx, unsigned argc, Value* vp)
|
|||
}
|
||||
|
||||
class CloneBufferObject : public NativeObject {
|
||||
static const JSPropertySpec props_[2];
|
||||
static const JSPropertySpec props_[3];
|
||||
static const size_t DATA_SLOT = 0;
|
||||
static const size_t LENGTH_SLOT = 1;
|
||||
static const size_t NUM_SLOTS = 2;
|
||||
|
@ -2413,15 +2413,6 @@ class CloneBufferObject : public NativeObject {
|
|||
|
||||
static bool
|
||||
setCloneBuffer_impl(JSContext* cx, const CallArgs& args) {
|
||||
if (args.length() != 1) {
|
||||
JS_ReportErrorASCII(cx, "clonebuffer setter requires a single string argument");
|
||||
return false;
|
||||
}
|
||||
if (!args[0].isString()) {
|
||||
JS_ReportErrorASCII(cx, "clonebuffer value must be a string");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fuzzingSafe) {
|
||||
// A manually-created clonebuffer could easily trigger a crash
|
||||
args.rval().setUndefined();
|
||||
|
@ -2429,20 +2420,37 @@ class CloneBufferObject : public NativeObject {
|
|||
}
|
||||
|
||||
Rooted<CloneBufferObject*> obj(cx, &args.thisv().toObject().as<CloneBufferObject>());
|
||||
obj->discard();
|
||||
|
||||
char* str = JS_EncodeString(cx, args[0].toString());
|
||||
if (!str)
|
||||
return false;
|
||||
size_t nbytes = JS_GetStringLength(args[0].toString());
|
||||
MOZ_ASSERT(nbytes % sizeof(uint64_t) == 0);
|
||||
auto buf = js::MakeUnique<JSStructuredCloneData>(0, 0, nbytes);
|
||||
if (!buf->Init(nbytes, nbytes)) {
|
||||
JS_free(cx, str);
|
||||
uint8_t* data = nullptr;
|
||||
UniquePtr<uint8_t[], JS::FreePolicy> dataOwner;
|
||||
uint32_t nbytes;
|
||||
|
||||
if (args.get(0).isObject() && args[0].toObject().is<ArrayBufferObject>()) {
|
||||
ArrayBufferObject* buffer = &args[0].toObject().as<ArrayBufferObject>();
|
||||
bool isSharedMemory;
|
||||
js::GetArrayBufferLengthAndData(buffer, &nbytes, &isSharedMemory, &data);
|
||||
MOZ_ASSERT(!isSharedMemory);
|
||||
} else {
|
||||
JSString* str = JS::ToString(cx, args.get(0));
|
||||
if (!str)
|
||||
return false;
|
||||
data = reinterpret_cast<uint8_t*>(JS_EncodeString(cx, str));
|
||||
if (!data)
|
||||
return false;
|
||||
dataOwner.reset(data);
|
||||
nbytes = JS_GetStringLength(str);
|
||||
}
|
||||
|
||||
if (nbytes % sizeof(uint64_t) != 0) {
|
||||
JS_ReportErrorASCII(cx, "Invalid length for clonebuffer data");
|
||||
return false;
|
||||
}
|
||||
js_memcpy(buf->Start(), str, nbytes);
|
||||
JS_free(cx, str);
|
||||
|
||||
auto buf = js::MakeUnique<JSStructuredCloneData>(0, 0, nbytes);
|
||||
if (!buf->Init(nbytes, nbytes))
|
||||
return false;
|
||||
js_memcpy(buf->Start(), data, nbytes);
|
||||
obj->discard();
|
||||
obj->setData(buf.release());
|
||||
|
||||
args.rval().setUndefined();
|
||||
|
@ -2461,12 +2469,9 @@ class CloneBufferObject : public NativeObject {
|
|||
}
|
||||
|
||||
static bool
|
||||
getCloneBuffer_impl(JSContext* cx, const CallArgs& args) {
|
||||
Rooted<CloneBufferObject*> obj(cx, &args.thisv().toObject().as<CloneBufferObject>());
|
||||
MOZ_ASSERT(args.length() == 0);
|
||||
|
||||
getData(JSContext* cx, Handle<CloneBufferObject*> obj, JSStructuredCloneData** data) {
|
||||
if (!obj->data()) {
|
||||
args.rval().setUndefined();
|
||||
*data = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -2479,14 +2484,27 @@ class CloneBufferObject : public NativeObject {
|
|||
return false;
|
||||
}
|
||||
|
||||
size_t size = obj->data()->Size();
|
||||
*data = obj->data();
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
getCloneBuffer_impl(JSContext* cx, const CallArgs& args) {
|
||||
Rooted<CloneBufferObject*> obj(cx, &args.thisv().toObject().as<CloneBufferObject>());
|
||||
MOZ_ASSERT(args.length() == 0);
|
||||
|
||||
JSStructuredCloneData* data;
|
||||
if (!getData(cx, obj, &data))
|
||||
return false;
|
||||
|
||||
size_t size = data->Size();
|
||||
UniqueChars buffer(static_cast<char*>(js_malloc(size)));
|
||||
if (!buffer) {
|
||||
ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
auto iter = obj->data()->Iter();
|
||||
obj->data()->ReadBytes(iter, buffer.get(), size);
|
||||
auto iter = data->Iter();
|
||||
data->ReadBytes(iter, buffer.get(), size);
|
||||
JSString* str = JS_NewStringCopyN(cx, buffer.get(), size);
|
||||
if (!str)
|
||||
return false;
|
||||
|
@ -2500,6 +2518,36 @@ class CloneBufferObject : public NativeObject {
|
|||
return CallNonGenericMethod<is, getCloneBuffer_impl>(cx, args);
|
||||
}
|
||||
|
||||
static bool
|
||||
getCloneBufferAsArrayBuffer_impl(JSContext* cx, const CallArgs& args) {
|
||||
Rooted<CloneBufferObject*> obj(cx, &args.thisv().toObject().as<CloneBufferObject>());
|
||||
MOZ_ASSERT(args.length() == 0);
|
||||
|
||||
JSStructuredCloneData* data;
|
||||
if (!getData(cx, obj, &data))
|
||||
return false;
|
||||
|
||||
size_t size = data->Size();
|
||||
UniqueChars buffer(static_cast<char*>(js_malloc(size)));
|
||||
if (!buffer) {
|
||||
ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
auto iter = data->Iter();
|
||||
data->ReadBytes(iter, buffer.get(), size);
|
||||
JSObject* arrayBuffer = JS_NewArrayBufferWithContents(cx, size, buffer.release());
|
||||
if (!arrayBuffer)
|
||||
return false;
|
||||
args.rval().setObject(*arrayBuffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
getCloneBufferAsArrayBuffer(JSContext* cx, unsigned int argc, JS::Value* vp) {
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return CallNonGenericMethod<is, getCloneBufferAsArrayBuffer_impl>(cx, args);
|
||||
}
|
||||
|
||||
static void Finalize(FreeOp* fop, JSObject* obj) {
|
||||
obj->as<CloneBufferObject>().discard();
|
||||
}
|
||||
|
@ -2524,6 +2572,7 @@ const Class CloneBufferObject::class_ = {
|
|||
|
||||
const JSPropertySpec CloneBufferObject::props_[] = {
|
||||
JS_PSGS("clonebuffer", getCloneBuffer, setCloneBuffer, 0),
|
||||
JS_PSG("arraybuffer", getCloneBufferAsArrayBuffer, 0),
|
||||
JS_PS_END
|
||||
};
|
||||
|
||||
|
@ -4545,7 +4594,7 @@ static const JSFunctionSpecWithHelp TestingFunctions[] = {
|
|||
JS_FN_HELP("oomThreadTypes", OOMThreadTypes, 0, 0,
|
||||
"oomThreadTypes()",
|
||||
" Get the number of thread types that can be used as an argument for\n"
|
||||
"oomAfterAllocations() and oomAtAllocation()."),
|
||||
" oomAfterAllocations() and oomAtAllocation()."),
|
||||
|
||||
JS_FN_HELP("oomAfterAllocations", OOMAfterAllocations, 2, 0,
|
||||
"oomAfterAllocations(count [,threadType])",
|
||||
|
@ -4699,8 +4748,8 @@ gc::ZealModeHelpText),
|
|||
|
||||
JS_FN_HELP("enableOsiPointRegisterChecks", EnableOsiPointRegisterChecks, 0, 0,
|
||||
"enableOsiPointRegisterChecks()",
|
||||
"Emit extra code to verify live regs at the start of a VM call are not\n"
|
||||
"modified before its OsiPoint."),
|
||||
" Emit extra code to verify live regs at the start of a VM call are not\n"
|
||||
" modified before its OsiPoint."),
|
||||
|
||||
JS_FN_HELP("displayName", DisplayName, 1, 0,
|
||||
"displayName(fn)",
|
||||
|
@ -4719,7 +4768,7 @@ gc::ZealModeHelpText),
|
|||
|
||||
JS_FN_HELP("getJitCompilerOptions", GetJitCompilerOptions, 0, 0,
|
||||
"getCompilerOptions()",
|
||||
"Return an object describing some of the JIT compiler options.\n"),
|
||||
" Return an object describing some of the JIT compiler options.\n"),
|
||||
|
||||
JS_FN_HELP("isAsmJSModule", IsAsmJSModule, 1, 0,
|
||||
"isAsmJSModule(fn)",
|
||||
|
@ -4772,7 +4821,7 @@ gc::ZealModeHelpText),
|
|||
|
||||
JS_FN_HELP("isRelazifiableFunction", IsRelazifiableFunction, 1, 0,
|
||||
"isRelazifiableFunction(fun)",
|
||||
" Ture if fun is a JSFunction with a relazifiable JSScript."),
|
||||
" True if fun is a JSFunction with a relazifiable JSScript."),
|
||||
|
||||
JS_FN_HELP("enableShellAllocationMetadataBuilder", EnableShellAllocationMetadataBuilder, 0, 0,
|
||||
"enableShellAllocationMetadataBuilder()",
|
||||
|
@ -4825,11 +4874,10 @@ gc::ZealModeHelpText),
|
|||
" Serialize 'data' using JS_WriteStructuredClone. Returns a structured\n"
|
||||
" clone buffer object. 'policy' may be an options hash. Valid keys:\n"
|
||||
" 'SharedArrayBuffer' - either 'allow' (the default) or 'deny'\n"
|
||||
" to specify whether SharedArrayBuffers may be serialized.\n"
|
||||
"\n"
|
||||
" to specify whether SharedArrayBuffers may be serialized.\n"
|
||||
" 'scope' - SameProcessSameThread, SameProcessDifferentThread, or\n"
|
||||
" DifferentProcess. Determines how some values will be serialized.\n"
|
||||
" Clone buffers may only be deserialized with a compatible scope."),
|
||||
" DifferentProcess. Determines how some values will be serialized.\n"
|
||||
" Clone buffers may only be deserialized with a compatible scope."),
|
||||
|
||||
JS_FN_HELP("deserialize", Deserialize, 1, 0,
|
||||
"deserialize(clonebuffer[, opts])",
|
||||
|
|
|
@ -2126,9 +2126,12 @@ InlineTypedObject::obj_trace(JSTracer* trc, JSObject* object)
|
|||
typedObj.typeDescr().traceInstances(trc, typedObj.inlineTypedMem(), 1);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
InlineTypedObject::objectMovedDuringMinorGC(JSTracer* trc, JSObject* dst, JSObject* src)
|
||||
/* static */ size_t
|
||||
InlineTypedObject::obj_moved(JSObject* dst, JSObject* src)
|
||||
{
|
||||
if (!IsInsideNursery(src))
|
||||
return 0;
|
||||
|
||||
// Inline typed object element arrays can be preserved on the stack by Ion
|
||||
// and need forwarding pointers created during a minor GC. We can't do this
|
||||
// in the trace hook because we don't have any stale data to determine
|
||||
|
@ -2140,9 +2143,12 @@ InlineTypedObject::objectMovedDuringMinorGC(JSTracer* trc, JSObject* dst, JSObje
|
|||
// but they will not set any direct forwarding pointers.
|
||||
uint8_t* oldData = reinterpret_cast<uint8_t*>(src) + offsetOfDataStart();
|
||||
uint8_t* newData = dst->as<InlineTypedObject>().inlineTypedMem();
|
||||
dst->zone()->group()->nursery().maybeSetForwardingPointer(trc, oldData, newData,
|
||||
descr.size() >= sizeof(uintptr_t));
|
||||
auto& nursery = dst->zone()->group()->nursery();
|
||||
bool direct = descr.size() >= sizeof(uintptr_t);
|
||||
nursery.setForwardingPointerWhileTenuring(oldData, newData, direct);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ArrayBufferObject*
|
||||
|
@ -2219,7 +2225,7 @@ const ObjectOps TypedObject::objectOps_ = {
|
|||
nullptr, /* thisValue */
|
||||
};
|
||||
|
||||
#define DEFINE_TYPEDOBJ_CLASS(Name, Trace, flag) \
|
||||
#define DEFINE_TYPEDOBJ_CLASS(Name, Trace, Moved, flag) \
|
||||
static const ClassOps Name##ClassOps = { \
|
||||
nullptr, /* addProperty */ \
|
||||
nullptr, /* delProperty */ \
|
||||
|
@ -2233,20 +2239,34 @@ const ObjectOps TypedObject::objectOps_ = {
|
|||
nullptr, /* construct */ \
|
||||
Trace, \
|
||||
}; \
|
||||
static const ClassExtension Name##ClassExt = { \
|
||||
nullptr, /* weakmapKeyDelegateOp */ \
|
||||
Moved /* objectMovedOp */ \
|
||||
}; \
|
||||
const Class Name::class_ = { \
|
||||
# Name, \
|
||||
Class::NON_NATIVE | flag, \
|
||||
&Name##ClassOps, \
|
||||
JS_NULL_CLASS_SPEC, \
|
||||
JS_NULL_CLASS_EXT, \
|
||||
&Name##ClassExt, \
|
||||
&TypedObject::objectOps_ \
|
||||
}
|
||||
|
||||
DEFINE_TYPEDOBJ_CLASS(OutlineTransparentTypedObject, OutlineTypedObject::obj_trace, 0);
|
||||
DEFINE_TYPEDOBJ_CLASS(OutlineOpaqueTypedObject, OutlineTypedObject::obj_trace, 0);
|
||||
DEFINE_TYPEDOBJ_CLASS(InlineTransparentTypedObject, InlineTypedObject::obj_trace,
|
||||
DEFINE_TYPEDOBJ_CLASS(OutlineTransparentTypedObject,
|
||||
OutlineTypedObject::obj_trace,
|
||||
nullptr,
|
||||
0);
|
||||
DEFINE_TYPEDOBJ_CLASS(OutlineOpaqueTypedObject,
|
||||
OutlineTypedObject::obj_trace,
|
||||
nullptr,
|
||||
0);
|
||||
DEFINE_TYPEDOBJ_CLASS(InlineTransparentTypedObject,
|
||||
InlineTypedObject::obj_trace,
|
||||
InlineTypedObject::obj_moved,
|
||||
JSCLASS_DELAY_METADATA_BUILDER);
|
||||
DEFINE_TYPEDOBJ_CLASS(InlineOpaqueTypedObject, InlineTypedObject::obj_trace,
|
||||
DEFINE_TYPEDOBJ_CLASS(InlineOpaqueTypedObject,
|
||||
InlineTypedObject::obj_trace,
|
||||
InlineTypedObject::obj_moved,
|
||||
JSCLASS_DELAY_METADATA_BUILDER);
|
||||
|
||||
static int32_t
|
||||
|
|
|
@ -723,7 +723,7 @@ class InlineTypedObject : public TypedObject
|
|||
}
|
||||
|
||||
static void obj_trace(JSTracer* trace, JSObject* object);
|
||||
static void objectMovedDuringMinorGC(JSTracer* trc, JSObject* dst, JSObject* src);
|
||||
static size_t obj_moved(JSObject* dst, JSObject* src);
|
||||
|
||||
static size_t offsetOfDataStart() {
|
||||
return offsetof(InlineTypedObject, data_);
|
||||
|
|
|
@ -231,10 +231,13 @@ parser.add_argument('--tag', '-t', type=str, nargs='?',
|
|||
help='name of job, also sets build command to "build.<tag>"')
|
||||
parser.add_argument('--expect-file', type=str, nargs='?',
|
||||
help='deprecated option, temporarily still present for backwards compatibility')
|
||||
parser.add_argument('--verbose', '-v', action='store_true',
|
||||
parser.add_argument('--verbose', '-v', action='count', default=1,
|
||||
help='Display cut & paste commands to run individual steps')
|
||||
parser.add_argument('--quiet', '-q', action='count', default=0,
|
||||
help='Suppress output')
|
||||
|
||||
args = parser.parse_args()
|
||||
args.verbose = max(0, args.verbose - args.quiet)
|
||||
|
||||
for default in defaults:
|
||||
try:
|
||||
|
@ -256,7 +259,7 @@ if args.tag and not args.buildcommand:
|
|||
if args.jobs is not None:
|
||||
data['jobs'] = args.jobs
|
||||
if not data.get('jobs'):
|
||||
data['jobs'] = subprocess.check_output(['nproc', '--ignore=1']).strip()
|
||||
data['jobs'] = int(subprocess.check_output(['nproc', '--ignore=1']).strip())
|
||||
|
||||
if args.buildcommand:
|
||||
data['buildcommand'] = args.buildcommand
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
loadRelativeToScript('utility.js');
|
||||
loadRelativeToScript('annotations.js');
|
||||
loadRelativeToScript('callgraph.js');
|
||||
loadRelativeToScript('dumpCFG.js');
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Annotations
|
||||
|
@ -24,9 +25,12 @@ function checkExternalFunction(entry)
|
|||
"fmod",
|
||||
"floor",
|
||||
"ceil",
|
||||
"atof",
|
||||
/memchr/,
|
||||
"strlen",
|
||||
"Servo_ComputedValues_EqualCustomProperties",
|
||||
/Servo_DeclarationBlock_GetCssText/,
|
||||
"Servo_GetArcStringData",
|
||||
/nsIFrame::AppendOwnedAnonBoxes/,
|
||||
// Assume that atomic accesses are threadsafe.
|
||||
/^__atomic_fetch_/,
|
||||
|
@ -221,6 +225,7 @@ function treatAsSafeArgument(entry, varName, csuName)
|
|||
["Gecko_StyleTransition_SetUnsupportedProperty", "aTransition", null],
|
||||
["Gecko_AddPropertyToSet", "aPropertySet", null],
|
||||
["Gecko_CalcStyleDifference", "aAnyStyleChanged", null],
|
||||
["Gecko_CalcStyleDifference", "aOnlyResetStructsChanged", null],
|
||||
["Gecko_nsStyleSVG_CopyContextProperties", "aDst", null],
|
||||
["Gecko_nsStyleFont_PrefillDefaultForGeneric", "aFont", null],
|
||||
["Gecko_nsStyleSVG_SetContextPropertiesLength", "aSvg", null],
|
||||
|
@ -229,6 +234,10 @@ function treatAsSafeArgument(entry, varName, csuName)
|
|||
["Gecko_CopyAlternateValuesFrom", "aDest", null],
|
||||
["Gecko_CounterStyle_GetName", "aResult", null],
|
||||
["Gecko_CounterStyle_GetSingleString", "aResult", null],
|
||||
["Gecko_EnsureMozBorderColors", "aBorder", null],
|
||||
["Gecko_ClearMozBorderColors", "aBorder", null],
|
||||
["Gecko_AppendMozBorderColors", "aBorder", null],
|
||||
["Gecko_CopyMozBorderColors", "aDest", null],
|
||||
];
|
||||
for (var [entryMatch, varMatch, csuMatch] of whitelist) {
|
||||
assert(entryMatch || varMatch || csuMatch);
|
||||
|
@ -243,6 +252,36 @@ function treatAsSafeArgument(entry, varName, csuName)
|
|||
return false;
|
||||
}
|
||||
|
||||
function isSafeAssignment(entry, edge, variable)
|
||||
{
|
||||
if (edge.Kind != 'Assign')
|
||||
return false;
|
||||
|
||||
var [mangled, unmangled] = splitFunction(entry.name);
|
||||
|
||||
// The assignment
|
||||
//
|
||||
// nsFont* font = fontTypes[eType];
|
||||
//
|
||||
// ends up with 'font' pointing to a member of 'this', so it should inherit
|
||||
// the safety of 'this'.
|
||||
if (unmangled.includes("mozilla::LangGroupFontPrefs::Initialize") &&
|
||||
variable == 'font')
|
||||
{
|
||||
const [lhs, rhs] = edge.Exp;
|
||||
const {Kind, Exp: [{Kind: indexKind, Exp: [collection, index]}]} = rhs;
|
||||
if (Kind == 'Drf' &&
|
||||
indexKind == 'Index' &&
|
||||
collection.Kind == 'Var' &&
|
||||
collection.Variable.Name[0] == 'fontTypes')
|
||||
{
|
||||
return entry.isSafeArgument(0); // 'this'
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function checkFieldWrite(entry, location, fields)
|
||||
{
|
||||
var name = entry.name;
|
||||
|
@ -255,11 +294,22 @@ function checkFieldWrite(entry, location, fields)
|
|||
return;
|
||||
if (/nsCOMPtr<.*?>.mRawPtr/.test(field))
|
||||
return;
|
||||
|
||||
if (/\bThreadLocal<\b/.test(field))
|
||||
return;
|
||||
}
|
||||
|
||||
var str = "";
|
||||
for (var field of fields)
|
||||
str += " " + field;
|
||||
|
||||
// Bug 1400435
|
||||
if (entry.stack[entry.stack.length - 1].callee.match(/^Gecko_CSSValue_Set/) &&
|
||||
str == " nsAutoRefCnt.mValue")
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
dumpError(entry, location, "Field write" + str);
|
||||
}
|
||||
|
||||
|
@ -281,6 +331,11 @@ function checkDereferenceWrite(entry, location, variable)
|
|||
if (hasThreadsafeReferenceCounts(entry, /nsCOMPtr<T>::swap\(.*?\[with T = (.*?)\]/))
|
||||
return;
|
||||
|
||||
// ConvertToLowerCase::write writes through a local pointer into the first
|
||||
// argument.
|
||||
if (/ConvertToLowerCase::write/.test(name) && entry.isSafeArgument(0))
|
||||
return;
|
||||
|
||||
dumpError(entry, location, "Dereference write " + (variable ? variable : "<unknown>"));
|
||||
}
|
||||
|
||||
|
@ -367,6 +422,8 @@ function ignoreContents(entry)
|
|||
"abort",
|
||||
/MOZ_ReportAssertionFailure/,
|
||||
/MOZ_ReportCrash/,
|
||||
/MOZ_CrashPrintf/,
|
||||
/MOZ_CrashOOL/,
|
||||
/AnnotateMozCrashReason/,
|
||||
/InvalidArrayIndex_CRASH/,
|
||||
/NS_ABORT_OOM/,
|
||||
|
@ -381,14 +438,14 @@ function ignoreContents(entry)
|
|||
/nsCSSValue::BufferFromString/,
|
||||
/NS_strdup/,
|
||||
/Assert_NoQueryNeeded/,
|
||||
/AssertCurrentThreadOwnsMe/,
|
||||
/PlatformThread::CurrentId/,
|
||||
/imgRequestProxy::GetProgressTracker/, // Uses an AutoLock
|
||||
/Smprintf/,
|
||||
"malloc",
|
||||
"free",
|
||||
"realloc",
|
||||
"jemalloc_thread_local_arena",
|
||||
/profiler_register_thread/,
|
||||
/profiler_unregister_thread/,
|
||||
|
||||
// These all create static strings in local storage, which is threadsafe
|
||||
// to do but not understood by the analysis yet.
|
||||
|
@ -397,38 +454,36 @@ function ignoreContents(entry)
|
|||
/nsCSSProps::ValueToKeyword/,
|
||||
/nsCSSKeywords::GetStringValue/,
|
||||
|
||||
// The analysis can't cope with the indirection used for the objects
|
||||
// being initialized here.
|
||||
"Gecko_GetOrCreateKeyframeAtStart",
|
||||
"Gecko_GetOrCreateInitialKeyframe",
|
||||
"Gecko_GetOrCreateFinalKeyframe",
|
||||
"Gecko_NewStyleQuoteValues",
|
||||
"Gecko_NewCSSValueSharedList",
|
||||
"Gecko_NewNoneTransform",
|
||||
"Gecko_NewGridTemplateAreasValue",
|
||||
/nsCSSValue::SetCalcValue/,
|
||||
/CSSValueSerializeCalcOps::Append/,
|
||||
"Gecko_CSSValue_SetFunction",
|
||||
"Gecko_CSSValue_SetArray",
|
||||
"Gecko_CSSValue_InitSharedList",
|
||||
"Gecko_EnsureMozBorderColors",
|
||||
"Gecko_ClearMozBorderColors",
|
||||
"Gecko_AppendMozBorderColors",
|
||||
"Gecko_CopyMozBorderColors",
|
||||
"Gecko_SetNullImageValue",
|
||||
// These could probably be handled by treating the scope of PSAutoLock
|
||||
// aka BaseAutoLock<PSMutex> as threadsafe.
|
||||
/profiler_register_thread/,
|
||||
/profiler_unregister_thread/,
|
||||
|
||||
// The analysis thinks we'll write to mBits in the DoGetStyleFoo<false>
|
||||
// call. Maybe the template parameter confuses it?
|
||||
/nsStyleContext::PeekStyle/,
|
||||
|
||||
// Needs main thread assertions or other fixes.
|
||||
/UndisplayedMap::GetEntryFor/,
|
||||
// The analysis can't cope with the indirection used for the objects
|
||||
// being initialized here, from nsCSSValue::Array::Create to the return
|
||||
// value of the Item(i) getter.
|
||||
/nsCSSValue::SetCalcValue/,
|
||||
|
||||
// Unable to analyze safety of linked list initialization.
|
||||
"Gecko_NewCSSValueSharedList",
|
||||
"Gecko_CSSValue_InitSharedList",
|
||||
|
||||
// Unable to trace through dataflow, but straightforward if inspected.
|
||||
"Gecko_NewNoneTransform",
|
||||
|
||||
// Bug 1368922
|
||||
"Gecko_UnsetDirtyStyleAttr",
|
||||
|
||||
// Bug 1400438
|
||||
"Gecko_AppendMozBorderColors",
|
||||
|
||||
// Need main thread assertions or other fixes.
|
||||
/EffectCompositor::GetServoAnimationRule/,
|
||||
/LookAndFeel::GetColor/,
|
||||
"Gecko_CopyStyleContentsFrom",
|
||||
"Gecko_CSSValue_SetPixelValue",
|
||||
"Gecko_UnsetDirtyStyleAttr",
|
||||
/nsCSSPropertyIDSet::AddProperty/,
|
||||
];
|
||||
if (entry.matches(whitelist))
|
||||
return true;
|
||||
|
@ -440,20 +495,21 @@ function ignoreContents(entry)
|
|||
/nsTArray_Impl.*?::AppendElement/,
|
||||
/nsTArray_Impl.*?::RemoveElementsAt/,
|
||||
/nsTArray_Impl.*?::ReplaceElementsAt/,
|
||||
/nsTArray_Impl.*?::InsertElementsAt/,
|
||||
/nsTArray_Impl.*?::InsertElementAt/,
|
||||
/nsTArray_Impl.*?::SetCapacity/,
|
||||
/nsTArray_Impl.*?::SetLength/,
|
||||
/nsTArray_base.*?::EnsureCapacity/,
|
||||
/nsTArray_base.*?::ShiftData/,
|
||||
/AutoTArray.*?::Init/,
|
||||
/nsTSubstring<T>::SetCapacity/,
|
||||
/nsTSubstring<T>::SetLength/,
|
||||
/nsTSubstring<T>::Assign/,
|
||||
/nsTSubstring<T>::Append/,
|
||||
/nsTSubstring<T>::Replace/,
|
||||
/nsTSubstring<T>::Trim/,
|
||||
/nsTSubstring<T>::Truncate/,
|
||||
/nsTSubstring<T>::StripTaggedASCII/,
|
||||
/nsTSubstring<T>::operator=/,
|
||||
/(nsTSubstring<T>|nsAC?String)::SetCapacity/,
|
||||
/(nsTSubstring<T>|nsAC?String)::SetLength/,
|
||||
/(nsTSubstring<T>|nsAC?String)::Assign/,
|
||||
/(nsTSubstring<T>|nsAC?String)::Append/,
|
||||
/(nsTSubstring<T>|nsAC?String)::Replace/,
|
||||
/(nsTSubstring<T>|nsAC?String)::Trim/,
|
||||
/(nsTSubstring<T>|nsAC?String)::Truncate/,
|
||||
/(nsTSubstring<T>|nsAC?String)::StripTaggedASCII/,
|
||||
/(nsTSubstring<T>|nsAC?String)::operator=/,
|
||||
/nsTAutoStringN<T, N>::nsTAutoStringN/,
|
||||
/nsTFixedString<T>::nsTFixedString/,
|
||||
|
||||
|
@ -491,8 +547,8 @@ function ignoreContents(entry)
|
|||
if (entry.isSafeArgument(2)) {
|
||||
var secondArgWhitelist = [
|
||||
/nsStringBuffer::ToString/,
|
||||
/AppendUTF8toUTF16/,
|
||||
/AppendASCIItoUTF16/,
|
||||
/AppendUTF\d+toUTF\d+/,
|
||||
/AppendASCIItoUTF\d+/,
|
||||
];
|
||||
if (entry.matches(secondArgWhitelist))
|
||||
return true;
|
||||
|
@ -571,12 +627,12 @@ function isZero(exp)
|
|||
// which are safe using a sorted array, so that this can be propagated down the
|
||||
// stack. Zero is |this|, and arguments are indexed starting at one.
|
||||
|
||||
function WorklistEntry(name, safeArguments, stack)
|
||||
function WorklistEntry(name, safeArguments, stack, parameterNames)
|
||||
{
|
||||
this.name = name;
|
||||
this.safeArguments = safeArguments;
|
||||
this.stack = stack;
|
||||
this.parameterNames = {};
|
||||
this.parameterNames = parameterNames;
|
||||
}
|
||||
|
||||
WorklistEntry.prototype.readable = function()
|
||||
|
@ -684,6 +740,12 @@ CallSite.prototype.safeString = function()
|
|||
var errorCount = 0;
|
||||
var errorLimit = 100;
|
||||
|
||||
// We want to suppress output for functions that ended up not having any
|
||||
// hazards, for brevity of the final output. So each new toplevel function will
|
||||
// initialize this to a string, which should be printed only if an error is
|
||||
// seen.
|
||||
var errorHeader;
|
||||
|
||||
var startTime = new Date;
|
||||
function elapsedTime()
|
||||
{
|
||||
|
@ -723,34 +785,40 @@ if (options.verbose) {
|
|||
}
|
||||
|
||||
print(elapsedTime() + "Loading types...");
|
||||
loadTypes('src_comp.xdb');
|
||||
if (os.getenv("TYPECACHE"))
|
||||
loadTypesWithCache('src_comp.xdb', os.getenv("TYPECACHE"));
|
||||
else
|
||||
loadTypes('src_comp.xdb');
|
||||
print(elapsedTime() + "Starting analysis...");
|
||||
|
||||
var reachable = {};
|
||||
|
||||
var xdb = xdbLibrary();
|
||||
xdb.open("src_body.xdb");
|
||||
|
||||
var minStream = xdb.min_data_stream();
|
||||
var maxStream = xdb.max_data_stream();
|
||||
|
||||
var roots = [];
|
||||
for (var bodyIndex = minStream; bodyIndex <= maxStream; bodyIndex++) {
|
||||
var key = xdb.read_key(bodyIndex);
|
||||
var name = key.readString();
|
||||
if (/^Gecko_/.test(name)) {
|
||||
var data = xdb.read_entry(key);
|
||||
if (/ServoBindings.cpp/.test(data.readString()))
|
||||
roots.push(name);
|
||||
xdb.free_string(data);
|
||||
|
||||
var [flag, arg] = scriptArgs;
|
||||
if (flag && (flag == '-f' || flag == '--function')) {
|
||||
roots = [arg];
|
||||
} else {
|
||||
for (var bodyIndex = minStream; bodyIndex <= maxStream; bodyIndex++) {
|
||||
var key = xdb.read_key(bodyIndex);
|
||||
var name = key.readString();
|
||||
if (/^Gecko_/.test(name)) {
|
||||
var data = xdb.read_entry(key);
|
||||
if (/ServoBindings.cpp/.test(data.readString()))
|
||||
roots.push(name);
|
||||
xdb.free_string(data);
|
||||
}
|
||||
xdb.free_string(key);
|
||||
}
|
||||
xdb.free_string(key);
|
||||
}
|
||||
|
||||
print(elapsedTime() + "Found " + roots.length + " roots.");
|
||||
for (var i = 0; i < roots.length; i++) {
|
||||
var root = roots[i];
|
||||
print(elapsedTime() + "#" + (i + 1) + " Analyzing " + root + " ...");
|
||||
errorHeader = elapsedTime() + "#" + (i + 1) + " Analyzing " + root + " ...";
|
||||
try {
|
||||
processRoot(root);
|
||||
} catch (e) {
|
||||
|
@ -771,8 +839,16 @@ var assignments;
|
|||
// All loops in the current function which are reachable off main thread.
|
||||
var reachableLoops;
|
||||
|
||||
// Functions that are reachable from the current root.
|
||||
var reachable = {};
|
||||
|
||||
function dumpError(entry, location, text)
|
||||
{
|
||||
if (errorHeader) {
|
||||
print(errorHeader);
|
||||
errorHeader = undefined;
|
||||
}
|
||||
|
||||
var stack = entry.stack;
|
||||
print("Error: " + text);
|
||||
print("Location: " + entry.name + (location ? " @ " + location : "") + stack[0].safeString());
|
||||
|
@ -803,7 +879,7 @@ function variableAssignRhs(edge)
|
|||
return null;
|
||||
}
|
||||
|
||||
function processAssign(entry, location, lhs, edge)
|
||||
function processAssign(body, entry, location, lhs, edge)
|
||||
{
|
||||
var fields;
|
||||
[lhs, fields] = stripFields(lhs);
|
||||
|
@ -817,17 +893,19 @@ function processAssign(entry, location, lhs, edge)
|
|||
// taken and indirect assignments might occur. This is an
|
||||
// unsoundness in the analysis.
|
||||
|
||||
let assign = [body, edge];
|
||||
|
||||
// Chain assignments if the RHS has only been assigned once.
|
||||
var rhsVariable = variableAssignRhs(edge);
|
||||
if (rhsVariable) {
|
||||
var rhsEdge = singleAssignment(variableName(rhsVariable));
|
||||
if (rhsEdge)
|
||||
edge = rhsEdge;
|
||||
var rhsAssign = singleAssignment(variableName(rhsVariable));
|
||||
if (rhsAssign)
|
||||
assign = rhsAssign;
|
||||
}
|
||||
|
||||
if (!(name in assignments))
|
||||
assignments[name] = [];
|
||||
assignments[name].push(edge);
|
||||
assignments[name].push(assign);
|
||||
} else {
|
||||
checkVariableAssignment(entry, location, name);
|
||||
}
|
||||
|
@ -838,6 +916,22 @@ function processAssign(entry, location, lhs, edge)
|
|||
variable = lhs.Exp[0].Variable;
|
||||
if (isSafeVariable(entry, variable))
|
||||
return;
|
||||
} else if (lhs.Exp[0].Kind == "Fld") {
|
||||
const {
|
||||
Type: {Kind, Type: fieldType},
|
||||
FieldCSU: {Type: {Kind: containerTypeKind,
|
||||
Name: containerTypeName}}
|
||||
} = lhs.Exp[0].Field;
|
||||
const [containerExpr] = lhs.Exp[0].Exp;
|
||||
|
||||
if (containerTypeKind == 'CSU' &&
|
||||
Kind == 'Pointer' &&
|
||||
isEdgeSafeArgument(entry, containerExpr) &&
|
||||
isSafeMemberPointer(containerTypeName, fieldType))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
if (fields.length)
|
||||
checkFieldWrite(entry, location, fields);
|
||||
|
@ -897,7 +991,7 @@ function process(entry, body, addCallee)
|
|||
switch (callee.kind) {
|
||||
case "direct":
|
||||
var safeArguments = getEdgeSafeArguments(entry, edge, callee.name);
|
||||
addCallee(new CallSite(callee.name, safeArguments, location, entry.parameterNames));
|
||||
addCallee(new CallSite(callee.name, safeArguments, location, {}));
|
||||
break;
|
||||
case "resolved-field":
|
||||
break;
|
||||
|
@ -921,11 +1015,11 @@ function process(entry, body, addCallee)
|
|||
|
||||
if (edge.Kind == "Assign") {
|
||||
assert(edge.Exp.length == 2);
|
||||
processAssign(entry, location, edge.Exp[0], edge);
|
||||
processAssign(body, entry, location, edge.Exp[0], edge);
|
||||
} else if (edge.Kind == "Call") {
|
||||
assert(edge.Exp.length <= 2);
|
||||
if (edge.Exp.length == 2)
|
||||
processAssign(entry, location, edge.Exp[1], edge);
|
||||
processAssign(body, entry, location, edge.Exp[1], edge);
|
||||
|
||||
// Treat assertion failures as if they don't return, so that
|
||||
// asserting NS_IsMainThread() is sufficient to prevent the
|
||||
|
@ -993,7 +1087,14 @@ function maybeProcessMissingFunction(entry, addCallee)
|
|||
function processRoot(name)
|
||||
{
|
||||
var safeArguments = [];
|
||||
var worklist = [new WorklistEntry(name, safeArguments, [new CallSite(name, safeArguments, null, {})])];
|
||||
var parameterNames = {};
|
||||
var worklist = [new WorklistEntry(name, safeArguments, [new CallSite(name, safeArguments, null, parameterNames)], parameterNames)];
|
||||
|
||||
reachable = {};
|
||||
|
||||
var armed = false;
|
||||
if (name.includes("Gecko_CSSValue_Set"))
|
||||
armed = true;
|
||||
|
||||
while (worklist.length > 0) {
|
||||
var entry = worklist.pop();
|
||||
|
@ -1003,6 +1104,7 @@ function processRoot(name)
|
|||
// analyzing functions separately for each subset if simpler, ensures that
|
||||
// the stack traces we produce accurately characterize the stack arguments,
|
||||
// and should be fast enough for now.
|
||||
|
||||
if (entry.mangledName() in reachable)
|
||||
continue;
|
||||
reachable[entry.mangledName()] = true;
|
||||
|
@ -1034,7 +1136,7 @@ function processRoot(name)
|
|||
for (var callee of callees) {
|
||||
if (!ignoreCallEdge(entry, callee.callee)) {
|
||||
var nstack = [callee, ...entry.stack];
|
||||
worklist.push(new WorklistEntry(callee.callee, callee.safeArguments, nstack));
|
||||
worklist.push(new WorklistEntry(callee.callee, callee.safeArguments, nstack, callee.parameterNames));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1085,9 +1187,13 @@ function singleAssignment(name)
|
|||
}
|
||||
|
||||
function expressionValueEdge(exp) {
|
||||
if (exp.Kind == "Var" && exp.Variable.Kind == "Temp")
|
||||
return singleAssignment(variableName(exp.Variable));
|
||||
return null;
|
||||
if (!(exp.Kind == "Var" && exp.Variable.Kind == "Temp"))
|
||||
return null;
|
||||
const assign = singleAssignment(variableName(exp.Variable));
|
||||
if (!assign)
|
||||
return null;
|
||||
const [body, edge] = assign;
|
||||
return edge;
|
||||
}
|
||||
|
||||
function isSafeVariable(entry, variable)
|
||||
|
@ -1100,10 +1206,24 @@ function isSafeVariable(entry, variable)
|
|||
return false;
|
||||
var name = variableName(variable);
|
||||
|
||||
if (!entry.safeLocals)
|
||||
entry.safeLocals = new Map;
|
||||
if (entry.safeLocals.has(name))
|
||||
return entry.safeLocals.get(name);
|
||||
|
||||
const safe = isSafeLocalVariable(entry, name);
|
||||
entry.safeLocals.set(name, safe);
|
||||
return safe;
|
||||
}
|
||||
|
||||
function isSafeLocalVariable(entry, name)
|
||||
{
|
||||
// If there is a single place where this variable has been assigned on
|
||||
// edges we are considering, look at that edge.
|
||||
var edge = singleAssignment(name);
|
||||
if (edge) {
|
||||
var assign = singleAssignment(name);
|
||||
if (assign) {
|
||||
const [body, edge] = assign;
|
||||
|
||||
// Treat temporary pointers to DebugOnly contents as thread local.
|
||||
if (isDirectCall(edge, /DebugOnly.*?::operator/))
|
||||
return true;
|
||||
|
@ -1121,23 +1241,32 @@ function isSafeVariable(entry, variable)
|
|||
// References to the contents of an array are threadsafe if the array
|
||||
// itself is threadsafe.
|
||||
if ((isDirectCall(edge, /operator\[\]/) ||
|
||||
isDirectCall(edge, /nsTArray.*?::InsertElementAt\b/) ||
|
||||
isDirectCall(edge, /nsStyleContent::ContentAt/)) &&
|
||||
isEdgeSafeArgument(entry, edge.PEdgeCallInstance.Exp))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Watch for the coerced result of a getter_AddRefs call.
|
||||
// Watch for the coerced result of a getter_AddRefs or getter_Copies call.
|
||||
if (isDirectCall(edge, /operator /)) {
|
||||
var otherEdge = expressionValueEdge(edge.PEdgeCallInstance.Exp);
|
||||
if (otherEdge &&
|
||||
isDirectCall(otherEdge, /getter_AddRefs/) &&
|
||||
isDirectCall(otherEdge, /getter_(?:AddRefs|Copies)/) &&
|
||||
isEdgeSafeArgument(entry, otherEdge.PEdgeCallArguments.Exp[0]))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// RefPtr::operator->() and operator* transmit the safety of the
|
||||
// RefPtr to the return value.
|
||||
if (isDirectCall(edge, /RefPtr<.*?>::operator(->|\*)\(\)/) &&
|
||||
isEdgeSafeArgument(entry, edge.PEdgeCallInstance.Exp))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Placement-new returns a pointer that is as safe as the pointer
|
||||
// passed to it. Exp[0] is the size, Exp[1] is the pointer/address.
|
||||
// Note that the invocation of the constructor is a separate call,
|
||||
|
@ -1155,8 +1284,31 @@ function isSafeVariable(entry, variable)
|
|||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Special case:
|
||||
//
|
||||
// keyframe->mTimingFunction.emplace()
|
||||
// keyframe->mTimingFunction->Init()
|
||||
//
|
||||
// The object calling Init should be considered safe here because
|
||||
// we just emplaced it, though in general keyframe::operator->
|
||||
// could do something crazy.
|
||||
if (isDirectCall(edge, /operator->/)) do {
|
||||
const predges = getPredecessors(body)[edge.Index[0]];
|
||||
if (!predges || predges.length != 1)
|
||||
break;
|
||||
const predge = predges[0];
|
||||
if (!isDirectCall(predge, /\bemplace\b/))
|
||||
break;
|
||||
const instance = predge.PEdgeCallInstance;
|
||||
if (JSON.stringify(instance) == JSON.stringify(edge.PEdgeCallInstance))
|
||||
return true;
|
||||
} while (false);
|
||||
}
|
||||
|
||||
if (isSafeAssignment(entry, edge, name))
|
||||
return true;
|
||||
|
||||
// Watch out for variables which were assigned arguments.
|
||||
var rhsVariable = variableAssignRhs(edge);
|
||||
if (rhsVariable)
|
||||
|
@ -1189,6 +1341,26 @@ function isSafeVariable(entry, variable)
|
|||
return true;
|
||||
}
|
||||
|
||||
function isSafeMemberPointer(containerType, memberType)
|
||||
{
|
||||
if (memberType.Kind != 'Pointer')
|
||||
return false;
|
||||
|
||||
const {Type: {Kind: pointeeKind, Name: pointeeTypeName}} = memberType;
|
||||
|
||||
// nsStyleBorder has a member mBorderColors of type nsBorderColors**. It is
|
||||
// lazily initialized to an array of 4 nsBorderColors, and should inherit
|
||||
// the safety of its container.
|
||||
if (containerType == 'nsStyleBorder' &&
|
||||
pointeeKind == 'CSU' &&
|
||||
pointeeTypeName == 'nsBorderColors')
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Return whether 'exp == value' holds only when execution is on the main thread.
|
||||
function testFailsOffMainThread(exp, value) {
|
||||
switch (exp.Kind) {
|
||||
|
|
|
@ -67,6 +67,8 @@ var ignoreClasses = {
|
|||
// Ignore calls through TYPE.FIELD, where TYPE is the class or struct name containing
|
||||
// a function pointer field named FIELD.
|
||||
var ignoreCallees = {
|
||||
"js::Class.trace" : true,
|
||||
"js::Class.finalize" : true,
|
||||
"js::ClassOps.trace" : true,
|
||||
"js::ClassOps.finalize" : true,
|
||||
"JSRuntime.destroyPrincipals" : true,
|
||||
|
|
|
@ -216,3 +216,20 @@ function loadTypes(type_xdb_filename) {
|
|||
xdb.free_string(data);
|
||||
}
|
||||
}
|
||||
|
||||
function loadTypesWithCache(type_xdb_filename, cache_filename) {
|
||||
try {
|
||||
const cacheAB = os.file.readFile(cache_filename, "binary");
|
||||
const cb = serialize();
|
||||
cb.clonebuffer = cacheAB.buffer;
|
||||
const cacheData = deserialize(cb);
|
||||
subclasses = cacheData.subclasses;
|
||||
superclasses = cacheData.superclasses;
|
||||
classFunctions = cacheData.classFunctions;
|
||||
} catch (e) {
|
||||
loadTypes(type_xdb_filename);
|
||||
const cb = serialize({subclasses, superclasses, classFunctions});
|
||||
os.file.writeTypedArrayToFile(cache_filename,
|
||||
new Uint8Array(cb.arraybuffer));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,259 @@
|
|||
// const cfg = loadCFG(scriptArgs[0]);
|
||||
// dump_CFG(cfg);
|
||||
|
||||
function loadCFG(filename) {
|
||||
const data = os.file.readFile(filename);
|
||||
return JSON.parse(data);
|
||||
}
|
||||
|
||||
function dump_CFG(cfg) {
|
||||
for (const body of cfg)
|
||||
dump_body(body);
|
||||
}
|
||||
|
||||
function dump_body(body, src, dst) {
|
||||
const {BlockId,Command,DefineVariable,Index,Location,PEdge,PPoint,Version} = body;
|
||||
|
||||
const [mangled, unmangled] = splitFunction(BlockId.Variable.Name[0]);
|
||||
print(`${unmangled} at ${Location[0].CacheString}:${Location[0].Line}`);
|
||||
|
||||
if (src === undefined) {
|
||||
for (const def of DefineVariable)
|
||||
print(str_definition(def));
|
||||
print("");
|
||||
}
|
||||
|
||||
for (const edge of PEdge) {
|
||||
if (src === undefined || edge.Index[0] == src) {
|
||||
if (dst == undefined || edge.Index[1] == dst)
|
||||
print(str_edge(edge, body));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function str_definition(def) {
|
||||
const {Type, Variable} = def;
|
||||
return `define ${str_Variable(Variable)} : ${str_Type(Type)}`;
|
||||
}
|
||||
|
||||
function badFormat(what, val) {
|
||||
printErr("Bad format of " + what + ": " + JSON.stringify(val, null, 4));
|
||||
printErr((new Error).stack);
|
||||
}
|
||||
|
||||
function str_Variable(variable) {
|
||||
if (variable.Kind == 'Return')
|
||||
return '<returnval>';
|
||||
else if (variable.Kind == 'This')
|
||||
return 'this';
|
||||
|
||||
try {
|
||||
return variable.Name[1];
|
||||
} catch(e) {
|
||||
badFormat("variable", variable);
|
||||
}
|
||||
}
|
||||
|
||||
function str_Type(type) {
|
||||
try {
|
||||
const {Kind, Type, Name, TypeFunctionArguments} = type;
|
||||
if (Kind == 'Pointer')
|
||||
return str_Type(Type) + "*";
|
||||
else if (Kind == 'CSU') {
|
||||
return Name;
|
||||
}
|
||||
|
||||
return Kind;
|
||||
} catch(e) {
|
||||
badFormat("type", type);
|
||||
}
|
||||
}
|
||||
|
||||
var OpCodeNames = {
|
||||
'LessEqual': ['<=', '>'],
|
||||
'LessThan': ['<', '>='],
|
||||
'GreaterEqual': ['>=', '<'],
|
||||
'Greater': ['>', '<='],
|
||||
'Plus': '+',
|
||||
'Minus': '-',
|
||||
};
|
||||
|
||||
function opcode_name(opcode, invert) {
|
||||
if (opcode in OpCodeNames) {
|
||||
const name = OpCodeNames[opcode];
|
||||
if (invert === undefined)
|
||||
return name;
|
||||
return name[invert ? 1 : 0];
|
||||
} else {
|
||||
if (invert === undefined)
|
||||
return opcode;
|
||||
return (invert ? '!' : '') + opcode;
|
||||
}
|
||||
}
|
||||
|
||||
function str_value(val, env, options) {
|
||||
const {Kind, Variable, String, Exp} = val;
|
||||
if (Kind == 'Var')
|
||||
return str_Variable(Variable);
|
||||
else if (Kind == 'Drf') {
|
||||
// Suppress the vtable lookup dereference
|
||||
if (Exp[0].Kind == 'Fld' && "FieldInstanceFunction" in Exp[0].Field)
|
||||
return str_value(Exp[0], env);
|
||||
const exp = str_value(Exp[0], env);
|
||||
if (options && options.noderef)
|
||||
return exp;
|
||||
return "*" + exp;
|
||||
} else if (Kind == 'Fld') {
|
||||
const {Exp, Field} = val;
|
||||
const name = Field.Name[0];
|
||||
if ("FieldInstanceFunction" in Field) {
|
||||
return Field.FieldCSU.Type.Name + "::" + name;
|
||||
}
|
||||
const container = str_value(Exp[0]);
|
||||
if (container.startsWith("*"))
|
||||
return container.substring(1) + "->" + name;
|
||||
return container + "." + name;
|
||||
} else if (Kind == 'Empty') {
|
||||
return '<unknown>';
|
||||
} else if (Kind == 'Binop') {
|
||||
const {OpCode} = val;
|
||||
const op = opcode_name(OpCode);
|
||||
return `${str_value(Exp[0], env)} ${op} ${str_value(Exp[1], env)}`;
|
||||
} else if (Kind == 'Unop') {
|
||||
const exp = str_value(Exp[0], env);
|
||||
const {OpCode} = val;
|
||||
if (OpCode == 'LogicalNot')
|
||||
return `not ${exp}`;
|
||||
return `${OpCode}(${exp})`;
|
||||
} else if (Kind == 'Index') {
|
||||
const index = str_value(Exp[1], env);
|
||||
if (Exp[0].Kind == 'Drf')
|
||||
return `${str_value(Exp[0], env, {noderef:true})}[${index}]`;
|
||||
else
|
||||
return `&${str_value(Exp[0], env)}[${index}]`;
|
||||
} else if (Kind == 'NullTest') {
|
||||
return `nullptr == ${str_value(Exp[0], env)}`;
|
||||
} else if (Kind == "String") {
|
||||
return '"' + String + '"';
|
||||
} else if (String !== undefined) {
|
||||
return String;
|
||||
}
|
||||
badFormat("value", val);
|
||||
}
|
||||
|
||||
function str_thiscall_Exp(exp) {
|
||||
return exp.Kind == 'Drf' ? str_value(exp.Exp[0]) + "->" : str_value(exp) + ".";
|
||||
}
|
||||
|
||||
function stripcsu(s) {
|
||||
return s.replace("class ", "").replace("struct ", "").replace("union ");
|
||||
}
|
||||
|
||||
function str_call(prefix, edge, env) {
|
||||
const {Exp, Type, PEdgeCallArguments, PEdgeCallInstance} = edge;
|
||||
const {Kind, Type:cType, TypeFunctionArguments, TypeFunctionCSU} = Type;
|
||||
|
||||
if (Kind == 'Function') {
|
||||
const params = PEdgeCallArguments ? PEdgeCallArguments.Exp : [];
|
||||
const strParams = params.map(str_value);
|
||||
|
||||
let func;
|
||||
let comment = "";
|
||||
let assign_exp;
|
||||
if (PEdgeCallInstance) {
|
||||
const csu = TypeFunctionCSU.Type.Name;
|
||||
const method = str_value(Exp[0], env);
|
||||
|
||||
// Heuristic to only display the csu for constructors
|
||||
if (csu.includes(method)) {
|
||||
func = stripcsu(csu) + "::" + method;
|
||||
} else {
|
||||
func = method;
|
||||
comment = "# " + csu + "::" + method + "\n";
|
||||
}
|
||||
|
||||
const {Exp: thisExp} = PEdgeCallInstance;
|
||||
func = str_thiscall_Exp(thisExp) + func;
|
||||
} else {
|
||||
func = str_value(Exp[0]);
|
||||
}
|
||||
assign_exp = Exp[1];
|
||||
|
||||
let assign = "";
|
||||
if (assign_exp) {
|
||||
assign = str_value(assign_exp) + " := ";
|
||||
}
|
||||
return `${comment}${prefix} Call ${assign}${func}(${strParams.join(", ")})`;
|
||||
}
|
||||
|
||||
print(JSON.stringify(edge, null, 4));
|
||||
throw "unhandled format error";
|
||||
}
|
||||
|
||||
function str_assign(prefix, edge) {
|
||||
const {Exp} = edge;
|
||||
const [lhs, rhs] = Exp;
|
||||
return `${prefix} Assign ${str_value(lhs)} := ${str_value(rhs)}`;
|
||||
}
|
||||
|
||||
function str_loop(prefix, edge) {
|
||||
const {BlockId: {Loop}} = edge;
|
||||
return `${prefix} Loop ${Loop}`;
|
||||
}
|
||||
|
||||
function str_assume(prefix, edge) {
|
||||
const {Exp, PEdgeAssumeNonZero} = edge;
|
||||
const cmp = PEdgeAssumeNonZero ? "" : "!";
|
||||
|
||||
const {Exp: aExp, Kind, OpCode} = Exp[0];
|
||||
if (Kind == 'Binop') {
|
||||
const [lhs, rhs] = aExp;
|
||||
const op = opcode_name(OpCode, !PEdgeAssumeNonZero);
|
||||
return `${prefix} Assume ${str_value(lhs)} ${op} ${str_value(rhs)}`;
|
||||
} else if (Kind == 'Unop') {
|
||||
return `${prefix} Assume ${cmp}${OpCode} ${str_value(aExp[0])}`;
|
||||
} else if (Kind == 'NullTest') {
|
||||
return `${prefix} Assume nullptr ${cmp}== ${str_value(aExp[0])}`;
|
||||
} else if (Kind == 'Drf') {
|
||||
return `${prefix} Assume ${cmp}${str_value(Exp[0])}`;
|
||||
}
|
||||
|
||||
print(JSON.stringify(edge, null, 4));
|
||||
throw "unhandled format error";
|
||||
}
|
||||
|
||||
function str_edge(edge, env) {
|
||||
const {Index, Kind} = edge;
|
||||
const [src, dst] = Index;
|
||||
const prefix = `[${src},${dst}]`;
|
||||
|
||||
if (Kind == "Call")
|
||||
return str_call(prefix, edge, env);
|
||||
if (Kind == 'Assign')
|
||||
return str_assign(prefix, edge);
|
||||
if (Kind == 'Assume')
|
||||
return str_assume(prefix, edge);
|
||||
if (Kind == 'Loop')
|
||||
return str_loop(prefix, edge);
|
||||
|
||||
print(JSON.stringify(edge, null, 4));
|
||||
throw "unhandled edge type";
|
||||
}
|
||||
|
||||
function str(unknown) {
|
||||
if ("Index" in unknown) {
|
||||
return str_edge(unknown);
|
||||
} else if ("Kind" in unknown) {
|
||||
if ("BlockId" in unknown)
|
||||
return str_Variable(unknown);
|
||||
return str_value(unknown);
|
||||
} else if ("Type" in unknown) {
|
||||
return str_Type(unknown);
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
function jdump(x) {
|
||||
print(JSON.stringify(x, null, 4));
|
||||
quit(0);
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
@ -81,9 +81,14 @@ for name in cfg.tests:
|
|||
except OSError:
|
||||
pass
|
||||
|
||||
test = Test(indir, outdir, cfg)
|
||||
test = Test(indir, outdir, cfg, verbose=cfg.verbose)
|
||||
|
||||
os.chdir(outdir)
|
||||
subprocess.call(["sh", "-c", "rm *.xdb"])
|
||||
execfile(os.path.join(indir, "test.py"), {'test': test, 'equal': equal})
|
||||
if cfg.verbose:
|
||||
print("Running test %s" % name)
|
||||
testpath = os.path.join(indir, "test.py")
|
||||
testscript = open(testpath).read()
|
||||
testcode = compile(testscript, testpath, 'exec')
|
||||
exec(testcode, {'test': test, 'equal': equal})
|
||||
print("TEST-PASSED: %s" % name)
|
||||
|
|
|
@ -9,9 +9,9 @@ suppressed = test.load_suppressed_functions()
|
|||
|
||||
# Only one of these is fully suppressed (ie, *always* called within the scope
|
||||
# of an AutoSuppressGC).
|
||||
assert(len(filter(lambda f: 'suppressedFunction' in f, suppressed)) == 1)
|
||||
assert(len(filter(lambda f: 'halfSuppressedFunction' in f, suppressed)) == 0)
|
||||
assert(len(filter(lambda f: 'unsuppressedFunction' in f, suppressed)) == 0)
|
||||
assert(len(list(filter(lambda f: 'suppressedFunction' in f, suppressed))) == 1)
|
||||
assert(len(list(filter(lambda f: 'halfSuppressedFunction' in f, suppressed))) == 0)
|
||||
assert(len(list(filter(lambda f: 'unsuppressedFunction' in f, suppressed))) == 0)
|
||||
|
||||
# gcFunctions should be the inverse, but we get to rely on unmangled names here.
|
||||
gcFunctions = test.load_gcFunctions()
|
||||
|
|
|
@ -10,7 +10,6 @@ scriptdir = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
|
|||
|
||||
HazardSummary = namedtuple('HazardSummary', ['function', 'variable', 'type', 'GCFunction', 'location'])
|
||||
|
||||
|
||||
def equal(got, expected):
|
||||
if got != expected:
|
||||
print("Got '%s', expected '%s'" % (got, expected))
|
||||
|
@ -20,10 +19,11 @@ def extract_unmangled(func):
|
|||
|
||||
|
||||
class Test(object):
|
||||
def __init__(self, indir, outdir, cfg):
|
||||
def __init__(self, indir, outdir, cfg, verbose=0):
|
||||
self.indir = indir
|
||||
self.outdir = outdir
|
||||
self.cfg = cfg
|
||||
self.verbose = verbose
|
||||
|
||||
def infile(self, path):
|
||||
return os.path.join(self.indir, path)
|
||||
|
@ -44,24 +44,26 @@ class Test(object):
|
|||
'''Look up an entry from an XDB database file, 'pattern' may be an exact
|
||||
matching string, or an re pattern object matching a single entry.'''
|
||||
|
||||
if not isinstance(pattern, basestring):
|
||||
output = subprocess.check_output([self.binpath("xdbkeys"), dbname + ".xdb"])
|
||||
matches = filter(lambda _: re.search(pattern, _), output.splitlines())
|
||||
if hasattr(pattern, 'match'):
|
||||
output = subprocess.check_output([self.binpath("xdbkeys"), dbname + ".xdb"],
|
||||
universal_newlines=True)
|
||||
matches = list(filter(lambda _: re.search(pattern, _), output.splitlines()))
|
||||
if len(matches) == 0:
|
||||
raise Exception("entry not found")
|
||||
if len(matches) > 1:
|
||||
raise Exception("multiple entries found")
|
||||
pattern = matches[0]
|
||||
|
||||
output = subprocess.check_output([self.binpath("xdbfind"), "-json", dbname + ".xdb", pattern])
|
||||
output = subprocess.check_output([self.binpath("xdbfind"), "-json", dbname + ".xdb", pattern],
|
||||
universal_newlines=True)
|
||||
return json.loads(output)
|
||||
|
||||
def run_analysis_script(self, phase, upto=None):
|
||||
file("defaults.py", "w").write('''\
|
||||
open("defaults.py", "w").write('''\
|
||||
analysis_scriptdir = '{scriptdir}'
|
||||
sixgill_bin = '{bindir}'
|
||||
'''.format(scriptdir=scriptdir, bindir=self.cfg.sixgill_bin))
|
||||
cmd = [os.path.join(scriptdir, "analyze.py"), phase]
|
||||
cmd = [os.path.join(scriptdir, "analyze.py"), '-v' if self.verbose else '-q', phase]
|
||||
if upto:
|
||||
cmd += ["--upto", upto]
|
||||
cmd.append("--source=%s" % self.indir)
|
||||
|
@ -80,8 +82,8 @@ sixgill_bin = '{bindir}'
|
|||
|
||||
def load_text_file(self, filename, extract=lambda l: l):
|
||||
fullpath = os.path.join(self.outdir, filename)
|
||||
values = (extract(line.strip()) for line in file(fullpath))
|
||||
return filter(lambda _: _ is not None, values)
|
||||
values = (extract(line.strip()) for line in open(fullpath, "r"))
|
||||
return list(filter(lambda _: _ is not None, values))
|
||||
|
||||
def load_suppressed_functions(self):
|
||||
return set(self.load_text_file("suppressedFunctions.lst"))
|
||||
|
|
|
@ -1107,6 +1107,9 @@ static_assert(js::gc::ChunkRuntimeOffset == offsetof(Chunk, trailer) +
|
|||
static_assert(js::gc::ChunkLocationOffset == offsetof(Chunk, trailer) +
|
||||
offsetof(ChunkTrailer, location),
|
||||
"The hardcoded API location offset must match the actual offset.");
|
||||
static_assert(js::gc::ChunkStoreBufferOffset == offsetof(Chunk, trailer) +
|
||||
offsetof(ChunkTrailer, storeBuffer),
|
||||
"The hardcoded API storeBuffer offset must match the actual offset.");
|
||||
|
||||
/*
|
||||
* Tracks the used sizes for owned heap data and automatically maintains the
|
||||
|
|
|
@ -2976,23 +2976,10 @@ js::TenuringTracer::moveObjectToTenured(JSObject* dst, JSObject* src, AllocKind
|
|||
// shape list. This is updated in Nursery::sweepDictionaryModeObjects().
|
||||
}
|
||||
|
||||
if (src->is<InlineTypedObject>()) {
|
||||
InlineTypedObject::objectMovedDuringMinorGC(this, dst, src);
|
||||
} else if (src->is<TypedArrayObject>()) {
|
||||
tenuredSize += TypedArrayObject::objectMovedDuringMinorGC(this, dst, src, dstKind);
|
||||
} else if (src->is<UnboxedArrayObject>()) {
|
||||
tenuredSize += UnboxedArrayObject::objectMovedDuringMinorGC(this, dst, src, dstKind);
|
||||
} else if (src->is<ArgumentsObject>()) {
|
||||
tenuredSize += ArgumentsObject::objectMovedDuringMinorGC(this, dst, src);
|
||||
} else if (src->is<ProxyObject>()) {
|
||||
// Objects in the nursery are never swapped so the proxy must have an
|
||||
// inline ProxyValueArray.
|
||||
MOZ_ASSERT(src->as<ProxyObject>().usingInlineValueArray());
|
||||
dst->as<ProxyObject>().setInlineValueArray();
|
||||
if (JSObjectMovedOp op = dst->getClass()->extObjectMovedOp())
|
||||
op(dst, src);
|
||||
} else if (JSObjectMovedOp op = dst->getClass()->extObjectMovedOp()) {
|
||||
op(dst, src);
|
||||
JSObjectMovedOp op = dst->getClass()->extObjectMovedOp();
|
||||
MOZ_ASSERT_IF(src->is<ProxyObject>(), op == proxy_ObjectMoved);
|
||||
if (op) {
|
||||
tenuredSize += op(dst, src);
|
||||
} else {
|
||||
MOZ_ASSERT_IF(src->getClass()->hasFinalize(),
|
||||
CanNurseryAllocateFinalizedClass(src->getClass()));
|
||||
|
|
|
@ -217,7 +217,12 @@ class Nursery
|
|||
void forwardBufferPointer(HeapSlot** pSlotsElems);
|
||||
|
||||
void maybeSetForwardingPointer(JSTracer* trc, void* oldData, void* newData, bool direct) {
|
||||
if (trc->isTenuringTracer() && isInside(oldData))
|
||||
if (trc->isTenuringTracer())
|
||||
setForwardingPointerWhileTenuring(oldData, newData, direct);
|
||||
}
|
||||
|
||||
void setForwardingPointerWhileTenuring(void* oldData, void* newData, bool direct) {
|
||||
if (isInside(oldData))
|
||||
setForwardingPointer(oldData, newData, direct);
|
||||
}
|
||||
|
||||
|
|
|
@ -126,6 +126,9 @@ struct InternalGCPointerPolicy {
|
|||
static void trace(JSTracer* trc, T* vp, const char* name) {
|
||||
TraceManuallyBarrieredEdge(trc, vp, name);
|
||||
}
|
||||
static bool isValid(T v) {
|
||||
return gc::IsCellPointerValid(v);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace js
|
||||
|
|
|
@ -40,6 +40,8 @@ int
|
|||
irregexp::CaseInsensitiveCompareStrings(const CharT* substring1, const CharT* substring2,
|
||||
size_t byteLength)
|
||||
{
|
||||
AutoUnsafeCallWithABI unsafe;
|
||||
|
||||
MOZ_ASSERT(byteLength % sizeof(CharT) == 0);
|
||||
size_t length = byteLength / sizeof(CharT);
|
||||
|
||||
|
@ -70,6 +72,8 @@ int
|
|||
irregexp::CaseInsensitiveCompareUCStrings(const CharT* substring1, const CharT* substring2,
|
||||
size_t byteLength)
|
||||
{
|
||||
AutoUnsafeCallWithABI unsafe;
|
||||
|
||||
MOZ_ASSERT(byteLength % sizeof(CharT) == 0);
|
||||
size_t length = byteLength / sizeof(CharT);
|
||||
|
||||
|
|
|
@ -47,6 +47,7 @@ RegExpStackScope::~RegExpStackScope()
|
|||
int
|
||||
irregexp::GrowBacktrackStack(JSRuntime* rt)
|
||||
{
|
||||
AutoUnsafeCallWithABI unsafe;
|
||||
return TlsContext.get()->regexpStack.ref().grow();
|
||||
}
|
||||
|
||||
|
|
|
@ -153,6 +153,11 @@ testComparison32('gt_u', 40, 40, 0);
|
|||
testComparison32('ge_s', 40, 40, 1);
|
||||
testComparison32('ge_u', 40, 40, 1);
|
||||
|
||||
// On 32-bit debug builds, with --ion-eager, this test can run into our
|
||||
// per-process JIT code limits and OOM. Trigger a GC to discard code.
|
||||
if (getJitCompilerOptions()["ion.warmup.trigger"] === 0)
|
||||
gc();
|
||||
|
||||
// Test MTest's GVN branch inversion.
|
||||
var testTrunc = wasmEvalText(`(module (func (param f32) (result i32) (if i32 (i32.eqz (i32.trunc_s/f32 (get_local 0))) (i32.const 0) (i32.const 1))) (export "" 0))`).exports[""];
|
||||
assertEq(testTrunc(0), 0);
|
||||
|
|
|
@ -29,6 +29,8 @@ using mozilla::IsInRange;
|
|||
uint32_t
|
||||
jit::Bailout(BailoutStack* sp, BaselineBailoutInfo** bailoutInfo)
|
||||
{
|
||||
AutoUnsafeCallWithABI unsafe;
|
||||
|
||||
JSContext* cx = TlsContext.get();
|
||||
MOZ_ASSERT(bailoutInfo);
|
||||
|
||||
|
@ -104,6 +106,8 @@ uint32_t
|
|||
jit::InvalidationBailout(InvalidationBailoutStack* sp, size_t* frameSizeOut,
|
||||
BaselineBailoutInfo** bailoutInfo)
|
||||
{
|
||||
AutoUnsafeCallWithABI unsafe;
|
||||
|
||||
sp->checkInvariants();
|
||||
|
||||
JSContext* cx = TlsContext.get();
|
||||
|
|
|
@ -988,6 +988,7 @@ EmitBranchIsReturningFromCallVM(MacroAssembler& masm, Register entry, Label* lab
|
|||
static void
|
||||
SyncBaselineDebugModeOSRInfo(BaselineFrame* frame, Value* vp, bool rv)
|
||||
{
|
||||
AutoUnsafeCallWithABI unsafe;
|
||||
BaselineDebugModeOSRInfo* info = frame->debugModeOSRInfo();
|
||||
MOZ_ASSERT(info);
|
||||
MOZ_ASSERT(frame->script()->baselineScript()->containsCodeAddress(info->resumeAddr));
|
||||
|
@ -1022,6 +1023,7 @@ SyncBaselineDebugModeOSRInfo(BaselineFrame* frame, Value* vp, bool rv)
|
|||
static void
|
||||
FinishBaselineDebugModeOSR(BaselineFrame* frame)
|
||||
{
|
||||
AutoUnsafeCallWithABI unsafe;
|
||||
frame->deleteDebugModeOSRInfo();
|
||||
|
||||
// We will return to JIT code now so we have to clear the override pc.
|
||||
|
|
|
@ -1591,12 +1591,14 @@ CreateDependentString::generate(MacroAssembler& masm, const JSAtomState& names,
|
|||
static void*
|
||||
AllocateString(JSContext* cx)
|
||||
{
|
||||
AutoUnsafeCallWithABI unsafe;
|
||||
return js::Allocate<JSString, NoGC>(cx);
|
||||
}
|
||||
|
||||
static void*
|
||||
AllocateFatInlineString(JSContext* cx)
|
||||
{
|
||||
AutoUnsafeCallWithABI unsafe;
|
||||
return js::Allocate<JSFatInlineString, NoGC>(cx);
|
||||
}
|
||||
|
||||
|
@ -1629,6 +1631,7 @@ CreateDependentString::generateFallback(MacroAssembler& masm, LiveRegisterSet re
|
|||
static void*
|
||||
CreateMatchResultFallbackFunc(JSContext* cx, gc::AllocKind kind, size_t nDynamicSlots)
|
||||
{
|
||||
AutoUnsafeCallWithABI unsafe;
|
||||
return js::Allocate<JSObject, NoGC>(cx, kind, nDynamicSlots, gc::DefaultHeap,
|
||||
&ArrayObject::class_);
|
||||
}
|
||||
|
@ -4000,7 +4003,8 @@ CodeGenerator::visitCallNative(LCallNative* call)
|
|||
if (jitInfo && jitInfo->type() == JSJitInfo::IgnoresReturnValueNative)
|
||||
native = jitInfo->ignoresReturnValueMethod;
|
||||
}
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, native));
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, native), MoveOp::GENERAL,
|
||||
CheckUnsafeCallWithABI::DontCheckHasExitFrame);
|
||||
|
||||
emitTracelogStopEvent(TraceLogger_Call);
|
||||
|
||||
|
@ -4123,7 +4127,8 @@ CodeGenerator::visitCallDOMNative(LCallDOMNative* call)
|
|||
masm.passABIArg(argObj);
|
||||
masm.passABIArg(argPrivate);
|
||||
masm.passABIArg(argArgs);
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, target->jitInfo()->method));
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, target->jitInfo()->method), MoveOp::GENERAL,
|
||||
CheckUnsafeCallWithABI::DontCheckHasExitFrame);
|
||||
|
||||
if (target->jitInfo()->isInfallible) {
|
||||
masm.loadValue(Address(masm.getStackPointer(), IonDOMMethodExitFrameLayout::offsetOfResult()),
|
||||
|
@ -7059,15 +7064,24 @@ CodeGenerator::visitMathFunctionF(LMathFunctionF* ins)
|
|||
masm.passABIArg(input, MoveOp::FLOAT32);
|
||||
|
||||
void* funptr = nullptr;
|
||||
CheckUnsafeCallWithABI check = CheckUnsafeCallWithABI::Check;
|
||||
switch (ins->mir()->function()) {
|
||||
case MMathFunction::Floor: funptr = JS_FUNC_TO_DATA_PTR(void*, floorf); break;
|
||||
case MMathFunction::Round: funptr = JS_FUNC_TO_DATA_PTR(void*, math_roundf_impl); break;
|
||||
case MMathFunction::Ceil: funptr = JS_FUNC_TO_DATA_PTR(void*, ceilf); break;
|
||||
case MMathFunction::Floor:
|
||||
funptr = JS_FUNC_TO_DATA_PTR(void*, floorf);
|
||||
check = CheckUnsafeCallWithABI::DontCheckOther;
|
||||
break;
|
||||
case MMathFunction::Round:
|
||||
funptr = JS_FUNC_TO_DATA_PTR(void*, math_roundf_impl);
|
||||
break;
|
||||
case MMathFunction::Ceil:
|
||||
funptr = JS_FUNC_TO_DATA_PTR(void*, ceilf);
|
||||
check = CheckUnsafeCallWithABI::DontCheckOther;
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("Unknown or unsupported float32 math function");
|
||||
}
|
||||
|
||||
masm.callWithABI(funptr, MoveOp::FLOAT32);
|
||||
masm.callWithABI(funptr, MoveOp::FLOAT32, check);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -7960,7 +7974,8 @@ JitRuntime::generateFreeStub(JSContext* cx)
|
|||
|
||||
masm.setupUnalignedABICall(regTemp);
|
||||
masm.passABIArg(regSlots);
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, js_free));
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, js_free), MoveOp::GENERAL,
|
||||
CheckUnsafeCallWithABI::DontCheckOther);
|
||||
|
||||
masm.PopRegsInMask(save);
|
||||
|
||||
|
@ -7998,7 +8013,8 @@ JitRuntime::generateLazyLinkStub(JSContext* cx)
|
|||
|
||||
masm.setupUnalignedABICall(temp0);
|
||||
masm.passABIArg(temp0);
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, LazyLinkTopActivation));
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, LazyLinkTopActivation), MoveOp::GENERAL,
|
||||
CheckUnsafeCallWithABI::DontCheckHasExitFrame);
|
||||
|
||||
masm.leaveExitFrame(/* stub code */ sizeof(JitCode*));
|
||||
|
||||
|
@ -11799,7 +11815,8 @@ CodeGenerator::visitGetDOMProperty(LGetDOMProperty* ins)
|
|||
masm.passABIArg(ObjectReg);
|
||||
masm.passABIArg(PrivateReg);
|
||||
masm.passABIArg(ValueReg);
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, ins->mir()->fun()));
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, ins->mir()->fun()), MoveOp::GENERAL,
|
||||
CheckUnsafeCallWithABI::DontCheckHasExitFrame);
|
||||
|
||||
if (ins->mir()->isInfallible()) {
|
||||
masm.loadValue(Address(masm.getStackPointer(), IonDOMExitFrameLayout::offsetOfResult()),
|
||||
|
@ -11897,7 +11914,8 @@ CodeGenerator::visitSetDOMProperty(LSetDOMProperty* ins)
|
|||
masm.passABIArg(ObjectReg);
|
||||
masm.passABIArg(PrivateReg);
|
||||
masm.passABIArg(ValueReg);
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, ins->mir()->fun()));
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, ins->mir()->fun()), MoveOp::GENERAL,
|
||||
CheckUnsafeCallWithABI::DontCheckHasExitFrame);
|
||||
|
||||
masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
|
||||
|
||||
|
|
|
@ -1078,7 +1078,8 @@ IonCacheIRCompiler::emitCallNativeGetterResult()
|
|||
masm.passABIArg(argJSContext);
|
||||
masm.passABIArg(argUintN);
|
||||
masm.passABIArg(argVp);
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, target->native()));
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, target->native()), MoveOp::GENERAL,
|
||||
CheckUnsafeCallWithABI::DontCheckHasExitFrame);
|
||||
|
||||
// Test for failure.
|
||||
masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
|
||||
|
@ -1136,7 +1137,8 @@ IonCacheIRCompiler::emitCallProxyGetResult()
|
|||
masm.passABIArg(argProxy);
|
||||
masm.passABIArg(argId);
|
||||
masm.passABIArg(argVp);
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, ProxyGetProperty));
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, ProxyGetProperty), MoveOp::GENERAL,
|
||||
CheckUnsafeCallWithABI::DontCheckHasExitFrame);
|
||||
|
||||
// Test for failure.
|
||||
masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
|
||||
|
@ -1324,6 +1326,7 @@ IonCacheIRCompiler::emitCallStringSplitResult()
|
|||
static bool
|
||||
GroupHasPropertyTypes(ObjectGroup* group, jsid* id, Value* v)
|
||||
{
|
||||
AutoUnsafeCallWithABI unsafe;
|
||||
if (group->unknownPropertiesDontCheckGeneration())
|
||||
return true;
|
||||
HeapTypeSet* propTypes = group->maybeGetPropertyDontCheckGeneration(*id);
|
||||
|
@ -1991,7 +1994,8 @@ IonCacheIRCompiler::emitCallNativeSetter()
|
|||
masm.passABIArg(argJSContext);
|
||||
masm.passABIArg(argUintN);
|
||||
masm.passABIArg(argVp);
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, target->native()));
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, target->native()), MoveOp::GENERAL,
|
||||
CheckUnsafeCallWithABI::DontCheckHasExitFrame);
|
||||
|
||||
// Test for failure.
|
||||
masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
|
||||
|
|
|
@ -126,8 +126,22 @@ MacroAssembler::passABIArg(FloatRegister reg, MoveOp::Type type)
|
|||
passABIArg(MoveOperand(reg), type);
|
||||
}
|
||||
|
||||
template <typename T> void
|
||||
MacroAssembler::callWithABI(const T& fun, MoveOp::Type result)
|
||||
void
|
||||
MacroAssembler::callWithABI(void* fun, MoveOp::Type result, CheckUnsafeCallWithABI check)
|
||||
{
|
||||
AutoProfilerCallInstrumentation profiler(*this);
|
||||
callWithABINoProfiler(fun, result, check);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::callWithABI(Register fun, MoveOp::Type result)
|
||||
{
|
||||
AutoProfilerCallInstrumentation profiler(*this);
|
||||
callWithABINoProfiler(fun, result);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::callWithABI(const Address& fun, MoveOp::Type result)
|
||||
{
|
||||
AutoProfilerCallInstrumentation profiler(*this);
|
||||
callWithABINoProfiler(fun, result);
|
||||
|
|
|
@ -1043,7 +1043,7 @@ FindStartOfUninitializedAndUndefinedSlots(NativeObject* templateObj, uint32_t ns
|
|||
static void
|
||||
AllocateObjectBufferWithInit(JSContext* cx, TypedArrayObject* obj, int32_t count)
|
||||
{
|
||||
JS::AutoCheckCannotGC nogc(cx);
|
||||
AutoUnsafeCallWithABI unsafe;
|
||||
|
||||
obj->initPrivate(nullptr);
|
||||
|
||||
|
@ -1550,7 +1550,8 @@ MacroAssembler::generateBailoutTail(Register scratch, Register bailoutInfo)
|
|||
loadJSContext(ReturnReg);
|
||||
setupUnalignedABICall(scratch);
|
||||
passABIArg(ReturnReg);
|
||||
callWithABI(JS_FUNC_TO_DATA_PTR(void*, BailoutReportOverRecursed));
|
||||
callWithABI(JS_FUNC_TO_DATA_PTR(void*, BailoutReportOverRecursed), MoveOp::GENERAL,
|
||||
CheckUnsafeCallWithABI::DontCheckHasExitFrame);
|
||||
jump(exceptionLabel());
|
||||
}
|
||||
|
||||
|
@ -1615,7 +1616,8 @@ MacroAssembler::generateBailoutTail(Register scratch, Register bailoutInfo)
|
|||
// Call a stub to free allocated memory and create arguments objects.
|
||||
setupUnalignedABICall(temp);
|
||||
passABIArg(bailoutInfo);
|
||||
callWithABI(JS_FUNC_TO_DATA_PTR(void*, FinishBailoutToBaseline));
|
||||
callWithABI(JS_FUNC_TO_DATA_PTR(void*, FinishBailoutToBaseline),
|
||||
MoveOp::GENERAL, CheckUnsafeCallWithABI::DontCheckHasExitFrame);
|
||||
branchTest32(Zero, ReturnReg, ReturnReg, exceptionLabel());
|
||||
|
||||
// Restore values where they need to be and resume execution.
|
||||
|
@ -1653,7 +1655,8 @@ MacroAssembler::generateBailoutTail(Register scratch, Register bailoutInfo)
|
|||
// Call a stub to free allocated memory and create arguments objects.
|
||||
setupUnalignedABICall(temp);
|
||||
passABIArg(bailoutInfo);
|
||||
callWithABI(JS_FUNC_TO_DATA_PTR(void*, FinishBailoutToBaseline));
|
||||
callWithABI(JS_FUNC_TO_DATA_PTR(void*, FinishBailoutToBaseline),
|
||||
MoveOp::GENERAL, CheckUnsafeCallWithABI::DontCheckHasExitFrame);
|
||||
branchTest32(Zero, ReturnReg, ReturnReg, exceptionLabel());
|
||||
|
||||
// Restore values where they need to be and resume execution.
|
||||
|
@ -1729,7 +1732,9 @@ MacroAssembler::assumeUnreachable(const char* output)
|
|||
setupUnalignedABICall(temp);
|
||||
movePtr(ImmPtr(output), temp);
|
||||
passABIArg(temp);
|
||||
callWithABI(JS_FUNC_TO_DATA_PTR(void*, AssumeUnreachable_));
|
||||
callWithABI(JS_FUNC_TO_DATA_PTR(void*, AssumeUnreachable_),
|
||||
MoveOp::GENERAL,
|
||||
CheckUnsafeCallWithABI::DontCheckOther);
|
||||
|
||||
PopRegsInMask(save);
|
||||
}
|
||||
|
@ -1753,7 +1758,10 @@ MacroAssembler::assertTestInt32(Condition cond, const T& value, const char* outp
|
|||
template void MacroAssembler::assertTestInt32(Condition, const Address&, const char*);
|
||||
|
||||
static void
|
||||
Printf0_(const char* output) {
|
||||
Printf0_(const char* output)
|
||||
{
|
||||
AutoUnsafeCallWithABI unsafe;
|
||||
|
||||
// Use stderr instead of stdout because this is only used for debug
|
||||
// output. stderr is less likely to interfere with the program's normal
|
||||
// output, and it's always unbuffered.
|
||||
|
@ -1778,7 +1786,9 @@ MacroAssembler::printf(const char* output)
|
|||
}
|
||||
|
||||
static void
|
||||
Printf1_(const char* output, uintptr_t value) {
|
||||
Printf1_(const char* output, uintptr_t value)
|
||||
{
|
||||
AutoUnsafeCallWithABI unsafe;
|
||||
AutoEnterOOMUnsafeRegion oomUnsafe;
|
||||
js::UniqueChars line = JS_sprintf_append(nullptr, output, value);
|
||||
if (!line)
|
||||
|
@ -1824,7 +1834,8 @@ MacroAssembler::tracelogStartId(Register logger, uint32_t textId, bool force)
|
|||
passABIArg(logger);
|
||||
move32(Imm32(textId), temp);
|
||||
passABIArg(temp);
|
||||
callWithABI(JS_FUNC_TO_DATA_PTR(void*, TraceLogStartEventPrivate));
|
||||
callWithABI(JS_FUNC_TO_DATA_PTR(void*, TraceLogStartEventPrivate), MoveOp::GENERAL,
|
||||
CheckUnsafeCallWithABI::DontCheckOther);
|
||||
|
||||
PopRegsInMask(save);
|
||||
}
|
||||
|
@ -1843,7 +1854,8 @@ MacroAssembler::tracelogStartId(Register logger, Register textId)
|
|||
setupUnalignedABICall(temp);
|
||||
passABIArg(logger);
|
||||
passABIArg(textId);
|
||||
callWithABI(JS_FUNC_TO_DATA_PTR(void*, TraceLogStartEventPrivate));
|
||||
callWithABI(JS_FUNC_TO_DATA_PTR(void*, TraceLogStartEventPrivate), MoveOp::GENERAL,
|
||||
CheckUnsafeCallWithABI::DontCheckOther);
|
||||
|
||||
PopRegsInMask(save);
|
||||
}
|
||||
|
@ -1864,7 +1876,8 @@ MacroAssembler::tracelogStartEvent(Register logger, Register event)
|
|||
setupUnalignedABICall(temp);
|
||||
passABIArg(logger);
|
||||
passABIArg(event);
|
||||
callWithABI(JS_FUNC_TO_DATA_PTR(void*, TraceLogFunc));
|
||||
callWithABI(JS_FUNC_TO_DATA_PTR(void*, TraceLogFunc), MoveOp::GENERAL,
|
||||
CheckUnsafeCallWithABI::DontCheckOther);
|
||||
|
||||
PopRegsInMask(save);
|
||||
}
|
||||
|
@ -1887,7 +1900,8 @@ MacroAssembler::tracelogStopId(Register logger, uint32_t textId, bool force)
|
|||
move32(Imm32(textId), temp);
|
||||
passABIArg(temp);
|
||||
|
||||
callWithABI(JS_FUNC_TO_DATA_PTR(void*, TraceLogStopEventPrivate));
|
||||
callWithABI(JS_FUNC_TO_DATA_PTR(void*, TraceLogStopEventPrivate), MoveOp::GENERAL,
|
||||
CheckUnsafeCallWithABI::DontCheckOther);
|
||||
|
||||
PopRegsInMask(save);
|
||||
}
|
||||
|
@ -1906,7 +1920,8 @@ MacroAssembler::tracelogStopId(Register logger, Register textId)
|
|||
setupUnalignedABICall(temp);
|
||||
passABIArg(logger);
|
||||
passABIArg(textId);
|
||||
callWithABI(JS_FUNC_TO_DATA_PTR(void*, TraceLogStopEventPrivate));
|
||||
callWithABI(JS_FUNC_TO_DATA_PTR(void*, TraceLogStopEventPrivate), MoveOp::GENERAL,
|
||||
CheckUnsafeCallWithABI::DontCheckOther);
|
||||
|
||||
PopRegsInMask(save);
|
||||
}
|
||||
|
@ -2107,7 +2122,8 @@ MacroAssembler::outOfLineTruncateSlow(FloatRegister src, Register dest, bool wid
|
|||
} else {
|
||||
setupUnalignedABICall(dest);
|
||||
passABIArg(src, MoveOp::DOUBLE);
|
||||
callWithABI(mozilla::BitwiseCast<void*, int32_t(*)(double)>(JS::ToInt32));
|
||||
callWithABI(mozilla::BitwiseCast<void*, int32_t(*)(double)>(JS::ToInt32),
|
||||
MoveOp::GENERAL, CheckUnsafeCallWithABI::DontCheckOther);
|
||||
}
|
||||
storeCallWordResult(dest);
|
||||
|
||||
|
@ -2801,7 +2817,7 @@ MacroAssembler::passABIArg(const MoveOperand& from, MoveOp::Type type)
|
|||
}
|
||||
|
||||
void
|
||||
MacroAssembler::callWithABINoProfiler(void* fun, MoveOp::Type result)
|
||||
MacroAssembler::callWithABINoProfiler(void* fun, MoveOp::Type result, CheckUnsafeCallWithABI check)
|
||||
{
|
||||
appendSignatureType(result);
|
||||
#ifdef JS_SIMULATOR
|
||||
|
@ -2810,8 +2826,33 @@ MacroAssembler::callWithABINoProfiler(void* fun, MoveOp::Type result)
|
|||
|
||||
uint32_t stackAdjust;
|
||||
callWithABIPre(&stackAdjust);
|
||||
|
||||
#ifdef DEBUG
|
||||
if (check == CheckUnsafeCallWithABI::Check) {
|
||||
push(ReturnReg);
|
||||
loadJSContext(ReturnReg);
|
||||
Address flagAddr(ReturnReg, JSContext::offsetOfInUnsafeCallWithABI());
|
||||
store32(Imm32(1), flagAddr);
|
||||
pop(ReturnReg);
|
||||
}
|
||||
#endif
|
||||
|
||||
call(ImmPtr(fun));
|
||||
|
||||
callWithABIPost(stackAdjust, result);
|
||||
|
||||
#ifdef DEBUG
|
||||
if (check == CheckUnsafeCallWithABI::Check) {
|
||||
Label ok;
|
||||
push(ReturnReg);
|
||||
loadJSContext(ReturnReg);
|
||||
Address flagAddr(ReturnReg, JSContext::offsetOfInUnsafeCallWithABI());
|
||||
branch32(Assembler::Equal, flagAddr, Imm32(0), &ok);
|
||||
assumeUnreachable("callWithABI: callee did not use AutoInUnsafeCallWithABI");
|
||||
bind(&ok);
|
||||
pop(ReturnReg);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -193,6 +193,20 @@ enum class ExitFrameToken : uint8_t;
|
|||
|
||||
class AutoSaveLiveRegisters;
|
||||
|
||||
enum class CheckUnsafeCallWithABI {
|
||||
// Require the callee to use AutoUnsafeCallWithABI.
|
||||
Check,
|
||||
|
||||
// We pushed an exit frame so this callWithABI can safely GC and walk the
|
||||
// stack.
|
||||
DontCheckHasExitFrame,
|
||||
|
||||
// Don't check this callWithABI uses AutoUnsafeCallWithABI, for instance
|
||||
// because we're calling a simple helper function (like malloc or js_free)
|
||||
// that we can't change and/or that we know won't GC.
|
||||
DontCheckOther,
|
||||
};
|
||||
|
||||
// The public entrypoint for emitting assembly. Note that a MacroAssembler can
|
||||
// use cx->lifoAlloc, so take care not to interleave masm use with other
|
||||
// lifoAlloc use if one will be destroyed before the other.
|
||||
|
@ -565,8 +579,10 @@ class MacroAssembler : public MacroAssemblerSpecific
|
|||
inline void passABIArg(Register reg);
|
||||
inline void passABIArg(FloatRegister reg, MoveOp::Type type);
|
||||
|
||||
template <typename T>
|
||||
inline void callWithABI(const T& fun, MoveOp::Type result = MoveOp::GENERAL);
|
||||
inline void callWithABI(void* fun, MoveOp::Type result = MoveOp::GENERAL,
|
||||
CheckUnsafeCallWithABI check = CheckUnsafeCallWithABI::Check);
|
||||
inline void callWithABI(Register fun, MoveOp::Type result = MoveOp::GENERAL);
|
||||
inline void callWithABI(const Address& fun, MoveOp::Type result = MoveOp::GENERAL);
|
||||
|
||||
void callWithABI(wasm::BytecodeOffset offset, wasm::SymbolicAddress fun,
|
||||
MoveOp::Type result = MoveOp::GENERAL);
|
||||
|
@ -580,7 +596,7 @@ class MacroAssembler : public MacroAssemblerSpecific
|
|||
void callWithABIPre(uint32_t* stackAdjust, bool callFromWasm = false) PER_ARCH;
|
||||
|
||||
// Emits a call to a C/C++ function, resolving all argument moves.
|
||||
void callWithABINoProfiler(void* fun, MoveOp::Type result);
|
||||
void callWithABINoProfiler(void* fun, MoveOp::Type result, CheckUnsafeCallWithABI check);
|
||||
void callWithABINoProfiler(Register fun, MoveOp::Type result) PER_ARCH;
|
||||
void callWithABINoProfiler(const Address& fun, MoveOp::Type result) PER_ARCH;
|
||||
|
||||
|
|
|
@ -259,16 +259,8 @@ struct MapTypeToRootKind<js::jit::RematerializedFrame*>
|
|||
|
||||
template <>
|
||||
struct GCPolicy<js::jit::RematerializedFrame*>
|
||||
{
|
||||
static js::jit::RematerializedFrame* initial() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void trace(JSTracer* trc, js::jit::RematerializedFrame** frame, const char* name) {
|
||||
if (*frame)
|
||||
(*frame)->trace(trc);
|
||||
}
|
||||
};
|
||||
: public NonGCPointerPolicy<js::jit::RematerializedFrame*>
|
||||
{};
|
||||
|
||||
} // namespace JS
|
||||
|
||||
|
|
|
@ -1205,7 +1205,8 @@ ICBinaryArith_DoubleWithInt32::Compiler::generateStubCode(MacroAssembler& masm)
|
|||
masm.push(intReg);
|
||||
masm.setupUnalignedABICall(scratchReg);
|
||||
masm.passABIArg(FloatReg0, MoveOp::DOUBLE);
|
||||
masm.callWithABI(mozilla::BitwiseCast<void*, int32_t(*)(double)>(JS::ToInt32));
|
||||
masm.callWithABI(mozilla::BitwiseCast<void*, int32_t(*)(double)>(JS::ToInt32),
|
||||
MoveOp::GENERAL, CheckUnsafeCallWithABI::DontCheckOther);
|
||||
masm.storeCallWordResult(scratchReg);
|
||||
masm.pop(intReg);
|
||||
|
||||
|
@ -1358,7 +1359,8 @@ ICUnaryArith_Double::Compiler::generateStubCode(MacroAssembler& masm)
|
|||
masm.bind(&truncateABICall);
|
||||
masm.setupUnalignedABICall(scratchReg);
|
||||
masm.passABIArg(FloatReg0, MoveOp::DOUBLE);
|
||||
masm.callWithABI(BitwiseCast<void*, int32_t(*)(double)>(JS::ToInt32));
|
||||
masm.callWithABI(BitwiseCast<void*, int32_t(*)(double)>(JS::ToInt32),
|
||||
MoveOp::GENERAL, CheckUnsafeCallWithABI::DontCheckOther);
|
||||
masm.storeCallWordResult(scratchReg);
|
||||
|
||||
masm.bind(&doneTruncate);
|
||||
|
|
|
@ -544,6 +544,7 @@ InterruptCheck(JSContext* cx)
|
|||
void*
|
||||
MallocWrapper(JSRuntime* rt, size_t nbytes)
|
||||
{
|
||||
AutoUnsafeCallWithABI unsafe;
|
||||
return rt->pod_malloc<uint8_t>(nbytes);
|
||||
}
|
||||
|
||||
|
@ -649,6 +650,8 @@ GetDynamicName(JSContext* cx, JSObject* envChain, JSString* str, Value* vp)
|
|||
// undefined through rval. This function is infallible, and cannot GC or
|
||||
// invalidate.
|
||||
|
||||
AutoUnsafeCallWithABI unsafe;
|
||||
|
||||
JSAtom* atom;
|
||||
if (str->isAtom()) {
|
||||
atom = &str->asAtom();
|
||||
|
@ -679,7 +682,7 @@ GetDynamicName(JSContext* cx, JSObject* envChain, JSString* str, Value* vp)
|
|||
void
|
||||
PostWriteBarrier(JSRuntime* rt, JSObject* obj)
|
||||
{
|
||||
JS::AutoCheckCannotGC nogc;
|
||||
AutoUnsafeCallWithABI unsafe;
|
||||
MOZ_ASSERT(!IsInsideNursery(obj));
|
||||
rt->gc.storeBuffer().putWholeCell(obj);
|
||||
}
|
||||
|
@ -690,7 +693,7 @@ template <IndexInBounds InBounds>
|
|||
void
|
||||
PostWriteElementBarrier(JSRuntime* rt, JSObject* obj, int32_t index)
|
||||
{
|
||||
JS::AutoCheckCannotGC nogc;
|
||||
AutoUnsafeCallWithABI unsafe;
|
||||
|
||||
MOZ_ASSERT(!IsInsideNursery(obj));
|
||||
|
||||
|
@ -744,7 +747,7 @@ int32_t
|
|||
GetIndexFromString(JSString* str)
|
||||
{
|
||||
// We shouldn't GC here as this is called directly from IC code.
|
||||
JS::AutoCheckCannotGC nogc;
|
||||
AutoUnsafeCallWithABI unsafe;
|
||||
|
||||
if (!str->isFlat())
|
||||
return -1;
|
||||
|
@ -760,7 +763,7 @@ JSObject*
|
|||
WrapObjectPure(JSContext* cx, JSObject* obj)
|
||||
{
|
||||
// IC code calls this directly so we shouldn't GC.
|
||||
JS::AutoCheckCannotGC nogc;
|
||||
AutoUnsafeCallWithABI unsafe;
|
||||
|
||||
MOZ_ASSERT(obj);
|
||||
MOZ_ASSERT(cx->compartment() != obj->compartment());
|
||||
|
@ -864,6 +867,7 @@ DebugEpilogue(JSContext* cx, BaselineFrame* frame, jsbytecode* pc, bool ok)
|
|||
void
|
||||
FrameIsDebuggeeCheck(BaselineFrame* frame)
|
||||
{
|
||||
AutoUnsafeCallWithABI unsafe;
|
||||
if (frame->script()->isDebuggee())
|
||||
frame->setIsDebuggee();
|
||||
}
|
||||
|
@ -1140,6 +1144,7 @@ OnDebuggerStatement(JSContext* cx, BaselineFrame* frame, jsbytecode* pc, bool* m
|
|||
bool
|
||||
GlobalHasLiveOnDebuggerStatement(JSContext* cx)
|
||||
{
|
||||
AutoUnsafeCallWithABI unsafe;
|
||||
return cx->compartment()->isDebuggee() &&
|
||||
Debugger::hasLiveHook(cx->global(), Debugger::OnDebuggerStatement);
|
||||
}
|
||||
|
@ -1232,6 +1237,7 @@ bool
|
|||
InitBaselineFrameForOsr(BaselineFrame* frame, InterpreterFrame* interpFrame,
|
||||
uint32_t numStackValues)
|
||||
{
|
||||
AutoUnsafeCallWithABI unsafe;
|
||||
return frame->initForOsr(interpFrame, numStackValues);
|
||||
}
|
||||
|
||||
|
@ -1318,6 +1324,7 @@ AutoDetectInvalidation::setReturnOverride()
|
|||
void
|
||||
AssertValidObjectPtr(JSContext* cx, JSObject* obj)
|
||||
{
|
||||
AutoUnsafeCallWithABI unsafe;
|
||||
#ifdef DEBUG
|
||||
// Check what we can, so that we'll hopefully assert/crash if we get a
|
||||
// bogus object (pointer).
|
||||
|
@ -1339,6 +1346,7 @@ AssertValidObjectPtr(JSContext* cx, JSObject* obj)
|
|||
void
|
||||
AssertValidObjectOrNullPtr(JSContext* cx, JSObject* obj)
|
||||
{
|
||||
AutoUnsafeCallWithABI unsafe;
|
||||
if (obj)
|
||||
AssertValidObjectPtr(cx, obj);
|
||||
}
|
||||
|
@ -1346,6 +1354,7 @@ AssertValidObjectOrNullPtr(JSContext* cx, JSObject* obj)
|
|||
void
|
||||
AssertValidStringPtr(JSContext* cx, JSString* str)
|
||||
{
|
||||
AutoUnsafeCallWithABI unsafe;
|
||||
#ifdef DEBUG
|
||||
// We can't closely inspect strings from another runtime.
|
||||
if (str->runtimeFromAnyThread() != cx->runtime()) {
|
||||
|
@ -1382,6 +1391,8 @@ AssertValidStringPtr(JSContext* cx, JSString* str)
|
|||
void
|
||||
AssertValidSymbolPtr(JSContext* cx, JS::Symbol* sym)
|
||||
{
|
||||
AutoUnsafeCallWithABI unsafe;
|
||||
|
||||
// We can't closely inspect symbols from another runtime.
|
||||
if (sym->runtimeFromAnyThread() != cx->runtime()) {
|
||||
MOZ_ASSERT(sym->isWellKnownSymbol());
|
||||
|
@ -1401,6 +1412,7 @@ AssertValidSymbolPtr(JSContext* cx, JS::Symbol* sym)
|
|||
void
|
||||
AssertValidValue(JSContext* cx, Value* v)
|
||||
{
|
||||
AutoUnsafeCallWithABI unsafe;
|
||||
if (v->isObject())
|
||||
AssertValidObjectPtr(cx, &v->toObject());
|
||||
else if (v->isString())
|
||||
|
@ -1412,24 +1424,28 @@ AssertValidValue(JSContext* cx, Value* v)
|
|||
bool
|
||||
ObjectIsCallable(JSObject* obj)
|
||||
{
|
||||
AutoUnsafeCallWithABI unsafe;
|
||||
return obj->isCallable();
|
||||
}
|
||||
|
||||
bool
|
||||
ObjectIsConstructor(JSObject* obj)
|
||||
{
|
||||
AutoUnsafeCallWithABI unsafe;
|
||||
return obj->isConstructor();
|
||||
}
|
||||
|
||||
void
|
||||
MarkValueFromIon(JSRuntime* rt, Value* vp)
|
||||
{
|
||||
AutoUnsafeCallWithABI unsafe;
|
||||
TraceManuallyBarrieredEdge(&rt->gc.marker, vp, "write barrier");
|
||||
}
|
||||
|
||||
void
|
||||
MarkStringFromIon(JSRuntime* rt, JSString** stringp)
|
||||
{
|
||||
AutoUnsafeCallWithABI unsafe;
|
||||
MOZ_ASSERT(*stringp);
|
||||
TraceManuallyBarrieredEdge(&rt->gc.marker, stringp, "write barrier");
|
||||
}
|
||||
|
@ -1437,6 +1453,7 @@ MarkStringFromIon(JSRuntime* rt, JSString** stringp)
|
|||
void
|
||||
MarkObjectFromIon(JSRuntime* rt, JSObject** objp)
|
||||
{
|
||||
AutoUnsafeCallWithABI unsafe;
|
||||
MOZ_ASSERT(*objp);
|
||||
TraceManuallyBarrieredEdge(&rt->gc.marker, objp, "write barrier");
|
||||
}
|
||||
|
@ -1444,12 +1461,14 @@ MarkObjectFromIon(JSRuntime* rt, JSObject** objp)
|
|||
void
|
||||
MarkShapeFromIon(JSRuntime* rt, Shape** shapep)
|
||||
{
|
||||
AutoUnsafeCallWithABI unsafe;
|
||||
TraceManuallyBarrieredEdge(&rt->gc.marker, shapep, "write barrier");
|
||||
}
|
||||
|
||||
void
|
||||
MarkObjectGroupFromIon(JSRuntime* rt, ObjectGroup** groupp)
|
||||
{
|
||||
AutoUnsafeCallWithABI unsafe;
|
||||
TraceManuallyBarrieredEdge(&rt->gc.marker, groupp, "write barrier");
|
||||
}
|
||||
|
||||
|
@ -1551,7 +1570,7 @@ bool
|
|||
EqualStringsHelper(JSString* str1, JSString* str2)
|
||||
{
|
||||
// IC code calls this directly so we shouldn't GC.
|
||||
JS::AutoCheckCannotGC nogc;
|
||||
AutoUnsafeCallWithABI unsafe;
|
||||
|
||||
MOZ_ASSERT(str1->isAtom());
|
||||
MOZ_ASSERT(!str2->isAtom());
|
||||
|
@ -1581,7 +1600,7 @@ GetNativeDataProperty(JSContext* cx, NativeObject* obj, jsid id, Value* vp)
|
|||
// lookup paths, this is optimized to be as fast as possible for simple
|
||||
// data property lookups.
|
||||
|
||||
JS::AutoCheckCannotGC nogc;
|
||||
AutoUnsafeCallWithABI unsafe;
|
||||
|
||||
MOZ_ASSERT(JSID_IS_ATOM(id) || JSID_IS_SYMBOL(id));
|
||||
|
||||
|
@ -1633,7 +1652,7 @@ GetNativeDataProperty<false>(JSContext* cx, JSObject* obj, PropertyName* name, V
|
|||
static MOZ_ALWAYS_INLINE bool
|
||||
ValueToAtomOrSymbol(JSContext* cx, Value& idVal, jsid* id)
|
||||
{
|
||||
JS::AutoCheckCannotGC nogc;
|
||||
AutoUnsafeCallWithABI unsafe;
|
||||
|
||||
if (MOZ_LIKELY(idVal.isString())) {
|
||||
JSString* s = idVal.toString();
|
||||
|
@ -1666,7 +1685,7 @@ template <bool HandleMissing>
|
|||
bool
|
||||
GetNativeDataPropertyByValue(JSContext* cx, JSObject* obj, Value* vp)
|
||||
{
|
||||
JS::AutoCheckCannotGC nogc;
|
||||
AutoUnsafeCallWithABI unsafe;
|
||||
|
||||
// Condition checked by caller.
|
||||
MOZ_ASSERT(obj->isNative());
|
||||
|
@ -1691,7 +1710,7 @@ template <bool NeedsTypeBarrier>
|
|||
bool
|
||||
SetNativeDataProperty(JSContext* cx, JSObject* obj, PropertyName* name, Value* val)
|
||||
{
|
||||
JS::AutoCheckCannotGC nogc;
|
||||
AutoUnsafeCallWithABI unsafe;
|
||||
|
||||
if (MOZ_UNLIKELY(!obj->isNative()))
|
||||
return false;
|
||||
|
@ -1723,7 +1742,7 @@ SetNativeDataProperty<false>(JSContext* cx, JSObject* obj, PropertyName* name, V
|
|||
bool
|
||||
ObjectHasGetterSetter(JSContext* cx, JSObject* objArg, Shape* propShape)
|
||||
{
|
||||
JS::AutoCheckCannotGC nogc;
|
||||
AutoUnsafeCallWithABI unsafe;
|
||||
|
||||
MOZ_ASSERT(propShape->hasGetterObject() || propShape->hasSetterObject());
|
||||
|
||||
|
@ -1766,7 +1785,7 @@ ObjectHasGetterSetter(JSContext* cx, JSObject* objArg, Shape* propShape)
|
|||
bool
|
||||
HasOwnNativeDataProperty(JSContext* cx, JSObject* obj, Value* vp)
|
||||
{
|
||||
JS::AutoCheckCannotGC nogc;
|
||||
AutoUnsafeCallWithABI unsafe;
|
||||
|
||||
// vp[0] contains the id, result will be stored in vp[1].
|
||||
Value idVal = vp[0];
|
||||
|
@ -1803,6 +1822,7 @@ HasOwnNativeDataProperty(JSContext* cx, JSObject* obj, Value* vp)
|
|||
JSString*
|
||||
TypeOfObject(JSObject* obj, JSRuntime* rt)
|
||||
{
|
||||
AutoUnsafeCallWithABI unsafe;
|
||||
JSType type = js::TypeOfObject(obj);
|
||||
return TypeName(type, *rt->commonNames);
|
||||
}
|
||||
|
|
|
@ -630,7 +630,8 @@ CodeGeneratorARM::visitSoftDivI(LSoftDivI* ins)
|
|||
masm.setupAlignedABICall();
|
||||
masm.passABIArg(lhs);
|
||||
masm.passABIArg(rhs);
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, __aeabi_idivmod));
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, __aeabi_idivmod), MoveOp::GENERAL,
|
||||
CheckUnsafeCallWithABI::DontCheckOther);
|
||||
}
|
||||
|
||||
// idivmod returns the quotient in r0, and the remainder in r1.
|
||||
|
@ -819,7 +820,8 @@ CodeGeneratorARM::visitSoftModI(LSoftModI* ins)
|
|||
masm.setupAlignedABICall();
|
||||
masm.passABIArg(lhs);
|
||||
masm.passABIArg(rhs);
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, __aeabi_idivmod));
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, __aeabi_idivmod), MoveOp::GENERAL,
|
||||
CheckUnsafeCallWithABI::DontCheckOther);
|
||||
}
|
||||
|
||||
MOZ_ASSERT(r1 != output);
|
||||
|
@ -2841,7 +2843,8 @@ CodeGeneratorARM::visitSoftUDivOrMod(LSoftUDivOrMod* ins)
|
|||
masm.setupAlignedABICall();
|
||||
masm.passABIArg(lhs);
|
||||
masm.passABIArg(rhs);
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, __aeabi_uidivmod));
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, __aeabi_uidivmod), MoveOp::GENERAL,
|
||||
CheckUnsafeCallWithABI::DontCheckOther);
|
||||
}
|
||||
|
||||
if (mod) {
|
||||
|
|
|
@ -3598,7 +3598,7 @@ MacroAssemblerARMCompat::handleFailureWithHandlerTail(void* handler)
|
|||
// Call the handler.
|
||||
asMasm().setupUnalignedABICall(r1);
|
||||
asMasm().passABIArg(r0);
|
||||
asMasm().callWithABI(handler);
|
||||
asMasm().callWithABI(handler, MoveOp::GENERAL, CheckUnsafeCallWithABI::DontCheckHasExitFrame);
|
||||
|
||||
Label entryFrame;
|
||||
Label catch_;
|
||||
|
|
|
@ -94,7 +94,8 @@ ICBinaryArith_Int32::Compiler::generateStubCode(MacroAssembler& masm)
|
|||
masm.setupAlignedABICall();
|
||||
masm.passABIArg(R0.payloadReg());
|
||||
masm.passABIArg(R1.payloadReg());
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, __aeabi_idivmod));
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, __aeabi_idivmod), MoveOp::GENERAL,
|
||||
CheckUnsafeCallWithABI::DontCheckOther);
|
||||
|
||||
// idivmod returns the quotient in r0, and the remainder in r1.
|
||||
if (op_ == JSOP_DIV) {
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче