Merge m-c to autoland, a=merge

MozReview-Commit-ID: BLgnf5zTCU3
This commit is contained in:
Wes Kocher 2017-09-19 14:54:09 -07:00
commit 082e7c650c
205 changed files with 3030 additions and 1485 deletions

View File

@ -44,7 +44,6 @@ function openContextMenu(aMessage) {
spellInfo,
principal: data.principal,
customMenuItems: data.customMenuItems,
addonInfo: data.addonInfo,
documentURIObject,
docLocation: data.docLocation,
charSet: data.charSet,

View File

@ -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;

View File

@ -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;

View File

@ -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"

View File

@ -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">

View File

@ -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,

View File

@ -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,

View File

@ -161,6 +161,10 @@ DOMIntersectionObserver::Observe(Element& aTarget)
void
DOMIntersectionObserver::Unobserve(Element& aTarget)
{
if (!mObservationTargets.Contains(&aTarget)) {
return;
}
if (mObservationTargets.Length() == 1) {
Disconnect();
return;

View File

@ -50,3 +50,4 @@ DEPRECATED_OPERATION(ImageBitmapRenderingContext_TransferImageBitmap)
DEPRECATED_OPERATION(URLCreateObjectURL_MediaStream)
DEPRECATED_OPERATION(XMLBaseAttribute)
DEPRECATED_OPERATION(XMLBaseAttributeForStyleAttr)
DEPRECATED_OPERATION(WindowContentUntrusted)

View File

@ -4965,6 +4965,8 @@ nsIDocument::SetContainer(nsDocShell* aContainer)
static_cast<nsDocument*>(this)->SetIsContentDocument(true);
}
mAncestorPrincipals = aContainer->AncestorPrincipals();
}
nsISupports*

View File

@ -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();

View File

@ -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)

View File

@ -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

View File

@ -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]

View File

@ -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>

View File

@ -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>

View File

@ -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;
}

View File

@ -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 = {

View File

@ -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:

View File

@ -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);

View File

@ -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
};

View File

@ -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
};

View File

@ -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
};

View File

@ -85,7 +85,7 @@ MultipartBlobImpl::GetInternalStream(nsIInputStream** aStream,
}
}
stream.forget(aStream);
CallQueryInterface(stream, aStream);
}
already_AddRefed<BlobImpl>

View File

@ -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();
}

View File

@ -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

View File

@ -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();
};

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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 */

View File

@ -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

View File

@ -70,6 +70,9 @@ public:
ErrorResult& aRv);
private:
void
Cancel();
nsString mOrigin;
nsCOMPtr<nsPIDOMWindowInner> mParent;
nsCOMPtr<nsISerialEventTarget> mEventTarget;

View File

@ -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 = {

View File

@ -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.

View File

@ -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"

View File

@ -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");
}

View File

@ -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;

View File

@ -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)); });

View File

@ -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 })
}

View File

@ -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(

View File

@ -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)); });

View File

@ -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 })
}

View File

@ -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,

View File

@ -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)); });

View File

@ -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,

View File

@ -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"]

View File

@ -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;
}

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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();
}

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -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;
};

View File

@ -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

View File

@ -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

View File

@ -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 */

View File

@ -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

View File

@ -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 */

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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>,
}

View File

@ -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()

View File

@ -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

View File

@ -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);

View File

@ -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>();

View File

@ -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])",

View File

@ -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

View File

@ -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_);

View File

@ -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

View File

@ -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) {

View File

@ -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,

View File

@ -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));
}
}

View File

@ -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);
}

View File

@ -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)

View File

@ -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()

View File

@ -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"))

View File

@ -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

View File

@ -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()));

View File

@ -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);
}

View File

@ -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

View File

@ -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);

View File

@ -47,6 +47,7 @@ RegExpStackScope::~RegExpStackScope()
int
irregexp::GrowBacktrackStack(JSRuntime* rt)
{
AutoUnsafeCallWithABI unsafe;
return TlsContext.get()->regexpStack.ref().grow();
}

View File

@ -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);

View File

@ -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();

View File

@ -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.

View File

@ -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());

View File

@ -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());

View File

@ -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);

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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);
}

View File

@ -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) {

View File

@ -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_;

View File

@ -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) {

Some files were not shown because too many files have changed in this diff Show More