author | Phil Ringnalda <philringnalda@gmail.com> |
Sun, 14 Jun 2015 15:54:58 -0700 | |
changeset 248789 | 3c26bef95d54870e5891b43b5fdbfabd1c8b026e |
parent 248788 | 19ba04cd72c2b7dc49b5b6fd59f42859665f5a8e (current diff) |
parent 248785 | 2a0e025a2251ee1eda2ff4ee416242ca6c912b4d (diff) |
child 248790 | e4053dff4ac34f688b25a6e055de9d5d0841267b |
child 248853 | 3839d9084daf8c96e2c9270d7287877a320160ce |
child 248896 | e4738a23d0c488a38e2eee42b7df46e221055ed7 |
push id | 61057 |
push user | philringnalda@gmail.com |
push date | Sun, 14 Jun 2015 22:59:54 +0000 |
treeherder | mozilla-inbound@e4053dff4ac3 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge |
milestone | 41.0a1 |
first release with | nightly linux32
3c26bef95d54
/
41.0a1
/
20150615030204
/
files
nightly linux64
3c26bef95d54
/
41.0a1
/
20150615030204
/
files
nightly mac
3c26bef95d54
/
41.0a1
/
20150615030204
/
files
nightly win32
3c26bef95d54
/
41.0a1
/
20150615030204
/
files
nightly win64
3c26bef95d54
/
41.0a1
/
20150615030204
/
files
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
releases | nightly linux32
41.0a1
/
20150615030204
/
pushlog to previous
nightly linux64
41.0a1
/
20150615030204
/
pushlog to previous
nightly mac
41.0a1
/
20150615030204
/
pushlog to previous
nightly win32
41.0a1
/
20150615030204
/
pushlog to previous
nightly win64
41.0a1
/
20150615030204
/
pushlog to previous
|
testing/web-platform/tests/webaudio/the-audio-api/the-gainnode-interface/test.html | file | annotate | diff | comparison | revisions |
--- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -7801,8 +7801,21 @@ nsContentUtils::FirePageShowEvent(nsIDoc } nsCOMPtr<nsIDocument> doc = aItem->GetDocument(); NS_ASSERTION(doc, "What happened here?"); if (doc->IsShowing() == aFireIfShowing) { doc->OnPageShow(true, aChromeEventHandler); } } + +/* static */ +already_AddRefed<nsPIWindowRoot> +nsContentUtils::GetWindowRoot(nsIDocument* aDoc) +{ + if (aDoc) { + nsPIDOMWindow* win = aDoc->GetWindow(); + if (win) { + return win->GetTopWindowRoot(); + } + } + return nullptr; +}
--- a/dom/base/nsContentUtils.h +++ b/dom/base/nsContentUtils.h @@ -94,16 +94,17 @@ class nsPresContext; class nsStringBuffer; class nsStringHashKey; class nsTextFragment; class nsView; class nsViewportInfo; class nsWrapperCache; class nsAttrValue; class nsITransferable; +class nsPIWindowRoot; struct JSPropertyDescriptor; struct JSRuntime; struct nsIntMargin; template<class E> class nsCOMArray; template<class K, class V> class nsDataHashtable; template<class K, class V> class nsRefPtrHashtable; @@ -2379,16 +2380,18 @@ public: static void FirePageShowEvent(nsIDocShellTreeItem* aItem, mozilla::dom::EventTarget* aChromeEventHandler, bool aFireIfShowing); static void FirePageHideEvent(nsIDocShellTreeItem* aItem, mozilla::dom::EventTarget* aChromeEventHandler); + static already_AddRefed<nsPIWindowRoot> GetWindowRoot(nsIDocument* aDoc); + private: static bool InitializeEventTable(); static nsresult EnsureStringBundle(PropertiesFile aFile); static bool CanCallerAccess(nsIPrincipal* aSubjectPrincipal, nsIPrincipal* aPrincipal);
--- a/dom/base/nsPIWindowRoot.h +++ b/dom/base/nsPIWindowRoot.h @@ -4,24 +4,31 @@ * 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/. */ #ifndef nsPIWindowRoot_h__ #define nsPIWindowRoot_h__ #include "nsISupports.h" #include "mozilla/dom/EventTarget.h" +#include "nsWeakReference.h" class nsPIDOMWindow; class nsIControllers; class nsIController; +namespace mozilla { +namespace dom { +class TabParent; +} +} + #define NS_IWINDOWROOT_IID \ -{ 0x728a2682, 0x55c0, 0x4860, \ - { 0x82, 0x6b, 0x0c, 0x30, 0x0a, 0xac, 0xaa, 0x60 } } +{ 0x238edca0, 0xb30d, 0x46d3, \ + { 0xb2, 0x6a, 0x17, 0xb6, 0x21, 0x28, 0x89, 0x7e } } class nsPIWindowRoot : public mozilla::dom::EventTarget { public: NS_DECLARE_STATIC_IID_ACCESSOR(NS_IWINDOWROOT_IID) virtual nsPIDOMWindow* GetWindow()=0; @@ -33,13 +40,22 @@ public: nsIController** aResult) = 0; virtual nsresult GetControllers(nsIControllers** aResult) = 0; virtual void GetEnabledDisabledCommands(nsTArray<nsCString>& aEnabledCommands, nsTArray<nsCString>& aDisabledCommands) = 0; virtual void SetParentTarget(mozilla::dom::EventTarget* aTarget) = 0; virtual mozilla::dom::EventTarget* GetParentTarget() = 0; + + // Stores a weak reference to the browser. + virtual void AddBrowser(mozilla::dom::TabParent* aBrowser) = 0; + virtual void RemoveBrowser(mozilla::dom::TabParent* aBrowser) = 0; + + typedef void (*BrowserEnumerator)(mozilla::dom::TabParent* aTab, void* aArg); + + // Enumerate all stored browsers that for which the weak reference is valid. + virtual void EnumerateBrowsers(BrowserEnumerator aEnumFunc, void* aArg) = 0; }; NS_DEFINE_STATIC_IID_ACCESSOR(nsPIWindowRoot, NS_IWINDOWROOT_IID) #endif // nsPIWindowRoot_h__
--- a/dom/base/nsWindowRoot.cpp +++ b/dom/base/nsWindowRoot.cpp @@ -19,16 +19,17 @@ #include "nsFocusManager.h" #include "nsIContent.h" #include "nsIDOMHTMLInputElement.h" #include "nsIDOMHTMLTextAreaElement.h" #include "nsIControllers.h" #include "nsIController.h" #include "xpcpublic.h" #include "nsCycleCollectionParticipant.h" +#include "mozilla/dom/TabParent.h" #ifdef MOZ_XUL #include "nsIDOMXULElement.h" #endif using namespace mozilla; using namespace mozilla::dom; @@ -380,16 +381,56 @@ nsWindowRoot::GetParentObject() } JSObject* nsWindowRoot::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) { return mozilla::dom::WindowRootBinding::Wrap(aCx, this, aGivenProto); } +void +nsWindowRoot::AddBrowser(mozilla::dom::TabParent* aBrowser) +{ + nsWeakPtr weakBrowser = do_GetWeakReference(static_cast<nsITabParent*>(aBrowser)); + mWeakBrowsers.PutEntry(weakBrowser); +} + +void +nsWindowRoot::RemoveBrowser(mozilla::dom::TabParent* aBrowser) +{ + nsWeakPtr weakBrowser = do_GetWeakReference(static_cast<nsITabParent*>(aBrowser)); + mWeakBrowsers.RemoveEntry(weakBrowser); +} + +static PLDHashOperator +WeakBrowserEnumFunc(nsRefPtrHashKey<nsIWeakReference>* aKey, void* aArg) +{ + nsTArray<nsRefPtr<TabParent>>* tabParents = + static_cast<nsTArray<nsRefPtr<TabParent>>*>(aArg); + nsCOMPtr<nsITabParent> tabParent(do_QueryReferent((*aKey).GetKey())); + TabParent* tab = TabParent::GetFrom(tabParent); + if (tab) { + tabParents->AppendElement(tab); + } + return PL_DHASH_NEXT; +} + +void +nsWindowRoot::EnumerateBrowsers(BrowserEnumerator aEnumFunc, void* aArg) +{ + // Collect strong references to all browsers in a separate array in + // case aEnumFunc alters mWeakBrowsers. + nsTArray<nsRefPtr<TabParent>> tabParents; + mWeakBrowsers.EnumerateEntries(WeakBrowserEnumFunc, &tabParents); + + for (uint32_t i = 0; i < tabParents.Length(); ++i) { + aEnumFunc(tabParents[i], aArg); + } +} + /////////////////////////////////////////////////////////////////////////////////// already_AddRefed<EventTarget> NS_NewWindowRoot(nsPIDOMWindow* aWindow) { nsCOMPtr<EventTarget> result = new nsWindowRoot(aWindow); return result.forget(); }
--- a/dom/base/nsWindowRoot.h +++ b/dom/base/nsWindowRoot.h @@ -64,29 +64,37 @@ public: nsIGlobalObject* GetParentObject(); virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsWindowRoot, nsIDOMEventTarget) + virtual void AddBrowser(mozilla::dom::TabParent* aBrowser) override; + virtual void RemoveBrowser(mozilla::dom::TabParent* aBrowser) override; + virtual void EnumerateBrowsers(BrowserEnumerator aEnumFunc, void *aArg) override; + protected: virtual ~nsWindowRoot(); void GetEnabledDisabledCommandsForControllers(nsIControllers* aControllers, nsTHashtable<nsCharPtrHashKey>& aCommandsHandled, nsTArray<nsCString>& aEnabledCommands, nsTArray<nsCString>& aDisabledCommands); // Members nsCOMPtr<nsPIDOMWindow> mWindow; // We own the manager, which owns event listeners attached to us. nsRefPtr<mozilla::EventListenerManager> mListenerManager; // [Strong] nsCOMPtr<nsIDOMNode> mPopupNode; // [OWNER] nsCOMPtr<mozilla::dom::EventTarget> mParent; + + // The TabParents that are currently registered with this top-level window. + typedef nsTHashtable<nsRefPtrHashKey<nsIWeakReference>> WeakBrowserTable; + WeakBrowserTable mWeakBrowsers; }; extern already_AddRefed<mozilla::dom::EventTarget> NS_NewWindowRoot(nsPIDOMWindow* aWindow); #endif
--- a/dom/ipc/PBrowser.ipdl +++ b/dom/ipc/PBrowser.ipdl @@ -20,16 +20,17 @@ include JavaScriptTypes; include URIParams; include BrowserConfiguration; using class IPC::Principal from "mozilla/dom/PermissionMessageUtils.h"; using class mozilla::gfx::Matrix from "mozilla/gfx/Matrix.h"; using struct gfxSize from "gfxPoint.h"; using CSSRect from "Units.h"; +using CSSSize from "Units.h"; using LayoutDeviceIntRect from "Units.h"; using mozilla::LayoutDeviceIntPoint from "Units.h"; using ScreenIntSize from "Units.h"; using struct mozilla::layers::FrameMetrics from "FrameMetrics.h"; using struct mozilla::layers::ScrollableLayerGuid from "FrameMetrics.h"; using struct mozilla::layers::ZoomConstraints from "FrameMetrics.h"; using FrameMetrics::ViewID from "FrameMetrics.h"; using struct mozilla::void_t from "ipc/IPCMessageUtils.h"; @@ -543,17 +544,17 @@ child: uint64_t layersId, nullable PRenderFrame renderFrame, bool parentIsActive); LoadURL(nsCString uri, BrowserConfiguration config); CacheFileDescriptor(nsString path, FileDescriptor fd); - UpdateDimensions(IntRect rect, ScreenIntSize size, ScreenOrientation orientation, + UpdateDimensions(CSSRect rect, CSSSize size, ScreenOrientation orientation, LayoutDeviceIntPoint chromeDisp) compressall; UpdateFrame(FrameMetrics frame); // The following methods correspond to functions on the GeckoContentController // interface in gfx/layers/apz/public/GeckoContentController.h. Refer to documentation // in that file for these functions. RequestFlingSnap(ViewID aScrollID, CSSPoint aDestination);
--- a/dom/ipc/TabChild.cpp +++ b/dom/ipc/TabChild.cpp @@ -87,16 +87,17 @@ #include "nsDOMClassInfoID.h" #include "nsColorPickerProxy.h" #include "nsPresShell.h" #include "nsIAppsService.h" #include "nsNetUtil.h" #include "nsIPermissionManager.h" #include "nsIScriptError.h" #include "mozilla/EventForwards.h" +#include "nsDeviceContext.h" #define BROWSER_ELEMENT_CHILD_SCRIPT \ NS_LITERAL_STRING("chrome://global/content/BrowserElementChild.js") #define TABC_LOG(...) // #define TABC_LOG(...) printf_stderr("TABC: " __VA_ARGS__) using namespace mozilla; @@ -160,17 +161,16 @@ UsingCompositorLRU() } NS_IMPL_ISUPPORTS(TabChild::DelayedFireContextMenuEvent, nsITimerCallback) TabChildBase::TabChildBase() : mContentDocumentIsDisplayed(false) , mTabChildGlobal(nullptr) - , mInnerSize(0, 0) { mozilla::HoldJSObjects(this); } TabChildBase::~TabChildBase() { mAnonymousGlobalScopes.Clear(); mozilla::DropJSObjects(this); @@ -231,19 +231,21 @@ void TabChildBase::InitializeRootMetrics() { // Calculate a really simple resolution that we probably won't // be keeping, as well as putting the scroll offset back to // the top-left of the page. mLastRootMetrics.SetViewport(CSSRect(CSSPoint(), kDefaultViewportSize)); mLastRootMetrics.SetCompositionBounds(ParentLayerRect( ParentLayerPoint(), - ParentLayerSize(ViewAs<ParentLayerPixel>(mInnerSize, PixelCastJustification::ScreenIsParentLayerForRoot)))); + ParentLayerSize( + ViewAs<ParentLayerPixel>(GetInnerSize(), + PixelCastJustification::ScreenIsParentLayerForRoot)))); mLastRootMetrics.SetZoom(CSSToParentLayerScale2D( - ConvertScaleForRoot(CalculateIntrinsicScale(mInnerSize, kDefaultViewportSize)))); + ConvertScaleForRoot(CalculateIntrinsicScale(GetInnerSize(), kDefaultViewportSize)))); mLastRootMetrics.SetDevPixelsPerCSSPixel(WebWidget()->GetDefaultScale()); // We use ParentLayerToLayerScale(1) below in order to turn the // async zoom amount into the gecko zoom amount. mLastRootMetrics.SetCumulativeResolution(mLastRootMetrics.GetZoom() / mLastRootMetrics.GetDevPixelsPerCSSPixel() * ParentLayerToLayerScale(1)); // This is the root layer, so the cumulative resolution is the same // as the resolution. mLastRootMetrics.SetPresShellResolution(mLastRootMetrics.GetCumulativeResolution().ToScaleFactor().scale); mLastRootMetrics.SetScrollOffset(CSSPoint(0, 0)); @@ -294,42 +296,42 @@ bool TabChildBase::HandlePossibleViewportChange(const ScreenIntSize& aOldScreenSize) { nsIWidget* widget = WebWidget(); if (!widget || !widget->AsyncPanZoomEnabled()) { return false; } TABC_LOG("HandlePossibleViewportChange aOldScreenSize=%s mInnerSize=%s\n", - Stringify(aOldScreenSize).c_str(), Stringify(mInnerSize).c_str()); + Stringify(aOldScreenSize).c_str(), Stringify(GetInnerSize()).c_str()); nsCOMPtr<nsIDocument> document(GetDocument()); if (!document) { return false; } - nsViewportInfo viewportInfo = nsContentUtils::GetViewportInfo(document, mInnerSize); + nsViewportInfo viewportInfo = nsContentUtils::GetViewportInfo(document, GetInnerSize()); uint32_t presShellId = 0; mozilla::layers::FrameMetrics::ViewID viewId = FrameMetrics::NULL_SCROLL_ID; bool scrollIdentifiersValid = APZCCallbackHelper::GetOrCreateScrollIdentifiers( document->GetDocumentElement(), &presShellId, &viewId); if (scrollIdentifiersValid) { ZoomConstraints constraints( viewportInfo.IsZoomAllowed(), viewportInfo.IsDoubleTapZoomAllowed(), ConvertScaleForRoot(viewportInfo.GetMinZoom()), ConvertScaleForRoot(viewportInfo.GetMaxZoom())); DoUpdateZoomConstraints(presShellId, viewId, /* isRoot = */ true, constraints); } - float screenW = mInnerSize.width; - float screenH = mInnerSize.height; + float screenW = GetInnerSize().width; + float screenH = GetInnerSize().height; CSSSize viewport(viewportInfo.GetSize()); // We're not being displayed in any way; don't bother doing anything because // that will just confuse future adjustments. if (!screenW || !screenH) { return false; } @@ -353,56 +355,58 @@ TabChildBase::HandlePossibleViewportChan // window.innerWidth before they are painted have a correct value (bug // 771575). if (!mContentDocumentIsDisplayed) { return false; } ScreenIntSize oldScreenSize = aOldScreenSize; if (oldScreenSize == ScreenIntSize()) { - oldScreenSize = mInnerSize; + oldScreenSize = GetInnerSize(); } FrameMetrics metrics(mLastRootMetrics); metrics.SetViewport(CSSRect(CSSPoint(), viewport)); - // Calculate the composition bounds based on mInnerSize, excluding the sizes + // Calculate the composition bounds based on the inner size, excluding the sizes // of the scrollbars if they are not overlay scrollbars. - ScreenSize compositionSize(mInnerSize); + ScreenSize compositionSize(GetInnerSize()); nsCOMPtr<nsIPresShell> shell = GetPresShell(); if (shell) { nsMargin scrollbarsAppUnits = nsLayoutUtils::ScrollbarAreaToExcludeFromCompositionBoundsFor(shell->GetRootScrollFrame()); // Scrollbars are not subject to scaling, so CSS pixels = screen pixels for them. ScreenMargin scrollbars = CSSMargin::FromAppUnits(scrollbarsAppUnits) * CSSToScreenScale(1.0f); compositionSize.width -= scrollbars.LeftRight(); compositionSize.height -= scrollbars.TopBottom(); } metrics.SetCompositionBounds(ParentLayerRect( ParentLayerPoint(), - ParentLayerSize(ViewAs<ParentLayerPixel>(compositionSize, PixelCastJustification::ScreenIsParentLayerForRoot)))); + ParentLayerSize( + ViewAs<ParentLayerPixel>(GetInnerSize(), + PixelCastJustification::ScreenIsParentLayerForRoot)))); metrics.SetRootCompositionSize( ScreenSize(compositionSize) * ScreenToLayoutDeviceScale(1.0f) / metrics.GetDevPixelsPerCSSPixel()); // This change to the zoom accounts for all types of changes I can conceive: // 1. screen size changes, CSS viewport does not (pages with no meta viewport // or a fixed size viewport) // 2. screen size changes, CSS viewport also does (pages with a device-width // viewport) // 3. screen size remains constant, but CSS viewport changes (meta viewport // tag is added or removed) // 4. neither screen size nor CSS viewport changes // // In all of these cases, we maintain how much actual content is visible // within the screen width. Note that "actual content" may be different with // respect to CSS pixels because of the CSS viewport size changing. CSSToScreenScale oldIntrinsicScale = CalculateIntrinsicScale(oldScreenSize, oldBrowserSize); - CSSToScreenScale newIntrinsicScale = CalculateIntrinsicScale(mInnerSize, viewport); + CSSToScreenScale newIntrinsicScale = CalculateIntrinsicScale(GetInnerSize(), viewport); metrics.ZoomBy(newIntrinsicScale.scale / oldIntrinsicScale.scale); // Changing the zoom when we're not doing a first paint will get ignored // by AsyncPanZoomController and causes a blurry flash. bool isFirstPaint = true; if (shell) { isFirstPaint = shell->GetIsFirstPaint(); } @@ -876,17 +880,16 @@ TabChild::TabChild(nsIContentChild* aMan const TabId& aTabId, const TabContext& aContext, uint32_t aChromeFlags) : TabContext(aContext) , mRemoteFrame(nullptr) , mManager(aManager) , mChromeFlags(aChromeFlags) , mLayersId(0) - , mOuterRect(0, 0, 0, 0) , mActivePointerId(-1) , mAppPackageFileDescriptorRecved(false) , mLastBackgroundColor(NS_RGB(255, 255, 255)) , mDidFakeShow(false) , mNotified(false) , mTriedBrowserInit(false) , mOrientation(eScreenOrientation_PortraitPrimary) , mUpdateHitRegion(false) @@ -917,19 +920,19 @@ TabChild::TabChild(nsIContentChild* aMan NS_IMETHODIMP TabChild::HandleEvent(nsIDOMEvent* aEvent) { nsAutoString eventType; aEvent->GetType(eventType); if (eventType.EqualsLiteral("DOMMetaAdded")) { // This meta data may or may not have been a meta viewport tag. If it was, // we should handle it immediately. - HandlePossibleViewportChange(mInnerSize); + HandlePossibleViewportChange(GetInnerSize()); } else if (eventType.EqualsLiteral("FullZoomChange")) { - HandlePossibleViewportChange(mInnerSize); + HandlePossibleViewportChange(GetInnerSize()); } return NS_OK; } NS_IMETHODIMP TabChild::Observe(nsISupports *aSubject, const char *aTopic, @@ -961,24 +964,24 @@ TabChild::Observe(nsISupports *aSubject, if (shell) { shell->SetIsFirstPaint(true); } mContentDocumentIsDisplayed = true; // In some cases before-first-paint gets called before // RecvUpdateDimensions is called and therefore before we have an - // mInnerSize value set. In such cases defer initializing the viewport + // inner size value set. In such cases defer initializing the viewport // until we we get an inner size. if (HasValidInnerSize()) { InitializeRootMetrics(); if (shell) { nsLayoutUtils::SetResolutionAndScaleTo(shell, mLastRootMetrics.GetPresShellResolution()); } - HandlePossibleViewportChange(mInnerSize); + HandlePossibleViewportChange(GetInnerSize()); } } } } return NS_OK; } @@ -1308,27 +1311,28 @@ TabChild::SetDimensions(uint32_t aFlags, return NS_OK; } NS_IMETHODIMP TabChild::GetDimensions(uint32_t aFlags, int32_t* aX, int32_t* aY, int32_t* aCx, int32_t* aCy) { + ScreenIntRect rect = GetOuterRect(); if (aX) { - *aX = mOuterRect.x; + *aX = rect.x; } if (aY) { - *aY = mOuterRect.y; + *aY = rect.y; } if (aCx) { - *aCx = mOuterRect.width; + *aCx = rect.width; } if (aCy) { - *aCy = mOuterRect.height; + *aCy = rect.height; } return NS_OK; } NS_IMETHODIMP TabChild::SetFocus() { @@ -2045,47 +2049,51 @@ TabChild::RecvShow(const ScreenIntSize& bool res = InitTabChildGlobal(); ApplyShowInfo(aInfo); RecvParentActivated(aParentIsActive); return res; } bool -TabChild::RecvUpdateDimensions(const nsIntRect& rect, const ScreenIntSize& size, - const ScreenOrientation& orientation, const LayoutDeviceIntPoint& chromeDisp) +TabChild::RecvUpdateDimensions(const CSSRect& rect, const CSSSize& size, + const ScreenOrientation& orientation, + const LayoutDeviceIntPoint& chromeDisp) { if (!mRemoteFrame) { return true; } - mOuterRect = rect; + mUnscaledOuterRect = rect; mChromeDisp = chromeDisp; bool initialSizing = !HasValidInnerSize() && (size.width != 0 && size.height != 0); + + mOrientation = orientation; + ScreenIntSize oldScreenSize = GetInnerSize(); + SetUnscaledInnerSize(size); + ScreenIntSize screenSize = GetInnerSize(); bool sizeChanged = true; if (initialSizing) { mHasValidInnerSize = true; - } else if (mInnerSize == size) { + } else if (screenSize == oldScreenSize) { sizeChanged = false; } - mOrientation = orientation; - ScreenIntSize oldScreenSize = mInnerSize; - mInnerSize = size; - mWidget->Resize(rect.x + chromeDisp.x, rect.y + chromeDisp.y, size.width, size.height, - true); + ScreenIntRect screenRect = GetOuterRect(); + mWidget->Resize(screenRect.x + chromeDisp.x, screenRect.y + chromeDisp.y, + screenSize.width, screenSize.height, true); nsCOMPtr<nsIBaseWindow> baseWin = do_QueryInterface(WebNavigation()); - baseWin->SetPositionAndSize(0, 0, size.width, size.height, + baseWin->SetPositionAndSize(0, 0, screenSize.width, screenSize.height, true); if (initialSizing && mContentDocumentIsDisplayed) { - // If this is the first time we're getting a valid mInnerSize, and the + // If this is the first time we're getting a valid inner size, and the // before-first-paint event has already been handled, then we need to set // up our default viewport here. See the corresponding call to // InitializeRootMetrics in the before-first-paint handler. InitializeRootMetrics(); } if (sizeChanged) { HandlePossibleViewportChange(oldScreenSize); @@ -3239,27 +3247,40 @@ TabChild::RecvRequestNotifyAfterRemotePa // RenderFrameParent. compositor->RequestNotifyAfterRemotePaint(this); return true; } bool TabChild::RecvUIResolutionChanged() { + ScreenIntSize oldScreenSize = GetInnerSize(); mDPI = 0; mDefaultScale = 0; static_cast<PuppetWidget*>(mWidget.get())->ClearBackingScaleCache(); nsCOMPtr<nsIDocument> document(GetDocument()); nsCOMPtr<nsIPresShell> presShell = document->GetShell(); if (presShell) { nsRefPtr<nsPresContext> presContext = presShell->GetPresContext(); if (presContext) { - presContext->UIResolutionChanged(); + presContext->UIResolutionChangedSync(); } } + + ScreenIntSize screenSize = GetInnerSize(); + if (mHasValidInnerSize && oldScreenSize != screenSize) { + ScreenIntRect screenRect = GetOuterRect(); + mWidget->Resize(screenRect.x + mChromeDisp.x, screenRect.y + mChromeDisp.y, + screenSize.width, screenSize.height, true); + + nsCOMPtr<nsIBaseWindow> baseWin = do_QueryInterface(WebNavigation()); + baseWin->SetPositionAndSize(0, 0, screenSize.width, screenSize.height, + true); + } + return true; } bool TabChild::RecvThemeChanged(nsTArray<LookAndFeelInt>&& aLookAndFeelIntCache) { LookAndFeel::SetIntCache(aLookAndFeelIntCache); nsCOMPtr<nsIDocument> document(GetDocument()); @@ -3311,16 +3332,32 @@ TabChild::CreatePluginWidget(nsIWidget* nsIntSize(0, 0)), &initData); if (NS_FAILED(rv)) { NS_WARNING("Creating native plugin widget on the chrome side failed."); } pluginWidget.forget(aOut); return rv; } +ScreenIntSize +TabChild::GetInnerSize() +{ + LayoutDeviceIntSize innerSize = + RoundedToInt(mUnscaledInnerSize * mWidget->GetDefaultScale()); + return ViewAs<ScreenPixel>(innerSize, PixelCastJustification::LayoutDeviceIsScreenForTabDims); +}; + +ScreenIntRect +TabChild::GetOuterRect() +{ + LayoutDeviceIntRect outerRect = + RoundedToInt(mUnscaledOuterRect * mWidget->GetDefaultScale()); + return ViewAs<ScreenPixel>(outerRect, PixelCastJustification::LayoutDeviceIsScreenForTabDims); +} + TabChildGlobal::TabChildGlobal(TabChildBase* aTabChild) : mTabChild(aTabChild) { SetIsNotDOMBinding(); } TabChildGlobal::~TabChildGlobal() {
--- a/dom/ipc/TabChild.h +++ b/dom/ipc/TabChild.h @@ -184,16 +184,18 @@ public: // change, this function doesn't do anything. However, it should // not be called all the time as it is fairly expensive. bool HandlePossibleViewportChange(const ScreenIntSize& aOldScreenSize); virtual bool DoUpdateZoomConstraints(const uint32_t& aPresShellId, const mozilla::layers::FrameMetrics::ViewID& aViewId, const bool& aIsRoot, const mozilla::layers::ZoomConstraints& aConstraints) = 0; + virtual ScreenIntSize GetInnerSize() = 0; + protected: virtual ~TabChildBase(); CSSSize GetPageSize(nsCOMPtr<nsIDocument> aDocument, const CSSSize& aViewport); // Get the DOMWindowUtils for the top-level window in this tab. already_AddRefed<nsIDOMWindowUtils> GetDOMWindowUtils(); // Get the Document for the top-level window in this tab. already_AddRefed<nsIDocument> GetDocument() const; @@ -217,17 +219,16 @@ protected: mozilla::layers::FrameMetrics ProcessUpdateFrame(const mozilla::layers::FrameMetrics& aFrameMetrics); bool UpdateFrameHandler(const mozilla::layers::FrameMetrics& aFrameMetrics); protected: CSSSize mOldViewportSize; bool mContentDocumentIsDisplayed; nsRefPtr<TabChildGlobal> mTabChildGlobal; - ScreenIntSize mInnerSize; mozilla::layers::FrameMetrics mLastRootMetrics; nsCOMPtr<nsIWebBrowserChrome3> mWebBrowserChrome; }; class TabChild final : public TabChildBase, public PBrowserChild, public nsIWebBrowserChrome2, public nsIEmbeddingSiteWindow, @@ -314,18 +315,18 @@ public: const FileDescriptor& aFileDescriptor) override; virtual bool RecvShow(const ScreenIntSize& aSize, const ShowInfo& aInfo, const TextureFactoryIdentifier& aTextureFactoryIdentifier, const uint64_t& aLayersId, PRenderFrameChild* aRenderFrame, const bool& aParentIsActive) override; - virtual bool RecvUpdateDimensions(const nsIntRect& rect, - const ScreenIntSize& size, + virtual bool RecvUpdateDimensions(const CSSRect& rect, + const CSSSize& size, const ScreenOrientation& orientation, const LayoutDeviceIntPoint& chromeDisp) override; virtual bool RecvUpdateFrame(const layers::FrameMetrics& aFrameMetrics) override; virtual bool RecvRequestFlingSnap(const ViewID& aScrollId, const CSSPoint& aDestination) override; virtual bool RecvAcknowledgeScrollUpdate(const ViewID& aScrollId, const uint32_t& aScrollGeneration) override; virtual bool RecvHandleDoubleTap(const CSSPoint& aPoint, @@ -505,16 +506,18 @@ public: bool IPCOpen() { return mIPCOpen; } bool ParentIsActive() { return mParentIsActive; } bool AsyncPanZoomEnabled() { return mAsyncPanZoomEnabled; } + virtual ScreenIntSize GetInnerSize() override; + protected: virtual ~TabChild(); virtual PRenderFrameChild* AllocPRenderFrameChild() override; virtual bool DeallocPRenderFrameChild(PRenderFrameChild* aFrame) override; virtual bool RecvDestroy() override; virtual bool RecvSetUpdateHitRegion(const bool& aEnabled) override; virtual bool RecvSetIsDocShellActive(const bool& aIsActive) override; @@ -594,29 +597,35 @@ private: bool HasValidInnerSize(); // Get the pres shell resolution of the document in this tab. float GetPresShellResolution() const; void SetTabId(const TabId& aTabId); + ScreenIntRect GetOuterRect(); + + void SetUnscaledInnerSize(const CSSSize& aSize) { + mUnscaledInnerSize = aSize; + } + class CachedFileDescriptorInfo; class CachedFileDescriptorCallbackRunnable; class DelayedDeleteRunnable; TextureFactoryIdentifier mTextureFactoryIdentifier; nsCOMPtr<nsIWebNavigation> mWebNav; nsCOMPtr<nsIWidget> mWidget; nsCOMPtr<nsIURI> mLastURI; RenderFrameChild* mRemoteFrame; nsRefPtr<nsIContentChild> mManager; uint32_t mChromeFlags; uint64_t mLayersId; - nsIntRect mOuterRect; + CSSRect mUnscaledOuterRect; // When we're tracking a possible tap gesture, this is the "down" // point of the touchstart. LayoutDevicePoint mGestureDownPoint; // The touch identifier of the active gesture. int32_t mActivePointerId; // A timer task that fires if the tap-hold timeout is exceeded by // the touch we're tracking. That is, if touchend or a touchmove // that exceeds the gesture threshold doesn't happen. @@ -641,16 +650,17 @@ private: // Position of tab, relative to parent widget (typically the window) LayoutDeviceIntPoint mChromeDisp; TabId mUniqueId; float mDPI; double mDefaultScale; bool mIPCOpen; bool mParentIsActive; bool mAsyncPanZoomEnabled; + CSSSize mUnscaledInnerSize; DISALLOW_EVIL_CONSTRUCTORS(TabChild); }; } } #endif // mozilla_dom_TabChild_h
--- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -85,16 +85,17 @@ #include "SourceSurfaceRawData.h" #include "nsAuthInformationHolder.h" #include "nsICancelable.h" #include "gfxPrefs.h" #include "nsILoginManagerPrompter.h" #include "nsPIWindowRoot.h" #include "gfxDrawable.h" #include "ImageOps.h" +#include "UnitTransforms.h" #include <algorithm> using namespace mozilla::dom; using namespace mozilla::ipc; using namespace mozilla::layers; using namespace mozilla::layout; using namespace mozilla::services; using namespace mozilla::widget; @@ -328,19 +329,37 @@ TabParent::CacheFrameLoader(nsFrameLoade } void TabParent::SetOwnerElement(Element* aElement) { // If we held previous content then unregister for its events. RemoveWindowListeners(); + // If we change top-level documents then we need to change our + // registration with them. + nsRefPtr<nsPIWindowRoot> curTopLevelWin, newTopLevelWin; + if (mFrameElement) { + curTopLevelWin = nsContentUtils::GetWindowRoot(mFrameElement->OwnerDoc()); + } + if (aElement) { + newTopLevelWin = nsContentUtils::GetWindowRoot(aElement->OwnerDoc()); + } + bool isSameTopLevelWin = curTopLevelWin == newTopLevelWin; + if (curTopLevelWin && !isSameTopLevelWin) { + curTopLevelWin->RemoveBrowser(this); + } + // Update to the new content, and register to listen for events from it. mFrameElement = aElement; + if (newTopLevelWin && !isSameTopLevelWin) { + newTopLevelWin->AddBrowser(this); + } + AddWindowListeners(); TryCacheDPIAndScale(); } void TabParent::AddWindowListeners() { if (mFrameElement && mFrameElement->OwnerDoc()) { @@ -946,33 +965,46 @@ TabParent::UpdateDimensions(const nsIntR return; } hal::ScreenConfiguration config; hal::GetCurrentScreenConfiguration(&config); ScreenOrientation orientation = config.orientation(); LayoutDeviceIntPoint chromeOffset = -GetChildProcessOffset(); nsCOMPtr<nsIWidget> widget = GetWidget(); + if (!widget) { + NS_WARNING("No widget found in TabParent::UpdateDimensions"); + return; + } nsIntRect contentRect = rect; - if (widget) { - contentRect.x += widget->GetClientOffset().x; - contentRect.y += widget->GetClientOffset().y; - } + contentRect.x += widget->GetClientOffset().x; + contentRect.y += widget->GetClientOffset().y; if (!mUpdatedDimensions || mOrientation != orientation || mDimensions != size || !mRect.IsEqualEdges(contentRect) || chromeOffset != mChromeOffset) { mUpdatedDimensions = true; mRect = contentRect; mDimensions = size; mOrientation = orientation; mChromeOffset = chromeOffset; - unused << SendUpdateDimensions(mRect, mDimensions, mOrientation, mChromeOffset); + CSSToLayoutDeviceScale widgetScale = widget->GetDefaultScale(); + + LayoutDeviceIntRect devicePixelRect = + ViewAs<LayoutDevicePixel>(mRect, + PixelCastJustification::LayoutDeviceIsScreenForTabDims); + LayoutDeviceIntSize devicePixelSize = + ViewAs<LayoutDevicePixel>(mDimensions.ToUnknownSize(), + PixelCastJustification::LayoutDeviceIsScreenForTabDims); + + CSSRect unscaledRect = devicePixelRect / widgetScale; + CSSSize unscaledSize = devicePixelSize / widgetScale; + unused << SendUpdateDimensions(unscaledRect, unscaledSize, orientation, chromeOffset); } } void TabParent::UpdateFrame(const FrameMetrics& aFrameMetrics) { if (!mIsDestroyed) { unused << SendUpdateFrame(aFrameMetrics); @@ -981,16 +1013,17 @@ TabParent::UpdateFrame(const FrameMetric void TabParent::UIResolutionChanged() { if (!mIsDestroyed) { // TryCacheDPIAndScale()'s cache is keyed off of // mDPI being greater than 0, so this invalidates it. mDPI = -1; + TryCacheDPIAndScale(); unused << SendUIResolutionChanged(); } } void TabParent::ThemeChanged() { if (!mIsDestroyed) {
--- a/dom/media/webspeech/recognition/test/mochitest.ini +++ b/dom/media/webspeech/recognition/test/mochitest.ini @@ -7,17 +7,17 @@ support-files = silence.ogg silence.ogg^headers^ [test_abort.html] skip-if = toolkit == 'android' || toolkit == 'gonk' # bug 1037287 [test_audio_capture_error.html] [test_call_start_from_end_handler.html] tags=capturestream -skip-if = (android_version == '18' && debug) # bug 967606 +skip-if = ((android_version == '18' || buildapp == 'b2g') && debug) # bug 967606 [test_nested_eventloop.html] skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' || e10s # b2g(showmodaldialog) [test_preference_enable.html] [test_recognition_service_error.html] skip-if = buildapp == 'b2g' # b2g(timed out) [test_success_without_recognition_service.html] [test_timeout.html] skip-if = os == "win"
--- a/js/src/gc/Barrier.cpp +++ b/js/src/gc/Barrier.cpp @@ -40,16 +40,22 @@ js::RuntimeFromMainThreadIsHeapMajorColl bool js::CurrentThreadIsIonCompiling() { return TlsPerThreadData.get()->ionCompiling; } bool +js::CurrentThreadIsIonCompilingSafeForMinorGC() +{ + return TlsPerThreadData.get()->ionCompilingSafeForMinorGC; +} + +bool js::CurrentThreadIsGCSweeping() { return js::TlsPerThreadData.get()->gcSweeping; } bool js::CurrentThreadIsHandlingInitFailure() {
--- a/js/src/gc/Barrier.h +++ b/js/src/gc/Barrier.h @@ -206,16 +206,19 @@ class JitCode; #ifdef DEBUG // Barriers can't be triggered during backend Ion compilation, which may run on // a helper thread. bool CurrentThreadIsIonCompiling(); bool +CurrentThreadIsIonCompilingSafeForMinorGC(); + +bool CurrentThreadIsGCSweeping(); bool CurrentThreadIsHandlingInitFailure(); #endif namespace gc {
--- a/js/src/gc/Marking.cpp +++ b/js/src/gc/Marking.cpp @@ -1810,18 +1810,22 @@ js::gc::StoreBuffer::WholeCellEdges::tra // capture any tenured->nursery edges in the expando as well. if (object->is<UnboxedPlainObject>()) { if (UnboxedExpandoObject* expando = object->as<UnboxedPlainObject>().maybeExpando()) expando->traceChildren(&mover); } return; } - MOZ_ASSERT(kind == JS::TraceKind::JitCode); - static_cast<jit::JitCode*>(edge)->traceChildren(&mover); + if (kind == JS::TraceKind::Script) + static_cast<JSScript*>(edge)->traceChildren(&mover); + else if (kind == JS::TraceKind::JitCode) + static_cast<jit::JitCode*>(edge)->traceChildren(&mover); + else + MOZ_CRASH(); } void js::gc::StoreBuffer::CellPtrEdge::trace(TenuringTracer& mover) const { if (!*edge) return; @@ -1873,21 +1877,21 @@ js::TenuringTracer::moveToTenured(JSObje void js::Nursery::collectToFixedPoint(TenuringTracer& mover, TenureCountCache& tenureCounts) { for (RelocationOverlay* p = mover.head; p; p = p->next()) { JSObject* obj = static_cast<JSObject*>(p->forwardingAddress()); mover.traceObject(obj); - TenureCount& entry = tenureCounts.findEntry(obj->group()); - if (entry.group == obj->group()) { + TenureCount& entry = tenureCounts.findEntry(obj->groupRaw()); + if (entry.group == obj->groupRaw()) { entry.count++; } else if (!entry.group) { - entry.group = obj->group(); + entry.group = obj->groupRaw(); entry.count = 1; } } } struct TenuringFunctor { template <typename T>
--- a/js/src/gc/Nursery.cpp +++ b/js/src/gc/Nursery.cpp @@ -416,16 +416,24 @@ js::Nursery::collect(JSRuntime* rt, JS:: AutoStopVerifyingBarriers av(rt, false); AutoDisableProxyCheck disableStrictProxyChecking(rt); mozilla::DebugOnly<AutoEnterOOMUnsafeRegion> oomUnsafeRegion; // Move objects pointed to by roots from the nursery to the major heap. TenuringTracer mover(rt, this); // Mark the store buffer. This must happen first. + + TIME_START(cancelIonCompilations); + if (sb.cancelIonCompilations()) { + for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) + jit::StopAllOffThreadCompilations(c); + } + TIME_END(cancelIonCompilations); + TIME_START(traceValues); sb.traceValues(mover); TIME_END(traceValues); TIME_START(traceCells); sb.traceCells(mover); TIME_END(traceCells); @@ -550,21 +558,22 @@ js::Nursery::collect(JSRuntime* rt, JS:: if (!printedHeader) { fprintf(stderr, "MinorGC: Reason PRate Size Time mkVals mkClls mkSlts mkWCll mkRVal mkRCll mkGnrc ckTbls mkRntm mkDbgr clrNOC collct swpABO updtIn runFin frSlts clrSB sweep resize pretnr\n"); printedHeader = true; } #define FMT " %6" PRIu64 fprintf(stderr, - "MinorGC: %20s %5.1f%% %4d" FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT "\n", + "MinorGC: %20s %5.1f%% %4d" FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT "\n", js::gcstats::ExplainReason(reason), promotionRate * 100, numActiveChunks_, totalTime, + TIME_TOTAL(cancelIonCompilations), TIME_TOTAL(traceValues), TIME_TOTAL(traceCells), TIME_TOTAL(traceSlots), TIME_TOTAL(traceWholeCells), TIME_TOTAL(traceRelocatableValues), TIME_TOTAL(traceRelocatableCells), TIME_TOTAL(traceGenericEntries), TIME_TOTAL(checkHashTables),
--- a/js/src/gc/StoreBuffer.cpp +++ b/js/src/gc/StoreBuffer.cpp @@ -68,16 +68,17 @@ StoreBuffer::disable() bool StoreBuffer::clear() { if (!enabled_) return true; aboutToOverflow_ = false; + cancelIonCompilations_ = false; bufferVal.clear(); bufferCell.clear(); bufferSlot.clear(); bufferWholeCell.clear(); bufferRelocVal.clear(); bufferRelocCell.clear(); bufferGeneric.clear();
--- a/js/src/gc/StoreBuffer.h +++ b/js/src/gc/StoreBuffer.h @@ -379,42 +379,45 @@ class StoreBuffer MonoTypeBuffer<ValueEdge> bufferVal; MonoTypeBuffer<CellPtrEdge> bufferCell; MonoTypeBuffer<SlotsEdge> bufferSlot; MonoTypeBuffer<WholeCellEdges> bufferWholeCell; MonoTypeBuffer<ValueEdge> bufferRelocVal; MonoTypeBuffer<CellPtrEdge> bufferRelocCell; GenericBuffer bufferGeneric; + bool cancelIonCompilations_; JSRuntime* runtime_; const Nursery& nursery_; bool aboutToOverflow_; bool enabled_; mozilla::DebugOnly<bool> mEntered; /* For ReentrancyGuard. */ public: explicit StoreBuffer(JSRuntime* rt, const Nursery& nursery) : bufferVal(), bufferCell(), bufferSlot(), bufferWholeCell(), - bufferRelocVal(), bufferRelocCell(), bufferGeneric(), + bufferRelocVal(), bufferRelocCell(), bufferGeneric(), cancelIonCompilations_(false), runtime_(rt), nursery_(nursery), aboutToOverflow_(false), enabled_(false), mEntered(false) { } bool enable(); void disable(); bool isEnabled() const { return enabled_; } bool clear(); /* Get the overflowed status. */ bool isAboutToOverflow() const { return aboutToOverflow_; } + bool cancelIonCompilations() const { return cancelIonCompilations_; } + /* Insert a single edge into the buffer/remembered set. */ void putValueFromAnyThread(JS::Value* valuep) { putFromAnyThread(bufferVal, ValueEdge(valuep)); } void putCellFromAnyThread(Cell** cellp) { putFromAnyThread(bufferCell, CellPtrEdge(cellp)); } void putSlotFromAnyThread(NativeObject* obj, int kind, int32_t start, int32_t count) { putFromAnyThread(bufferSlot, SlotsEdge(obj, kind, start, count)); } void putWholeCellFromMainThread(Cell* cell) { MOZ_ASSERT(cell->isTenured()); @@ -440,16 +443,20 @@ class StoreBuffer void putGeneric(const T& t) { putFromAnyThread(bufferGeneric, t);} /* Insert or update a callback entry. */ template <typename Key> void putCallback(void (*callback)(JSTracer* trc, Key* key, void* data), Key* key, void* data) { putFromAnyThread(bufferGeneric, CallbackRef<Key>(callback, key, data)); } + void setShouldCancelIonCompilations() { + cancelIonCompilations_ = true; + } + /* Methods to trace the source of all edges in the store buffer. */ void traceValues(TenuringTracer& mover) { bufferVal.trace(this, mover); } void traceCells(TenuringTracer& mover) { bufferCell.trace(this, mover); } void traceSlots(TenuringTracer& mover) { bufferSlot.trace(this, mover); } void traceWholeCells(TenuringTracer& mover) { bufferWholeCell.trace(this, mover); } void traceRelocatableValues(TenuringTracer& mover) { bufferRelocVal.trace(this, mover); } void traceRelocatableCells(TenuringTracer& mover) { bufferRelocCell.trace(this, mover); } void traceGenericEntries(JSTracer *trc) { bufferGeneric.trace(this, trc); }
--- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -2231,29 +2231,16 @@ CodeGenerator::visitPointer(LPointer* li { if (lir->kind() == LPointer::GC_THING) masm.movePtr(ImmGCPtr(lir->gcptr()), ToRegister(lir->output())); else masm.movePtr(ImmPtr(lir->ptr()), ToRegister(lir->output())); } void -CodeGenerator::visitNurseryObject(LNurseryObject* lir) -{ - Register output = ToRegister(lir->output()); - uint32_t index = lir->mir()->index(); - - // Store a dummy JSObject pointer. We will fix it up on the main thread, - // in JitCode::fixupNurseryObjects. The low bit is set to distinguish - // it from a real JSObject pointer. - JSObject* ptr = reinterpret_cast<JSObject*>((uintptr_t(index) << 1) | 1); - masm.movePtr(ImmGCPtr(IonNurseryPtr(ptr)), output); -} - -void CodeGenerator::visitKeepAliveObject(LKeepAliveObject* lir) { // No-op. } void CodeGenerator::visitSlots(LSlots* lir) { @@ -3610,21 +3597,26 @@ CodeGenerator::generateArgumentsChecks(b } else { Label success; masm.jump(&success); masm.bind(&miss); // Check for cases where the type set guard might have missed due to // changing object groups. for (uint32_t i = info.startArgSlot(); i < info.endArgSlot(); i++) { + MParameter* param = rp->getOperand(i)->toParameter(); + const TemporaryTypeSet* types = param->resultTypeSet(); + if (!types || types->unknown()) + continue; + Label skip; Address addr(StackPointer, ArgToStackOffset((i - info.startArgSlot()) * sizeof(Value))); masm.branchTestObject(Assembler::NotEqual, addr, &skip); Register obj = masm.extractObject(addr, temp); - masm.guardTypeSetMightBeIncomplete(obj, temp, &success); + masm.guardTypeSetMightBeIncomplete(types, obj, temp, &success); masm.bind(&skip); } masm.assumeUnreachable("Argument check fail."); masm.bind(&success); } } } @@ -3844,17 +3836,17 @@ CodeGenerator::branchIfInvalidated(Regis // If IonScript::invalidationCount_ != 0, the script has been invalidated. masm.branch32(Assembler::NotEqual, Address(temp, IonScript::offsetOfInvalidationCount()), Imm32(0), invalidated); } void -CodeGenerator::emitAssertObjectOrStringResult(Register input, MIRType type, TemporaryTypeSet* typeset) +CodeGenerator::emitAssertObjectOrStringResult(Register input, MIRType type, const TemporaryTypeSet* typeset) { MOZ_ASSERT(type == MIRType_Object || type == MIRType_ObjectOrNull || type == MIRType_String || type == MIRType_Symbol); AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); regs.take(input); Register temp = regs.takeAny(); @@ -3874,17 +3866,17 @@ CodeGenerator::emitAssertObjectOrStringR masm.branchPtr(Assembler::Equal, input, ImmWord(0), &ok); if (typeset->getObjectCount() > 0) masm.guardObjectType(input, typeset, temp, &miss); else masm.jump(&miss); masm.jump(&ok); masm.bind(&miss); - masm.guardTypeSetMightBeIncomplete(input, temp, &ok); + masm.guardTypeSetMightBeIncomplete(typeset, input, temp, &ok); masm.assumeUnreachable("MIR instruction returned object with unexpected type"); masm.bind(&ok); } // Check that we have a valid GC pointer. saveVolatile(); @@ -3914,17 +3906,17 @@ CodeGenerator::emitAssertObjectOrStringR masm.callWithABI(callee); restoreVolatile(); masm.bind(&done); masm.pop(temp); } void -CodeGenerator::emitAssertResultV(const ValueOperand input, TemporaryTypeSet* typeset) +CodeGenerator::emitAssertResultV(const ValueOperand input, const TemporaryTypeSet* typeset) { AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); regs.take(input); Register temp1 = regs.takeAny(); Register temp2 = regs.takeAny(); masm.push(temp1); masm.push(temp2); @@ -3942,17 +3934,17 @@ CodeGenerator::emitAssertResultV(const V masm.bind(&miss); // Check for cases where the type set guard might have missed due to // changing object groups. Label realMiss; masm.branchTestObject(Assembler::NotEqual, input, &realMiss); Register payload = masm.extractObject(input, temp1); - masm.guardTypeSetMightBeIncomplete(payload, temp1, &ok); + masm.guardTypeSetMightBeIncomplete(typeset, payload, temp1, &ok); masm.bind(&realMiss); masm.assumeUnreachable("MIR instruction returned value with unexpected type"); masm.bind(&ok); } // Check that we have a valid GC pointer. @@ -8067,24 +8059,30 @@ CodeGenerator::link(JSContext* cx, Compi ionScript->copyBailoutTable(&bailouts_[0]); if (osiIndices_.length()) ionScript->copyOsiIndices(&osiIndices_[0], masm); if (snapshots_.listSize()) ionScript->copySnapshots(&snapshots_); MOZ_ASSERT_IF(snapshots_.listSize(), recovers_.size()); if (recovers_.size()) ionScript->copyRecovers(&recovers_); - if (graph.numConstants()) - ionScript->copyConstants(graph.constantPool()); + if (graph.numConstants()) { + const Value* vp = graph.constantPool(); + ionScript->copyConstants(vp); + for (size_t i = 0; i < graph.numConstants(); i++) { + const Value& v = vp[i]; + if (v.isObject() && IsInsideNursery(&v.toObject())) { + cx->runtime()->gc.storeBuffer.putWholeCellFromMainThread(script); + break; + } + } + } if (patchableBackedges_.length() > 0) ionScript->copyPatchableBackedges(cx, code, patchableBackedges_.begin(), masm); - // Replace dummy JSObject pointers embedded by LNurseryObject. - code->fixupNurseryObjects(cx, gen->nurseryObjects()); - // The correct state for prebarriers is unknown until the end of compilation, // since a GC can occur during code generation. All barriers are emitted // off-by-default, and are toggled on here if necessary. if (cx->zone()->needsIncrementalBarrier()) ionScript->toggleBarriers(true); // Attach any generated script counts to the script. if (IonScriptCounts* counts = extractScriptCounts())
--- a/js/src/jit/CodeGenerator.h +++ b/js/src/jit/CodeGenerator.h @@ -104,17 +104,16 @@ class CodeGenerator : public CodeGenerat void visitOutOfLineRegExpTest(OutOfLineRegExpTest* ool); void visitRegExpReplace(LRegExpReplace* lir); void visitStringReplace(LStringReplace* lir); void visitLambda(LLambda* lir); void visitOutOfLineLambdaArrow(OutOfLineLambdaArrow* ool); void visitLambdaArrow(LLambdaArrow* lir); void visitLambdaForSingleton(LLambdaForSingleton* lir); void visitPointer(LPointer* lir); - void visitNurseryObject(LNurseryObject* lir); void visitKeepAliveObject(LKeepAliveObject* lir); void visitSlots(LSlots* lir); void visitLoadSlotT(LLoadSlotT* lir); void visitLoadSlotV(LLoadSlotV* lir); void visitStoreSlotT(LStoreSlotT* lir); void visitStoreSlotV(LStoreSlotV* lir); void visitElements(LElements* lir); void visitConvertElementsToDoubles(LConvertElementsToDoubles* lir); @@ -366,18 +365,18 @@ class CodeGenerator : public CodeGenerat void visitAssertRangeI(LAssertRangeI* ins); void visitAssertRangeD(LAssertRangeD* ins); void visitAssertRangeF(LAssertRangeF* ins); void visitAssertRangeV(LAssertRangeV* ins); void visitAssertResultV(LAssertResultV* ins); void visitAssertResultT(LAssertResultT* ins); - void emitAssertResultV(const ValueOperand output, TemporaryTypeSet* typeset); - void emitAssertObjectOrStringResult(Register input, MIRType type, TemporaryTypeSet* typeset); + void emitAssertResultV(const ValueOperand output, const TemporaryTypeSet* typeset); + void emitAssertObjectOrStringResult(Register input, MIRType type, const TemporaryTypeSet* typeset); void visitInterruptCheck(LInterruptCheck* lir); void visitAsmJSInterruptCheck(LAsmJSInterruptCheck* lir); void visitRecompileCheck(LRecompileCheck* ins); IonScriptCounts* extractScriptCounts() { IonScriptCounts* counts = scriptCounts_; scriptCounts_ = nullptr; // prevent delete in dtor
--- a/js/src/jit/CompileWrappers.cpp +++ b/js/src/jit/CompileWrappers.cpp @@ -184,16 +184,23 @@ CompileRuntime::maybeGetMathCache() } const Nursery& CompileRuntime::gcNursery() { return runtime()->gc.nursery; } +void +CompileRuntime::setMinorGCShouldCancelIonCompilations() +{ + MOZ_ASSERT(onMainThread()); + runtime()->gc.storeBuffer.setShouldCancelIonCompilations(); +} + Zone* CompileZone::zone() { return reinterpret_cast<Zone*>(this); } /* static */ CompileZone* CompileZone::get(Zone* zone)
--- a/js/src/jit/CompileWrappers.h +++ b/js/src/jit/CompileWrappers.h @@ -80,16 +80,17 @@ class CompileRuntime #endif // DOM callbacks must be threadsafe (and will hopefully be removed soon). const DOMCallbacks* DOMcallbacks(); const MathCache* maybeGetMathCache(); const Nursery& gcNursery(); + void setMinorGCShouldCancelIonCompilations(); }; class CompileZone { Zone* zone(); public: static CompileZone* get(Zone* zone);
--- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -163,18 +163,17 @@ JitRuntime::JitRuntime() argumentsRectifierReturnAddr_(nullptr), invalidator_(nullptr), debugTrapHandler_(nullptr), baselineDebugModeOSRHandler_(nullptr), functionWrappers_(nullptr), osrTempData_(nullptr), mutatingBackedgeList_(false), ionReturnOverride_(MagicValue(JS_ARG_POISON)), - jitcodeGlobalTable_(nullptr), - hasIonNurseryObjects_(false) + jitcodeGlobalTable_(nullptr) { } JitRuntime::~JitRuntime() { js_delete(functionWrappers_); freeOsrTempData(); @@ -637,19 +636,20 @@ JitCompartment::mark(JSTracer* trc, JSCo { // Free temporary OSR buffer. trc->runtime()->jitRuntime()->freeOsrTempData(); } void JitCompartment::sweep(FreeOp* fop, JSCompartment* compartment) { - // Cancel any active or pending off thread compilations. Note that the - // MIR graph does not hold any nursery pointers, so there's no need to - // do this for minor GCs. + // Cancel any active or pending off thread compilations. The MIR graph only + // contains nursery pointers if cancelIonCompilations() is set on the store + // buffer, in which case store buffer marking will take care of this during + // minor GCs. MOZ_ASSERT(!fop->runtime()->isHeapMinorCollecting()); CancelOffThreadIonCompile(compartment, nullptr); FinishAllOffThreadCompilations(compartment); stubCodes_->sweep(fop); // If the sweep removed the ICCall_Fallback stub, nullptr the baselineCallReturnAddr_ field. if (!stubCodes_->lookup(ICCall_Fallback::Compiler::BASELINE_CALL_KEY)) @@ -781,29 +781,16 @@ JitCode::traceChildren(JSTracer* trc) if (dataRelocTableBytes_) { uint8_t* start = code_ + dataRelocTableOffset(); CompactBufferReader reader(start, start + dataRelocTableBytes_); MacroAssembler::TraceDataRelocations(trc, this, reader); } } void -JitCode::fixupNurseryObjects(JSContext* cx, const ObjectVector& nurseryObjects) -{ - if (nurseryObjects.empty() || !dataRelocTableBytes_) - return; - - AutoWritableJitCode awjc(this); - - uint8_t* start = code_ + dataRelocTableOffset(); - CompactBufferReader reader(start, start + dataRelocTableBytes_); - MacroAssembler::FixupNurseryObjects(cx, this, reader, nurseryObjects); -} - -void JitCode::finalize(FreeOp* fop) { // If this jitcode had a bytecode map, it must have already been removed. #ifdef DEBUG JSRuntime* rt = fop->runtime(); if (hasBytecodeMap_) { JitcodeGlobalEntry result; MOZ_ASSERT(rt->jitRuntime()->hasJitcodeGlobalTable()); @@ -1733,17 +1720,17 @@ GenerateCode(MIRGenerator* mir, LIRGraph return codegen; } CodeGenerator* CompileBackEnd(MIRGenerator* mir) { // Everything in CompileBackEnd can potentially run on a helper thread. - AutoEnterIonCompilation enter; + AutoEnterIonCompilation enter(mir->safeForMinorGC()); AutoSpewEndFunction spewEndFunction(mir); if (!OptimizeMIR(mir)) return nullptr; LIRGraph* lir = GenerateLIR(mir); if (!lir) return nullptr; @@ -1862,75 +1849,16 @@ AttachFinishedCompilations(JSContext* cx sliceScripts.infallibleAppend(debugScripts[info.scriptIndex + b]); Debugger::onIonCompilation(cx, sliceScripts, info.graph); } js_delete(debuggerAlloc); } -void -MIRGenerator::traceNurseryObjects(JSTracer* trc) -{ - TraceRootRange(trc, nurseryObjects_.length(), nurseryObjects_.begin(), "ion-nursery-objects"); -} - -class MarkOffThreadNurseryObjects : public gc::BufferableRef -{ - public: - void trace(JSTracer* trc) override; -}; - -void -MarkOffThreadNurseryObjects::trace(JSTracer* trc) -{ - JSRuntime* rt = trc->runtime(); - - if (trc->runtime()->isHeapMinorCollecting()) { - // Only reset hasIonNurseryObjects if we're doing an actual minor GC. - MOZ_ASSERT(rt->jitRuntime()->hasIonNurseryObjects()); - rt->jitRuntime()->setHasIonNurseryObjects(false); - } - - AutoLockHelperThreadState lock; - if (!HelperThreadState().threads) - return; - - // Trace nursery objects of any builders which haven't started yet. - GlobalHelperThreadState::IonBuilderVector& worklist = HelperThreadState().ionWorklist(); - for (size_t i = 0; i < worklist.length(); i++) { - jit::IonBuilder* builder = worklist[i]; - if (builder->script()->runtimeFromAnyThread() == rt) - builder->traceNurseryObjects(trc); - } - - // Trace nursery objects of in-progress entries. - for (size_t i = 0; i < HelperThreadState().threadCount; i++) { - HelperThread& helper = HelperThreadState().threads[i]; - if (helper.ionBuilder && helper.ionBuilder->script()->runtimeFromAnyThread() == rt) - helper.ionBuilder->traceNurseryObjects(trc); - } - - // Trace nursery objects of any completed entries. - GlobalHelperThreadState::IonBuilderVector& finished = HelperThreadState().ionFinishedList(); - for (size_t i = 0; i < finished.length(); i++) { - jit::IonBuilder* builder = finished[i]; - if (builder->script()->runtimeFromAnyThread() == rt) - builder->traceNurseryObjects(trc); - } - - // Trace nursery objects of lazy-linked builders. - jit::IonBuilder* builder = HelperThreadState().ionLazyLinkList().getFirst(); - while (builder) { - if (builder->script()->runtimeFromAnyThread() == rt) - builder->traceNurseryObjects(trc); - builder = builder->getNext(); - } -} - static void TrackAllProperties(JSContext* cx, JSObject* obj) { MOZ_ASSERT(obj->isSingleton()); for (Shape::Range<NoGC> range(obj->as<NativeObject>().lastProperty()); !range.empty(); range.popFront()) EnsureTrackPropertyTypes(cx, obj, range.front().propid()); } @@ -2054,16 +1982,19 @@ IonCompile(JSContext* cx, JSScript* scri IonBuilder* builder = alloc->new_<IonBuilder>((JSContext*) nullptr, CompileCompartment::get(cx->compartment()), options, temp, graph, constraints, inspector, info, optimizationInfo, baselineFrameInspector); if (!builder) return AbortReason_Alloc; + if (cx->runtime()->gc.storeBuffer.cancelIonCompilations()) + builder->setNotSafeForMinorGC(); + MOZ_ASSERT(recompile == builder->script()->hasIonScript()); MOZ_ASSERT(builder->script()->canIonCompile()); RootedScript builderScript(cx, builder->script()); if (recompile) builderScript->ionScript()->setRecompiling(); @@ -2111,25 +2042,16 @@ IonCompile(JSContext* cx, JSScript* scri if (options.offThreadCompilationAvailable()) { if (!recompile) builderScript->setIonScript(cx, ION_COMPILING_SCRIPT); JitSpew(JitSpew_IonSyncLogs, "Can't log script %s:%" PRIuSIZE ". (Compiled on background thread.)", builderScript->filename(), builderScript->lineno()); - JSRuntime* rt = cx->runtime(); - if (!builder->nurseryObjects().empty() && !rt->jitRuntime()->hasIonNurseryObjects()) { - // Ensure the builder's nursery objects are marked when a nursery - // GC happens on the main thread. - MarkOffThreadNurseryObjects mark; - rt->gc.storeBuffer.putGeneric(mark); - rt->jitRuntime()->setHasIonNurseryObjects(true); - } - if (!StartOffThreadIonCompile(cx, builder)) { JitSpew(JitSpew_IonAbort, "Unable to start off-thread ion compilation."); builder->graphSpewer().endFunction(); return AbortReason_Alloc; } // The allocator and associated data will be destroyed after being // processed in the finishedOffThreadCompilations list.
--- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -227,49 +227,16 @@ void IonBuilder::spew(const char* message) { // Don't call PCToLineNumber in release builds. #ifdef DEBUG JitSpew(JitSpew_IonMIR, "%s @ %s:%d", message, script()->filename(), PCToLineNumber(script(), pc)); #endif } -MInstruction* -IonBuilder::constantMaybeNursery(JSObject* obj) -{ - MOZ_ASSERT(obj); - if (!IsInsideNursery(obj)) - return constant(ObjectValue(*obj)); - - // If |obj| is in the nursery, we have to add it to the list of nursery - // objects that get traced during off-thread compilation. We use - // MNurseryObject to ensure we will patch the code with the right - // pointer after codegen is done. - - ObjectVector& nurseryObjects = outermostBuilder()->nurseryObjects_; - - size_t index = UINT32_MAX; - for (size_t i = 0, len = nurseryObjects.length(); i < len; i++) { - if (nurseryObjects[i] == obj) { - index = i; - break; - } - } - - if (index == UINT32_MAX) { - if (!nurseryObjects.append(obj)) - return nullptr; - index = nurseryObjects.length() - 1; - } - - MNurseryObject* ins = MNurseryObject::New(alloc(), obj, index, constraints()); - current->add(ins); - return ins; -} - static inline int32_t GetJumpOffset(jsbytecode* pc) { MOZ_ASSERT(js_CodeSpec[JSOp(*pc)].type() == JOF_JUMP); return GET_JUMP_OFFSET(pc); } IonBuilder::CFGState @@ -970,16 +937,18 @@ IonBuilder::buildInline(IonBuilder* call failedBoundsCheck_ = true; if (callerBuilder->failedShapeGuard_) failedShapeGuard_ = true; if (callerBuilder->failedLexicalCheck_) failedLexicalCheck_ = true; + safeForMinorGC_ = callerBuilder->safeForMinorGC_; + // Generate single entrance block. if (!setCurrentAndSpecializePhis(newBlock(pc))) return false; if (!current) return false; current->setCallerResumePoint(callerResumePoint); @@ -4731,19 +4700,16 @@ IonBuilder::inlineScriptedCall(CallInfo& for (size_t i = 0; i < groups.length(); i++) addAbortedPreliminaryGroup(groups[i]); abortReason_ = AbortReason_PreliminaryObjects; } return false; } - MOZ_ASSERT(inlineBuilder.nurseryObjects_.empty(), - "Nursery objects should be added to outer builder"); - // Create return block. jsbytecode* postCall = GetNextPc(pc); MBasicBlock* returnBlock = newBlock(nullptr, postCall); if (!returnBlock) return false; returnBlock->setCallerResumePoint(callerResumePoint_); // Inherit the slots from current and pop |fun|. @@ -5498,17 +5464,17 @@ IonBuilder::inlineCalls(CallInfo& callIn if (!inlineBlock) return false; // Create a function MConstant to use in the entry ResumePoint. If we // can't use a constant, add a no-op MPolyInlineGuard, to prevent // hoisting scope chain gets above the dispatch instruction. MInstruction* funcDef; if (target->isSingleton()) - funcDef = MConstant::New(alloc(), ObjectValue(*target), constraints()); + funcDef = MConstant::New(alloc(), ObjectValue(*target), constraints(), this); else funcDef = MPolyInlineGuard::New(alloc(), callInfo.fun()); funcDef->setImplicitlyUsedUnchecked(); dispatchBlock->add(funcDef); // Use the inlined callee in the inline resume point and on stack. int funIndex = inlineBlock->entryResumePoint()->stackDepth() - callInfo.numFormals(); @@ -5813,17 +5779,17 @@ IonBuilder::createThisScriptedSingleton( return nullptr; StackTypeSet* thisTypes = TypeScript::ThisTypes(target->nonLazyScript()); if (!thisTypes || !thisTypes->hasType(TypeSet::ObjectType(templateObject))) return nullptr; // Generate an inline path to create a new |this| object with // the given singleton prototype. - MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject); + MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject, this); MCreateThisWithTemplate* createThis = MCreateThisWithTemplate::New(alloc(), constraints(), templateConst, templateObject->group()->initialHeap(constraints())); current->add(templateConst); current->add(createThis); return createThis; } @@ -5844,17 +5810,17 @@ IonBuilder::createThisScriptedBaseline(M Shape* shape = target->lookupPure(compartment->runtime()->names().prototype); if (!shape || !shape->hasDefaultGetter() || !shape->hasSlot()) return nullptr; Value protov = target->getSlot(shape->slot()); if (!protov.isObject()) return nullptr; - JSObject* proto = &protov.toObject(); + JSObject* proto = checkNurseryObject(&protov.toObject()); if (proto != templateObject->getProto()) return nullptr; TypeSet::ObjectKey* templateObjectKey = TypeSet::ObjectKey::get(templateObject->group()); if (templateObjectKey->hasFlags(constraints(), OBJECT_FLAG_NEW_SCRIPT_CLEARED)) return nullptr; StackTypeSet* thisTypes = TypeScript::ThisTypes(target->nonLazyScript()); @@ -5865,24 +5831,24 @@ IonBuilder::createThisScriptedBaseline(M callee = addShapeGuard(callee, target->lastProperty(), Bailout_ShapeGuard); // Guard callee.prototype == proto. MOZ_ASSERT(shape->numFixedSlots() == 0, "Must be a dynamic slot"); MSlots* slots = MSlots::New(alloc(), callee); current->add(slots); MLoadSlot* prototype = MLoadSlot::New(alloc(), slots, shape->slot()); current->add(prototype); - MDefinition* protoConst = constantMaybeNursery(proto); + MDefinition* protoConst = constant(ObjectValue(*proto)); MGuardObjectIdentity* guard = MGuardObjectIdentity::New(alloc(), prototype, protoConst, /* bailOnEquality = */ false); current->add(guard); // Generate an inline path to create a new |this| object with // the given prototype. - MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject); + MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject, this); MCreateThisWithTemplate* createThis = MCreateThisWithTemplate::New(alloc(), constraints(), templateConst, templateObject->group()->initialHeap(constraints())); current->add(templateConst); current->add(createThis); return createThis; } @@ -6492,17 +6458,17 @@ bool IonBuilder::jsop_newarray(uint32_t count) { JSObject* templateObject = inspector->getTemplateObject(pc); gc::InitialHeap heap; MConstant* templateConst; if (templateObject) { heap = templateObject->group()->initialHeap(constraints()); - templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject); + templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject, this); } else { heap = gc::DefaultHeap; templateConst = MConstant::New(alloc(), NullValue()); } current->add(templateConst); MNewArray* ins = MNewArray::New(alloc(), constraints(), count, templateConst, heap, pc); current->add(ins); @@ -6543,17 +6509,17 @@ bool IonBuilder::jsop_newobject() { JSObject* templateObject = inspector->getTemplateObject(pc); gc::InitialHeap heap; MConstant* templateConst; if (templateObject) { heap = templateObject->group()->initialHeap(constraints()); - templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject); + templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject, this); } else { heap = gc::DefaultHeap; templateConst = MConstant::New(alloc(), NullValue()); } current->add(templateConst); MNewObject* ins = MNewObject::New(alloc(), constraints(), templateConst, heap, MNewObject::ObjectLiteral); @@ -7207,17 +7173,17 @@ IonBuilder::testSingletonProperty(JSObje if (obj->isSingleton()) return property.singleton(constraints()); return nullptr; } if (ObjectHasExtraOwnProperty(compartment, objKey, name)) return nullptr; - obj = obj->getProto(); + obj = checkNurseryObject(obj->getProto()); } return nullptr; } JSObject* IonBuilder::testSingletonPropertyTypes(MDefinition* obj, PropertyName* name) { @@ -7274,17 +7240,17 @@ IonBuilder::testSingletonPropertyTypes(M if (!ClassHasEffectlessLookup(clasp) || ObjectHasExtraOwnProperty(compartment, key, name)) return nullptr; if (key->unknownProperties()) return nullptr; HeapTypeSetKey property = key->property(NameToId(name)); if (property.isOwnProperty(constraints())) return nullptr; - if (JSObject* proto = key->proto().toObjectOrNull()) { + if (JSObject* proto = checkNurseryObject(key->proto().toObjectOrNull())) { // Test this type. JSObject* thisSingleton = testSingletonProperty(proto, name); if (!thisSingleton) return nullptr; if (singleton) { if (thisSingleton != singleton) return nullptr; } else { @@ -9038,17 +9004,17 @@ IonBuilder::setElemTryDense(bool* emitte JSValueType unboxedType = UnboxedArrayElementType(constraints(), object, index); if (unboxedType == JSVAL_TYPE_MAGIC) { if (!ElementAccessIsDenseNative(constraints(), object, index)) { trackOptimizationOutcome(TrackedOutcome::AccessNotDense); return true; } } - if (PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current, + if (PropertyWriteNeedsTypeBarrier(this, constraints(), current, &object, nullptr, &value, /* canModify = */ true)) { trackOptimizationOutcome(TrackedOutcome::NeedsTypeBarrier); return true; } if (!object->resultTypeSet()) { trackOptimizationOutcome(TrackedOutcome::NoTypeInfo); @@ -9118,17 +9084,17 @@ IonBuilder::setElemTryCache(bool* emitte // Temporary disable the cache if non dense native, // until the cache supports more ics SetElemICInspector icInspect(inspector->setElemICInspector(pc)); if (!icInspect.sawDenseWrite() && !icInspect.sawTypedArrayWrite()) { trackOptimizationOutcome(TrackedOutcome::SetElemNonDenseNonTANotCached); return true; } - if (PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current, + if (PropertyWriteNeedsTypeBarrier(this, constraints(), current, &object, nullptr, &value, /* canModify = */ true)) { trackOptimizationOutcome(TrackedOutcome::NeedsTypeBarrier); return true; } // We can avoid worrying about holes in the IC if we know a priori we are safe // from them. If TI can guard that there are no indexed properties on the prototype @@ -9456,17 +9422,17 @@ IonBuilder::jsop_rest() return true; } // We know the exact number of arguments the callee pushed. unsigned numActuals = inlineCallInfo_->argc(); unsigned numFormals = info().nargs() - 1; unsigned numRest = numActuals > numFormals ? numActuals - numFormals : 0; - MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject); + MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject, this); current->add(templateConst); MNewArray* array = MNewArray::New(alloc(), constraints(), numRest, templateConst, templateObject->group()->initialHeap(constraints()), pc); current->add(array); if (numRest == 0) { // No more updating to do. (Note that in this one case the length from @@ -9710,17 +9676,18 @@ IonBuilder::objectsHaveCommonPrototype(T } if (singleton) { if (CanHaveEmptyPropertyTypesForOwnProperty(singleton)) { MOZ_ASSERT(singleton->is<GlobalObject>()); *guardGlobal = true; } } - JSObject* proto = key->proto().toObjectOrNull(); + JSObject* proto = checkNurseryObject(key->proto().toObjectOrNull()); + if (proto == foundProto) break; if (!proto) { // The foundProto being searched for did not show up on the // object's prototype chain. return false; } key = TypeSet::ObjectKey::get(proto); @@ -9805,17 +9772,17 @@ IonBuilder::testCommonGetterSetter(Tempo Shape* propShape = nativeProto.lookupPure(name); MOZ_ASSERT_IF(isGetter, propShape->getterObject() == getterOrSetter); MOZ_ASSERT_IF(!isGetter, propShape->setterObject() == getterOrSetter); if (propShape && !propShape->configurable()) return true; } } - MInstruction* wrapper = constantMaybeNursery(foundProto); + MInstruction* wrapper = constant(ObjectValue(*foundProto)); *guard = addShapeGuard(wrapper, lastProperty, Bailout_ShapeGuard); return true; } void IonBuilder::replaceMaybeFallbackFunctionGetter(MGetPropertyCache* cache) { // Discard the last prior resume point of the previous MGetPropertyCache. @@ -9855,26 +9822,27 @@ IonBuilder::annotateGetPropertyCache(MDe // is a single-object typeset containing a JSFunction for (unsigned int i = 0; i < objCount; i++) { ObjectGroup* group = objTypes->getGroup(i); if (!group) continue; TypeSet::ObjectKey* key = TypeSet::ObjectKey::get(group); if (key->unknownProperties() || !key->proto().isObject()) continue; + JSObject* proto = checkNurseryObject(key->proto().toObject()); const Class* clasp = key->clasp(); if (!ClassHasEffectlessLookup(clasp) || ObjectHasExtraOwnProperty(compartment, key, name)) continue; HeapTypeSetKey ownTypes = key->property(NameToId(name)); if (ownTypes.isOwnProperty(constraints())) continue; - JSObject* singleton = testSingletonProperty(key->proto().toObject(), name); + JSObject* singleton = testSingletonProperty(proto, name); if (!singleton || !singleton->is<JSFunction>()) continue; // Don't add cases corresponding to non-observed pushes if (!pushedTypes->hasType(TypeSet::ObjectType(singleton))) continue; if (!inlinePropTable->addEntry(alloc(), group, &singleton->as<JSFunction>())) @@ -10700,17 +10668,17 @@ IonBuilder::addShapeGuardsForGetterSette obj = convertUnboxedObjects(obj, convertUnboxedGroups); if (isOwnProperty) { MOZ_ASSERT(receivers.empty()); return addShapeGuard(obj, holderShape, Bailout_ShapeGuard); } - MDefinition* holderDef = constantMaybeNursery(holder); + MDefinition* holderDef = constant(ObjectValue(*holder)); addShapeGuard(holderDef, holderShape, Bailout_ShapeGuard); return addGuardReceiverPolymorphic(obj, receivers); } bool IonBuilder::getPropTryCommonGetter(bool* emitted, MDefinition* obj, PropertyName* name, TemporaryTypeSet* types) @@ -10794,17 +10762,17 @@ IonBuilder::getPropTryCommonGetter(bool* obj = guardObj; } // Spoof stack to expected state for call. // Make sure there's enough room if (!current->ensureHasSlots(2)) return false; - current->push(constantMaybeNursery(commonGetter)); + current->push(constant(ObjectValue(*commonGetter))); current->push(obj); CallInfo callInfo(alloc(), false); if (!callInfo.init(current, 0)) return false; if (commonGetter->isNative()) { @@ -11036,17 +11004,17 @@ IonBuilder::getPropTryCache(bool* emitte // able to attach stubs for them. if (inspector->hasSeenAccessedGetter(pc)) barrier = BarrierKind::TypeSet; // Caches can read values from prototypes, so update the barrier to // reflect such possible values. if (barrier != BarrierKind::TypeSet) { BarrierKind protoBarrier = - PropertyReadOnPrototypeNeedsTypeBarrier(constraints(), obj, name, types); + PropertyReadOnPrototypeNeedsTypeBarrier(this, constraints(), obj, name, types); if (protoBarrier != BarrierKind::NoBarrier) { MOZ_ASSERT(barrier <= protoBarrier); barrier = protoBarrier; } } MGetPropertyCache* load = MGetPropertyCache::New(alloc(), obj, name, barrier == BarrierKind::TypeSet); @@ -11205,17 +11173,17 @@ IonBuilder::jsop_setprop(PropertyName* n // Try to emit stores to known binary data blocks trackOptimizationAttempt(TrackedStrategy::SetProp_TypedObject); if (!setPropTryTypedObject(&emitted, obj, name, value) || emitted) return emitted; } TemporaryTypeSet* objTypes = obj->resultTypeSet(); - bool barrier = PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current, &obj, name, &value, + bool barrier = PropertyWriteNeedsTypeBarrier(this, constraints(), current, &obj, name, &value, /* canModify = */ true); if (!forceInlineCaches()) { // Try to emit stores to unboxed objects. trackOptimizationAttempt(TrackedStrategy::SetProp_Unboxed); if (!setPropTryUnboxed(&emitted, obj, name, value, barrier, objTypes) || emitted) return emitted; } @@ -11299,17 +11267,17 @@ IonBuilder::setPropTryCommonSetter(bool* obj = guardObj; } // Dummy up the stack, as in getprop. We are pushing an extra value, so // ensure there is enough space. if (!current->ensureHasSlots(3)) return false; - current->push(constantMaybeNursery(commonSetter)); + current->push(constant(ObjectValue(*commonSetter))); current->push(obj); current->push(value); // Call the setter. Note that we have to push the original value, not // the setter's return value. CallInfo callInfo(alloc(), false); if (!callInfo.init(current, 1)) return false; @@ -11847,17 +11815,17 @@ bool IonBuilder::jsop_lambda(JSFunction* fun) { MOZ_ASSERT(analysis().usesScopeChain()); MOZ_ASSERT(!fun->isArrow()); if (fun->isNative() && IsAsmJSModuleNative(fun->native())) return abort("asm.js module function"); - MConstant* cst = MConstant::NewConstraintlessObject(alloc(), fun); + MConstant* cst = MConstant::NewConstraintlessObject(alloc(), fun, this); current->add(cst); MLambda* ins = MLambda::New(alloc(), constraints(), current->scopeChain(), cst); current->add(ins); current->push(ins); return resumeAfter(ins); } @@ -12533,17 +12501,17 @@ IonBuilder::jsop_instanceof() rhs = addShapeGuard(rhs, shape, Bailout_ShapeGuard); // Guard .prototype == protoObject. MOZ_ASSERT(shape->numFixedSlots() == 0, "Must be a dynamic slot"); MSlots* slots = MSlots::New(alloc(), rhs); current->add(slots); MLoadSlot* prototype = MLoadSlot::New(alloc(), slots, slot); current->add(prototype); - MConstant* protoConst = MConstant::NewConstraintlessObject(alloc(), protoObject); + MConstant* protoConst = MConstant::NewConstraintlessObject(alloc(), protoObject, this); current->add(protoConst); MGuardObjectIdentity* guard = MGuardObjectIdentity::New(alloc(), prototype, protoConst, /* bailOnEquality = */ false); current->add(guard); if (tryFoldInstanceOf(obj, protoObject)) return true; @@ -12944,17 +12912,17 @@ IonBuilder::storeReferenceTypedObjectVal // Make sure we aren't adding new type information for writes of object and value // references. if (type != ReferenceTypeDescr::TYPE_STRING) { MOZ_ASSERT(type == ReferenceTypeDescr::TYPE_ANY || type == ReferenceTypeDescr::TYPE_OBJECT); MIRType implicitType = (type == ReferenceTypeDescr::TYPE_ANY) ? MIRType_Undefined : MIRType_Null; - if (PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current, &typedObj, name, &value, + if (PropertyWriteNeedsTypeBarrier(this, constraints(), current, &typedObj, name, &value, /* canModify = */ true, implicitType)) { trackOptimizationOutcome(TrackedOutcome::NeedsTypeBarrier); return false; } } // Find location within the owner object. @@ -12985,23 +12953,45 @@ IonBuilder::storeReferenceTypedObjectVal store = MStoreUnboxedString::New(alloc(), elements, scaledOffset, value, adjustment); break; } current->add(store); return true; } +JSObject* +IonBuilder::checkNurseryObject(JSObject* obj) +{ + // If we try to use any nursery pointers during compilation, make sure that + // the main thread will cancel this compilation before performing a minor + // GC. All constants used during compilation should either go through this + // function or should come from a type set (which has a similar barrier). + if (obj && IsInsideNursery(obj)) { + compartment->runtime()->setMinorGCShouldCancelIonCompilations(); + IonBuilder* builder = this; + while (builder) { + builder->setNotSafeForMinorGC(); + builder = builder->callerBuilder_; + } + } + + return obj; +} + MConstant* IonBuilder::constant(const Value& v) { MOZ_ASSERT(!v.isString() || v.toString()->isAtom(), "Handle non-atomized strings outside IonBuilder."); - MConstant* c = MConstant::New(alloc(), v, constraints()); + if (v.isObject()) + checkNurseryObject(&v.toObject()); + + MConstant* c = MConstant::New(alloc(), v, constraints(), this); current->add(c); return c; } MConstant* IonBuilder::constantInt(int32_t i) { return constant(Int32Value(i));
--- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -223,18 +223,16 @@ class IonBuilder bool processIterators(); bool inspectOpcode(JSOp op); uint32_t readIndex(jsbytecode* pc); JSAtom* readAtom(jsbytecode* pc); bool abort(const char* message, ...); void trackActionableAbort(const char* message); void spew(const char* message); - MInstruction* constantMaybeNursery(JSObject* obj); - JSFunction* getSingleCallTarget(TemporaryTypeSet* calleeTypes); bool getPolyCallTargets(TemporaryTypeSet* calleeTypes, bool constructing, ObjectVector& targets, uint32_t maxTargets); void popCfgStack(); DeferredEdge* filterDeadDeferredEdges(DeferredEdge* edge); bool processDeferredContinues(CFGState& state); ControlStatus processControlEnd(); @@ -982,16 +980,17 @@ class IonBuilder // are recorded here so they may be propagated to the script's // corresponding JitcodeGlobalEntry::BaselineEntry. JSScript* actionableAbortScript_; jsbytecode* actionableAbortPc_; const char* actionableAbortMessage_; public: void clearForBackEnd(); + JSObject* checkNurseryObject(JSObject* obj); JSScript* script() const { return script_; } CodeGenerator* backgroundCodegen() const { return backgroundCodegen_; } void setBackgroundCodegen(CodeGenerator* codegen) { backgroundCodegen_ = codegen; } CompilerConstraintList* constraints() { return constraints_;
--- a/js/src/jit/IonCaches.cpp +++ b/js/src/jit/IonCaches.cpp @@ -409,29 +409,28 @@ GeneratePrototypeGuards(JSContext* cx, I */ MOZ_ASSERT(obj != holder); if (obj->hasUncacheableProto()) { // Note: objectReg and scratchReg may be the same register, so we cannot // use objectReg in the rest of this function. masm.loadPtr(Address(objectReg, JSObject::offsetOfGroup()), scratchReg); Address proto(scratchReg, ObjectGroup::offsetOfProto()); - masm.branchPtr(Assembler::NotEqual, proto, - ImmMaybeNurseryPtr(obj->getProto()), failures); + masm.branchPtr(Assembler::NotEqual, proto, ImmGCPtr(obj->getProto()), failures); } JSObject* pobj = IsCacheableDOMProxy(obj) ? obj->getTaggedProto().toObjectOrNull() : obj->getProto(); if (!pobj) return; while (pobj != holder) { if (pobj->hasUncacheableProto()) { MOZ_ASSERT(!pobj->isSingleton()); - masm.movePtr(ImmMaybeNurseryPtr(pobj), scratchReg); + masm.movePtr(ImmGCPtr(pobj), scratchReg); Address groupAddr(scratchReg, JSObject::offsetOfGroup()); masm.branchPtr(Assembler::NotEqual, groupAddr, ImmGCPtr(pobj->group()), failures); } pobj = pobj->getProto(); } } // Note: This differs from IsCacheableProtoChain in BaselineIC.cpp in that @@ -800,17 +799,17 @@ GenerateReadSlot(JSContext* cx, IonScrip if (obj != holder) { // Note: this may clobber the object register if it's used as scratch. GeneratePrototypeGuards(cx, ion, masm, obj, holder, object, scratchReg, &prototypeFailures); if (holder) { // Guard on the holder's shape. holderReg = scratchReg; - masm.movePtr(ImmMaybeNurseryPtr(holder), holderReg); + masm.movePtr(ImmGCPtr(holder), holderReg); masm.branchPtr(Assembler::NotEqual, Address(holderReg, JSObject::offsetOfShape()), ImmGCPtr(holder->as<NativeObject>().lastProperty()), &prototypeFailures); } else { // The property does not exist. Guard on everything in the // prototype chain. JSObject* proto = obj->getTaggedProto().toObjectOrNull(); @@ -976,17 +975,17 @@ EmitGetterCall(JSContext* cx, MacroAssem if (obj == holder) { // When the holder is also the current receiver, we just have a shape guard, // so we might end up with a random object which is also guaranteed to have // this JSGetterOp. masm.Push(object); } else { // If the holder is on the prototype chain, the prototype-guarding // only allows objects with the same holder. - masm.movePtr(ImmMaybeNurseryPtr(holder), scratchReg); + masm.movePtr(ImmGCPtr(holder), scratchReg); masm.Push(scratchReg); } masm.moveStackPtrTo(argObjReg); masm.loadJSContext(argJSContextReg); if (!masm.icBuildOOLFakeExitFrame(returnAddr, aic)) return false; @@ -1029,17 +1028,17 @@ EmitGetterCall(JSContext* cx, MacroAssem MOZ_ASSERT(padding % sizeof(uintptr_t) == 0); MOZ_ASSERT(padding < JitStackAlignment); masm.reserveStack(padding); for (size_t i = 0; i < target->nargs(); i++) masm.Push(UndefinedValue()); masm.Push(TypedOrValueRegister(MIRType_Object, AnyRegister(object))); - masm.movePtr(ImmMaybeNurseryPtr(target), scratchReg); + masm.movePtr(ImmGCPtr(target), scratchReg); descriptor = MakeFrameDescriptor(argSize + padding, JitFrame_IonAccessorIC); masm.Push(Imm32(0)); // argc masm.Push(scratchReg); masm.Push(Imm32(descriptor)); // Check stack alignment. Add sizeof(uintptr_t) for the return address. MOZ_ASSERT(((masm.framePushed() + sizeof(uintptr_t)) % JitStackAlignment) == 0); @@ -1086,17 +1085,17 @@ GenerateCallGetter(JSContext* cx, IonScr } // Note: this may clobber the object register if it's used as scratch. if (obj != holder) GeneratePrototypeGuards(cx, ion, masm, obj, holder, object, scratchReg, failures); // Guard on the holder's shape. Register holderReg = scratchReg; - masm.movePtr(ImmMaybeNurseryPtr(holder), holderReg); + masm.movePtr(ImmGCPtr(holder), holderReg); masm.branchPtr(Assembler::NotEqual, Address(holderReg, JSObject::offsetOfShape()), ImmGCPtr(holder->as<NativeObject>().lastProperty()), maybePopAndFail); if (spillObjReg) masm.pop(object); @@ -1689,17 +1688,17 @@ GetPropertyIC::tryAttachDOMProxyUnshadow // getprop. Register scratchReg = output().valueReg().scratchReg(); GeneratePrototypeGuards(cx, ion, masm, obj, holder, object(), scratchReg, &failures); // Rename scratch for clarity. Register holderReg = scratchReg; // Guard on the holder of the property - masm.movePtr(ImmMaybeNurseryPtr(holder), holderReg); + masm.movePtr(ImmGCPtr(holder), holderReg); masm.branchPtr(Assembler::NotEqual, Address(holderReg, JSObject::offsetOfShape()), ImmGCPtr(holder->lastProperty()), &failures); if (canCache == CanAttachReadSlot) { EmitLoadSlot(masm, holder, shape, holderReg, output(), scratchReg); } else { @@ -2399,17 +2398,17 @@ GenerateCallSetter(JSContext* cx, IonScr Label protoFailure; Label protoSuccess; // Generate prototype/shape guards. if (obj != holder) GeneratePrototypeGuards(cx, ion, masm, obj, holder, object, scratchReg, &protoFailure); - masm.movePtr(ImmMaybeNurseryPtr(holder), scratchReg); + masm.movePtr(ImmGCPtr(holder), scratchReg); masm.branchPtr(Assembler::NotEqual, Address(scratchReg, JSObject::offsetOfShape()), ImmGCPtr(holder->as<NativeObject>().lastProperty()), &protoFailure); masm.jump(&protoSuccess); masm.bind(&protoFailure); @@ -2584,17 +2583,17 @@ GenerateCallSetter(JSContext* cx, IonScr MOZ_ASSERT(padding < JitStackAlignment); masm.reserveStack(padding); for (size_t i = 1; i < target->nargs(); i++) masm.Push(UndefinedValue()); masm.Push(value); masm.Push(TypedOrValueRegister(MIRType_Object, AnyRegister(object))); - masm.movePtr(ImmMaybeNurseryPtr(target), scratchReg); + masm.movePtr(ImmGCPtr(target), scratchReg); descriptor = MakeFrameDescriptor(argSize + padding, JitFrame_IonAccessorIC); masm.Push(Imm32(1)); // argc masm.Push(scratchReg); masm.Push(Imm32(descriptor)); // Check stack alignment. Add sizeof(uintptr_t) for the return address. MOZ_ASSERT(((masm.framePushed() + sizeof(uintptr_t)) % JitStackAlignment) == 0); @@ -3583,25 +3582,24 @@ GenerateDenseElementHole(JSContext* cx, attacher.branchNextStubOrLabel(masm, Assembler::NotEqual, Address(object, JSObject::offsetOfShape()), ImmGCPtr(obj->as<NativeObject>().lastProperty()), &failures); if (obj->hasUncacheableProto()) { masm.loadPtr(Address(object, JSObject::offsetOfGroup()), scratchReg); Address proto(scratchReg, ObjectGroup::offsetOfProto()); - masm.branchPtr(Assembler::NotEqual, proto, - ImmMaybeNurseryPtr(obj->getProto()), &failures); + masm.branchPtr(Assembler::NotEqual, proto, ImmGCPtr(obj->getProto()), &failures); } JSObject* pobj = obj->getProto(); while (pobj) { MOZ_ASSERT(pobj->as<NativeObject>().lastProperty()); - masm.movePtr(ImmMaybeNurseryPtr(pobj), scratchReg); + masm.movePtr(ImmGCPtr(pobj), scratchReg); if (pobj->hasUncacheableProto()) { MOZ_ASSERT(!pobj->isSingleton()); Address groupAddr(scratchReg, JSObject::offsetOfGroup()); masm.branchPtr(Assembler::NotEqual, groupAddr, ImmGCPtr(pobj->group()), &failures); } // Make sure the shape matches, to avoid non-dense elements. masm.branchPtr(Assembler::NotEqual, Address(scratchReg, JSObject::offsetOfShape()),
--- a/js/src/jit/IonCode.h +++ b/js/src/jit/IonCode.h @@ -109,18 +109,16 @@ class JitCode : public gc::TenuredCell void traceChildren(JSTracer* trc); void finalize(FreeOp* fop); void fixupAfterMovingGC() {} void setInvalidated() { invalidated_ = true; } - void fixupNurseryObjects(JSContext* cx, const ObjectVector& nurseryObjects); - void setHasBytecodeMap() { hasBytecodeMap_ = true; } void togglePreBarriers(bool enabled); // If this JitCode object has been, effectively, corrupted due to // invalidation patching, then we have to remember this so we don't try and
--- a/js/src/jit/JitCompartment.h +++ b/js/src/jit/JitCompartment.h @@ -223,18 +223,16 @@ class JitRuntime // value that will be temporarily corrupt. This special override value is set // only in callVM() targets that are about to return *and* have invalidated // their callee. js::Value ionReturnOverride_; // Global table of jitcode native address => bytecode address mappings. JitcodeGlobalTable* jitcodeGlobalTable_; - bool hasIonNurseryObjects_; - private: JitCode* generateLazyLinkStub(JSContext* cx); JitCode* generateProfilerExitFrameTailStub(JSContext* cx); JitCode* generateExceptionTailStub(JSContext* cx, void* handler); JitCode* generateBailoutTailStub(JSContext* cx); JitCode* generateEnterJIT(JSContext* cx, EnterJitType type); JitCode* generateArgumentsRectifier(JSContext* cx, void** returnAddrOut); JitCode* generateBailoutTable(JSContext* cx, uint32_t frameClass); @@ -371,23 +369,16 @@ class JitRuntime return v; } void setIonReturnOverride(const js::Value& v) { MOZ_ASSERT(!hasIonReturnOverride()); MOZ_ASSERT(!v.isMagic()); ionReturnOverride_ = v; } - bool hasIonNurseryObjects() const { - return hasIonNurseryObjects_; - } - void setHasIonNurseryObjects(bool b) { - hasIonNurseryObjects_ = b; - } - bool hasJitcodeGlobalTable() const { return jitcodeGlobalTable_ != nullptr; } JitcodeGlobalTable* getJitcodeGlobalTable() { MOZ_ASSERT(hasJitcodeGlobalTable()); return jitcodeGlobalTable_; }
--- a/js/src/jit/LIR-Common.h +++ b/js/src/jit/LIR-Common.h @@ -706,26 +706,16 @@ class LValue : public LInstructionHelper : v_(v) { } Value value() const { return v_; } }; -class LNurseryObject : public LInstructionHelper<1, 0, 0> -{ - public: - LIR_HEADER(NurseryObject); - - MNurseryObject* mir() const { - return mir_->toNurseryObject(); - } -}; - // Clone an object literal such as we are not modifying the object contained in // the sources. class LCloneLiteral : public LCallInstructionHelper<1, 1, 0> { public: LIR_HEADER(CloneLiteral) explicit LCloneLiteral(const LAllocation& obj)
--- a/js/src/jit/LOpcodes.h +++ b/js/src/jit/LOpcodes.h @@ -344,17 +344,16 @@ _(AssertRangeI) \ _(AssertRangeD) \ _(AssertRangeF) \ _(AssertRangeV) \ _(AssertResultV) \ _(AssertResultT) \ _(LexicalCheck) \ _(ThrowUninitializedLexical) \ - _(NurseryObject) \ _(Debugger) \ _(NewTarget) \ _(ArrowNewTarget) #if defined(JS_CODEGEN_X86) # include "jit/x86/LOpcodes-x86.h" #elif defined(JS_CODEGEN_X64) # include "jit/x64/LOpcodes-x64.h"
--- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -4202,22 +4202,16 @@ LIRGenerator::visitThrowUninitializedLex void LIRGenerator::visitDebugger(MDebugger* ins) { LDebugger* lir = new(alloc()) LDebugger(tempFixed(CallTempReg0), tempFixed(CallTempReg1)); assignSnapshot(lir, Bailout_Debugger); add(lir, ins); } -void -LIRGenerator::visitNurseryObject(MNurseryObject* ins) -{ - define(new(alloc()) LNurseryObject(), ins); -} - static void SpewResumePoint(MBasicBlock* block, MInstruction* ins, MResumePoint* resumePoint) { Fprinter& out = JitSpewPrinter(); out.printf("Current resume point %p details:\n", (void*)resumePoint); out.printf(" frame count: %u\n", resumePoint->frameCount()); if (ins) {
--- a/js/src/jit/Lowering.h +++ b/js/src/jit/Lowering.h @@ -291,17 +291,16 @@ class LIRGenerator : public LIRGenerator void visitPhi(MPhi* ins); void visitBeta(MBeta* ins); void visitObjectState(MObjectState* ins); void visitArrayState(MArrayState* ins); void visitUnknownValue(MUnknownValue* ins); void visitLexicalCheck(MLexicalCheck* ins); void visitThrowUninitializedLexical(MThrowUninitializedLexical* ins); void visitDebugger(MDebugger* ins); - void visitNurseryObject(MNurseryObject* ins); void visitNewTarget(MNewTarget* ins); void visitArrowNewTarget(MArrowNewTarget* ins); }; } // namespace jit } // namespace js #endif /* jit_Lowering_h */
--- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -586,17 +586,17 @@ IonBuilder::inlineArray(CallInfo& callIn // Don't inline large allocations. if (initLength > ArrayObject::EagerAllocationMaxLength) return InliningStatus_NotInlined; } callInfo.setImplicitlyUsedUnchecked(); - MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject); + MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject, this); current->add(templateConst); MNewArray* ins = MNewArray::New(alloc(), constraints(), initLength, templateConst, templateObject->group()->initialHeap(constraints()), pc); current->add(ins); current->push(ins); if (callInfo.argc() >= 2) { @@ -760,17 +760,17 @@ IonBuilder::inlineArrayPush(CallInfo& ca { if (callInfo.argc() != 1 || callInfo.constructing()) { trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm); return InliningStatus_NotInlined; } MDefinition* obj = callInfo.thisArg(); MDefinition* value = callInfo.getArg(0); - if (PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current, + if (PropertyWriteNeedsTypeBarrier(this, constraints(), current, &obj, nullptr, &value, /* canModify = */ false)) { trackOptimizationOutcome(TrackedOutcome::NeedsTypeBarrier); return InliningStatus_NotInlined; } MOZ_ASSERT(obj == callInfo.thisArg() && value == callInfo.getArg(0)); if (getInlineReturnType() != MIRType_Int32) @@ -1016,16 +1016,26 @@ IonBuilder::inlineArraySlice(CallInfo& c return InliningStatus_NotInlined; } // Inline the call. JSObject* templateObj = inspector->getTemplateObjectForNative(pc, js::array_slice); if (!templateObj) return InliningStatus_NotInlined; + if (unboxedType == JSVAL_TYPE_MAGIC) { + if (!templateObj->is<ArrayObject>()) + return InliningStatus_NotInlined; + } else { + if (!templateObj->is<UnboxedArrayObject>()) + return InliningStatus_NotInlined; + if (templateObj->as<UnboxedArrayObject>().elementType() != unboxedType) + return InliningStatus_NotInlined; + } + callInfo.setImplicitlyUsedUnchecked(); MDefinition* begin; if (callInfo.argc() > 0) begin = callInfo.getArg(0); else begin = constant(Int32Value(0)); @@ -1686,17 +1696,17 @@ IonBuilder::inlineConstantStringSplit(Ca } callInfo.setImplicitlyUsedUnchecked(); TemporaryTypeSet::DoubleConversion conversion = getInlineReturnTypeSet()->convertDoubleElements(constraints()); if (conversion == TemporaryTypeSet::AlwaysConvertToDoubles) return InliningStatus_NotInlined; - MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject); + MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject, this); current->add(templateConst); MNewArray* ins = MNewArray::New(alloc(), constraints(), initLength, templateConst, templateObject->group()->initialHeap(constraints()), pc); current->add(ins); current->push(ins); @@ -1756,17 +1766,18 @@ IonBuilder::inlineStringSplit(CallInfo& return InliningStatus_NotInlined; if (!key.maybeTypes()->hasType(TypeSet::StringType())) { key.freeze(constraints()); return InliningStatus_NotInlined; } callInfo.setImplicitlyUsedUnchecked(); - MConstant* templateObjectDef = MConstant::New(alloc(), ObjectValue(*templateObject), constraints()); + MConstant* templateObjectDef = MConstant::New(alloc(), ObjectValue(*templateObject), + constraints(), this); current->add(templateObjectDef); MStringSplit* ins = MStringSplit::New(alloc(), constraints(), callInfo.thisArg(), callInfo.getArg(0), templateObjectDef); current->add(ins); current->push(ins); return InliningStatus_Inlined; @@ -2081,17 +2092,17 @@ IonBuilder::inlineObjectCreate(CallInfo& MOZ_ASSERT(types->getKnownMIRType() == MIRType_Object); } else { if (arg->type() != MIRType_Null) return InliningStatus_NotInlined; } callInfo.setImplicitlyUsedUnchecked(); - MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject); + MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject, this); current->add(templateConst); MNewObject* ins = MNewObject::New(alloc(), constraints(), templateConst, templateObject->group()->initialHeap(constraints()), MNewObject::ObjectCreate); current->add(ins); current->push(ins); if (!resumeAfter(ins)) return InliningStatus_Error;
--- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -636,43 +636,46 @@ MDefinition::replaceAllLiveUsesWith(MDef bool MDefinition::emptyResultTypeSet() const { return resultTypeSet() && resultTypeSet()->empty(); } MConstant* -MConstant::New(TempAllocator& alloc, const Value& v, CompilerConstraintList* constraints) -{ - return new(alloc) MConstant(v, constraints); +MConstant::New(TempAllocator& alloc, const Value& v, + CompilerConstraintList* constraints, MIRGenerator* gen) +{ + return new(alloc) MConstant(v, constraints, gen); } MConstant* -MConstant::NewTypedValue(TempAllocator& alloc, const Value& v, MIRType type, CompilerConstraintList* constraints) +MConstant::NewTypedValue(TempAllocator& alloc, const Value& v, MIRType type, + CompilerConstraintList* constraints, MIRGenerator* gen) { MOZ_ASSERT(!IsSimdType(type)); - MOZ_ASSERT_IF(type == MIRType_Float32, IsNaN(v.toDouble()) || v.toDouble() == double(float(v.toDouble()))); - MConstant* constant = new(alloc) MConstant(v, constraints); + MOZ_ASSERT_IF(type == MIRType_Float32, + IsNaN(v.toDouble()) || v.toDouble() == double(float(v.toDouble()))); + MConstant* constant = new(alloc) MConstant(v, constraints, gen); constant->setResultType(type); return constant; } MConstant* MConstant::NewAsmJS(TempAllocator& alloc, const Value& v, MIRType type) { if (type == MIRType_Float32) return NewTypedValue(alloc, Float32Value(v.toNumber()), type); return NewTypedValue(alloc, v, type); } MConstant* -MConstant::NewConstraintlessObject(TempAllocator& alloc, JSObject* v) -{ - return new(alloc) MConstant(v); +MConstant::NewConstraintlessObject(TempAllocator& alloc, JSObject* v, MIRGenerator* gen) +{ + return new(alloc) MConstant(v, gen); } static TemporaryTypeSet* MakeSingletonTypeSetFromKey(CompilerConstraintList* constraints, TypeSet::ObjectKey* key) { // Invalidate when this object's ObjectGroup gets unknown properties. This // happens for instance when we mutate an object's __proto__, in this case // we want to invalidate and mark this TypeSet as containing AnyObject @@ -698,24 +701,25 @@ jit::MakeSingletonTypeSet(CompilerConstr static TemporaryTypeSet* MakeUnknownTypeSet() { LifoAlloc* alloc = GetJitContext()->temp->lifoAlloc(); return alloc->new_<TemporaryTypeSet>(alloc, TypeSet::UnknownType()); } -MConstant::MConstant(const js::Value& vp, CompilerConstraintList* constraints) +MConstant::MConstant(const js::Value& vp, CompilerConstraintList* constraints, MIRGenerator* gen) : value_(vp) { setResultType(MIRTypeFromValue(vp)); if (vp.isObject()) { // Create a singleton type set for the object. This isn't necessary for // other types as the result type encodes all needed information. - MOZ_ASSERT(!IsInsideNursery(&vp.toObject())); + MOZ_ASSERT(gen); + MOZ_ASSERT_IF(IsInsideNursery(&vp.toObject()), !gen->safeForMinorGC()); setResultTypeSet(MakeSingletonTypeSet(constraints, &vp.toObject())); } if (vp.isMagic() && vp.whyMagic() == JS_UNINITIALIZED_LEXICAL) { // JS_UNINITIALIZED_LEXICAL does not escape to script and is not // observed in type sets. However, it may flow around freely during // Ion compilation. Give it an unknown typeset to poison any type sets // it merges with. // @@ -724,20 +728,21 @@ MConstant::MConstant(const js::Value& vp setResultTypeSet(MakeUnknownTypeSet()); } MOZ_ASSERT_IF(vp.isString(), vp.toString()->isAtom()); setMovable(); } -MConstant::MConstant(JSObject* obj) +MConstant::MConstant(JSObject* obj, MIRGenerator* gen) : value_(ObjectValue(*obj)) { - MOZ_ASSERT(!IsInsideNursery(obj)); + MOZ_ASSERT(gen); + MOZ_ASSERT_IF(IsInsideNursery(obj), !gen->safeForMinorGC()); setResultType(MIRType_Object); setMovable(); } HashNumber MConstant::valueHash() const { // Fold all 64 bits into the 32-bit result. It's tempting to just discard @@ -837,49 +842,16 @@ MConstant::canProduceFloat32() const if (type() == MIRType_Int32) return IsFloat32Representable(static_cast<double>(value_.toInt32())); if (type() == MIRType_Double) return IsFloat32Representable(value_.toDouble()); return true; } -MNurseryObject::MNurseryObject(JSObject* obj, uint32_t index, CompilerConstraintList* constraints) - : index_(index) -{ - setResultType(MIRType_Object); - - MOZ_ASSERT(IsInsideNursery(obj)); - MOZ_ASSERT(!obj->isSingleton()); - setResultTypeSet(MakeSingletonTypeSet(constraints, obj)); - - setMovable(); -} - -MNurseryObject* -MNurseryObject::New(TempAllocator& alloc, JSObject* obj, uint32_t index, - CompilerConstraintList* constraints) -{ - return new(alloc) MNurseryObject(obj, index, constraints); -} - -HashNumber -MNurseryObject::valueHash() const -{ - return HashNumber(index_); -} - -bool -MNurseryObject::congruentTo(const MDefinition* ins) const -{ - if (!ins->isNurseryObject()) - return false; - return ins->toNurseryObject()->index_ == index_; -} - MDefinition* MSimdValueX4::foldsTo(TempAllocator& alloc) { DebugOnly<MIRType> scalarType = SimdTypeToScalarType(type()); bool allConstants = true; bool allSame = true; for (size_t i = 0; i < 4; ++i) { @@ -5015,17 +4987,18 @@ jit::PropertyReadNeedsTypeBarrier(JSCont } } } return res; } BarrierKind -jit::PropertyReadOnPrototypeNeedsTypeBarrier(CompilerConstraintList* constraints, +jit::PropertyReadOnPrototypeNeedsTypeBarrier(IonBuilder* builder, + CompilerConstraintList* constraints, MDefinition* obj, PropertyName* name, TemporaryTypeSet* observed) { if (observed->unknown()) return BarrierKind::NoBarrier; TypeSet* types = obj->resultTypeSet(); if (!types || types->unknownObject()) @@ -5037,17 +5010,18 @@ jit::PropertyReadOnPrototypeNeedsTypeBar TypeSet::ObjectKey* key = types->getObject(i); if (!key) continue; while (true) { if (!key->hasStableClassAndProto(constraints)) return BarrierKind::TypeSet; if (!key->proto().isObject()) break; - key = TypeSet::ObjectKey::get(key->proto().toObject()); + JSObject* proto = builder->checkNurseryObject(key->proto().toObject()); + key = TypeSet::ObjectKey::get(proto); BarrierKind kind = PropertyReadNeedsTypeBarrier(constraints, key, name, observed); if (kind == BarrierKind::TypeSet) return BarrierKind::TypeSet; if (kind == BarrierKind::TypeTagOnly) { MOZ_ASSERT(res == BarrierKind::NoBarrier || res == BarrierKind::TypeTagOnly); res = BarrierKind::TypeTagOnly; } else { @@ -5229,28 +5203,28 @@ TryAddTypeBarrierForWrite(TempAllocator& kind = BarrierKind::TypeTagOnly; MInstruction* ins = MMonitorTypes::New(alloc, *pvalue, types, kind); current->add(ins); return true; } static MInstruction* -AddGroupGuard(TempAllocator& alloc, MBasicBlock* current, MDefinition* obj, +AddGroupGuard(MIRGenerator* gen, MBasicBlock* current, MDefinition* obj, TypeSet::ObjectKey* key, bool bailOnEquality) { MInstruction* guard; if (key->isGroup()) { - guard = MGuardObjectGroup::New(alloc, obj, key->group(), bailOnEquality, + guard = MGuardObjectGroup::New(gen->alloc(), obj, key->group(), bailOnEquality, Bailout_ObjectIdentityOrTypeGuard); } else { - MConstant* singletonConst = MConstant::NewConstraintlessObject(alloc, key->singleton()); + MConstant* singletonConst = MConstant::NewConstraintlessObject(gen->alloc(), key->singleton(), gen); current->add(singletonConst); - guard = MGuardObjectIdentity::New(alloc, obj, singletonConst, bailOnEquality); + guard = MGuardObjectIdentity::New(gen->alloc(), obj, singletonConst, bailOnEquality); } current->add(guard); // For now, never move object group / identity guards. guard->setNotMovable(); return guard; @@ -5263,17 +5237,17 @@ jit::CanWriteProperty(TempAllocator& all MIRType implicitType /* = MIRType_None */) { if (property.couldBeConstant(constraints)) return false; return PropertyTypeIncludes(alloc, property, value, implicitType); } bool -jit::PropertyWriteNeedsTypeBarrier(TempAllocator& alloc, CompilerConstraintList* constraints, +jit::PropertyWriteNeedsTypeBarrier(MIRGenerator* gen, CompilerConstraintList* constraints, MBasicBlock* current, MDefinition** pobj, PropertyName* name, MDefinition** pvalue, bool canModify, MIRType implicitType) { // If any value being written is not reflected in the type information for // objects which obj could represent, a type barrier is needed when writing // the value. As for propertyReadNeedsTypeBarrier, this only applies for // properties that are accounted for by type information, i.e. normal data @@ -5296,24 +5270,24 @@ jit::PropertyWriteNeedsTypeBarrier(TempA // TI doesn't track TypedArray indexes and should never insert a type // barrier for them. if (!name && IsAnyTypedArrayClass(key->clasp())) continue; jsid id = name ? NameToId(name) : JSID_VOID; HeapTypeSetKey property = key->property(id); - if (!CanWriteProperty(alloc, constraints, property, *pvalue, implicitType)) { + if (!CanWriteProperty(gen->alloc(), constraints, property, *pvalue, implicitType)) { // Either pobj or pvalue needs to be modified to filter out the // types which the value could have but are not in the property, // or a VM call is required. A VM call is always required if pobj // and pvalue cannot be modified. if (!canModify) return true; - success = TryAddTypeBarrierForWrite(alloc, constraints, current, types, name, pvalue, + success = TryAddTypeBarrierForWrite(gen->alloc(), constraints, current, types, name, pvalue, implicitType); break; } } if (success) return false; @@ -5329,17 +5303,17 @@ jit::PropertyWriteNeedsTypeBarrier(TempA TypeSet::ObjectKey* key = types->getObject(i); if (!key || key->unknownProperties()) continue; if (!name && IsAnyTypedArrayClass(key->clasp())) continue; jsid id = name ? NameToId(name) : JSID_VOID; HeapTypeSetKey property = key->property(id); - if (CanWriteProperty(alloc, constraints, property, *pvalue, implicitType)) + if (CanWriteProperty(gen->alloc(), constraints, property, *pvalue, implicitType)) continue; if ((property.maybeTypes() && !property.maybeTypes()->empty()) || excluded) return true; excluded = key; } MOZ_ASSERT(excluded); @@ -5350,11 +5324,11 @@ jit::PropertyWriteNeedsTypeBarrier(TempA if (excluded->isGroup()) { if (UnboxedLayout* layout = excluded->group()->maybeUnboxedLayout()) { if (layout->nativeGroup()) return true; excluded->watchStateChangeForUnboxedConvertedToNative(constraints); } } - *pobj = AddGroupGuard(alloc, current, *pobj, excluded, /* bailOnEquality = */ true); + *pobj = AddGroupGuard(gen, current, *pobj, excluded, /* bailOnEquality = */ true); return false; }
--- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -1304,27 +1304,30 @@ class MLimitedTruncate }; // A constant js::Value. class MConstant : public MNullaryInstruction { Value value_; protected: - MConstant(const Value& v, CompilerConstraintList* constraints); - explicit MConstant(JSObject* obj); + MConstant(const Value& v, CompilerConstraintList* constraints, MIRGenerator* gen); + explicit MConstant(JSObject* obj, MIRGenerator* gen); public: INSTRUCTION_HEADER(Constant) static MConstant* New(TempAllocator& alloc, const Value& v, - CompilerConstraintList* constraints = nullptr); + CompilerConstraintList* constraints = nullptr, + MIRGenerator* gen = nullptr); static MConstant* NewTypedValue(TempAllocator& alloc, const Value& v, MIRType type, - CompilerConstraintList* constraints = nullptr); + CompilerConstraintList* constraints = nullptr, + MIRGenerator* gen = nullptr); static MConstant* NewAsmJS(TempAllocator& alloc, const Value& v, MIRType type); - static MConstant* NewConstraintlessObject(TempAllocator& alloc, JSObject* v); + static MConstant* NewConstraintlessObject(TempAllocator& alloc, JSObject* v, + MIRGenerator* gen); const js::Value& value() const { return value_; } const js::Value* vp() const { return &value_; } bool valueToBoolean() const { @@ -1356,43 +1359,16 @@ class MConstant : public MNullaryInstruc bool needTruncation(TruncateKind kind) override; void truncate() override; bool canProduceFloat32() const override; ALLOW_CLONE(MConstant) }; -class MNurseryObject : public MNullaryInstruction -{ - // Index in MIRGenerator::nurseryObjects_. - uint32_t index_; - - protected: - MNurseryObject(JSObject* obj, uint32_t index, CompilerConstraintList* constraints); - - public: - INSTRUCTION_HEADER(NurseryObject) - static MNurseryObject* New(TempAllocator& alloc, JSObject* obj, uint32_t index, - CompilerConstraintList* constraints = nullptr); - - HashNumber valueHash() const override; - bool congruentTo(const MDefinition* ins) const override; - - uint32_t index() const { - return index_; - } - - AliasSet getAliasSet() const override { - return AliasSet::None(); - } - - ALLOW_CLONE(MNurseryObject) -}; - // Generic constructor of SIMD valuesX4. class MSimdValueX4 : public MQuaternaryInstruction, public Mix4Policy<SimdScalarPolicy<0>, SimdScalarPolicy<1>, SimdScalarPolicy<2>, SimdScalarPolicy<3> >::Data { protected: MSimdValueX4(MIRType type, MDefinition* x, MDefinition* y, MDefinition* z, MDefinition* w) @@ -13618,27 +13594,28 @@ MIRType DenseNativeElementType(CompilerC BarrierKind PropertyReadNeedsTypeBarrier(JSContext* propertycx, CompilerConstraintList* constraints, TypeSet::ObjectKey* key, PropertyName* name, TemporaryTypeSet* observed, bool updateObserved); BarrierKind PropertyReadNeedsTypeBarrier(JSContext* propertycx, CompilerConstraintList* constraints, MDefinition* obj, PropertyName* name, TemporaryTypeSet* observed); -BarrierKind PropertyReadOnPrototypeNeedsTypeBarrier(CompilerConstraintList* constraints, +BarrierKind PropertyReadOnPrototypeNeedsTypeBarrier(IonBuilder* builder, + CompilerConstraintList* constraints, MDefinition* obj, PropertyName* name, TemporaryTypeSet* observed); bool PropertyReadIsIdempotent(CompilerConstraintList* constraints, MDefinition* obj, PropertyName* name); void AddObjectsForPropertyRead(MDefinition* obj, PropertyName* name, TemporaryTypeSet* observed); bool CanWriteProperty(TempAllocator& alloc, CompilerConstraintList* constraints, HeapTypeSetKey property, MDefinition* value, MIRType implicitType = MIRType_None); -bool PropertyWriteNeedsTypeBarrier(TempAllocator& alloc, CompilerConstraintList* constraints, +bool PropertyWriteNeedsTypeBarrier(MIRGenerator* gen, CompilerConstraintList* constraints, MBasicBlock* current, MDefinition** pobj, PropertyName* name, MDefinition** pvalue, bool canModify, MIRType implicitType = MIRType_None); } // namespace jit } // namespace js #endif /* jit_MIR_h */
--- a/js/src/jit/MIRGenerator.h +++ b/js/src/jit/MIRGenerator.h @@ -88,16 +88,23 @@ class MIRGenerator bool isProfilerInstrumentationEnabled() { return !compilingAsmJS() && instrumentedProfiling(); } bool isOptimizationTrackingEnabled() { return isProfilerInstrumentationEnabled() && !info().isAnalysis(); } + bool safeForMinorGC() const { + return safeForMinorGC_; + } + void setNotSafeForMinorGC() { + safeForMinorGC_ = false; + } + // Whether the main thread is trying to cancel this build. bool shouldCancel(const char* why) { maybePause(); return cancelBuild_; } void cancel() { cancelBuild_ = true; } @@ -189,22 +196,17 @@ class MIRGenerator // Keep track of whether frame arguments are modified during execution. // RegAlloc needs to know this as spilling values back to their register // slots is not compatible with that. bool modifiesFrameArguments_; bool instrumentedProfiling_; bool instrumentedProfilingIsCached_; - - // List of nursery objects used by this compilation. Can be traced by a - // minor GC while compilation happens off-thread. This Vector should only - // be accessed on the main thread (IonBuilder, nursery GC or - // CodeGenerator::link). - ObjectVector nurseryObjects_; + bool safeForMinorGC_; void addAbortedPreliminaryGroup(ObjectGroup* group); Label* outOfBoundsLabel_; // Label where we should jump in asm.js mode, in the case where we have an // invalid conversion or a loss of precision (when converting from a // floating point SIMD type into an integer SIMD type). Label* conversionErrorLabel_; @@ -224,22 +226,16 @@ class MIRGenerator public: AsmJSPerfSpewer& perfSpewer() { return asmJSPerfSpewer_; } #endif public: const JitCompileOptions options; - void traceNurseryObjects(JSTracer* trc); - - const ObjectVector& nurseryObjects() const { - return nurseryObjects_; - } - Label* conversionErrorLabel() const { MOZ_ASSERT((conversionErrorLabel_ != nullptr) == compilingAsmJS()); return conversionErrorLabel_; } Label* outOfBoundsLabel() const { MOZ_ASSERT(compilingAsmJS()); return outOfBoundsLabel_; }
--- a/js/src/jit/MIRGraph.cpp +++ b/js/src/jit/MIRGraph.cpp @@ -37,17 +37,17 @@ MIRGenerator::MIRGenerator(CompileCompar maxAsmJSStackArgBytes_(0), performsCall_(false), usesSimd_(false), usesSimdCached_(false), minAsmJSHeapLength_(0), modifiesFrameArguments_(false), instrumentedProfiling_(false), instrumentedProfilingIsCached_(false), - nurseryObjects_(*alloc), + safeForMinorGC_(true), outOfBoundsLabel_(outOfBoundsLabel), conversionErrorLabel_(conversionErrorLabel), #if defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB) usesSignalHandlersForAsmJSOOB_(usesSignalHandlersForAsmJSOOB), #endif options(options), gs_(alloc) { }
--- a/js/src/jit/MOpcodes.h +++ b/js/src/jit/MOpcodes.h @@ -7,17 +7,16 @@ #ifndef jit_MOpcodes_h #define jit_MOpcodes_h namespace js { namespace jit { #define MIR_OPCODE_LIST(_) \ _(Constant) \ - _(NurseryObject) \ _(SimdBox) \ _(SimdUnbox) \ _(SimdValueX4) \ _(SimdSplatX4) \ _(SimdConstant) \ _(SimdConvert) \ _(SimdReinterpretCast) \ _(SimdExtractElement) \
--- a/js/src/jit/MacroAssembler.cpp +++ b/js/src/jit/MacroAssembler.cpp @@ -97,39 +97,57 @@ MacroAssembler::guardTypeSet(const Sourc Label fail; Register obj = extractObject(address, scratch); guardObjectType(obj, types, scratch, &fail); jump(&matched); bind(&fail); if (obj == scratch) extractObject(address, scratch); - guardTypeSetMightBeIncomplete(obj, scratch, &matched); + guardTypeSetMightBeIncomplete(types, obj, scratch, &matched); assumeUnreachable("Unexpected object type"); #endif } bind(&matched); } +template <typename TypeSet> void -MacroAssembler::guardTypeSetMightBeIncomplete(Register obj, Register scratch, Label* label) +MacroAssembler::guardTypeSetMightBeIncomplete(TypeSet* types, Register obj, Register scratch, Label* label) { // Type set guards might miss when an object's group changes. In this case - // either its properties will become unknown, or it will change to a native - // object with an original unboxed group. Jump to label if this might have - // happened for the input object. + // either its old group's properties will become unknown, or it will change + // to a native object with an original unboxed group. Jump to label if this + // might have happened for the input object. + + if (types->unknownObject()) { + jump(label); + return; + } loadPtr(Address(obj, JSObject::offsetOfGroup()), scratch); load32(Address(scratch, ObjectGroup::offsetOfFlags()), scratch); - branchTest32(Assembler::NonZero, scratch, Imm32(OBJECT_FLAG_UNKNOWN_PROPERTIES), label); and32(Imm32(OBJECT_FLAG_ADDENDUM_MASK), scratch); branch32(Assembler::Equal, scratch, Imm32(ObjectGroup::addendumOriginalUnboxedGroupValue()), label); + + for (size_t i = 0; i < types->getObjectCount(); i++) { + if (JSObject* singleton = types->getSingletonNoBarrier(i)) { + movePtr(ImmGCPtr(singleton), scratch); + loadPtr(Address(scratch, JSObject::offsetOfGroup()), scratch); + } else if (ObjectGroup* group = types->getGroupNoBarrier(i)) { + movePtr(ImmGCPtr(group), scratch); + } else { + continue; + } + branchTest32(Assembler::NonZero, Address(scratch, ObjectGroup::offsetOfFlags()), + Imm32(OBJECT_FLAG_UNKNOWN_PROPERTIES), label); + } } void MacroAssembler::guardObjectType(Register obj, const TypeSet *types, Register scratch, Label* miss) { MOZ_ASSERT(!types->unknown()); MOZ_ASSERT(!types->hasType(TypeSet::AnyObjectType())); @@ -200,16 +218,20 @@ MacroAssembler::guardObjectType(Register template void MacroAssembler::guardTypeSet(const Address& address, const TypeSet* types, BarrierKind kind, Register scratch, Label* miss); template void MacroAssembler::guardTypeSet(const ValueOperand& value, const TypeSet* types, BarrierKind kind, Register scratch, Label* miss); template void MacroAssembler::guardTypeSet(const TypedOrValueRegister& value, const TypeSet* types, BarrierKind kind, Register scratch, Label* miss); +template void MacroAssembler::guardTypeSetMightBeIncomplete(const TemporaryTypeSet* types, + Register obj, Register scratch, + Label* label); + template<typename S, typename T> static void StoreToTypedFloatArray(MacroAssembler& masm, int arrayType, const S& value, const T& dest, unsigned numElems) { switch (arrayType) { case Scalar::Float32: masm.storeFloat32(value, dest);
--- a/js/src/jit/MacroAssembler.h +++ b/js/src/jit/MacroAssembler.h @@ -350,17 +350,18 @@ class MacroAssembler : public MacroAssem // Emits a test of a value against all types in a TypeSet. A scratch // register is required. template <typename Source> void guardTypeSet(const Source& address, const TypeSet* types, BarrierKind kind, Register scratch, Label* miss); void guardObjectType(Register obj, const TypeSet* types, Register scratch, Label* miss); - void guardTypeSetMightBeIncomplete(Register obj, Register scratch, Label* label); + template <typename TypeSet> + void guardTypeSetMightBeIncomplete(TypeSet* types, Register obj, Register scratch, Label* label); void loadObjShape(Register objReg, Register dest) { loadPtr(Address(objReg, JSObject::offsetOfShape()), dest); } void loadObjGroup(Register objReg, Register dest) { loadPtr(Address(objReg, JSObject::offsetOfGroup()), dest); } void loadBaseShape(Register objReg, Register dest) {
--- a/js/src/jit/arm/Assembler-arm.cpp +++ b/js/src/jit/arm/Assembler-arm.cpp @@ -809,22 +809,16 @@ static void TraceOneDataRelocation(JSTracer* trc, Iter* iter) { Instruction* ins = iter->cur(); Register dest; Assembler::RelocStyle rs; const void* prior = Assembler::GetPtr32Target(iter, &dest, &rs); void* ptr = const_cast<void*>(prior); - // The low bit shouldn't be set. If it is, we probably got a dummy - // pointer inserted by CodeGenerator::visitNurseryObject, but we - // shouldn't be able to trigger GC before those are patched to their - // real values. - MOZ_ASSERT(!(uintptr_t(ptr) & 0x1)); - // No barrier needed since these are constants. TraceManuallyBarrieredGenericPointerEdge(trc, reinterpret_cast<gc::Cell**>(&ptr), "ion-masm-ptr"); if (ptr != prior) { MacroAssemblerARM::ma_mov_patch(Imm32(int32_t(ptr)), dest, Assembler::Always, rs, ins); // L_LDR won't cause any instructions to be updated. @@ -858,60 +852,16 @@ TraceDataRelocations(JSTracer* trc, ARMB void Assembler::TraceDataRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader) { ::TraceDataRelocations(trc, code->raw(), reader); } void -Assembler::FixupNurseryObjects(JSContext* cx, JitCode* code, CompactBufferReader& reader, - const ObjectVector& nurseryObjects) -{ - MOZ_ASSERT(!nurseryObjects.empty()); - - uint8_t* buffer = code->raw(); - bool hasNurseryPointers = false; - - while (reader.more()) { - size_t offset = reader.readUnsigned(); - InstructionIterator iter((Instruction*)(buffer + offset)); - Instruction* ins = iter.cur(); - Register dest; - Assembler::RelocStyle rs; - const void* prior = Assembler::GetPtr32Target(&iter, &dest, &rs); - void* ptr = const_cast<void*>(prior); - uintptr_t word = reinterpret_cast<uintptr_t>(ptr); - - if (!(word & 0x1)) - continue; - - uint32_t index = word >> 1; - JSObject* obj = nurseryObjects[index]; - MacroAssembler::ma_mov_patch(Imm32(int32_t(obj)), dest, Assembler::Always, rs, ins); - - if (rs != Assembler::L_LDR) { - // L_LDR won't cause any instructions to be updated. - AutoFlushICache::flush(uintptr_t(ins), 4); - AutoFlushICache::flush(uintptr_t(ins->next()), 4); - } - - // Either all objects are still in the nursery, or all objects are - // tenured. - MOZ_ASSERT_IF(hasNurseryPointers, IsInsideNursery(obj)); - - if (!hasNurseryPointers && IsInsideNursery(obj)) - hasNurseryPointers = true; - } - - if (hasNurseryPointers) - cx->runtime()->gc.storeBuffer.putWholeCellFromMainThread(code); -} - -void Assembler::copyJumpRelocationTable(uint8_t* dest) { if (jumpRelocations_.length()) memcpy(dest, jumpRelocations_.buffer(), jumpRelocations_.length()); } void Assembler::copyDataRelocationTable(uint8_t* dest)
--- a/js/src/jit/arm/Assembler-arm.h +++ b/js/src/jit/arm/Assembler-arm.h @@ -1280,18 +1280,22 @@ class Assembler : public AssemblerShared void trace(JSTracer* trc); void writeRelocation(BufferOffset src) { tmpJumpRelocations_.append(src); } // As opposed to x86/x64 version, the data relocation has to be executed // before to recover the pointer, and not after. void writeDataRelocation(ImmGCPtr ptr) { - if (ptr.value) - tmpDataRelocations_.append(nextOffset()); + if (ptr.value) { + if (gc::IsInsideNursery(ptr.value)) + embedsNurseryPointers_ = true; + if (ptr.value) + tmpDataRelocations_.append(nextOffset()); + } } void writePrebarrierOffset(CodeOffsetLabel label) { tmpPreBarriers_.append(BufferOffset(label.offset())); } enum RelocBranchStyle { B_MOVWT, B_LDR_BX, @@ -1638,19 +1642,16 @@ class Assembler : public AssemblerShared void call(void* target); void as_bkpt(); public: static void TraceJumpRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader); static void TraceDataRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader); - static void FixupNurseryObjects(JSContext* cx, JitCode* code, CompactBufferReader& reader, - const ObjectVector& nurseryObjects); - static bool SupportsFloatingPoint() { return HasVFP(); } static bool SupportsSimd() { return js::jit::SupportsSimd; } protected:
--- a/js/src/jit/arm/MacroAssembler-arm.cpp +++ b/js/src/jit/arm/MacroAssembler-arm.cpp @@ -2069,21 +2069,16 @@ MacroAssemblerARMCompat::movePtr(ImmWord ma_mov(Imm32(imm.value), dest); } void MacroAssemblerARMCompat::movePtr(ImmGCPtr imm, Register dest) { ma_mov(imm, dest); } void -MacroAssemblerARMCompat::movePtr(ImmMaybeNurseryPtr imm, Register dest) -{ - movePtr(noteMaybeNurseryPtr(imm), dest); -} -void MacroAssemblerARMCompat::movePtr(ImmPtr imm, Register dest) { movePtr(ImmWord(uintptr_t(imm.value)), dest); } void MacroAssemblerARMCompat::movePtr(AsmJSImmPtr imm, Register dest) { RelocStyle rs;
--- a/js/src/jit/arm/MacroAssembler-arm.h +++ b/js/src/jit/arm/MacroAssembler-arm.h @@ -638,19 +638,16 @@ class MacroAssemblerARMCompat : public M } void push(ImmWord imm) { push(Imm32(imm.value)); } void push(ImmGCPtr imm) { ma_mov(imm, ScratchRegister); ma_push(ScratchRegister); } - void push(ImmMaybeNurseryPtr imm) { - push(noteMaybeNurseryPtr(imm)); - } void push(const Address& addr) { ma_ldr(addr, ScratchRegister); ma_push(ScratchRegister); } void push(Register reg) { ma_push(reg); } void push(FloatRegister reg) { @@ -1069,19 +1066,16 @@ class MacroAssemblerARMCompat : public M ma_cmp(secondScratchReg_, ptr); return jumpWithPatch(label, cond); } void branchPtr(Condition cond, Address addr, ImmGCPtr ptr, Label* label) { ma_ldr(addr, secondScratchReg_); ma_cmp(secondScratchReg_, ptr); ma_b(label, cond); } - void branchPtr(Condition cond, Address addr, ImmMaybeNurseryPtr ptr, Label* label) { - branchPtr(cond, addr, noteMaybeNurseryPtr(ptr), label); - } void branchPtr(Condition cond, Address addr, ImmWord ptr, Label* label) { ma_ldr(addr, secondScratchReg_); ma_cmp(secondScratchReg_, ptr); ma_b(label, cond); } void branchPtr(Condition cond, Address addr, ImmPtr ptr, Label* label) { branchPtr(cond, addr, ImmWord(uintptr_t(ptr.value)), label); } @@ -1210,17 +1204,17 @@ class MacroAssemblerARMCompat : public M void tagValue(JSValueType type, Register payload, ValueOperand dest); void pushValue(ValueOperand val); void popValue(ValueOperand val); void pushValue(const Value& val) { jsval_layout jv = JSVAL_TO_IMPL(val); push(Imm32(jv.s.tag)); if (val.isMarkable()) - push(ImmMaybeNurseryPtr(reinterpret_cast<gc::Cell*>(val.toGCThing()))); + push(ImmGCPtr(reinterpret_cast<gc::Cell*>(val.toGCThing()))); else push(Imm32(jv.s.payload.i32)); } void pushValue(JSValueType type, Register reg) { push(ImmTag(JSVAL_TYPE_TO_TAG(type))); ma_push(reg); } void pushValue(const Address& addr); @@ -1293,17 +1287,16 @@ class MacroAssemblerARMCompat : public M void move32(Imm32 imm, Register dest); void move32(Register src, Register dest); void movePtr(Register src, Register dest); void movePtr(ImmWord imm, Register dest); void movePtr(ImmPtr imm, Register dest); void movePtr(AsmJSImmPtr imm, Register dest); void movePtr(ImmGCPtr imm, Register dest); - void movePtr(ImmMaybeNurseryPtr imm, Register dest); void load8SignExtend(const Address& address, Register dest); void load8SignExtend(const BaseIndex& src, Register dest); void load8ZeroExtend(const Address& address, Register dest); void load8ZeroExtend(const BaseIndex& src, Register dest); void load16SignExtend(const Address& address, Register dest);
--- a/js/src/jit/mips/Assembler-mips.cpp +++ b/js/src/jit/mips/Assembler-mips.cpp @@ -283,22 +283,16 @@ Assembler::TraceJumpRelocations(JSTracer } static void TraceOneDataRelocation(JSTracer* trc, Instruction* inst) { void* ptr = (void*)Assembler::ExtractLuiOriValue(inst, inst->next()); void* prior = ptr; - // The low bit shouldn't be set. If it is, we probably got a dummy - // pointer inserted by CodeGenerator::visitNurseryObject, but we - // shouldn't be able to trigger GC before those are patched to their - // real values. - MOZ_ASSERT(!(uintptr_t(ptr) & 0x1)); - // No barrier needed since these are constants. TraceManuallyBarrieredGenericPointerEdge(trc, reinterpret_cast<gc::Cell**>(&ptr), "ion-masm-ptr"); if (ptr != prior) { Assembler::UpdateLuiOriValue(inst, inst->next(), uint32_t(ptr)); AutoFlushICache::flush(uintptr_t(inst), 8); } } @@ -325,53 +319,16 @@ TraceDataRelocations(JSTracer* trc, MIPS void Assembler::TraceDataRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader) { ::TraceDataRelocations(trc, code->raw(), reader); } void -Assembler::FixupNurseryObjects(JSContext* cx, JitCode* code, CompactBufferReader& reader, - const ObjectVector& nurseryObjects) -{ - MOZ_ASSERT(!nurseryObjects.empty()); - - uint8_t* buffer = code->raw(); - bool hasNurseryPointers = false; - - while (reader.more()) { - size_t offset = reader.readUnsigned(); - Instruction* inst = (Instruction*)(buffer + offset); - - void* ptr = (void*)Assembler::ExtractLuiOriValue(inst, inst->next()); - uintptr_t word = uintptr_t(ptr); - - if (!(word & 0x1)) - continue; - - uint32_t index = word >> 1; - JSObject* obj = nurseryObjects[index]; - - Assembler::UpdateLuiOriValue(inst, inst->next(), uint32_t(obj)); - AutoFlushICache::flush(uintptr_t(inst), 8); - - // Either all objects are still in the nursery, or all objects are - // tenured. - MOZ_ASSERT_IF(hasNurseryPointers, IsInsideNursery(obj)); - - if (!hasNurseryPointers && IsInsideNursery(obj)) - hasNurseryPointers = true; - } - - if (hasNurseryPointers) - cx->runtime()->gc.storeBuffer.putWholeCellFromMainThread(code); -} - -void Assembler::copyJumpRelocationTable(uint8_t* dest) { if (jumpRelocations_.length()) memcpy(dest, jumpRelocations_.buffer(), jumpRelocations_.length()); } void Assembler::copyDataRelocationTable(uint8_t* dest)
--- a/js/src/jit/mips/Assembler-mips.h +++ b/js/src/jit/mips/Assembler-mips.h @@ -766,18 +766,21 @@ class Assembler : public AssemblerShared void trace(JSTracer* trc); void writeRelocation(BufferOffset src) { jumpRelocations_.writeUnsigned(src.getOffset()); } // As opposed to x86/x64 version, the data relocation has to be executed // before to recover the pointer, and not after. void writeDataRelocation(ImmGCPtr ptr) { - if (ptr.value) + if (ptr.value) { + if (gc::IsInsideNursery(ptr.value)) + embedsNurseryPointers_ = true; dataRelocations_.writeUnsigned(nextOffset().getOffset()); + } } void writePrebarrierOffset(CodeOffsetLabel label) { preBarriers_.writeUnsigned(label.offset()); } public: static uintptr_t GetPointer(uint8_t*); @@ -1015,19 +1018,16 @@ class Assembler : public AssemblerShared void call(void* target); void as_break(uint32_t code); public: static void TraceJumpRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader); static void TraceDataRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader); - static void FixupNurseryObjects(JSContext* cx, JitCode* code, CompactBufferReader& reader, - const ObjectVector& nurseryObjects); - static bool SupportsFloatingPoint() { #if (defined(__mips_hard_float) && !defined(__mips_single_float)) || defined(JS_MIPS_SIMULATOR) return true; #else return false; #endif } static bool SupportsSimd() {
--- a/js/src/jit/mips/MacroAssembler-mips.cpp +++ b/js/src/jit/mips/MacroAssembler-mips.cpp @@ -1746,21 +1746,16 @@ MacroAssemblerMIPSCompat::movePtr(ImmWor void MacroAssemblerMIPSCompat::movePtr(ImmGCPtr imm, Register dest) { ma_li(dest, imm); } void -MacroAssemblerMIPSCompat::movePtr(ImmMaybeNurseryPtr imm, Register dest) -{ - movePtr(noteMaybeNurseryPtr(imm), dest); -} -void MacroAssemblerMIPSCompat::movePtr(ImmPtr imm, Register dest) { movePtr(ImmWord(uintptr_t(imm.value)), dest); } void MacroAssemblerMIPSCompat::movePtr(AsmJSImmPtr imm, Register dest) { append(AsmJSAbsoluteLink(CodeOffsetLabel(nextOffset().getOffset()), imm.kind()));
--- a/js/src/jit/mips/MacroAssembler-mips.h +++ b/js/src/jit/mips/MacroAssembler-mips.h @@ -810,19 +810,16 @@ public: bind(&skipJump); return off; } void branchPtr(Condition cond, Address addr, ImmGCPtr ptr, Label* label) { ma_lw(SecondScratchReg, addr); ma_li(ScratchRegister, ptr); ma_b(SecondScratchReg, ScratchRegister, label, cond); } - void branchPtr(Condition cond, Address addr, ImmMaybeNurseryPtr ptr, Label* label) { - branchPtr(cond, addr, noteMaybeNurseryPtr(ptr), label); - } void branchPtr(Condition cond, Address addr, ImmWord ptr, Label* label) { ma_lw(SecondScratchReg, addr); ma_b(SecondScratchReg, Imm32(ptr.value), label, cond); } void branchPtr(Condition cond, Address addr, ImmPtr ptr, Label* label) { branchPtr(cond, addr, ImmWord(uintptr_t(ptr.value)), label); } @@ -1223,17 +1220,16 @@ public: void move32(Imm32 imm, Register dest); void move32(Register src, Register dest); void movePtr(Register src, Register dest); void movePtr(ImmWord imm, Register dest); void movePtr(ImmPtr imm, Register dest); void movePtr(AsmJSImmPtr imm, Register dest); void movePtr(ImmGCPtr imm, Register dest); - void movePtr(ImmMaybeNurseryPtr imm, Register dest); void load8SignExtend(const Address& address, Register dest); void load8SignExtend(const BaseIndex& src, Register dest); void load8ZeroExtend(const Address& address, Register dest); void load8ZeroExtend(const BaseIndex& src, Register dest); void load16SignExtend(const Address& address, Register dest);
--- a/js/src/jit/none/MacroAssembler-none.h +++ b/js/src/jit/none/MacroAssembler-none.h @@ -161,22 +161,16 @@ class MacroAssemblerNone : public Assemb size_t numCodeLabels() const { MOZ_CRASH(); } CodeLabel codeLabel(size_t) { MOZ_CRASH(); } void trace(JSTracer*) { MOZ_CRASH(); } static void TraceJumpRelocations(JSTracer*, JitCode*, CompactBufferReader&) { MOZ_CRASH(); } static void TraceDataRelocations(JSTracer*, JitCode*, CompactBufferReader&) { MOZ_CRASH(); } - static void FixupNurseryObjects(JSContext*, JitCode*, CompactBufferReader&, - const ObjectVector&) - { - MOZ_CRASH(); - } - static bool SupportsFloatingPoint() { return false; } static bool SupportsSimd() { return false; } void executableCopy(void*) { MOZ_CRASH(); } void copyJumpRelocationTable(uint8_t*) { MOZ_CRASH(); } void copyDataRelocationTable(uint8_t*) { MOZ_CRASH(); } void copyPreBarrierTable(uint8_t*) { MOZ_CRASH(); } void processCodeLabels(uint8_t*) { MOZ_CRASH(); }
--- a/js/src/jit/shared/Assembler-shared.h +++ b/js/src/jit/shared/Assembler-shared.h @@ -209,80 +209,35 @@ struct PatchedImmPtr { explicit PatchedImmPtr(const void* value) : value(const_cast<void*>(value)) { } }; class AssemblerShared; class ImmGCPtr; -// Used for immediates which require relocation and may be traced during minor GC. -class ImmMaybeNurseryPtr -{ - friend class AssemblerShared; - friend class ImmGCPtr; - const gc::Cell* value; - - ImmMaybeNurseryPtr() : value(0) {} - - public: - explicit ImmMaybeNurseryPtr(const gc::Cell* ptr) : value(ptr) - { - // asm.js shouldn't be creating GC things - MOZ_ASSERT(!IsCompilingAsmJS()); - } -}; - -// Dummy value used for nursery pointers during Ion compilation, see -// LNurseryObject. -class IonNurseryPtr -{ - const gc::Cell* ptr; - - public: - friend class ImmGCPtr; - - explicit IonNurseryPtr(const gc::Cell* ptr) : ptr(ptr) - { - MOZ_ASSERT(ptr); - MOZ_ASSERT(uintptr_t(ptr) & 0x1); - } -}; - // Used for immediates which require relocation. class ImmGCPtr { public: const gc::Cell* value; explicit ImmGCPtr(const gc::Cell* ptr) : value(ptr) { - MOZ_ASSERT_IF(ptr, ptr->isTenured()); - - // asm.js shouldn't be creating GC things - MOZ_ASSERT(!IsCompilingAsmJS()); - } - - explicit ImmGCPtr(IonNurseryPtr ptr) : value(ptr.ptr) - { - MOZ_ASSERT(value); + // Nursery pointers can't be used if the main thread might be currently + // performing a minor GC. + MOZ_ASSERT_IF(ptr && !ptr->isTenured(), + !CurrentThreadIsIonCompilingSafeForMinorGC()); // asm.js shouldn't be creating GC things MOZ_ASSERT(!IsCompilingAsmJS()); } private: ImmGCPtr() : value(0) {} - - friend class AssemblerShared; - explicit ImmGCPtr(ImmMaybeNurseryPtr ptr) : value(ptr.value) - { - // asm.js shouldn't be creating GC things - MOZ_ASSERT(!IsCompilingAsmJS()); - } }; // Pointer to be embedded as an immediate that is loaded/stored from by an // instruction. struct AbsoluteAddress { void* addr; @@ -981,28 +936,16 @@ class AssemblerShared void appendProfilerCallSite(CodeOffsetLabel label) { enoughMemory_ &= profilerCallSites_.append(label); } bool embedsNurseryPointers() const { return embedsNurseryPointers_; } - ImmGCPtr noteMaybeNurseryPtr(ImmMaybeNurseryPtr ptr) { - if (ptr.value && gc::IsInsideNursery(ptr.value)) { - // noteMaybeNurseryPtr can be reached from off-thread compilation, - // though not with an actual nursery pointer argument in that case. - MOZ_ASSERT(GetJitContext()->runtime->onMainThread()); - // Do not be ion-compiling on the main thread. - MOZ_ASSERT(!GetJitContext()->runtime->mainThread()->ionCompiling); - embedsNurseryPointers_ = true; - } - return ImmGCPtr(ptr); - } - void append(const CallSiteDesc& desc, size_t currentOffset, size_t framePushed) { // framePushed does not include sizeof(AsmJSFrame), so add it in here (see // CallSite::stackDepth). CallSite callsite(desc, currentOffset, framePushed + sizeof(AsmJSFrame)); enoughMemory_ &= callsites_.append(callsite); } CallSiteVector&& extractCallSites() { return Move(callsites_); }
--- a/js/src/jit/x64/MacroAssembler-x64.h +++ b/js/src/jit/x64/MacroAssembler-x64.h @@ -497,19 +497,16 @@ class MacroAssemblerX64 : public MacroAs void cmpPtr(Register lhs, const Imm32 rhs) { cmpq(rhs, lhs); } void cmpPtr(const Operand& lhs, const ImmGCPtr rhs) { MOZ_ASSERT(!lhs.containsReg(ScratchReg)); movePtr(rhs, ScratchReg); cmpPtr(lhs, ScratchReg); } - void cmpPtr(const Operand& lhs, const ImmMaybeNurseryPtr rhs) { - cmpPtr(lhs, noteMaybeNurseryPtr(rhs)); - } void cmpPtr(const Operand& lhs, const ImmWord rhs) { if ((intptr_t)rhs.value <= INT32_MAX && (intptr_t)rhs.value >= INT32_MIN) { cmpPtr(lhs, Imm32((int32_t)rhs.value)); } else { movePtr(rhs, ScratchReg); cmpPtr(lhs, ScratchReg); } } @@ -728,19 +725,16 @@ class MacroAssemblerX64 : public MacroAs mov(imm, dest); } void movePtr(AsmJSImmPtr imm, Register dest) { mov(imm, dest); } void movePtr(ImmGCPtr imm, Register dest) { movq(imm, dest); } - void movePtr(ImmMaybeNurseryPtr imm, Register dest) { - movePtr(noteMaybeNurseryPtr(imm), dest); - } void loadPtr(AbsoluteAddress address, Register dest) { if (X86Encoding::IsAddressImmediate(address.addr)) { movq(Operand(address), dest); } else { mov(ImmPtr(address.addr), ScratchReg); loadPtr(Address(ScratchReg, 0x0), dest); } }
--- a/js/src/jit/x86-shared/Assembler-x86-shared.cpp +++ b/js/src/jit/x86-shared/Assembler-x86-shared.cpp @@ -63,75 +63,30 @@ TraceDataRelocations(JSTracer* trc, uint Value v = IMPL_TO_JSVAL(layout); TraceManuallyBarrieredEdge(trc, &v, "ion-masm-value"); if (*word != JSVAL_TO_IMPL(v).asBits) *word = JSVAL_TO_IMPL(v).asBits; continue; } #endif - // The low bit shouldn't be set. If it is, we probably got a dummy - // pointer inserted by CodeGenerator::visitNurseryObject, but we - // shouldn't be able to trigger GC before those are patched to their - // real values. - MOZ_ASSERT(!(*reinterpret_cast<uintptr_t*>(ptr) & 0x1)); - // No barrier needed since these are constants. TraceManuallyBarrieredGenericPointerEdge(trc, reinterpret_cast<gc::Cell**>(ptr), "ion-masm-ptr"); } } void AssemblerX86Shared::TraceDataRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader) { ::TraceDataRelocations(trc, code->raw(), reader); } void -AssemblerX86Shared::FixupNurseryObjects(JSContext* cx, JitCode* code, CompactBufferReader& reader, - const ObjectVector& nurseryObjects) -{ - MOZ_ASSERT(!nurseryObjects.empty()); - - uint8_t* buffer = code->raw(); - bool hasNurseryPointers = false; - - while (reader.more()) { - size_t offset = reader.readUnsigned(); - void** ptr = X86Encoding::GetPointerRef(buffer + offset); - - uintptr_t* word = reinterpret_cast<uintptr_t*>(ptr); - -#ifdef JS_PUNBOX64 - if (*word >> JSVAL_TAG_SHIFT) - continue; // This is a Value. -#endif - - if (!(*word & 0x1)) - continue; - - uint32_t index = *word >> 1; - JSObject* obj = nurseryObjects[index]; - *word = uintptr_t(obj); - - // Either all objects are still in the nursery, or all objects are - // tenured. - MOZ_ASSERT_IF(hasNurseryPointers, IsInsideNursery(obj)); - - if (!hasNurseryPointers && IsInsideNursery(obj)) - hasNurseryPointers = true; - } - - if (hasNurseryPointers) - cx->runtime()->gc.storeBuffer.putWholeCellFromMainThread(code); -} - -void AssemblerX86Shared::trace(JSTracer* trc) { for (size_t i = 0; i < jumps_.length(); i++) { RelativePatch& rp = jumps_[i]; if (rp.kind == Relocation::JITCODE) { JitCode* code = JitCode::FromExecutable((uint8_t*)rp.target); TraceManuallyBarrieredEdge(trc, &code, "masmrel32"); MOZ_ASSERT(code == JitCode::FromExecutable((uint8_t*)rp.target));
--- a/js/src/jit/x86-shared/Assembler-x86-shared.h +++ b/js/src/jit/x86-shared/Assembler-x86-shared.h @@ -224,18 +224,21 @@ class AssemblerX86Shared : public Assemb Vector<CodeLabel, 0, SystemAllocPolicy> codeLabels_; Vector<RelativePatch, 8, SystemAllocPolicy> jumps_; CompactBufferWriter jumpRelocations_; CompactBufferWriter dataRelocations_; CompactBufferWriter preBarriers_; void writeDataRelocation(ImmGCPtr ptr) { - if (ptr.value) + if (ptr.value) { + if (gc::IsInsideNursery(ptr.value)) + embedsNurseryPointers_ = true; dataRelocations_.writeUnsigned(masm.currentOffset()); + } } void writePrebarrierOffset(CodeOffsetLabel label) { preBarriers_.writeUnsigned(label.offset()); } protected: X86Encoding::BaseAssembler masm; @@ -343,19 +346,16 @@ class AssemblerX86Shared : public Assemb // handle NaNs properly and may therefore require a secondary condition. // Use NaNCondFromDoubleCondition to determine what else is needed. static inline Condition ConditionFromDoubleCondition(DoubleCondition cond) { return static_cast<Condition>(cond & ~DoubleConditionBits); } static void TraceDataRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader); - static void FixupNurseryObjects(JSContext* cx, JitCode* code, CompactBufferReader& reader, - const ObjectVector& nurseryObjects); - // MacroAssemblers hold onto gcthings, so they are traced by the GC. void trace(JSTracer* trc); bool oom() const { return AssemblerShared::oom() || masm.oom() || jumpRelocations_.oom() || dataRelocations_.oom() ||
--- a/js/src/jit/x86/Assembler-x86.h +++ b/js/src/jit/x86/Assembler-x86.h @@ -211,19 +211,16 @@ class Assembler : public AssemblerX86Sha void executableCopy(uint8_t* buffer); // Actual assembly emitting functions. void push(ImmGCPtr ptr) { masm.push_i32(int32_t(ptr.value)); writeDataRelocation(ptr); } - void push(ImmMaybeNurseryPtr ptr) { - push(noteMaybeNurseryPtr(ptr)); - } void push(const ImmWord imm) { push(Imm32(imm.value)); } void push(const ImmPtr imm) { push(ImmWord(uintptr_t(imm.value))); } void push(FloatRegister src) { subl(Imm32(sizeof(double)), StackPointer); @@ -364,19 +361,16 @@ class Assembler : public AssemblerX86Sha case Operand::MEM_ADDRESS32: masm.cmpl_i32m(uintptr_t(rhs.value), lhs.address()); writeDataRelocation(rhs); break; default: MOZ_CRASH("unexpected operand kind"); } } - void cmpl(ImmMaybeNurseryPtr rhs, const Operand& lhs) { - cmpl(noteMaybeNurseryPtr(rhs), lhs); - } void cmpl(Register rhs, AsmJSAbsoluteAddress lhs) { masm.cmpl_rm_disp32(rhs.encoding(), (void*)-1); append(AsmJSAbsoluteLink(CodeOffsetLabel(masm.currentOffset()), lhs.kind())); } void cmpl(Imm32 rhs, AsmJSAbsoluteAddress lhs) { JmpSrc src = masm.cmpl_im_disp32(rhs.value, (void*)-1); append(AsmJSAbsoluteLink(CodeOffsetLabel(src.offset()), lhs.kind())); }
--- a/js/src/jit/x86/MacroAssembler-x86.h +++ b/js/src/jit/x86/MacroAssembler-x86.h @@ -233,17 +233,17 @@ class MacroAssemblerX86 : public MacroAs void popValue(ValueOperand val) { pop(val.payloadReg()); pop(val.typeReg()); } void pushValue(const Value& val) { jsval_layout jv = JSVAL_TO_IMPL(val); push(Imm32(jv.s.tag)); if (val.isMarkable()) - push(ImmMaybeNurseryPtr(reinterpret_cast<gc::Cell*>(val.toGCThing()))); + push(ImmGCPtr(reinterpret_cast<gc::Cell*>(val.toGCThing()))); else push(Imm32(jv.s.payload.i32)); } void pushValue(JSValueType type, Register reg) { push(ImmTag(JSVAL_TYPE_TO_TAG(type))); push(reg); } void pushValue(const Address& addr) { @@ -564,19 +564,16 @@ class MacroAssemblerX86 : public MacroAs cmpPtr(lhs, ImmWord(uintptr_t(rhs.value))); } void cmpPtr(const Address& lhs, const ImmGCPtr rhs) { cmpPtr(Operand(lhs), rhs); } void cmpPtr(Register lhs, Register rhs) { cmp32(lhs, rhs); } - void cmpPtr(const Operand& lhs, ImmMaybeNurseryPtr rhs) { - cmpl(rhs, lhs); - } void testPtr(Register lhs, Register rhs) { test32(lhs, rhs); } void testPtr(Register lhs, Imm32 rhs) { test32(lhs, rhs); } void testPtr(Register lhs, ImmWord rhs) { test32(lhs, Imm32(rhs.value)); @@ -730,19 +727,16 @@ class MacroAssemblerX86 : public MacroAs movl(imm, dest); } void movePtr(AsmJSImmPtr imm, Register dest) { mov(imm, dest); } void movePtr(ImmGCPtr imm, Register dest) { movl(imm, dest); } - void movePtr(ImmMaybeNurseryPtr imm, Register dest) { - movePtr(noteMaybeNurseryPtr(imm), dest); - } void loadPtr(const Address& address, Register dest) { movl(Operand(address), dest); } void loadPtr(const Operand& src, Register dest) { movl(src, dest); } void loadPtr(const BaseIndex& src, Register dest) { movl(Operand(src), dest);
--- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -2387,16 +2387,32 @@ js::SetClassAndProto(JSContext* cx, Hand MarkObjectGroupUnknownProperties(cx, obj->group()); MarkObjectGroupUnknownProperties(cx, group); obj->setGroup(group); return true; } +/* static */ bool +JSObject::changeToSingleton(JSContext* cx, HandleObject obj) +{ + MOZ_ASSERT(!obj->isSingleton()); + + MarkObjectGroupUnknownProperties(cx, obj->group()); + + ObjectGroup* group = ObjectGroup::lazySingletonGroup(cx, obj->getClass(), + obj->getTaggedProto()); + if (!group) + return false; + + obj->group_ = group; + return true; +} + static bool MaybeResolveConstructor(ExclusiveContext* cxArg, Handle<GlobalObject*> global, JSProtoKey key) { if (global->isStandardClassResolved(key)) return true; if (!cxArg->shouldBeJSContext()) return false;
--- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -306,22 +306,24 @@ class JSObject : public js::gc::Cell void addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::ClassInfo* info); // We can only use addSizeOfExcludingThis on tenured objects: it assumes it // can apply mallocSizeOf to bits and pieces of the object, whereas objects // in the nursery may have those bits and pieces allocated in the nursery // along with them, and are not each their own malloc blocks. size_t sizeOfIncludingThisInNursery() const; - /* - * Marks this object as having a singleton type, and leave the group lazy. - * Constructs a new, unique shape for the object. - */ + // Marks this object as having a singleton group, and leave the group lazy. + // Constructs a new, unique shape for the object. This should only be + // called for an object that was just created. static inline bool setSingleton(js::ExclusiveContext* cx, js::HandleObject obj); + // Change an existing object to have a singleton group. + static bool changeToSingleton(JSContext* cx, js::HandleObject obj); + inline js::ObjectGroup* getGroup(JSContext* cx); const js::HeapPtrObjectGroup& groupFromGC() const { /* Direct field access for use by GC. */ return group_; } /*
--- a/js/src/vm/ObjectGroup.cpp +++ b/js/src/vm/ObjectGroup.cpp @@ -502,18 +502,30 @@ ObjectGroup::defaultNewGroup(ExclusiveCo // function, don't try to construct another one. if (associated && associated->wasNewScriptCleared()) associated = nullptr; if (!associated) clasp = &PlainObject::class_; } - if (proto.isObject() && !proto.toObject()->setDelegate(cx)) - return nullptr; + if (proto.isObject() && !proto.toObject()->isDelegate()) { + RootedObject protoObj(cx, proto.toObject()); + if (!protoObj->setDelegate(cx)) + return nullptr; + + // Objects which are prototypes of one another should be singletons, so + // that their type information can be tracked more precisely. Limit + // this group change to plain objects, to avoid issues with other types + // of singletons like typed arrays. + if (protoObj->is<PlainObject>() && !protoObj->isSingleton()) { + if (!JSObject::changeToSingleton(cx->asJSContext(), protoObj)) + return nullptr; + } + } ObjectGroupCompartment::NewTable::AddPtr p = table->lookupForAdd(ObjectGroupCompartment::NewEntry::Lookup(clasp, proto, associated)); if (p) { ObjectGroup* group = p->group; MOZ_ASSERT_IF(clasp, group->clasp() == clasp); MOZ_ASSERT_IF(!clasp, group->clasp() == &PlainObject::class_ || group->clasp() == &UnboxedPlainObject::class_);
--- a/js/src/vm/ObjectGroup.h +++ b/js/src/vm/ObjectGroup.h @@ -152,20 +152,21 @@ enum NewObjectKind { TenuredObject }; /* * Lazy object groups overview. * * Object groups which represent at most one JS object are constructed lazily. * These include groups for native functions, standard classes, scripted - * functions defined at the top level of global/eval scripts, and in some - * other cases. Typical web workloads often create many windows (and many - * copies of standard natives) and many scripts, with comparatively few - * non-singleton groups. + * functions defined at the top level of global/eval scripts, objects which + * dynamically become the prototype of some other object, and in some other + * cases. Typical web workloads often create many windows (and many copies of + * standard natives) and many scripts, with comparatively few non-singleton + * groups. * * We can recover the type information for the object from examining it, * so don't normally track the possible types of its properties as it is * updated. Property type sets for the object are only constructed when an * analyzed script attaches constraints to it: the script is querying that * property off the object or another which delegates to it, and the analysis * information is sensitive to changes in the property's type. Future changes * to the property (whether those uncovered by analysis or those occurring
--- a/js/src/vm/Runtime.cpp +++ b/js/src/vm/Runtime.cpp @@ -76,16 +76,17 @@ PerThreadData::PerThreadData(JSRuntime* #ifdef JS_TRACE_LOGGING traceLogger(nullptr), #endif autoFlushICache_(nullptr), dtoaState(nullptr), suppressGC(0), #ifdef DEBUG ionCompiling(false), + ionCompilingSafeForMinorGC(false), gcSweeping(false), #endif activeCompilations(0) {} PerThreadData::~PerThreadData() { if (dtoaState)
--- a/js/src/vm/Runtime.h +++ b/js/src/vm/Runtime.h @@ -518,16 +518,21 @@ class PerThreadData : public PerThreadDa * in non-exposed debugging facilities. */ int32_t suppressGC; #ifdef DEBUG // Whether this thread is actively Ion compiling. bool ionCompiling; + // Whether this thread is actively Ion compiling in a context where a minor + // GC could happen simultaneously. If this is true, this thread cannot use + // any pointers into the nursery. + bool ionCompilingSafeForMinorGC; + // Whether this thread is currently sweeping GC things. bool gcSweeping; #endif // Number of active bytecode compilation on this thread. unsigned activeCompilations; explicit PerThreadData(JSRuntime* runtime); @@ -1928,31 +1933,35 @@ class RuntimeAllocPolicy extern const JSSecurityCallbacks NullSecurityCallbacks; // Debugging RAII class which marks the current thread as performing an Ion // compilation, for use by CurrentThreadCan{Read,Write}CompilationData class AutoEnterIonCompilation { public: - explicit AutoEnterIonCompilation(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM) { + explicit AutoEnterIonCompilation(bool safeForMinorGC + MOZ_GUARD_OBJECT_NOTIFIER_PARAM) { MOZ_GUARD_OBJECT_NOTIFIER_INIT; #ifdef DEBUG PerThreadData* pt = js::TlsPerThreadData.get(); MOZ_ASSERT(!pt->ionCompiling); + MOZ_ASSERT(!pt->ionCompilingSafeForMinorGC); pt->ionCompiling = true; + pt->ionCompilingSafeForMinorGC = safeForMinorGC; #endif } ~AutoEnterIonCompilation() { #ifdef DEBUG PerThreadData* pt = js::TlsPerThreadData.get(); MOZ_ASSERT(pt->ionCompiling); pt->ionCompiling = false; + pt->ionCompilingSafeForMinorGC = false; #endif } MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER }; /* * AutoInitGCManagedObject is a wrapper for use when initializing a object whose
--- a/js/src/vm/TypeInference.cpp +++ b/js/src/vm/TypeInference.cpp @@ -243,37 +243,43 @@ js::ObjectGroupHasProperty(JSContext* cx id = IdToTypeId(id); /* Watch for properties which inference does not monitor. */ if (id == id___proto__(cx) || id == id_constructor(cx) || id == id_caller(cx)) return true; TypeSet::Type type = TypeSet::GetValueType(value); - // Type set guards might miss when an object's group changes and its - // properties become unknown. - if (value.isObject() && - !value.toObject().hasLazyGroup() && - ((value.toObject().group()->flags() & OBJECT_FLAG_UNKNOWN_PROPERTIES) || - value.toObject().group()->maybeOriginalUnboxedGroup())) - { - return true; - } - AutoEnterAnalysis enter(cx); /* * We don't track types for properties inherited from prototypes which * haven't yet been accessed during analysis of the inheriting object. * Don't do the property instantiation now. */ TypeSet* types = group->maybeGetProperty(id); if (!types) return true; + // Type set guards might miss when an object's group changes and its + // properties become unknown. + if (value.isObject()) { + if (types->unknownObject()) + return true; + for (size_t i = 0; i < types->getObjectCount(); i++) { + if (TypeSet::ObjectKey* key = types->getObject(i)) { + if (key->unknownProperties()) + return true; + } + } + JSObject* obj = &value.toObject(); + if (!obj->hasLazyGroup() && obj->group()->maybeOriginalUnboxedGroup()) + return true; + } + if (!types->hasType(type)) { TypeFailure(cx, "Missing type in object %s %s: %s", TypeSet::ObjectGroupString(group), TypeIdString(id), TypeSet::TypeString(type)); } } return true; } @@ -600,29 +606,66 @@ TypeSet::addType(Type type, LifoAlloc* a if (false) { unknownObject: flags |= TYPE_FLAG_ANYOBJECT; clearObjects(); } } +// This class is used for post barriers on type set contents. The only times +// when type sets contain nursery references is when a nursery object has its +// group dynamically changed to a singleton. In such cases the type set will +// need to be traced at the next minor GC. +// +// There is no barrier used for TemporaryTypeSets. These type sets are only +// used during Ion compilation, and if some ConstraintTypeSet contains nursery +// pointers then any number of TemporaryTypeSets might as well. Thus, if there +// are any such ConstraintTypeSets in existence, all off thread Ion +// compilations are canceled by the next minor GC. +class TypeSetRef : public BufferableRef +{ + Zone* zone; + ConstraintTypeSet* types; + + public: + TypeSetRef(Zone* zone, ConstraintTypeSet* types) + : zone(zone), types(types) + {} + + void trace(JSTracer* trc) override { + types->trace(zone, trc); + } +}; + +void +ConstraintTypeSet::postWriteBarrier(ExclusiveContext* cx, Type type) +{ + if (type.isSingletonUnchecked() && IsInsideNursery(type.singletonNoBarrier())) { + JSRuntime* rt = cx->asJSContext()->runtime(); + rt->gc.storeBuffer.putGeneric(TypeSetRef(cx->zone(), this)); + rt->gc.storeBuffer.setShouldCancelIonCompilations(); + } +} + void ConstraintTypeSet::addType(ExclusiveContext* cxArg, Type type) { MOZ_ASSERT(cxArg->zone()->types.activeAnalysis); if (hasType(type)) return; TypeSet::addType(type, &cxArg->typeLifoAlloc()); if (type.isObjectUnchecked() && unknownObject()) type = AnyObjectType(); + postWriteBarrier(cxArg, type); + InferSpew(ISpewOps, "addType: %sT%p%s %s", InferSpewColor(this), this, InferSpewColorReset(), TypeString(type)); /* Propagate the type to all constraints. */ if (JSContext* cx = cxArg->maybeJSContext()) { TypeConstraint* constraint = constraintList; while (constraint) { @@ -2572,16 +2615,17 @@ UpdatePropertyType(ExclusiveContext* cx, * magic values and optimized out values) as appearing in CallObjects. */ MOZ_ASSERT_IF(TypeSet::IsUntrackedValue(value), obj->is<CallObject>()); if ((indexed || !value.isUndefined() || !CanHaveEmptyPropertyTypesForOwnProperty(obj)) && !TypeSet::IsUntrackedValue(value)) { TypeSet::Type type = TypeSet::GetValueType(value); types->TypeSet::addType(type, &cx->typeLifoAlloc()); + types->postWriteBarrier(cx, type); } if (indexed || shape->hadOverwrite()) { types->setNonConstantProperty(cx); } else { InferSpew(ISpewOps, "typeSet: %sT%p%s property %s %s - setConstant", InferSpewColor(types), types, InferSpewColorReset(), TypeSet::ObjectGroupString(obj->group()), TypeIdString(shape->propid())); @@ -2623,16 +2667,17 @@ ObjectGroup::updateNewPropertyTypes(Excl } /* Also get values of any dense elements in the object. */ for (size_t i = 0; i < obj->getDenseInitializedLength(); i++) { const Value& value = obj->getDenseElement(i); if (!value.isMagic(JS_ELEMENTS_HOLE)) { TypeSet::Type type = TypeSet::GetValueType(value); types->TypeSet::addType(type, &cx->typeLifoAlloc()); + types->postWriteBarrier(cx, type); } } } else if (!JSID_IS_EMPTY(id)) { RootedId rootedId(cx, id); Shape* shape = obj->lookup(cx, rootedId); if (shape) UpdatePropertyType(cx, types, obj, shape, false); } @@ -2852,16 +2897,19 @@ ObjectGroup::markUnknown(ExclusiveContex MOZ_ASSERT(cx->zone()->types.activeAnalysis); MOZ_ASSERT(!unknownProperties()); InferSpew(ISpewOps, "UnknownProperties: %s", TypeSet::ObjectGroupString(this)); clearNewScript(cx); ObjectStateChange(cx, this, true); + if (ObjectGroup* unboxedGroup = maybeOriginalUnboxedGroup()) + unboxedGroup->markUnknown(cx); + /* * Existing constraints may have already been added to this object, which we need * to do the right thing for. We can't ensure that we will mark all unknown * objects before they have been accessed, as the __proto__ of a known object * could be dynamically set to an unknown object, and we can decide to ignore * properties of an object during analysis (i.e. hashmaps). Adding unknown for * any properties accessed already accounts for possible values read from them. */ @@ -3932,16 +3980,65 @@ TypeNewScript::sweep() if (preliminaryObjects) preliminaryObjects->sweep(); } ///////////////////////////////////////////////////////////////////// // Tracing ///////////////////////////////////////////////////////////////////// +static inline void +TraceObjectKey(JSTracer* trc, TypeSet::ObjectKey** keyp) +{ + TypeSet::ObjectKey* key = *keyp; + if (key->isGroup()) { + ObjectGroup* group = key->groupNoBarrier(); + TraceManuallyBarrieredEdge(trc, &group, "objectKey_group"); + *keyp = TypeSet::ObjectKey::get(group); + } else { + JSObject* singleton = key->singletonNoBarrier(); + TraceManuallyBarrieredEdge(trc, &singleton, "objectKey_singleton"); + *keyp = TypeSet::ObjectKey::get(singleton); + } +} + +void +ConstraintTypeSet::trace(Zone* zone, JSTracer* trc) +{ + // ConstraintTypeSets only hold strong references during minor collections. + MOZ_ASSERT(zone->runtimeFromMainThread()->isHeapMinorCollecting()); + + unsigned objectCount = baseObjectCount(); + if (objectCount >= 2) { + unsigned oldCapacity = TypeHashSet::Capacity(objectCount); + ObjectKey** oldArray = objectSet; + + clearObjects(); + objectCount = 0; + for (unsigned i = 0; i < oldCapacity; i++) { + ObjectKey* key = oldArray[i]; + if (!key) + continue; + TraceObjectKey(trc, &key); + ObjectKey** pentry = + TypeHashSet::Insert<ObjectKey*, ObjectKey, ObjectKey> + (zone->types.typeLifoAlloc, objectSet, objectCount, key); + if (pentry) + *pentry = key; + else + CrashAtUnhandlableOOM("ConstraintTypeSet::trace"); + } + setBaseObjectCount(objectCount); + } else if (objectCount == 1) { + ObjectKey* key = (ObjectKey*) objectSet; + TraceObjectKey(trc, &key); + objectSet = reinterpret_cast<ObjectKey**>(key); + } +} + void ConstraintTypeSet::sweep(Zone* zone, AutoClearTypeInferenceStateOnOOM& oom) { MOZ_ASSERT(zone->isGCSweepingOrCompacting()); // IsAboutToBeFinalized doesn't work right on tenured objects when called // during a minor collection. MOZ_ASSERT(!zone->runtimeFromMainThread()->isHeapMinorCollecting());
--- a/js/src/vm/TypeInference.h +++ b/js/src/vm/TypeInference.h @@ -608,20 +608,25 @@ class ConstraintTypeSet : public TypeSet ConstraintTypeSet() : constraintList(nullptr) {} /* * Add a type to this set, calling any constraint handlers if this is a new * possible type. */ void addType(ExclusiveContext* cx, Type type); + // Trigger a post barrier when writing to this set, if necessary. + // addType(cx, type) takes care of this automatically. + void postWriteBarrier(ExclusiveContext* cx, Type type); + /* Add a new constraint to this set. */ bool addConstraint(JSContext* cx, TypeConstraint* constraint, bool callExisting = true); inline void sweep(JS::Zone* zone, AutoClearTypeInferenceStateOnOOM& oom); + inline void trace(JS::Zone* zone, JSTracer* trc); }; class StackTypeSet : public ConstraintTypeSet { public: }; class HeapTypeSet : public ConstraintTypeSet
--- a/js/src/vm/UnboxedObject.cpp +++ b/js/src/vm/UnboxedObject.cpp @@ -1883,16 +1883,19 @@ js::TryConvertToUnboxedLayout(ExclusiveC JSValueType elementType = JSVAL_TYPE_MAGIC; size_t objectCount = 0; for (size_t i = 0; i < PreliminaryObjectArray::COUNT; i++) { JSObject* obj = objects->get(i); if (!obj) continue; + if (obj->isSingleton() || obj->group() != group) + return true; + objectCount++; if (isArray) { if (!CombineArrayObjectElements(cx, &obj->as<ArrayObject>(), &elementType)) return true; } else { if (!CombinePlainObjectProperties(&obj->as<PlainObject>(), templateShape, properties)) return true;
--- a/layout/base/UnitTransforms.h +++ b/layout/base/UnitTransforms.h @@ -32,17 +32,20 @@ enum class PixelCastJustification : uint // The transform that is usually used to convert between two coordinate // systems is not available (for example, because the object that stores it // is being destroyed), so fall back to the identity. TransformNotAvailable, // When an OS event is initially constructed, its reference point is // technically in screen pixels, as it has not yet accounted for any // asynchronous transforms. This justification is for viewing the initial // reference point as a screen point. - LayoutDeviceToScreenForUntransformedEvent + LayoutDeviceToScreenForUntransformedEvent, + // Similar to LayoutDeviceToScreenForUntransformedEvent, PBrowser handles + // some widget/tab dimension information as the OS does -- in screen units. + LayoutDeviceIsScreenForTabDims }; template <class TargetUnits, class SourceUnits> gfx::SizeTyped<TargetUnits> ViewAs(const gfx::SizeTyped<SourceUnits>& aSize, PixelCastJustification) { return gfx::SizeTyped<TargetUnits>(aSize.width, aSize.height); } template <class TargetUnits, class SourceUnits> gfx::IntSizeTyped<TargetUnits> ViewAs(const gfx::IntSizeTyped<SourceUnits>& aSize, PixelCastJustification) {
--- a/layout/base/nsPresContext.cpp +++ b/layout/base/nsPresContext.cpp @@ -1793,50 +1793,72 @@ nsPresContext::UIResolutionChanged() nsCOMPtr<nsIRunnable> ev = NS_NewRunnableMethod(this, &nsPresContext::UIResolutionChangedInternal); if (NS_SUCCEEDED(NS_DispatchToCurrentThread(ev))) { mPendingUIResolutionChanged = true; } } } +void +nsPresContext::UIResolutionChangedSync() +{ + if (!mPendingUIResolutionChanged) { + mPendingUIResolutionChanged = true; + UIResolutionChangedInternal(); + } +} + /*static*/ bool nsPresContext::UIResolutionChangedSubdocumentCallback(nsIDocument* aDocument, void* aData) { nsIPresShell* shell = aDocument->GetShell(); if (shell) { nsPresContext* pc = shell->GetPresContext(); if (pc) { pc->UIResolutionChangedInternal(); } } return true; } static void -NotifyUIResolutionChanged(TabParent* aTabParent, void* aArg) +NotifyTabUIResolutionChanged(TabParent* aTab, void *aArg) +{ + aTab->UIResolutionChanged(); +} + +static void +NotifyChildrenUIResolutionChanged(nsIDOMWindow* aWindow) { - aTabParent->UIResolutionChanged(); + nsCOMPtr<nsPIDOMWindow> piWin = do_QueryInterface(aWindow); + if (!piWin) { + return; + } + nsCOMPtr<nsIDocument> doc = piWin->GetExtantDoc(); + nsRefPtr<nsPIWindowRoot> topLevelWin = nsContentUtils::GetWindowRoot(doc); + if (!topLevelWin) { + return; + } + topLevelWin->EnumerateBrowsers(NotifyTabUIResolutionChanged, nullptr); } void nsPresContext::UIResolutionChangedInternal() { mPendingUIResolutionChanged = false; mDeviceContext->CheckDPIChange(); if (mCurAppUnitsPerDevPixel != AppUnitsPerDevPixel()) { AppUnitsPerDevPixelChanged(); } - // Recursively notify all remote leaf descendants that the - // resolution of the user interface has changed. - nsContentUtils::CallOnAllRemoteChildren(mDocument->GetWindow(), - NotifyUIResolutionChanged, nullptr); + // Recursively notify all remote leaf descendants of the change. + NotifyChildrenUIResolutionChanged(mDocument->GetWindow()); mDocument->EnumerateSubDocuments(UIResolutionChangedSubdocumentCallback, nullptr); } void nsPresContext::EmulateMedium(const nsAString& aMediaType) {
--- a/layout/base/nsPresContext.h +++ b/layout/base/nsPresContext.h @@ -786,19 +786,25 @@ public: * has changed. */ void ThemeChanged(); /* * Notify the pres context that the resolution of the user interface has * changed. This happens if a window is moved between HiDPI and non-HiDPI * displays, so that the ratio of points to device pixels changes. + * The notification happens asynchronously. */ void UIResolutionChanged(); + /* + * Like UIResolutionChanged() but invalidates values immediately. + */ + void UIResolutionChangedSync(); + /* * Notify the pres context that a system color has changed */ void SysColorChanged(); /** Printing methods below should only be used for Medium() == print **/ void SetPrintSettings(nsIPrintSettings *aPrintSettings);
--- a/netwerk/cache2/CacheIndex.cpp +++ b/netwerk/cache2/CacheIndex.cpp @@ -15,16 +15,17 @@ #include "nsISizeOf.h" #include "nsPrintfCString.h" #include "mozilla/DebugOnly.h" #include "prinrval.h" #include "nsIFile.h" #include "nsITimer.h" #include "mozilla/AutoRestore.h" #include <algorithm> +#include "mozilla/Telemetry.h" #define kMinUnwrittenChanges 300 #define kMinDumpInterval 20000 // in milliseconds #define kMaxBufSize 16384 #define kIndexVersion 0x00000001 #define kUpdateIndexStartDelay 50000 // in milliseconds @@ -3125,16 +3126,21 @@ CacheIndex::ChangeState(EState aNewState // possible transition is to SHUTDOWN state. MOZ_ASSERT(!mShuttingDown || mState != READY || aNewState == SHUTDOWN); // Start updating process when switching to READY state if needed if (aNewState == READY && StartUpdatingIndexIfNeeded(true)) { return; } + if ((mState == READING || mState == BUILDING || mState == UPDATING) && + aNewState == READY) { + ReportHashStats(); + } + // Try to evict entries over limit everytime we're leaving state READING, // BUILDING or UPDATING, but not during shutdown or when removing all // entries. if (!mShuttingDown && !mRemovingAll && aNewState != SHUTDOWN && (mState == READING || mState == BUILDING || mState == UPDATING)) { CacheFileIOManager::EvictIfOverLimit(); } @@ -3622,10 +3628,78 @@ CacheIndex::SizeOfExcludingThis(mozilla: // static size_t CacheIndex::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) { return mallocSizeOf(gInstance) + SizeOfExcludingThis(mallocSizeOf); } +namespace { // anon + +class HashComparator +{ +public: + bool Equals(CacheIndexRecord* a, CacheIndexRecord* b) const { + return memcmp(&a->mHash, &b->mHash, sizeof(SHA1Sum::Hash)) == 0; + } + bool LessThan(CacheIndexRecord* a, CacheIndexRecord* b) const { + return memcmp(&a->mHash, &b->mHash, sizeof(SHA1Sum::Hash)) < 0; + } +}; + +void +ReportHashSizeMatch(const SHA1Sum::Hash *aHash1, const SHA1Sum::Hash *aHash2) +{ + const uint32_t *h1 = reinterpret_cast<const uint32_t *>(aHash1); + const uint32_t *h2 = reinterpret_cast<const uint32_t *>(aHash2); + + for (uint32_t i = 0; i < 5; ++i) { + if (h1[i] != h2[i]) { + uint32_t bitsDiff = h1[i] ^ h2[i]; + bitsDiff = NetworkEndian::readUint32(&bitsDiff); + + // count leading zeros in bitsDiff + static const uint8_t debruijn32[32] = + { 0, 31, 9, 30, 3, 8, 13, 29, 2, 5, 7, 21, 12, 24, 28, 19, + 1, 10, 4, 14, 6, 22, 25, 20, 11, 15, 23, 26, 16, 27, 17, 18}; + + bitsDiff |= bitsDiff>>1; + bitsDiff |= bitsDiff>>2; + bitsDiff |= bitsDiff>>4; + bitsDiff |= bitsDiff>>8; + bitsDiff |= bitsDiff>>16; + bitsDiff++; + + uint8_t hashSizeMatch = debruijn32[bitsDiff*0x076be629>>27] + (i<<5); + Telemetry::Accumulate(Telemetry::NETWORK_CACHE_HASH_STATS, hashSizeMatch); + + return; + } + } + + MOZ_ASSERT(false, "Found a collision in the index!"); +} + +} // anon + +void +CacheIndex::ReportHashStats() +{ + // We're gathering the hash stats only once, exclude too small caches. + if (CacheObserver::HashStatsReported() || mFrecencyArray.Length() < 15000) { + return; + } + + nsTArray<CacheIndexRecord *> records; + records.AppendElements(mFrecencyArray); + + records.Sort(HashComparator()); + + for (uint32_t i = 1; i < records.Length(); i++) { + ReportHashSizeMatch(&records[i-1]->mHash, &records[i]->mHash); + } + + CacheObserver::SetHashStatsReported(); +} + } // net } // mozilla
--- a/netwerk/cache2/CacheIndex.h +++ b/netwerk/cache2/CacheIndex.h @@ -921,16 +921,18 @@ private: void AddRecordToIterators(CacheIndexRecord *aRecord); void RemoveRecordFromIterators(CacheIndexRecord *aRecord); void ReplaceRecordInIterators(CacheIndexRecord *aOldRecord, CacheIndexRecord *aNewRecord); // Memory reporting (private part) size_t SizeOfExcludingThisInternal(mozilla::MallocSizeOf mallocSizeOf) const; + void ReportHashStats(); + static CacheIndex *gInstance; nsCOMPtr<nsIFile> mCacheDirectory; mozilla::Mutex mLock; EState mState; // Timestamp of time when the index was initialized. We use it to delay // initial update or build of index.
--- a/netwerk/cache2/CacheObserver.cpp +++ b/netwerk/cache2/CacheObserver.cpp @@ -84,16 +84,19 @@ static bool kDefaultSanitizeOnShutdown = bool CacheObserver::sSanitizeOnShutdown = kDefaultSanitizeOnShutdown; static bool kDefaultClearCacheOnShutdown = false; bool CacheObserver::sClearCacheOnShutdown = kDefaultClearCacheOnShutdown; static bool kDefaultCacheFSReported = false; bool CacheObserver::sCacheFSReported = kDefaultCacheFSReported; +static bool kDefaultHashStatsReported = false; +bool CacheObserver::sHashStatsReported = kDefaultHashStatsReported; + NS_IMPL_ISUPPORTS(CacheObserver, nsIObserver, nsISupportsWeakReference) // static nsresult CacheObserver::Init() { @@ -342,16 +345,42 @@ CacheObserver::SetCacheFSReported() void CacheObserver::StoreCacheFSReported() { mozilla::Preferences::SetInt("browser.cache.disk.filesystem_reported", sCacheFSReported); } // static +void +CacheObserver::SetHashStatsReported() +{ + sHashStatsReported = true; + + if (!sSelf) { + return; + } + + if (NS_IsMainThread()) { + sSelf->StoreHashStatsReported(); + } else { + nsCOMPtr<nsIRunnable> event = + NS_NewRunnableMethod(sSelf, &CacheObserver::StoreHashStatsReported); + NS_DispatchToMainThread(event); + } +} + +void +CacheObserver::StoreHashStatsReported() +{ + mozilla::Preferences::SetInt("browser.cache.disk.hashstats_reported", + sHashStatsReported); +} + +// static void CacheObserver::ParentDirOverride(nsIFile** aDir) { if (NS_WARN_IF(!aDir)) return; *aDir = nullptr; if (!sSelf)
--- a/netwerk/cache2/CacheObserver.h +++ b/netwerk/cache2/CacheObserver.h @@ -59,25 +59,29 @@ class CacheObserver : public nsIObserver { return sHalfLifeHours * 60.0F * 60.0F; } static int32_t const HalfLifeExperiment() { return sHalfLifeExperiment; } static bool const ClearCacheOnShutdown() { return sSanitizeOnShutdown && sClearCacheOnShutdown; } static bool const CacheFSReported() { return sCacheFSReported; } static void SetCacheFSReported(); + static bool const HashStatsReported() + { return sHashStatsReported; } + static void SetHashStatsReported(); static void ParentDirOverride(nsIFile ** aDir); static bool const EntryIsTooBig(int64_t aSize, bool aUsingDisk); private: static CacheObserver* sSelf; void StoreDiskCacheCapacity(); void StoreCacheFSReported(); + void StoreHashStatsReported(); void AttachToPreferences(); static uint32_t sUseNewCache; static bool sUseMemoryCache; static bool sUseDiskCache; static uint32_t sMetadataMemoryLimit; static int32_t sMemoryCacheCapacity; static int32_t sAutoMemoryCacheCapacity; @@ -91,16 +95,17 @@ private: static uint32_t sMaxDiskChunksMemoryUsage; static uint32_t sMaxDiskPriorityChunksMemoryUsage; static uint32_t sCompressionLevel; static float sHalfLifeHours; static int32_t sHalfLifeExperiment; static bool sSanitizeOnShutdown; static bool sClearCacheOnShutdown; static bool sCacheFSReported; + static bool sHashStatsReported; // Non static properties, accessible via sSelf nsCOMPtr<nsIFile> mCacheParentDirectoryOverride; }; } // net } // mozilla
--- a/testing/mach_commands.py +++ b/testing/mach_commands.py @@ -377,33 +377,16 @@ class JsapiTestsCommand(MachCommandBase) if params['test_name']: jsapi_tests_cmd.append(params['test_name']) jsapi_tests_result = subprocess.call(jsapi_tests_cmd) return jsapi_tests_result -AUTOTRY_HELP_MSG = """ -Autotry is in beta, please file bugs blocking 1149670. - -Push test from the specified paths to try. A set of test -jobs will be selected based on the tests present in the tree, however -specifying platforms is still required with the -p argument (a default -is taken from the AUTOTRY_PLATFORM_HINT environment variable if set). - -The -u argument may be used to specify additional unittest suites to run. - -Selected tests will be run in a single chunk of the relevant suite, at this -time in chunk 1. - -The following types of tests are eligible to be selected automatically -by this command at this time: %s -""" % list(AutoTry.test_flavors) - @CommandProvider class PushToTry(MachCommandBase): def validate_args(self, paths, tests, builds, platforms): if not len(paths) and not tests: print("Paths or tests must be specified as an argument to autotry.") sys.exit(1) @@ -418,39 +401,62 @@ class PushToTry(MachCommandBase): sys.exit(1) if len(p) <= len(self.topsrcdir): print('Specified path "%s" is at the top of the srcdir and would' ' select all tests.' % p) sys.exit(1) return builds, platforms - @Command('try', category='testing', description=AUTOTRY_HELP_MSG) + @Command('try', category='testing', description='Push selected tests to the try server') @CommandArgument('paths', nargs='*', help='Paths to search for tests to run on try.') - @CommandArgument('-v', dest='verbose', action='store_true', default=True, + @CommandArgument('-n', dest='verbose', action='store_true', default=False, help='Print detailed information about the resulting test selection ' 'and commands performed.') @CommandArgument('-p', dest='platforms', required='AUTOTRY_PLATFORM_HINT' not in os.environ, help='Platforms to run. (required if not found in the environment)') @CommandArgument('-u', dest='tests', - help='Test jobs to run. These will be use in place of test jobs ' + help='Test jobs to run. These will be used in place of suites ' 'determined by test paths, if any.') @CommandArgument('--extra', dest='extra_tests', - help='Additional tests to run. These will be added to test jobs ' + help='Additional tests to run. These will be added to suites ' 'determined by test paths, if any.') @CommandArgument('-b', dest='builds', default='do', help='Build types to run (d for debug, o for optimized)') @CommandArgument('--tag', dest='tags', action='append', help='Restrict tests to the given tag (may be specified multiple times)') @CommandArgument('--no-push', dest='push', action='store_false', help='Do not push to try as a result of running this command (if ' 'specified this command will only print calculated try ' 'syntax and selection info).') - def autotry(self, builds=None, platforms=None, paths=None, verbose=None, extra_tests=None, - push=None, tags=None, tests=None): + def autotry(self, builds=None, platforms=None, paths=None, verbose=None, + extra_tests=None, push=None, tags=None, tests=None): + """Autotry is in beta, please file bugs blocking 1149670. + + Pushes the specified tests to try. The simplest way to specify tests is + by using the -u argument, which will behave as usual for try syntax. + This command also provides a mechanism to select test jobs and tests + within a job by path based on tests present in the tree under that + path. Mochitests, xpcshell tests, and reftests are eligible for + selection by this mechanism. Selected tests will be run in a single + chunk of the relevant suite, at this time in chunk 1. + + Specifying platforms is still required with the -p argument (a default + is taken from the AUTOTRY_PLATFORM_HINT environment variable if set). + + Tests may be further filtered by passing one or more --tag to the + command. + + To run suites in addition to those determined from the tree, they + can be passed to the --extra arguent. + + The command requires either its own mercurial extension ("push-to-try", + installable from mach mercurial-setup) or a git repo using git-cinnabar + (available at https://github.com/glandium/git-cinnabar). + """ from mozbuild.testing import TestResolver from mozbuild.controller.building import BuildDriver print("mach try is under development, please file bugs blocking 1149670.") builds, platforms = self.validate_args(paths, tests, builds, platforms) resolver = self._spawn(TestResolver) @@ -473,19 +479,19 @@ class PushToTry(MachCommandBase): all_manifests = set() for m in manifests_by_flavor.values(): all_manifests |= m all_manifests = list(all_manifests) msg = at.calc_try_syntax(platforms, manifests_by_flavor.keys(), tests, extra_tests, builds, all_manifests, tags) - if verbose: + if verbose and manifests_by_flavor: print('Tests from the following manifests will be selected: ') pprint.pprint(manifests_by_flavor) if verbose: - print('The following try message was calculated:\n\n\t%s\n' % msg) + print('The following try syntax was calculated:\n\n\t%s\n' % msg) if push: at.push_to_try(msg, verbose) return
--- a/testing/web-platform/meta/MANIFEST.json +++ b/testing/web-platform/meta/MANIFEST.json @@ -24653,18 +24653,29 @@ "path": "webdriver/timeouts/page_load_timeouts_tests.py" }, { "path": "webdriver/user_input/clear_test.py" } ] }, "local_changes": { - "deleted": [], - "items": {}, + "deleted": [ + "webaudio/the-audio-api/the-gainnode-interface/test.html" + ], + "items": { + "testharness": { + "webaudio/the-audio-api/the-gainnode-interface/test-gainnode.html": [ + { + "path": "webaudio/the-audio-api/the-gainnode-interface/test-gainnode.html", + "url": "/webaudio/the-audio-api/the-gainnode-interface/test-gainnode.html" + } + ] + } + }, "reftest_nodes": {} }, "reftest_nodes": { "2dcontext/building-paths/canvas_complexshapes_arcto_001.htm": [ { "path": "2dcontext/building-paths/canvas_complexshapes_arcto_001.htm", "references": [ [ @@ -30110,9 +30121,9 @@ ], "url": "/webvtt/rendering/cues-with-video/processing-model/too_many_cues_wrapped.html" } ] }, "rev": "0fbf63dbe6f6b8b0dccee76d96ec4b7d50c3fff6", "url_base": "/", "version": 2 -} \ No newline at end of file +}
rename from testing/web-platform/tests/webaudio/the-audio-api/the-gainnode-interface/test.html rename to testing/web-platform/tests/webaudio/the-audio-api/the-gainnode-interface/test-gainnode.html
--- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -7051,16 +7051,22 @@ }, "NETWORK_CACHE_METADATA_SIZE": { "expires_in_version": "never", "kind": "linear", "high": "5119", "n_buckets": 256, "description": "Actual size of the metadata parsed from the disk." }, + "NETWORK_CACHE_HASH_STATS": { + "expires_in_version": "46", + "kind": "enumerated", + "n_values": "160", + "description": "The longest hash match between a newly added entry and all the existing entries." + }, "DATABASE_LOCKED_EXCEPTION": { "expires_in_version": "42", "kind": "enumerated", "description": "Record database locks when opening one of Fennec's databases. The index corresponds to how many attempts, beginning with 0.", "n_values": "5" }, "DATABASE_SUCCESSFUL_UNLOCK": { "expires_in_version": "42",