diff --git a/b2g/app/b2g.js b/b2g/app/b2g.js index 13d99dfbbc02..951bf358d0a1 100644 --- a/b2g/app/b2g.js +++ b/b2g/app/b2g.js @@ -319,7 +319,7 @@ pref("media.fragmented-mp4.gonk.enabled", true); pref("media.video-queue.default-size", 3); // optimize images' memory usage -pref("image.mem.decodeondraw", true); +pref("image.mem.decodeondraw", false); pref("image.mem.allow_locking_in_content_processes", false); /* don't allow image locking */ // Limit the surface cache to 1/8 of main memory or 128MB, whichever is smaller. // Almost everything that was factored into 'max_decoded_image_kb' is now stored diff --git a/b2g/config/dolphin/sources.xml b/b2g/config/dolphin/sources.xml index 0931cffbf8b1..9006d3bcd51c 100644 --- a/b2g/config/dolphin/sources.xml +++ b/b2g/config/dolphin/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator-ics/sources.xml b/b2g/config/emulator-ics/sources.xml index 8df3d0248551..08f9699d0554 100644 --- a/b2g/config/emulator-ics/sources.xml +++ b/b2g/config/emulator-ics/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/emulator-jb/sources.xml b/b2g/config/emulator-jb/sources.xml index 27c9f7610966..1b0893f608f4 100644 --- a/b2g/config/emulator-jb/sources.xml +++ b/b2g/config/emulator-jb/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/emulator-kk/sources.xml b/b2g/config/emulator-kk/sources.xml index 0194695809fc..b2e9feaff5ca 100644 --- a/b2g/config/emulator-kk/sources.xml +++ b/b2g/config/emulator-kk/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator/sources.xml b/b2g/config/emulator/sources.xml index 8df3d0248551..08f9699d0554 100644 --- a/b2g/config/emulator/sources.xml +++ b/b2g/config/emulator/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/flame-kk/sources.xml b/b2g/config/flame-kk/sources.xml index 4ca1b3822b17..6f748f41b8de 100644 --- a/b2g/config/flame-kk/sources.xml +++ b/b2g/config/flame-kk/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/flame/sources.xml b/b2g/config/flame/sources.xml index 5344147e21d0..cb27a1a0b6dc 100644 --- a/b2g/config/flame/sources.xml +++ b/b2g/config/flame/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index e40258533106..c85110c777b8 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -1,9 +1,9 @@ { "git": { - "git_revision": "0244121522343877d65a69377226a836688e3004", + "git_revision": "a0e71e61922bde009a3b6714cbe965021d3279f1", "remote": "https://git.mozilla.org/releases/gaia.git", "branch": "" }, - "revision": "33d9ca589ca27af7f98b29b97a232ca599f111fe", + "revision": "0829f98e30c6f7795f66052d32e25456eb54bda5", "repo_path": "integration/gaia-central" } diff --git a/b2g/config/nexus-4/sources.xml b/b2g/config/nexus-4/sources.xml index 163972b68a6c..6efea03d0359 100644 --- a/b2g/config/nexus-4/sources.xml +++ b/b2g/config/nexus-4/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/nexus-5-l/sources.xml b/b2g/config/nexus-5-l/sources.xml index dff4eb545101..75f5f8292c90 100644 --- a/b2g/config/nexus-5-l/sources.xml +++ b/b2g/config/nexus-5-l/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/browser/modules/ProcessHangMonitor.jsm b/browser/modules/ProcessHangMonitor.jsm index a175e1d1f6b8..c12964aaa8a9 100644 --- a/browser/modules/ProcessHangMonitor.jsm +++ b/browser/modules/ProcessHangMonitor.jsm @@ -278,6 +278,17 @@ let ProcessHangMonitor = { return; } + // On e10s this counts slow-script/hanged-plugin notice only once. + // This code is not reached on non-e10s. + if (report.hangType == report.SLOW_SCRIPT) { + // On non-e10s, SLOW_SCRIPT_NOTICE_COUNT is probed at nsGlobalWindow.cpp + Services.telemetry.getHistogramById("SLOW_SCRIPT_NOTICE_COUNT").add(); + } else if (report.hangType == report.PLUGIN_HANG) { + // On non-e10s we have sufficient plugin telemetry probes, + // so PLUGIN_HANG_NOTICE_COUNT is only probed on e10s. + Services.telemetry.getHistogramById("PLUGIN_HANG_NOTICE_COUNT").add(); + } + // Otherwise create a new timer and display the report. let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); timer.initWithCallback(this, HANG_EXPIRATION_TIME, timer.TYPE_ONE_SHOT); diff --git a/build/sanitizers/lsan_suppressions.txt b/build/sanitizers/lsan_suppressions.txt index 9b7d6029e2cb..78865b3049a1 100644 --- a/build/sanitizers/lsan_suppressions.txt +++ b/build/sanitizers/lsan_suppressions.txt @@ -46,7 +46,7 @@ leak:mozilla::TransportLayerDtls::Setup ### # Bug 981195 - Small leak in the parser. m4 -leak:TypeCompartment::fixObjectGroup +leak:ObjectGroup::fixPlainObjectGroup # Bug 982111 - WebM is leaking. m1 leak:nestegg_read_packet diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index 671609e4ef8e..3a621a03996d 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -3078,8 +3078,14 @@ nsGlobalWindow::PreHandleEvent(EventChainPreVisitor& aVisitor) gEntropyCollector->RandomUpdate((void*)&(aVisitor.mEvent->time), sizeof(uint32_t)); } - } else if (msg == NS_RESIZE_EVENT) { - mIsHandlingResizeEvent = true; + } else if (msg == NS_RESIZE_EVENT && aVisitor.mEvent->mFlags.mIsTrusted) { + // QIing to window so that we can keep the old behavior also in case + // a child window is handling resize. + nsCOMPtr window = + do_QueryInterface(aVisitor.mEvent->originalTarget); + if (window) { + mIsHandlingResizeEvent = true; + } } else if (msg == NS_MOUSE_BUTTON_DOWN && aVisitor.mEvent->mFlags.mIsTrusted) { gMouseDown = true; @@ -7259,7 +7265,7 @@ nsGlobalWindow::ScrollTo(const CSSIntPoint& aScroll, NS_IMETHODIMP nsGlobalWindow::ScrollBy(int32_t aXScrollDif, int32_t aYScrollDif) { - ScrollBy(aXScrollDif, aYScrollDif); + ScrollBy(double(aXScrollDif), double(aYScrollDif)); return NS_OK; } @@ -11018,6 +11024,10 @@ nsGlobalWindow::ShowSlowScriptDialog() return ContinueSlowScriptAndKeepNotifying; } + // Reached only on non-e10s - once per slow script dialog. + // On e10s - we probe once at ProcessHangsMonitor.jsm + Telemetry::Accumulate(Telemetry::SLOW_SCRIPT_NOTICE_COUNT, 1); + // Get the nsIPrompt interface from the docshell nsCOMPtr ds = GetDocShell(); NS_ENSURE_TRUE(ds, KillSlowScript); diff --git a/dom/base/nsIImageLoadingContent.idl b/dom/base/nsIImageLoadingContent.idl index 3c3594054e16..f477b67ebf31 100644 --- a/dom/base/nsIImageLoadingContent.idl +++ b/dom/base/nsIImageLoadingContent.idl @@ -37,7 +37,7 @@ interface nsIFrame; * interface to mirror this interface when changing it. */ -[scriptable, builtinclass, uuid(ce098f6c-baca-4178-a9aa-266e8bfe509b)] +[scriptable, builtinclass, uuid(5794d12b-3195-4526-a814-a2181f6c71fe)] interface nsIImageLoadingContent : imgINotificationObserver { /** @@ -170,9 +170,17 @@ interface nsIImageLoadingContent : imgINotificationObserver /** * A visible count is stored, if it is non-zero then this image is considered - * visible. These methods increment, decrement, or return the visible coount. + * visible. These methods increment, decrement, or return the visible count. + * + * @param aNonvisibleAction What to do if the image's visibility count is now + * zero. If ON_NONVISIBLE_NO_ACTION, nothing will be + * done. If ON_NONVISIBLE_REQUEST_DISCARD, the image + * will be asked to discard its surfaces if possible. */ [noscript, notxpcom] void IncrementVisibleCount(); - [noscript, notxpcom] void DecrementVisibleCount(); + [noscript, notxpcom] void DecrementVisibleCount(in uint32_t aNonvisibleAction); [noscript, notxpcom] uint32_t GetVisibleCount(); + + const long ON_NONVISIBLE_NO_ACTION = 0; + const long ON_NONVISIBLE_REQUEST_DISCARD = 1; }; diff --git a/dom/base/nsImageLoadingContent.cpp b/dom/base/nsImageLoadingContent.cpp index 3cb7700b3124..c1157d3d4d48 100644 --- a/dom/base/nsImageLoadingContent.cpp +++ b/dom/base/nsImageLoadingContent.cpp @@ -105,8 +105,8 @@ nsImageLoadingContent::DestroyImageLoadingContent() { // Cancel our requests so they won't hold stale refs to us // NB: Don't ask to discard the images here. - ClearCurrentRequest(NS_BINDING_ABORTED, 0); - ClearPendingRequest(NS_BINDING_ABORTED, 0); + ClearCurrentRequest(NS_BINDING_ABORTED, ON_NONVISIBLE_NO_ACTION); + ClearPendingRequest(NS_BINDING_ABORTED, ON_NONVISIBLE_NO_ACTION); } nsImageLoadingContent::~nsImageLoadingContent() @@ -554,7 +554,7 @@ nsImageLoadingContent::FrameDestroyed(nsIFrame* aFrame) if (aFrame->HasAnyStateBits(NS_FRAME_IN_POPUP)) { // We assume all images in popups are visible, so this decrement balances // out the increment in FrameCreated above. - DecrementVisibleCount(); + DecrementVisibleCount(ON_NONVISIBLE_NO_ACTION); } } @@ -777,14 +777,14 @@ nsImageLoadingContent::IncrementVisibleCount() } void -nsImageLoadingContent::DecrementVisibleCount() +nsImageLoadingContent::DecrementVisibleCount(uint32_t aNonvisibleAction) { NS_ASSERTION(mVisibleCount > 0, "visible count should be positive here"); mVisibleCount--; if (mVisibleCount == 0) { - UntrackImage(mCurrentRequest); - UntrackImage(mPendingRequest); + UntrackImage(mCurrentRequest, aNonvisibleAction); + UntrackImage(mPendingRequest, aNonvisibleAction); } } @@ -1096,8 +1096,8 @@ void nsImageLoadingContent::CancelImageRequests(bool aNotify) { AutoStateChanger changer(this, aNotify); - ClearPendingRequest(NS_BINDING_ABORTED, REQUEST_DISCARD); - ClearCurrentRequest(NS_BINDING_ABORTED, REQUEST_DISCARD); + ClearPendingRequest(NS_BINDING_ABORTED, ON_NONVISIBLE_REQUEST_DISCARD); + ClearCurrentRequest(NS_BINDING_ABORTED, ON_NONVISIBLE_REQUEST_DISCARD); } nsresult @@ -1109,8 +1109,8 @@ nsImageLoadingContent::UseAsPrimaryRequest(imgRequestProxy* aRequest, AutoStateChanger changer(this, aNotify); // Get rid if our existing images - ClearPendingRequest(NS_BINDING_ABORTED, REQUEST_DISCARD); - ClearCurrentRequest(NS_BINDING_ABORTED, REQUEST_DISCARD); + ClearPendingRequest(NS_BINDING_ABORTED, ON_NONVISIBLE_REQUEST_DISCARD); + ClearCurrentRequest(NS_BINDING_ABORTED, ON_NONVISIBLE_REQUEST_DISCARD); // Clone the request we were given. nsRefPtr& req = PrepareNextRequest(aImageLoadType); @@ -1227,7 +1227,7 @@ nsImageLoadingContent::SetBlockedRequest(nsIURI* aURI, int16_t aContentDecision) // reason "image source changed". However, apparently there's some abuse // over in nsImageFrame where the displaying of the "broken" icon for the // next image depends on the cancel reason of the previous image. ugh. - ClearPendingRequest(NS_ERROR_IMAGE_BLOCKED, REQUEST_DISCARD); + ClearPendingRequest(NS_ERROR_IMAGE_BLOCKED, ON_NONVISIBLE_REQUEST_DISCARD); // For the blocked case, we only want to cancel the existing current request // if size is not available. bz says the web depends on this behavior. @@ -1235,7 +1235,7 @@ nsImageLoadingContent::SetBlockedRequest(nsIURI* aURI, int16_t aContentDecision) mImageBlockingStatus = aContentDecision; uint32_t keepFlags = mCurrentRequestFlags & REQUEST_IS_IMAGESET; - ClearCurrentRequest(NS_ERROR_IMAGE_BLOCKED, REQUEST_DISCARD); + ClearCurrentRequest(NS_ERROR_IMAGE_BLOCKED, ON_NONVISIBLE_REQUEST_DISCARD); // We still want to remember what URI we were and if it was an imageset, // despite not having an actual request. These are both cleared as part of @@ -1253,7 +1253,8 @@ nsImageLoadingContent::PrepareCurrentRequest(ImageLoadType aImageLoadType) mImageBlockingStatus = nsIContentPolicy::ACCEPT; // Get rid of anything that was there previously. - ClearCurrentRequest(NS_ERROR_IMAGE_SRC_CHANGED, REQUEST_DISCARD); + ClearCurrentRequest(NS_ERROR_IMAGE_SRC_CHANGED, + ON_NONVISIBLE_REQUEST_DISCARD); if (mNewRequestsWillNeedAnimationReset) { mCurrentRequestFlags |= REQUEST_NEEDS_ANIMATION_RESET; @@ -1271,7 +1272,8 @@ nsRefPtr& nsImageLoadingContent::PreparePendingRequest(ImageLoadType aImageLoadType) { // Get rid of anything that was there previously. - ClearPendingRequest(NS_ERROR_IMAGE_SRC_CHANGED, REQUEST_DISCARD); + ClearPendingRequest(NS_ERROR_IMAGE_SRC_CHANGED, + ON_NONVISIBLE_REQUEST_DISCARD); if (mNewRequestsWillNeedAnimationReset) { mPendingRequestFlags |= REQUEST_NEEDS_ANIMATION_RESET; @@ -1336,7 +1338,7 @@ nsImageLoadingContent::MakePendingRequestCurrent() void nsImageLoadingContent::ClearCurrentRequest(nsresult aReason, - uint32_t aFlags) + uint32_t aNonvisibleAction) { if (!mCurrentRequest) { // Even if we didn't have a current request, we might have been keeping @@ -1354,7 +1356,7 @@ nsImageLoadingContent::ClearCurrentRequest(nsresult aReason, &mCurrentRequestRegistered); // Clean up the request. - UntrackImage(mCurrentRequest, aFlags); + UntrackImage(mCurrentRequest, aNonvisibleAction); mCurrentRequest->CancelAndForgetObserver(aReason); mCurrentRequest = nullptr; mCurrentRequestFlags = 0; @@ -1362,7 +1364,7 @@ nsImageLoadingContent::ClearCurrentRequest(nsresult aReason, void nsImageLoadingContent::ClearPendingRequest(nsresult aReason, - uint32_t aFlags) + uint32_t aNonvisibleAction) { if (!mPendingRequest) return; @@ -1372,7 +1374,7 @@ nsImageLoadingContent::ClearPendingRequest(nsresult aReason, nsLayoutUtils::DeregisterImageRequest(GetFramePresContext(), mPendingRequest, &mPendingRequestRegistered); - UntrackImage(mPendingRequest, aFlags); + UntrackImage(mPendingRequest, aNonvisibleAction); mPendingRequest->CancelAndForgetObserver(aReason); mPendingRequest = nullptr; mPendingRequestFlags = 0; @@ -1472,7 +1474,9 @@ nsImageLoadingContent::TrackImage(imgIRequest* aImage) } void -nsImageLoadingContent::UntrackImage(imgIRequest* aImage, uint32_t aFlags /* = 0 */) +nsImageLoadingContent::UntrackImage(imgIRequest* aImage, + uint32_t aNonvisibleAction + /* = ON_NONVISIBLE_NO_ACTION */) { if (!aImage) return; @@ -1489,9 +1493,10 @@ nsImageLoadingContent::UntrackImage(imgIRequest* aImage, uint32_t aFlags /* = 0 if (doc && (mCurrentRequestFlags & REQUEST_IS_TRACKED)) { mCurrentRequestFlags &= ~REQUEST_IS_TRACKED; doc->RemoveImage(mCurrentRequest, - (aFlags & REQUEST_DISCARD) ? nsIDocument::REQUEST_DISCARD : 0); - } - else if (aFlags & REQUEST_DISCARD) { + (aNonvisibleAction == ON_NONVISIBLE_REQUEST_DISCARD) + ? nsIDocument::REQUEST_DISCARD + : 0); + } else if (aNonvisibleAction == ON_NONVISIBLE_REQUEST_DISCARD) { // If we're not in the document we may still need to be discarded. aImage->RequestDiscard(); } @@ -1500,9 +1505,10 @@ nsImageLoadingContent::UntrackImage(imgIRequest* aImage, uint32_t aFlags /* = 0 if (doc && (mPendingRequestFlags & REQUEST_IS_TRACKED)) { mPendingRequestFlags &= ~REQUEST_IS_TRACKED; doc->RemoveImage(mPendingRequest, - (aFlags & REQUEST_DISCARD) ? nsIDocument::REQUEST_DISCARD : 0); - } - else if (aFlags & REQUEST_DISCARD) { + (aNonvisibleAction == ON_NONVISIBLE_REQUEST_DISCARD) + ? nsIDocument::REQUEST_DISCARD + : 0); + } else if (aNonvisibleAction == ON_NONVISIBLE_REQUEST_DISCARD) { // If we're not in the document we may still need to be discarded. aImage->RequestDiscard(); } diff --git a/dom/base/nsImageLoadingContent.h b/dom/base/nsImageLoadingContent.h index 97b8c54dfd67..6719ffed6f64 100644 --- a/dom/base/nsImageLoadingContent.h +++ b/dom/base/nsImageLoadingContent.h @@ -316,9 +316,12 @@ protected: /** * Cancels and nulls-out the "current" and "pending" requests if they exist. + * + * @param aNonvisibleAction An action to take if the image is no longer + * visible as a result; see |UntrackImage|. */ - void ClearCurrentRequest(nsresult aReason, uint32_t aFlags); - void ClearPendingRequest(nsresult aReason, uint32_t aFlags); + void ClearCurrentRequest(nsresult aReason, uint32_t aNonvisibleAction); + void ClearPendingRequest(nsresult aReason, uint32_t aNonvisibleAction); /** * Retrieve a pointer to the 'registered with the refresh driver' flag for @@ -347,14 +350,14 @@ protected: * * No-op if aImage is null. * - * REQUEST_DISCARD passed to UntrackImage means we request the discard of the - * decoded data of the image. + * @param aNonvisibleAction What to do if the image's visibility count is now + * zero. If ON_NONVISIBLE_NO_ACTION, nothing will be + * done. If ON_NONVISIBLE_REQUEST_DISCARD, the image + * will be asked to discard its surfaces if possible. */ void TrackImage(imgIRequest* aImage); - enum { - REQUEST_DISCARD = 0x1 - }; - void UntrackImage(imgIRequest* aImage, uint32_t aFlags = 0); + void UntrackImage(imgIRequest* aImage, + uint32_t aNonvisibleAction = ON_NONVISIBLE_NO_ACTION); /* MEMBERS */ nsRefPtr mCurrentRequest; diff --git a/dom/base/test/mochitest.ini b/dom/base/test/mochitest.ini index 4895b6c5ea1e..d84fc5cf1212 100644 --- a/dom/base/test/mochitest.ini +++ b/dom/base/test/mochitest.ini @@ -772,4 +772,6 @@ skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s [test_window_define_nonconfigurable.html] skip-if = true # bug 1107443 - code for newly-added test was disabled [test_root_iframe.html] -[test_performance_user_timing.html] \ No newline at end of file +[test_performance_user_timing.html] +[test_bug1126851.html] +skip-if = buildapp == 'mulet' || buildapp == 'b2g' \ No newline at end of file diff --git a/dom/base/test/test_bug1126851.html b/dom/base/test/test_bug1126851.html new file mode 100644 index 000000000000..d6d7bf4a2206 --- /dev/null +++ b/dom/base/test/test_bug1126851.html @@ -0,0 +1,44 @@ + + + + + + Test for Bug 1126851 + + + + + +Mozilla Bug 1126851 +

+ +
+
+ + diff --git a/dom/broadcastchannel/BroadcastChannel.cpp b/dom/broadcastchannel/BroadcastChannel.cpp index fbd103267b95..96b7c35123d8 100644 --- a/dom/broadcastchannel/BroadcastChannel.cpp +++ b/dom/broadcastchannel/BroadcastChannel.cpp @@ -195,13 +195,13 @@ private: ErrorResult& mRv; }; -class PostMessageRunnable MOZ_FINAL : public nsICancelableRunnable +class BCPostMessageRunnable MOZ_FINAL : public nsICancelableRunnable { public: NS_DECL_ISUPPORTS - PostMessageRunnable(BroadcastChannelChild* aActor, - BroadcastChannelMessage* aData) + BCPostMessageRunnable(BroadcastChannelChild* aActor, + BroadcastChannelMessage* aData) : mActor(aActor) , mData(aData) { @@ -249,13 +249,13 @@ public: } private: - ~PostMessageRunnable() {} + ~BCPostMessageRunnable() {} nsRefPtr mActor; nsRefPtr mData; }; -NS_IMPL_ISUPPORTS(PostMessageRunnable, nsICancelableRunnable, nsIRunnable) +NS_IMPL_ISUPPORTS(BCPostMessageRunnable, nsICancelableRunnable, nsIRunnable) class CloseRunnable MOZ_FINAL : public nsICancelableRunnable { @@ -559,8 +559,8 @@ void BroadcastChannel::PostMessageData(BroadcastChannelMessage* aData) { if (mActor) { - nsRefPtr runnable = - new PostMessageRunnable(mActor, aData); + nsRefPtr runnable = + new BCPostMessageRunnable(mActor, aData); if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) { NS_WARNING("Failed to dispatch to the current thread!"); diff --git a/dom/broadcastchannel/moz.build b/dom/broadcastchannel/moz.build index 01c795f8743e..6ff7e04872cf 100644 --- a/dom/broadcastchannel/moz.build +++ b/dom/broadcastchannel/moz.build @@ -8,7 +8,7 @@ EXPORTS.mozilla.dom += [ 'BroadcastChannel.h', ] -SOURCES += [ +UNIFIED_SOURCES += [ 'BroadcastChannel.cpp', 'BroadcastChannelChild.cpp', 'BroadcastChannelParent.cpp', diff --git a/dom/browser-element/mochitest/browserElement_Alert.js b/dom/browser-element/mochitest/browserElement_Alert.js index 269996bb5174..3c7c507205e8 100644 --- a/dom/browser-element/mochitest/browserElement_Alert.js +++ b/dom/browser-element/mochitest/browserElement_Alert.js @@ -109,7 +109,10 @@ function test3(e) { numPendingChildTests++; onTryAgain(); - waitForPendingTests(function() { test4(); }); + waitForPendingTests(function() { + mm.removeMessageListener('test-try-again', onTryAgain); + test4(); + }); } function test4() { @@ -240,7 +243,10 @@ function test6f() { numPendingChildTests++; onTryAgain(); - waitForPendingTests(test6g); + waitForPendingTests(function() { + mm.removeMessageListener('test-try-again', onTryAgain); + test6g(); + }); } function test6g() { @@ -269,7 +275,10 @@ function test6g() { numPendingChildTests++; onTryAgain(); - waitForPendingTests(test6h); + waitForPendingTests(function() { + mm.removeMessageListener('test-try-again', onTryAgain); + test6h(); + }); } function test6h() { diff --git a/dom/canvas/WebGLContext.cpp b/dom/canvas/WebGLContext.cpp index 38420ff232e2..749621074af9 100644 --- a/dom/canvas/WebGLContext.cpp +++ b/dom/canvas/WebGLContext.cpp @@ -505,7 +505,7 @@ IsFeatureInBlacklist(const nsCOMPtr& gfxInfo, int32_t feature) static already_AddRefed CreateHeadlessNativeGL(bool forceEnabled, const nsCOMPtr& gfxInfo, - bool requireCompatProfile, WebGLContext* webgl) + WebGLContext* webgl) { if (!forceEnabled && IsFeatureInBlacklist(gfxInfo, nsIGfxInfo::FEATURE_WEBGL_OPENGL)) @@ -515,7 +515,7 @@ CreateHeadlessNativeGL(bool forceEnabled, const nsCOMPtr& gfxInfo, return nullptr; } - nsRefPtr gl = gl::GLContextProvider::CreateHeadless(requireCompatProfile); + nsRefPtr gl = gl::GLContextProvider::CreateHeadless(); if (!gl) { webgl->GenerateWarning("Error during native OpenGL init."); return nullptr; @@ -530,7 +530,7 @@ CreateHeadlessNativeGL(bool forceEnabled, const nsCOMPtr& gfxInfo, // Eventually, we want to be able to pick ANGLE-EGL or native EGL. static already_AddRefed CreateHeadlessANGLE(bool forceEnabled, const nsCOMPtr& gfxInfo, - bool requireCompatProfile, WebGLContext* webgl) + WebGLContext* webgl) { nsRefPtr gl; @@ -543,7 +543,7 @@ CreateHeadlessANGLE(bool forceEnabled, const nsCOMPtr& gfxInfo, return nullptr; } - gl = gl::GLContextProviderEGL::CreateHeadless(requireCompatProfile); + gl = gl::GLContextProviderEGL::CreateHeadless(); if (!gl) { webgl->GenerateWarning("Error during ANGLE OpenGL init."); return nullptr; @@ -555,13 +555,13 @@ CreateHeadlessANGLE(bool forceEnabled, const nsCOMPtr& gfxInfo, } static already_AddRefed -CreateHeadlessEGL(bool forceEnabled, bool requireCompatProfile, +CreateHeadlessEGL(bool forceEnabled, const nsCOMPtr& gfxInfo, WebGLContext* webgl) { nsRefPtr gl; #ifdef ANDROID - gl = gl::GLContextProviderEGL::CreateHeadless(requireCompatProfile); + gl = gl::GLContextProviderEGL::CreateHeadless(); if (!gl) { webgl->GenerateWarning("Error during EGL OpenGL init."); return nullptr; @@ -583,22 +583,16 @@ CreateHeadlessGL(bool forceEnabled, const nsCOMPtr& gfxInfo, if (PR_GetEnv("MOZ_WEBGL_FORCE_OPENGL")) disableANGLE = true; - bool requireCompatProfile = webgl->IsWebGL2() ? false : true; - nsRefPtr gl; if (preferEGL) - gl = CreateHeadlessEGL(forceEnabled, requireCompatProfile, webgl); + gl = CreateHeadlessEGL(forceEnabled, gfxInfo, webgl); - if (!gl && !disableANGLE) { - gl = CreateHeadlessANGLE(forceEnabled, gfxInfo, requireCompatProfile, - webgl); - } + if (!gl && !disableANGLE) + gl = CreateHeadlessANGLE(forceEnabled, gfxInfo, webgl); - if (!gl) { - gl = CreateHeadlessNativeGL(forceEnabled, gfxInfo, - requireCompatProfile, webgl); - } + if (!gl) + gl = CreateHeadlessNativeGL(forceEnabled, gfxInfo, webgl); return gl.forget(); } diff --git a/dom/canvas/WebGLContext.h b/dom/canvas/WebGLContext.h index fdaada5f80b0..dabdd737782f 100644 --- a/dom/canvas/WebGLContext.h +++ b/dom/canvas/WebGLContext.h @@ -1209,10 +1209,9 @@ protected: // ------------------------------------------------------------------------- // WebGL 2 specifics (implemented in WebGL2Context.cpp) -public: + virtual bool IsWebGL2() const = 0; -protected: bool InitWebGL2(); // ------------------------------------------------------------------------- diff --git a/dom/canvas/WebGLContextGL.cpp b/dom/canvas/WebGLContextGL.cpp index 1e4d4c1b370a..999b71b67e42 100644 --- a/dom/canvas/WebGLContextGL.cpp +++ b/dom/canvas/WebGLContextGL.cpp @@ -2106,6 +2106,7 @@ WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width, // if we're reading alpha, we may need to do fixup. Note that we don't allow // GL_ALPHA to readpixels currently, but we had the code written for it already. + const bool formatHasAlpha = format == LOCAL_GL_ALPHA || format == LOCAL_GL_RGBA; if (!formatHasAlpha) diff --git a/dom/canvas/WebGLContextUtils.cpp b/dom/canvas/WebGLContextUtils.cpp index caba53cac181..b0e02a185770 100644 --- a/dom/canvas/WebGLContextUtils.cpp +++ b/dom/canvas/WebGLContextUtils.cpp @@ -1117,12 +1117,11 @@ WebGLContext::AssertCachedState() AssertUintParamCorrect(gl, LOCAL_GL_STENCIL_CLEAR_VALUE, mStencilClearValue); GLint stencilBits = 0; - if (GetStencilBits(&stencilBits)) { - const GLuint stencilRefMask = (1 << stencilBits) - 1; + gl->fGetIntegerv(LOCAL_GL_STENCIL_BITS, &stencilBits); + const GLuint stencilRefMask = (1 << stencilBits) - 1; - AssertMaskedUintParamCorrect(gl, LOCAL_GL_STENCIL_REF, stencilRefMask, mStencilRefFront); - AssertMaskedUintParamCorrect(gl, LOCAL_GL_STENCIL_BACK_REF, stencilRefMask, mStencilRefBack); - } + AssertMaskedUintParamCorrect(gl, LOCAL_GL_STENCIL_REF, stencilRefMask, mStencilRefFront); + AssertMaskedUintParamCorrect(gl, LOCAL_GL_STENCIL_BACK_REF, stencilRefMask, mStencilRefBack); // GLES 3.0.4, $4.1.4, p177: // [...] the front and back stencil mask are both set to the value `2^s - 1`, where diff --git a/dom/canvas/WebGLContextValidate.cpp b/dom/canvas/WebGLContextValidate.cpp index e942610793b9..1dbc000a1416 100644 --- a/dom/canvas/WebGLContextValidate.cpp +++ b/dom/canvas/WebGLContextValidate.cpp @@ -1779,8 +1779,8 @@ WebGLContext::InitAndValidateGL() MakeContextCurrent(); - // For OpenGL compat. profiles, we always keep vertex attrib 0 array enabled. - if (gl->IsCompatibilityProfile()) + // on desktop OpenGL, we always keep vertex attrib 0 array enabled + if (!gl->IsGLES()) gl->fEnableVertexAttribArray(0); if (MinCapabilityMode()) @@ -1889,7 +1889,7 @@ WebGLContext::InitAndValidateGL() // Always 1 for GLES2 mMaxFramebufferColorAttachments = 1; - if (gl->IsCompatibilityProfile()) { + if (!gl->IsGLES()) { // gl_PointSize is always available in ES2 GLSL, but has to be // specifically enabled on desktop GLSL. gl->fEnable(LOCAL_GL_VERTEX_PROGRAM_POINT_SIZE); diff --git a/dom/events/EventStateManager.cpp b/dom/events/EventStateManager.cpp index 90935dcbc96e..0d4e91df2a1c 100644 --- a/dom/events/EventStateManager.cpp +++ b/dom/events/EventStateManager.cpp @@ -481,32 +481,9 @@ EventStateManager::PreHandleEvent(nsPresContext* aPresContext, mCurrentTarget = aTargetFrame; mCurrentTargetContent = nullptr; - // Focus events don't necessarily need a frame. - if (NS_EVENT_NEEDS_FRAME(aEvent)) { - NS_ASSERTION(mCurrentTarget, "mCurrentTarget is null. this should not happen. see bug #13007"); - if (!mCurrentTarget) return NS_ERROR_NULL_POINTER; - } -#ifdef DEBUG - if (aEvent->HasDragEventMessage() && sIsPointerLocked) { - NS_ASSERTION(sIsPointerLocked, - "sIsPointerLocked is true. Drag events should be suppressed when the pointer is locked."); - } -#endif - // Store last known screenPoint and clientPoint so pointer lock - // can use these values as constants. - WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent(); - if (aEvent->mFlags.mIsTrusted && - ((mouseEvent && mouseEvent->IsReal()) || - aEvent->mClass == eWheelEventClass) && - !sIsPointerLocked) { - sLastScreenPoint = - UIEvent::CalculateScreenPoint(aPresContext, aEvent); - sLastClientPoint = - UIEvent::CalculateClientPoint(aPresContext, aEvent, nullptr); - } - // Do not take account NS_MOUSE_ENTER/EXIT so that loading a page // when user is not active doesn't change the state to active. + WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent(); if (aEvent->mFlags.mIsTrusted && ((mouseEvent && mouseEvent->IsReal() && mouseEvent->message != NS_MOUSE_ENTER && @@ -524,10 +501,35 @@ EventStateManager::PreHandleEvent(nsPresContext* aPresContext, ++gMouseOrKeyboardEventCounter; } - *aStatus = nsEventStatus_eIgnore; - WheelTransaction::OnEvent(aEvent); + // Focus events don't necessarily need a frame. + if (NS_EVENT_NEEDS_FRAME(aEvent)) { + if (!mCurrentTarget) { + return NS_ERROR_NULL_POINTER; + } + } +#ifdef DEBUG + if (aEvent->HasDragEventMessage() && sIsPointerLocked) { + NS_ASSERTION(sIsPointerLocked, + "sIsPointerLocked is true. Drag events should be suppressed when " + "the pointer is locked."); + } +#endif + // Store last known screenPoint and clientPoint so pointer lock + // can use these values as constants. + if (aEvent->mFlags.mIsTrusted && + ((mouseEvent && mouseEvent->IsReal()) || + aEvent->mClass == eWheelEventClass) && + !sIsPointerLocked) { + sLastScreenPoint = + UIEvent::CalculateScreenPoint(aPresContext, aEvent); + sLastClientPoint = + UIEvent::CalculateClientPoint(aPresContext, aEvent, nullptr); + } + + *aStatus = nsEventStatus_eIgnore; + switch (aEvent->message) { case NS_CONTEXTMENU: if (sIsPointerLocked) { diff --git a/dom/events/MessageEvent.cpp b/dom/events/MessageEvent.cpp index b76093ddc16d..876e99b201e2 100644 --- a/dom/events/MessageEvent.cpp +++ b/dom/events/MessageEvent.cpp @@ -200,6 +200,12 @@ MessageEvent::SetPorts(MessagePortList* aPorts) mPorts = aPorts; } +void +MessageEvent::SetSource(mozilla::dom::MessagePort* aPort) +{ + mPortSource = aPort; +} + } // namespace dom } // namespace mozilla diff --git a/dom/events/MessageEvent.h b/dom/events/MessageEvent.h index 44262455c20f..2dc8e35a5c04 100644 --- a/dom/events/MessageEvent.h +++ b/dom/events/MessageEvent.h @@ -58,10 +58,7 @@ public: void SetPorts(MessagePortList* aPorts); // Non WebIDL methods - void SetSource(mozilla::dom::MessagePort* aPort) - { - mPortSource = aPort; - } + void SetSource(mozilla::dom::MessagePort* aPort); void SetSource(nsPIDOMWindow* aWindow) { diff --git a/dom/html/test/test_bug1081037.html b/dom/html/test/test_bug1081037.html index 783e8557c561..9d878258023d 100644 --- a/dom/html/test/test_bug1081037.html +++ b/dom/html/test/test_bug1081037.html @@ -96,7 +96,6 @@ shouldThrow(function() { var getOwn = 0; var defineProp = 0; -var _has = 0; var handler2 = { getOwnPropertyDescriptor: function(target, name) { if (name == "constructor") { @@ -109,12 +108,6 @@ var handler2 = { defineProp++; } return Object.defineProperty(target, name, propertyDescriptor); - }, - has: function(target, name) { - if (name == "constructor") { - _has++; - } - return name in target; } }; var proxy2 = new Proxy({}, handler2); @@ -125,8 +118,6 @@ document.registerElement('x-proxymagic2', { is(getOwn, 1, "number of getOwnPropertyDescriptor calls from registerElement: " + getOwn); is(defineProp, 1, "number of defineProperty calls from registerElement: " + defineProp); -is(_has, 1, "number of 'has' calls from registerElement: " + _has); - @@ -139,4 +130,4 @@ is(_has, 1, "number of 'has' calls from registerElement: " + _has);
 
- \ No newline at end of file + diff --git a/dom/ipc/TabParent.cpp b/dom/ipc/TabParent.cpp index 218ab3e43408..021fd95475c0 100644 --- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -2419,6 +2419,17 @@ TabParent::MaybeForwardEventToRenderFrame(WidgetInputEvent& aEvent, // id. if (aOutTargetGuid) { *aOutTargetGuid = InputAPZContext::GetTargetLayerGuid(); + + // There may be cases where the APZ hit-testing code came to a different + // conclusion than the main-thread hit-testing code as to where the event + // is destined. In such cases the layersId of the APZ result may not match + // the layersId of this renderframe. In such cases the main-thread hit- + // testing code "wins" so we need to update the guid to reflect this. + if (RenderFrameParent* rfp = GetRenderFrame()) { + if (aOutTargetGuid->mLayersId != rfp->GetLayersId()) { + *aOutTargetGuid = ScrollableLayerGuid(rfp->GetLayersId(), 0, FrameMetrics::NULL_SCROLL_ID); + } + } } if (aOutInputBlockId) { *aOutInputBlockId = InputAPZContext::GetInputBlockId(); diff --git a/dom/media/fmp4/android/AndroidDecoderModule.cpp b/dom/media/fmp4/android/AndroidDecoderModule.cpp index bd00d07faa7f..84fbd4ef3f54 100644 --- a/dom/media/fmp4/android/AndroidDecoderModule.cpp +++ b/dom/media/fmp4/android/AndroidDecoderModule.cpp @@ -179,7 +179,7 @@ protected: return true; } - mGLContext = GLContextProvider::CreateHeadless(false); + mGLContext = GLContextProvider::CreateHeadless(); return mGLContext; } diff --git a/dom/plugins/base/nsNPAPIPluginInstance.cpp b/dom/plugins/base/nsNPAPIPluginInstance.cpp index 9f94d76639d1..811b75c31bf3 100644 --- a/dom/plugins/base/nsNPAPIPluginInstance.cpp +++ b/dom/plugins/base/nsNPAPIPluginInstance.cpp @@ -91,8 +91,9 @@ static nsRefPtr sPluginContext = nullptr; static bool EnsureGLContext() { if (!sPluginContext) { - bool requireCompatProfile = true; - sPluginContext = GLContextProvider::CreateHeadless(requireCompatProfile); + gfxIntSize dummySize(16, 16); + sPluginContext = GLContextProvider::CreateOffscreen(dummySize, + SurfaceCaps::Any()); } return sPluginContext != nullptr; diff --git a/dom/workers/ServiceWorkerEvents.cpp b/dom/workers/ServiceWorkerEvents.cpp index 74eb150d1766..2e53e4d6d598 100644 --- a/dom/workers/ServiceWorkerEvents.cpp +++ b/dom/workers/ServiceWorkerEvents.cpp @@ -8,7 +8,6 @@ #include "nsContentUtils.h" -#include "mozilla/dom/Promise.h" #include "mozilla/dom/WorkerScope.h" #include "mozilla/dom/workers/bindings/ServiceWorker.h" diff --git a/dom/workers/ServiceWorkerEvents.h b/dom/workers/ServiceWorkerEvents.h index 9c267260d99d..04f30ccef88c 100644 --- a/dom/workers/ServiceWorkerEvents.h +++ b/dom/workers/ServiceWorkerEvents.h @@ -9,12 +9,7 @@ #include "mozilla/dom/Event.h" #include "mozilla/dom/ExtendableEventBinding.h" #include "mozilla/dom/InstallEventBinding.h" - -namespace mozilla { -namespace dom { - class Promise; -} // namespace dom -} // namespace mozilla +#include "mozilla/dom/Promise.h" BEGIN_WORKERS_NAMESPACE diff --git a/gfx/gl/GLContext.cpp b/gfx/gl/GLContext.cpp index f84a47c54b02..b300ddff6085 100644 --- a/gfx/gl/GLContext.cpp +++ b/gfx/gl/GLContext.cpp @@ -8,7 +8,6 @@ #include #include #include -#include #include "GLContext.h" #include "GLBlitHelper.h" @@ -157,7 +156,8 @@ static const char *sExtensionNames[] = { "GL_OES_texture_half_float", "GL_OES_texture_half_float_linear", "GL_OES_texture_npot", - "GL_OES_vertex_array_object" + "GL_OES_vertex_array_object", + nullptr }; static bool @@ -501,8 +501,6 @@ GLContext::InitWithPrefix(const char *prefix, bool trygl) mInitialized = LoadSymbols(&symbols[0], trygl, prefix); MakeCurrent(); if (mInitialized) { - MOZ_ASSERT(mProfile != ContextProfile::Unknown); - uint32_t version = 0; ParseGLVersion(this, &version); @@ -658,15 +656,6 @@ GLContext::InitWithPrefix(const char *prefix, bool trygl) } } - if (IsFeatureProvidedByCoreSymbols(GLFeature::get_string_indexed)) { - SymLoadStruct moreSymbols[] = { - { (PRFuncPtr*) &mSymbols.fGetStringi, { "GetStringi", nullptr } }, - END_SYMBOLS - }; - - MOZ_ALWAYS_TRUE(LoadSymbols(moreSymbols, trygl, prefix)); - } - InitExtensions(); InitFeatures(); @@ -681,6 +670,12 @@ GLContext::InitWithPrefix(const char *prefix, bool trygl) MarkUnsupported(GLFeature::standard_derivatives); } + if (Vendor() == GLVendor::Imagination && + Renderer() == GLRenderer::SGX540) { + // Bug 980048 + MarkExtensionUnsupported(OES_EGL_sync); + } + if (Renderer() == GLRenderer::MicrosoftBasicRenderDriver) { // Bug 978966: on Microsoft's "Basic Render Driver" (software renderer) // multisampling hardcodes blending with the default blendfunc, which breaks WebGL. @@ -1473,13 +1468,10 @@ GLContext::InitWithPrefix(const char *prefix, bool trygl) // We're ready for final setup. fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0); - // TODO: Remove SurfaceCaps::any. - if (mCaps.any) { - mCaps.any = false; - mCaps.color = true; - mCaps.alpha = false; - } + if (mCaps.any) + DetermineCaps(); + UpdatePixelFormat(); UpdateGLFormats(mCaps); mTexGarbageBin = new TextureGarbageBin(this); @@ -1601,105 +1593,61 @@ GLContext::DebugCallback(GLenum source, void GLContext::InitExtensions() { - MOZ_ASSERT(IsCurrent()); + MakeCurrent(); + const char* extensions = (const char*)fGetString(LOCAL_GL_EXTENSIONS); + if (!extensions) + return; - std::vector driverExtensionList; + InitializeExtensionsBitSet(mAvailableExtensions, extensions, + sExtensionNames); - if (IsFeatureProvidedByCoreSymbols(GLFeature::get_string_indexed)) { - GLuint count = 0; - GetUIntegerv(LOCAL_GL_NUM_EXTENSIONS, &count); - for (GLuint i = 0; i < count; i++) { - // This is UTF-8. - const char* rawExt = (const char*)fGetStringi(LOCAL_GL_EXTENSIONS, i); + if (WorkAroundDriverBugs() && + Vendor() == GLVendor::Qualcomm) { - // We CANNOT use nsDependentCString here, because the spec doesn't guarantee - // that the pointers returned are different, only that their contents are. - // On Flame, each of these index string queries returns the same address. - driverExtensionList.push_back(nsCString(rawExt)); - } - } else { - MOZ_ALWAYS_TRUE(!fGetError()); - const char* rawExts = (const char*)fGetString(LOCAL_GL_EXTENSIONS); - MOZ_ALWAYS_TRUE(!fGetError()); - - if (rawExts) { - nsDependentCString exts(rawExts); - SplitByChar(exts, ' ', &driverExtensionList); - } + // Some Adreno drivers do not report GL_OES_EGL_sync, but they really do support it. + MarkExtensionSupported(OES_EGL_sync); } - const bool shouldDumpExts = ShouldDumpExts(); - if (shouldDumpExts) { - printf_stderr("%i GL driver extensions: (*: recognized)\n", - (uint32_t)driverExtensionList.size()); + if (WorkAroundDriverBugs() && + Renderer() == GLRenderer::AndroidEmulator) { + // the Android emulator, which we use to run B2G reftests on, + // doesn't expose the OES_rgb8_rgba8 extension, but it seems to + // support it (tautologically, as it only runs on desktop GL). + MarkExtensionSupported(OES_rgb8_rgba8); } - MarkBitfieldByStrings(driverExtensionList, shouldDumpExts, sExtensionNames, - &mAvailableExtensions); - - if (WorkAroundDriverBugs()) { - if (Vendor() == GLVendor::Qualcomm) { - // Some Adreno drivers do not report GL_OES_EGL_sync, but they really do support it. - MarkExtensionSupported(OES_EGL_sync); - } - - if (Vendor() == GLVendor::Imagination && - Renderer() == GLRenderer::SGX540) - { - // Bug 980048 - MarkExtensionUnsupported(OES_EGL_sync); - } - - if (Renderer() == GLRenderer::AndroidEmulator) { - // the Android emulator, which we use to run B2G reftests on, - // doesn't expose the OES_rgb8_rgba8 extension, but it seems to - // support it (tautologically, as it only runs on desktop GL). - MarkExtensionSupported(OES_rgb8_rgba8); - } - - if (Vendor() == GLVendor::VMware && - Renderer() == GLRenderer::GalliumLlvmpipe) - { - // The llvmpipe driver that is used on linux try servers appears to have - // buggy support for s3tc/dxt1 compressed textures. - // See Bug 975824. - MarkExtensionUnsupported(EXT_texture_compression_s3tc); - MarkExtensionUnsupported(EXT_texture_compression_dxt1); - MarkExtensionUnsupported(ANGLE_texture_compression_dxt3); - MarkExtensionUnsupported(ANGLE_texture_compression_dxt5); - } + if (WorkAroundDriverBugs() && + Vendor() == GLVendor::VMware && + Renderer() == GLRenderer::GalliumLlvmpipe) + { + // The llvmpipe driver that is used on linux try servers appears to have + // buggy support for s3tc/dxt1 compressed textures. + // See Bug 975824. + MarkExtensionUnsupported(EXT_texture_compression_s3tc); + MarkExtensionUnsupported(EXT_texture_compression_dxt1); + MarkExtensionUnsupported(ANGLE_texture_compression_dxt3); + MarkExtensionUnsupported(ANGLE_texture_compression_dxt5); + } #ifdef XP_MACOSX - // Bug 1009642: On OSX Mavericks (10.9), the driver for Intel HD - // 3000 appears to be buggy WRT updating sub-images of S3TC - // textures with glCompressedTexSubImage2D. Works on Intel HD 4000 - // and Intel HD 5000/Iris that I tested. - if (nsCocoaFeatures::OSXVersionMajor() == 10 && - nsCocoaFeatures::OSXVersionMinor() == 9 && - Renderer() == GLRenderer::IntelHD3000) - { - MarkExtensionUnsupported(EXT_texture_compression_s3tc); - } + // Bug 1009642: On OSX Mavericks (10.9), the driver for Intel HD + // 3000 appears to be buggy WRT updating sub-images of S3TC + // textures with glCompressedTexSubImage2D. Works on Intel HD 4000 + // and Intel HD 5000/Iris that I tested. + if (WorkAroundDriverBugs() && + nsCocoaFeatures::OSXVersionMajor() == 10 && + nsCocoaFeatures::OSXVersionMinor() == 9 && + Renderer() == GLRenderer::IntelHD3000) + { + MarkExtensionUnsupported(EXT_texture_compression_s3tc); + } #endif - } - - if (shouldDumpExts) { - printf_stderr("\nActivated extensions:\n"); - - for (size_t i = 0; i < mAvailableExtensions.size(); i++) { - if (!mAvailableExtensions[i]) - continue; - - const char* ext = sExtensionNames[i]; - printf_stderr("[%i] %s\n", (uint32_t)i, ext); - } - } } void GLContext::PlatformStartup() { - RegisterStrongMemoryReporter(new GfxTexturesReporter()); + RegisterStrongMemoryReporter(new GfxTexturesReporter()); } // Common code for checking for both GL extensions and GLX extensions. @@ -1740,6 +1688,66 @@ GLContext::ListHasExtension(const GLubyte *extensions, const char *extension) return false; } +void +GLContext::DetermineCaps() +{ + PixelBufferFormat format = QueryPixelFormat(); + + SurfaceCaps caps; + caps.color = !!format.red && !!format.green && !!format.blue; + caps.bpp16 = caps.color && format.ColorBits() == 16; + caps.alpha = !!format.alpha; + caps.depth = !!format.depth; + caps.stencil = !!format.stencil; + caps.antialias = format.samples > 1; + caps.preserve = true; + + mCaps = caps; +} + +PixelBufferFormat +GLContext::QueryPixelFormat() +{ + PixelBufferFormat format; + + ScopedBindFramebuffer autoFB(this, 0); + + fGetIntegerv(LOCAL_GL_RED_BITS , &format.red ); + fGetIntegerv(LOCAL_GL_GREEN_BITS, &format.green); + fGetIntegerv(LOCAL_GL_BLUE_BITS , &format.blue ); + fGetIntegerv(LOCAL_GL_ALPHA_BITS, &format.alpha); + + fGetIntegerv(LOCAL_GL_DEPTH_BITS, &format.depth); + fGetIntegerv(LOCAL_GL_STENCIL_BITS, &format.stencil); + + fGetIntegerv(LOCAL_GL_SAMPLES, &format.samples); + + return format; +} + +void +GLContext::UpdatePixelFormat() +{ + PixelBufferFormat format = QueryPixelFormat(); +#ifdef MOZ_GL_DEBUG + const SurfaceCaps& caps = Caps(); + MOZ_ASSERT(!caps.any, "Did you forget to DetermineCaps()?"); + + MOZ_ASSERT(caps.color == !!format.red); + MOZ_ASSERT(caps.color == !!format.green); + MOZ_ASSERT(caps.color == !!format.blue); + + // These we either must have if they're requested, or + // we can have if they're not. + MOZ_ASSERT(caps.alpha == !!format.alpha || !caps.alpha); + MOZ_ASSERT(caps.depth == !!format.depth || !caps.depth); + MOZ_ASSERT(caps.stencil == !!format.stencil || !caps.stencil); + + MOZ_ASSERT(caps.antialias == (format.samples > 1)); +#endif + mPixelFormat = new PixelBufferFormat(format); +} + GLFormats GLContext::ChooseGLFormats(const SurfaceCaps& caps) const { @@ -2409,13 +2417,6 @@ GLContext::FlushIfHeavyGLCallsSinceLastFlush() fFlush(); } -/*static*/ bool -GLContext::ShouldDumpExts() -{ - static bool ret = PR_GetEnv("MOZ_GL_DUMP_EXTS"); - return ret; -} - bool DoesStringMatch(const char* aString, const char *aWantedString) { @@ -2443,29 +2444,8 @@ DoesStringMatch(const char* aString, const char *aWantedString) /*static*/ bool GLContext::ShouldSpew() { - static bool ret = PR_GetEnv("MOZ_GL_SPEW"); - return ret; -} - -void -SplitByChar(const nsACString& str, const char delim, std::vector* const out) -{ - uint32_t start = 0; - while (true) { - int32_t end = str.FindChar(' ', start); - if (end == -1) - break; - - uint32_t len = (uint32_t)end - start; - nsDependentCSubstring substr(str, start, len); - out->push_back(nsCString(substr)); - - start = end + 1; - continue; - } - - nsDependentCSubstring substr(str, start); - out->push_back(nsCString(substr)); + static bool spew = PR_GetEnv("MOZ_GL_SPEW"); + return spew; } } /* namespace gl */ diff --git a/gfx/gl/GLContext.h b/gfx/gl/GLContext.h index cc6d8fcf2a42..9cc3a0906344 100644 --- a/gfx/gl/GLContext.h +++ b/gfx/gl/GLContext.h @@ -104,7 +104,6 @@ enum class GLFeature { get_integer_indexed, get_integer64_indexed, get_query_object_iv, - get_string_indexed, gpu_shader4, instanced_arrays, instanced_non_arrays, @@ -309,6 +308,7 @@ public: virtual bool IsCurrent() = 0; protected: + bool mInitialized; bool mIsOffscreen; bool mIsGlobalSharedContext; @@ -325,12 +325,9 @@ protected: GLVendor mVendor; GLRenderer mRenderer; - void SetProfileVersion(ContextProfile profile, uint32_t version) { - MOZ_ASSERT(!mInitialized, "SetProfileVersion can only be called before" - " initialization!"); - MOZ_ASSERT(profile != ContextProfile::Unknown && - profile != ContextProfile::OpenGL, - "Invalid `profile` for SetProfileVersion"); + inline void SetProfileVersion(ContextProfile profile, unsigned int version) { + MOZ_ASSERT(!mInitialized, "SetProfileVersion can only be called before initialization!"); + MOZ_ASSERT(profile != ContextProfile::Unknown && profile != ContextProfile::OpenGL, "Invalid `profile` for SetProfileVersion"); MOZ_ASSERT(version >= 100, "Invalid `version` for SetProfileVersion"); mVersion = version; @@ -460,7 +457,6 @@ public: return mAvailableExtensions[aKnownExtension]; } -protected: void MarkExtensionUnsupported(GLExtensions aKnownExtension) { mAvailableExtensions[aKnownExtension] = 0; } @@ -469,6 +465,42 @@ protected: mAvailableExtensions[aKnownExtension] = 1; } +public: + template + static void InitializeExtensionsBitSet(std::bitset& extensionsBitset, + const char* extStr, + const char** extList) + { + char* exts = ::strdup(extStr); + + if (ShouldSpew()) + printf_stderr("Extensions: %s\n", exts); + + char* cur = exts; + bool done = false; + while (!done) { + char* space = strchr(cur, ' '); + if (space) { + *space = '\0'; + } else { + done = true; + } + + for (int i = 0; extList[i]; ++i) { + if (PL_strcasecmp(cur, extList[i]) == 0) { + if (ShouldSpew()) + printf_stderr("Found extension %s\n", cur); + extensionsBitset[i] = true; + } + } + + cur = space + 1; + } + + free(exts); + } + +protected: std::bitset mAvailableExtensions; // ----------------------------------------------------------------------------- @@ -3148,17 +3180,6 @@ public: AFTER_GL_CALL; } -// ----------------------------------------------------------------------------- -// get_string_indexed - - const GLubyte* fGetStringi(GLenum name, GLuint index) { - BEFORE_GL_CALL; - ASSERT_SYMBOL_PRESENT(fGetStringi); - const GLubyte* ret = mSymbols.fGetStringi(name, index); - AFTER_GL_CALL; - return ret; - } - // ----------------------------------------------------------------------------- // Constructor protected: @@ -3427,9 +3448,11 @@ public: fViewport(0, 0, size.width, size.height); mCaps = mScreen->mCaps; - MOZ_ASSERT(!mCaps.any); + if (mCaps.any) + DetermineCaps(); UpdateGLFormats(mCaps); + UpdatePixelFormat(); return true; } @@ -3452,8 +3475,10 @@ public: protected: SurfaceCaps mCaps; nsAutoPtr mGLFormats; + nsAutoPtr mPixelFormat; public: + void DetermineCaps(); const SurfaceCaps& Caps() const { return mCaps; } @@ -3469,6 +3494,14 @@ public: return *mGLFormats; } + PixelBufferFormat QueryPixelFormat(); + void UpdatePixelFormat(); + + const PixelBufferFormat& GetPixelFormat() const { + MOZ_ASSERT(mPixelFormat); + return *mPixelFormat; + } + bool IsFramebufferComplete(GLuint fb, GLenum* status = nullptr); // Does not check completeness. @@ -3508,7 +3541,7 @@ public: } bool IsOffscreen() const { - return mIsOffscreen; + return !!mScreen; } GLScreenBuffer* Screen() const { @@ -3663,42 +3696,10 @@ protected: public: void FlushIfHeavyGLCallsSinceLastFlush(); static bool ShouldSpew(); - static bool ShouldDumpExts(); }; bool DoesStringMatch(const char* aString, const char *aWantedString); -void SplitByChar(const nsACString& str, const char delim, - std::vector* const out); - -template -bool -MarkBitfieldByString(const nsACString& str, const char* (&markStrList)[N], - std::bitset* const out_markList) -{ - for (size_t i = 0; i < N; i++) { - if (str.Equals(markStrList[i])) { - (*out_markList)[i] = 1; - return true; - } - } - return false; -} - -template -void -MarkBitfieldByStrings(const std::vector& strList, - bool dumpStrings, const char* (&markStrList)[N], - std::bitset* const out_markList) -{ - for (auto itr = strList.begin(); itr != strList.end(); ++itr) { - const nsACString& str = *itr; - const bool wasMarked = MarkBitfieldByString(str, markStrList, - out_markList); - if (dumpStrings) - printf_stderr(" %s%s\n", str.BeginReading(), wasMarked ? "(*)" : ""); - } -} } /* namespace gl */ } /* namespace mozilla */ diff --git a/gfx/gl/GLContextCGL.h b/gfx/gl/GLContextCGL.h index 5da7085450cb..9b3c52ddd8e5 100644 --- a/gfx/gl/GLContextCGL.h +++ b/gfx/gl/GLContextCGL.h @@ -28,8 +28,10 @@ class GLContextCGL : public GLContext public: MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GLContextCGL, MOZ_OVERRIDE) - GLContextCGL(const SurfaceCaps& caps, NSOpenGLContext* context, - bool isOffscreen, ContextProfile profile); + GLContextCGL(const SurfaceCaps& caps, + GLContext *shareContext, + NSOpenGLContext *context, + bool isOffscreen = false); ~GLContextCGL(); diff --git a/gfx/gl/GLContextFeatures.cpp b/gfx/gl/GLContextFeatures.cpp index ed2fa69ec9e2..0e47ecbae087 100644 --- a/gfx/gl/GLContextFeatures.cpp +++ b/gfx/gl/GLContextFeatures.cpp @@ -274,16 +274,6 @@ static const FeatureInfo sFeatureInfoArr[] = { * ARB_occlusion_query (added by OpenGL 2.0). */ }, - { - "get_string_indexed", - GLVersion::GL3, - GLESVersion::ES3, - GLContext::Extension_None, - { - GLContext::Extensions_End - } - // glGetStringi - }, { "gpu_shader4", GLVersion::GL3, diff --git a/gfx/gl/GLContextProviderCGL.mm b/gfx/gl/GLContextProviderCGL.mm index a5c3a9baf3e6..3492e4ab4eac 100644 --- a/gfx/gl/GLContextProviderCGL.mm +++ b/gfx/gl/GLContextProviderCGL.mm @@ -21,14 +21,16 @@ namespace gl { using namespace mozilla::gfx; +static bool gUseDoubleBufferedWindows = true; + class CGLLibrary { public: CGLLibrary() - : mInitialized(false) - , mUseDoubleBufferedWindows(true) - , mOGLLibrary(nullptr) - {} + : mInitialized(false), + mOGLLibrary(nullptr), + mPixelFormat(nullptr) + { } bool EnsureInitialized() { @@ -44,33 +46,48 @@ public: } const char* db = PR_GetEnv("MOZ_CGL_DB"); - if (db) { - mUseDoubleBufferedWindows = *db != '0'; - } + gUseDoubleBufferedWindows = (!db || *db != '0'); mInitialized = true; return true; } - bool UseDoubleBufferedWindows() const { - MOZ_ASSERT(mInitialized); - return mUseDoubleBufferedWindows; - } + NSOpenGLPixelFormat *PixelFormat() + { + if (mPixelFormat == nullptr) { + NSOpenGLPixelFormatAttribute attribs[] = { + NSOpenGLPFAAccelerated, + NSOpenGLPFAAllowOfflineRenderers, + NSOpenGLPFADoubleBuffer, + 0 + }; + if (!gUseDoubleBufferedWindows) { + attribs[2] = 0; + } + + mPixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs]; + } + + return mPixelFormat; + } private: bool mInitialized; - bool mUseDoubleBufferedWindows; PRLibrary *mOGLLibrary; + NSOpenGLPixelFormat *mPixelFormat; }; CGLLibrary sCGLLibrary; -GLContextCGL::GLContextCGL(const SurfaceCaps& caps, NSOpenGLContext* context, - bool isOffscreen, ContextProfile profile) - : GLContext(caps, nullptr, isOffscreen) - , mContext(context) +GLContextCGL::GLContextCGL( + const SurfaceCaps& caps, + GLContext *shareContext, + NSOpenGLContext *context, + bool isOffscreen) + : GLContext(caps, shareContext, isOffscreen), + mContext(context) { - SetProfileVersion(profile, 210); + SetProfileVersion(ContextProfile::OpenGLCompatibility, 210); } GLContextCGL::~GLContextCGL() @@ -145,7 +162,7 @@ GLContextCGL::SetupLookupFunction() bool GLContextCGL::IsDoubleBuffered() const { - return sCGLLibrary.UseDoubleBufferedWindows(); + return gUseDoubleBufferedWindows; } bool @@ -165,66 +182,26 @@ GLContextCGL::SwapBuffers() } +static GLContextCGL * +GetGlobalContextCGL() +{ + return static_cast(GLContextProviderCGL::GetGlobalContext()); +} + already_AddRefed GLContextProviderCGL::CreateWrappingExisting(void*, void*) { return nullptr; } -static const NSOpenGLPixelFormatAttribute kAttribs_singleBuffered[] = { - NSOpenGLPFAAccelerated, - NSOpenGLPFAAllowOfflineRenderers, - 0 -}; - -static const NSOpenGLPixelFormatAttribute kAttribs_doubleBuffered[] = { - NSOpenGLPFAAccelerated, - NSOpenGLPFAAllowOfflineRenderers, - NSOpenGLPFADoubleBuffer, - 0 -}; - -static const NSOpenGLPixelFormatAttribute kAttribs_offscreen[] = { - NSOpenGLPFAPixelBuffer, - 0 -}; - -static const NSOpenGLPixelFormatAttribute kAttribs_offscreen_coreProfile[] = { - NSOpenGLPFAAccelerated, - NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core, - 0 -}; - -static NSOpenGLContext* -CreateWithFormat(const NSOpenGLPixelFormatAttribute* attribs) -{ - NSOpenGLPixelFormat* format = [[NSOpenGLPixelFormat alloc] - initWithAttributes:attribs]; - if (!format) - return nullptr; - - NSOpenGLContext* context = [[NSOpenGLContext alloc] initWithFormat:format - shareContext:nullptr]; - - [format release]; - - return context; -} - already_AddRefed GLContextProviderCGL::CreateForWindow(nsIWidget *aWidget) { - if (!sCGLLibrary.EnsureInitialized()) { - return nullptr; - } + GLContextCGL *shareContext = GetGlobalContextCGL(); - const NSOpenGLPixelFormatAttribute* attribs; - if (sCGLLibrary.UseDoubleBufferedWindows()) { - attribs = kAttribs_doubleBuffered; - } else { - attribs = kAttribs_singleBuffered; - } - NSOpenGLContext* context = CreateWithFormat(attribs); + NSOpenGLContext *context = [[NSOpenGLContext alloc] + initWithFormat:sCGLLibrary.PixelFormat() + shareContext:(shareContext ? shareContext->mContext : NULL)]; if (!context) { return nullptr; } @@ -234,13 +211,10 @@ GLContextProviderCGL::CreateForWindow(nsIWidget *aWidget) [context setValues:&opaque forParameter:NSOpenGLCPSurfaceOpacity]; SurfaceCaps caps = SurfaceCaps::ForRGBA(); - ContextProfile profile = ContextProfile::OpenGLCompatibility; - nsRefPtr glContext = new GLContextCGL(caps, context, false, - profile); - + nsRefPtr glContext = new GLContextCGL(caps, + shareContext, + context); if (!glContext->Init()) { - glContext = nullptr; - [context release]; return nullptr; } @@ -248,54 +222,49 @@ GLContextProviderCGL::CreateForWindow(nsIWidget *aWidget) } static already_AddRefed -CreateOffscreenFBOContext(bool requireCompatProfile) +CreateOffscreenFBOContext(bool aShare = true) { if (!sCGLLibrary.EnsureInitialized()) { return nullptr; } - ContextProfile profile; - NSOpenGLContext* context = nullptr; + GLContextCGL *shareContext = aShare ? GetGlobalContextCGL() : nullptr; + if (aShare && !shareContext) { + // if there is no share context, then we can't use FBOs. + return nullptr; + } - if (!requireCompatProfile) { - profile = ContextProfile::OpenGLCore; - context = CreateWithFormat(kAttribs_offscreen_coreProfile); - } - if (!context) { - profile = ContextProfile::OpenGLCompatibility; - context = CreateWithFormat(kAttribs_offscreen); - } + NSOpenGLContext *context = [[NSOpenGLContext alloc] + initWithFormat:sCGLLibrary.PixelFormat() + shareContext:shareContext ? shareContext->GetNSOpenGLContext() : NULL]; if (!context) { return nullptr; } SurfaceCaps dummyCaps = SurfaceCaps::Any(); - nsRefPtr glContext = new GLContextCGL(dummyCaps, context, - true, profile); + nsRefPtr glContext = new GLContextCGL(dummyCaps, shareContext, context, true); return glContext.forget(); } already_AddRefed -GLContextProviderCGL::CreateHeadless(bool requireCompatProfile) +GLContextProviderCGL::CreateHeadless() { - nsRefPtr gl; - gl = CreateOffscreenFBOContext(requireCompatProfile); - if (!gl) + nsRefPtr glContext = CreateOffscreenFBOContext(); + if (!glContext) return nullptr; - if (!gl->Init()) + if (!glContext->Init()) return nullptr; - return gl.forget(); + return glContext.forget(); } already_AddRefed GLContextProviderCGL::CreateOffscreen(const gfxIntSize& size, - const SurfaceCaps& caps, - bool requireCompatProfile) + const SurfaceCaps& caps) { - nsRefPtr glContext = CreateHeadless(requireCompatProfile); + nsRefPtr glContext = CreateHeadless(); if (!glContext->InitOffscreen(ToIntSize(size), caps)) return nullptr; @@ -330,7 +299,7 @@ GLContextProviderCGL::GetGlobalContext() void GLContextProviderCGL::Shutdown() { - gGlobalContext = nullptr; + gGlobalContext = nullptr; } } /* namespace gl */ diff --git a/gfx/gl/GLContextProviderEGL.cpp b/gfx/gl/GLContextProviderEGL.cpp index b946167fa66b..12c2068091db 100644 --- a/gfx/gl/GLContextProviderEGL.cpp +++ b/gfx/gl/GLContextProviderEGL.cpp @@ -878,7 +878,7 @@ GLContextEGL::CreateEGLPixmapOffscreenContext(const gfxIntSize& size) } already_AddRefed -GLContextProviderEGL::CreateHeadless(bool) +GLContextProviderEGL::CreateHeadless() { if (!sEGLLibrary.EnsureInitialized()) { return nullptr; @@ -897,10 +897,9 @@ GLContextProviderEGL::CreateHeadless(bool) // often without the ability to texture from them directly. already_AddRefed GLContextProviderEGL::CreateOffscreen(const gfxIntSize& size, - const SurfaceCaps& caps, - bool requireCompatProfile) + const SurfaceCaps& caps) { - nsRefPtr glContext = CreateHeadless(requireCompatProfile); + nsRefPtr glContext = CreateHeadless(); if (!glContext) return nullptr; diff --git a/gfx/gl/GLContextProviderGLX.cpp b/gfx/gl/GLContextProviderGLX.cpp index f95ae85dafae..bdbc984370d2 100644 --- a/gfx/gl/GLContextProviderGLX.cpp +++ b/gfx/gl/GLContextProviderGLX.cpp @@ -1215,7 +1215,7 @@ DONE_CREATING_PIXMAP: } already_AddRefed -GLContextProviderGLX::CreateHeadless(bool) +GLContextProviderGLX::CreateHeadless() { gfxIntSize dummySize = gfxIntSize(16, 16); nsRefPtr glContext = CreateOffscreenPixmapContext(dummySize); @@ -1227,10 +1227,9 @@ GLContextProviderGLX::CreateHeadless(bool) already_AddRefed GLContextProviderGLX::CreateOffscreen(const gfxIntSize& size, - const SurfaceCaps& caps, - bool requireCompatProfile) + const SurfaceCaps& caps) { - nsRefPtr glContext = CreateHeadless(requireCompatProfile); + nsRefPtr glContext = CreateHeadless(); if (!glContext) return nullptr; diff --git a/gfx/gl/GLContextProviderImpl.h b/gfx/gl/GLContextProviderImpl.h index 9e87170c8237..6efc65de064f 100644 --- a/gfx/gl/GLContextProviderImpl.h +++ b/gfx/gl/GLContextProviderImpl.h @@ -58,12 +58,11 @@ public: */ static already_AddRefed CreateOffscreen(const gfxIntSize& size, - const SurfaceCaps& caps, - bool requireCompatProfile); + const SurfaceCaps& caps); // Just create a context. We'll add offscreen stuff ourselves. static already_AddRefed - CreateHeadless(bool requireCompatProfile); + CreateHeadless(); /** * Create wrapping Gecko GLContext for external gl context. diff --git a/gfx/gl/GLContextProviderNull.cpp b/gfx/gl/GLContextProviderNull.cpp index 231c75be5f3f..61732a6e6b63 100644 --- a/gfx/gl/GLContextProviderNull.cpp +++ b/gfx/gl/GLContextProviderNull.cpp @@ -22,14 +22,13 @@ GLContextProviderNull::CreateWrappingExisting(void*, void*) already_AddRefed GLContextProviderNull::CreateOffscreen(const gfxIntSize&, - const SurfaceCaps&, - bool) + const SurfaceCaps&) { return nullptr; } already_AddRefed -GLContextProviderNull::CreateHeadless(bool) +GLContextProviderNull::CreateHeadless() { return nullptr; } diff --git a/gfx/gl/GLContextProviderWGL.cpp b/gfx/gl/GLContextProviderWGL.cpp index bf605c057e9a..a8c2124d6788 100644 --- a/gfx/gl/GLContextProviderWGL.cpp +++ b/gfx/gl/GLContextProviderWGL.cpp @@ -607,7 +607,7 @@ CreateWindowOffscreenContext() } already_AddRefed -GLContextProviderWGL::CreateHeadless(bool) +GLContextProviderWGL::CreateHeadless() { if (!sWGLLib.EnsureInitialized()) { return nullptr; @@ -641,10 +641,9 @@ GLContextProviderWGL::CreateHeadless(bool) already_AddRefed GLContextProviderWGL::CreateOffscreen(const gfxIntSize& size, - const SurfaceCaps& caps, - bool requireCompatProfile) + const SurfaceCaps& caps) { - nsRefPtr glContext = CreateHeadless(requireCompatProfile); + nsRefPtr glContext = CreateHeadless(); if (!glContext) return nullptr; diff --git a/gfx/gl/GLContextSymbols.h b/gfx/gl/GLContextSymbols.h index 91ff5ac71dc9..3904beac1736 100644 --- a/gfx/gl/GLContextSymbols.h +++ b/gfx/gl/GLContextSymbols.h @@ -666,10 +666,6 @@ struct GLContextSymbols GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const GLvoid* data); PFNGLCOMPRESSEDTEXSUBIMAGE3D fCompressedTexSubImage3D; - - // get_string_indexed - typedef const GLubyte* (GLAPIENTRY * pfnGLGetStringiT)(GLenum name, GLuint index); - pfnGLGetStringiT fGetStringi; }; } diff --git a/gfx/gl/GLContextTypes.cpp b/gfx/gl/GLContextTypes.cpp index 2cca59dc96aa..b11c99098d28 100644 --- a/gfx/gl/GLContextTypes.cpp +++ b/gfx/gl/GLContextTypes.cpp @@ -12,3 +12,8 @@ GLFormats::GLFormats() { std::memset(this, 0, sizeof(GLFormats)); } + +PixelBufferFormat::PixelBufferFormat() +{ + std::memset(this, 0, sizeof(PixelBufferFormat)); +} diff --git a/gfx/gl/GLContextTypes.h b/gfx/gl/GLContextTypes.h index 979790bb1e58..f21cc11cf06e 100644 --- a/gfx/gl/GLContextTypes.h +++ b/gfx/gl/GLContextTypes.h @@ -43,6 +43,19 @@ struct GLFormats GLsizei samples; }; +struct PixelBufferFormat +{ + // Constructs a zeroed object: + PixelBufferFormat(); + + int red, green, blue; + int alpha; + int depth, stencil; + int samples; + + int ColorBits() const { return red + green + blue; } +}; + } /* namespace gl */ } /* namespace mozilla */ diff --git a/gfx/gl/GLLibraryEGL.cpp b/gfx/gl/GLLibraryEGL.cpp index 40cee8d0c98d..a07b3e79f3df 100644 --- a/gfx/gl/GLLibraryEGL.cpp +++ b/gfx/gl/GLLibraryEGL.cpp @@ -35,7 +35,8 @@ static const char *sEGLExtensionNames[] = { "EGL_EXT_create_context_robustness", "EGL_KHR_image", "EGL_KHR_fence_sync", - "EGL_ANDROID_native_fence_sync" + "EGL_ANDROID_native_fence_sync", + nullptr }; #if defined(ANDROID) @@ -239,8 +240,8 @@ GLLibraryEGL::EnsureInitialized() }; // Do not warn about the failure to load this - see bug 1092191 - GLLibraryLoader::LoadSymbols(mEGLLibrary, &optionalSymbols[0], nullptr, nullptr, - false); + GLLibraryLoader::LoadSymbols(mEGLLibrary, &optionalSymbols[0], + nullptr, nullptr, false); #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 18 MOZ_RELEASE_ASSERT(mSymbols.fQueryStringImplementationANDROID, @@ -420,24 +421,15 @@ GLLibraryEGL::EnsureInitialized() void GLLibraryEGL::InitExtensions() { - std::vector driverExtensionList; + const char *extensions = (const char*)fQueryString(mEGLDisplay, LOCAL_EGL_EXTENSIONS); - const char* rawExts = (const char*)fQueryString(mEGLDisplay, LOCAL_EGL_EXTENSIONS); - if (rawExts) { - nsDependentCString exts(rawExts); - SplitByChar(exts, ' ', &driverExtensionList); - } else { + if (!extensions) { NS_WARNING("Failed to load EGL extension list!"); + return; } - const bool shouldDumpExts = GLContext::ShouldDumpExts(); - if (shouldDumpExts) { - printf_stderr("%i EGL driver extensions: (*: recognized)\n", - (uint32_t)driverExtensionList.size()); - } - - MarkBitfieldByStrings(driverExtensionList, shouldDumpExts, sEGLExtensionNames, - &mAvailableExtensions); + GLContext::InitializeExtensionsBitSet(mAvailableExtensions, extensions, + sEGLExtensionNames); } void diff --git a/gfx/gl/GLLibraryEGL.h b/gfx/gl/GLLibraryEGL.h index 4e055344c8f5..1d175a217efe 100644 --- a/gfx/gl/GLLibraryEGL.h +++ b/gfx/gl/GLLibraryEGL.h @@ -15,7 +15,6 @@ #include "GeckoProfiler.h" #include -#include #if defined(XP_WIN) diff --git a/gfx/layers/GLImages.cpp b/gfx/layers/GLImages.cpp index 7525eefd40fa..66fa218df961 100644 --- a/gfx/layers/GLImages.cpp +++ b/gfx/layers/GLImages.cpp @@ -39,7 +39,7 @@ GLImage::GetAsSourceSurface() MOZ_ASSERT(NS_IsMainThread(), "Should be on the main thread"); if (!sSnapshotContext) { - sSnapshotContext = GLContextProvider::CreateHeadless(false); + sSnapshotContext = GLContextProvider::CreateHeadless(); if (!sSnapshotContext) { NS_WARNING("Failed to create snapshot GLContext"); return nullptr; diff --git a/gfx/layers/opengl/CompositorOGL.cpp b/gfx/layers/opengl/CompositorOGL.cpp index 4aab23384e89..bc78494c155b 100644 --- a/gfx/layers/opengl/CompositorOGL.cpp +++ b/gfx/layers/opengl/CompositorOGL.cpp @@ -124,11 +124,8 @@ CompositorOGL::CreateContext() SurfaceCaps caps = SurfaceCaps::ForRGB(); caps.preserve = false; caps.bpp16 = gfxPlatform::GetPlatform()->GetOffscreenFormat() == gfxImageFormat::RGB16_565; - - bool requireCompatProfile = true; context = GLContextProvider::CreateOffscreen(gfxIntSize(mSurfaceSize.width, - mSurfaceSize.height), - caps, requireCompatProfile); + mSurfaceSize.height), caps); } if (!context) diff --git a/gfx/tests/gtest/TestCompositor.cpp b/gfx/tests/gtest/TestCompositor.cpp index 273fc5516a4f..70ff21d8a3a0 100644 --- a/gfx/tests/gtest/TestCompositor.cpp +++ b/gfx/tests/gtest/TestCompositor.cpp @@ -45,7 +45,7 @@ public: caps.preserve = false; caps.bpp16 = false; nsRefPtr context = GLContextProvider::CreateOffscreen( - gfxIntSize(gCompWidth, gCompHeight), caps, true); + gfxIntSize(gCompWidth, gCompHeight), caps); return context.forget().take(); } return nullptr; diff --git a/gfx/thebes/gfxPlatform.cpp b/gfx/thebes/gfxPlatform.cpp index 1394387fceba..2e271cf3f52b 100644 --- a/gfx/thebes/gfxPlatform.cpp +++ b/gfx/thebes/gfxPlatform.cpp @@ -1099,9 +1099,8 @@ gfxPlatform::GetSkiaGLGlue() * FIXME: This should be stored in TLS or something, since there needs to be one for each thread using it. As it * stands, this only works on the main thread. */ - bool requireCompatProfile = true; - nsRefPtr glContext; - glContext = mozilla::gl::GLContextProvider::CreateHeadless(requireCompatProfile); + mozilla::gl::SurfaceCaps caps = mozilla::gl::SurfaceCaps::ForRGBA(); + nsRefPtr glContext = mozilla::gl::GLContextProvider::CreateOffscreen(gfxIntSize(16, 16), caps); if (!glContext) { printf_stderr("Failed to create GLContext for SkiaGL!\n"); return nullptr; diff --git a/image/decoders/nsGIFDecoder2.cpp b/image/decoders/nsGIFDecoder2.cpp index acf214af93df..f70ab06b9a56 100644 --- a/image/decoders/nsGIFDecoder2.cpp +++ b/image/decoders/nsGIFDecoder2.cpp @@ -48,6 +48,7 @@ mailing address. #include "gfxPlatform.h" #include "qcms.h" #include +#include "mozilla/Telemetry.h" namespace mozilla { namespace image { diff --git a/image/decoders/nsJPEGDecoder.cpp b/image/decoders/nsJPEGDecoder.cpp index d0387a9bbbfc..f8c3c10446dc 100644 --- a/image/decoders/nsJPEGDecoder.cpp +++ b/image/decoders/nsJPEGDecoder.cpp @@ -19,6 +19,7 @@ #include "gfxPlatform.h" #include "mozilla/Endian.h" +#include "mozilla/Telemetry.h" extern "C" { #include "iccjpeg.h" diff --git a/image/decoders/nsPNGDecoder.cpp b/image/decoders/nsPNGDecoder.cpp index 7079b64421b0..769fb152b5fd 100644 --- a/image/decoders/nsPNGDecoder.cpp +++ b/image/decoders/nsPNGDecoder.cpp @@ -15,6 +15,7 @@ #include "nspr.h" #include "png.h" #include "RasterImage.h" +#include "mozilla/Telemetry.h" #include diff --git a/image/src/Decoder.cpp b/image/src/Decoder.cpp index 751273f43137..cf8cff4976cb 100644 --- a/image/src/Decoder.cpp +++ b/image/src/Decoder.cpp @@ -15,6 +15,7 @@ #include "nsProxyRelease.h" #include "nsServiceManagerUtils.h" #include "nsComponentManagerUtils.h" +#include "mozilla/Telemetry.h" using mozilla::gfx::IntSize; using mozilla::gfx::SurfaceFormat; @@ -701,5 +702,12 @@ Decoder::NeedNewFrame(uint32_t framenum, uint32_t x_offset, uint32_t y_offset, mNeedsNewFrame = true; } +Telemetry::ID +Decoder::SpeedHistogram() +{ + // Use HistogramCount as an invalid Histogram ID. + return Telemetry::HistogramCount; +} + } // namespace image } // namespace mozilla diff --git a/image/src/Decoder.h b/image/src/Decoder.h index f5b235a57100..b276f2e36235 100644 --- a/image/src/Decoder.h +++ b/image/src/Decoder.h @@ -13,10 +13,13 @@ #include "ImageMetadata.h" #include "Orientation.h" #include "SourceBuffer.h" -#include "mozilla/Telemetry.h" namespace mozilla { +namespace Telemetry { + enum ID : uint32_t; +} + namespace image { class Decoder : public IResumable @@ -263,8 +266,7 @@ public: return mImageMetadata.GetSize(); } - // Use HistogramCount as an invalid Histogram ID - virtual Telemetry::ID SpeedHistogram() { return Telemetry::HistogramCount; } + virtual Telemetry::ID SpeedHistogram(); ImageMetadata& GetImageMetadata() { return mImageMetadata; } diff --git a/js/src/asmjs/AsmJSLink.cpp b/js/src/asmjs/AsmJSLink.cpp index 42457b9a65d0..94a4cedc337a 100644 --- a/js/src/asmjs/AsmJSLink.cpp +++ b/js/src/asmjs/AsmJSLink.cpp @@ -88,9 +88,9 @@ GetDataProperty(JSContext *cx, HandleValue objVal, HandlePropertyName field, Mut if (IsScriptedProxy(obj)) return LinkFail(cx, "accessing property of a Proxy"); - Rooted desc(cx); + Rooted desc(cx); RootedId id(cx, NameToId(field)); - if (!JS_GetPropertyDescriptorById(cx, obj, id, &desc)) + if (!GetPropertyDescriptor(cx, obj, id, &desc)) return false; if (!desc.object()) diff --git a/js/src/builtin/Object.cpp b/js/src/builtin/Object.cpp index 463dcbb6f566..1aefb09b067d 100644 --- a/js/src/builtin/Object.cpp +++ b/js/src/builtin/Object.cpp @@ -18,6 +18,7 @@ #include "jsobjinlines.h" #include "vm/NativeObject-inl.h" +#include "vm/Shape-inl.h" using namespace js; using namespace js::types; @@ -637,7 +638,7 @@ js::ObjectCreateImpl(JSContext *cx, HandleObject proto, NewObjectKind newKind, // instead. RootedObjectGroup ngroup(cx, group); if (!ngroup) { - ngroup = GetCallerInitGroup(cx, JSProto_Null); + ngroup = ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Null); if (!ngroup) return nullptr; } diff --git a/js/src/builtin/Object.h b/js/src/builtin/Object.h index 6302b098d705..b3919940ee8a 100644 --- a/js/src/builtin/Object.h +++ b/js/src/builtin/Object.h @@ -9,6 +9,8 @@ #include "jsapi.h" +#include "vm/NativeObject.h" + namespace JS { class CallArgs; class Value; diff --git a/js/src/builtin/TypedObject.cpp b/js/src/builtin/TypedObject.cpp index a751aa5181aa..b6fcbb4fe952 100644 --- a/js/src/builtin/TypedObject.cpp +++ b/js/src/builtin/TypedObject.cpp @@ -1495,7 +1495,9 @@ OutlineTypedObject::createUnattachedWithClass(JSContext *cx, MOZ_ASSERT(clasp == &OutlineTransparentTypedObject::class_ || clasp == &OutlineOpaqueTypedObject::class_); - RootedObjectGroup group(cx, cx->getNewGroup(clasp, TaggedProto(&descr->typedProto()), descr)); + RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, clasp, + TaggedProto(&descr->typedProto()), + descr)); if (!group) return nullptr; @@ -2131,7 +2133,9 @@ InlineTypedObject::create(JSContext *cx, HandleTypeDescr descr, gc::InitialHeap ? &InlineOpaqueTypedObject::class_ : &InlineTransparentTypedObject::class_; - RootedObjectGroup group(cx, cx->getNewGroup(clasp, TaggedProto(&descr->typedProto()), descr)); + RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, clasp, + TaggedProto(&descr->typedProto()), + descr)); if (!group) return nullptr; diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 019523839084..78d757e22436 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -4295,7 +4295,7 @@ ParseNode::getConstantValue(ExclusiveContext *cx, AllowConstantObjects allowObje } MOZ_ASSERT(idx == count); - types::FixArrayGroup(cx, obj); + ObjectGroup::fixArrayGroup(cx, obj); vp.setObject(*obj); return true; } @@ -4358,7 +4358,7 @@ ParseNode::getConstantValue(ExclusiveContext *cx, AllowConstantObjects allowObje } } - types::FixObjectGroup(cx, obj); + ObjectGroup::fixPlainObjectGroup(cx, obj); vp.setObject(*obj); return true; } @@ -7250,13 +7250,13 @@ frontend::EmitTree(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn) if (!pn->getConstantValue(cx, ParseNode::DontAllowNestedObjects, &value)) return false; if (!value.isMagic(JS_GENERIC_MAGIC)) { - // Note: the type of the template object might not yet reflect + // Note: the group of the template object might not yet reflect // that the object has copy on write elements. When the // interpreter or JIT compiler fetches the template, it should - // use types::GetOrFixupCopyOnWriteObject to make sure the type - // for the template is accurate. We don't do this here as we - // want to use types::InitObject, which requires a finished - // script. + // use ObjectGroup::getOrFixupCopyOnWriteObject to make sure the + // group for the template is accurate. We don't do this here as we + // want to use ObjectGroup::allocationSiteGroup, which requires a + // finished script. NativeObject *obj = &value.toObject().as(); if (!ObjectElements::MakeElementsCopyOnWrite(cx, obj)) return false; diff --git a/js/src/gc/Barrier.h b/js/src/gc/Barrier.h index 45645f103607..c1fedcb77b24 100644 --- a/js/src/gc/Barrier.h +++ b/js/src/gc/Barrier.h @@ -181,10 +181,7 @@ class ScopeObject; class ScriptSourceObject; class Shape; class UnownedBaseShape; - -namespace types { -struct ObjectGroup; -} +class ObjectGroup; namespace jit { class JitCode; @@ -232,7 +229,7 @@ template <> struct MapTypeToTraceKind{ static const JSG template <> struct MapTypeToTraceKind{ static const JSGCTraceKind kind = JSTRACE_OBJECT; }; template <> struct MapTypeToTraceKind { static const JSGCTraceKind kind = JSTRACE_BASE_SHAPE; }; template <> struct MapTypeToTraceKind { static const JSGCTraceKind kind = JSTRACE_JITCODE; }; -template <> struct MapTypeToTraceKind { static const JSGCTraceKind kind = JSTRACE_OBJECT_GROUP; }; +template <> struct MapTypeToTraceKind { static const JSGCTraceKind kind = JSTRACE_OBJECT_GROUP; }; // Direct value access used by the write barriers and the jits. void @@ -806,7 +803,7 @@ typedef HeapPtr HeapPtrPropertyName; typedef HeapPtr HeapPtrShape; typedef HeapPtr HeapPtrUnownedBaseShape; typedef HeapPtr HeapPtrJitCode; -typedef HeapPtr HeapPtrObjectGroup; +typedef HeapPtr HeapPtrObjectGroup; typedef PreBarriered PreBarrieredValue; typedef RelocatablePtr RelocatableValue; @@ -827,7 +824,7 @@ typedef ReadBarriered ReadBarrieredScriptSourceObject; typedef ReadBarriered ReadBarrieredShape; typedef ReadBarriered ReadBarrieredUnownedBaseShape; typedef ReadBarriered ReadBarrieredJitCode; -typedef ReadBarriered ReadBarrieredObjectGroup; +typedef ReadBarriered ReadBarrieredObjectGroup; typedef ReadBarriered ReadBarrieredAtom; typedef ReadBarriered ReadBarrieredSymbol; diff --git a/js/src/gc/GCTrace.h b/js/src/gc/GCTrace.h index a6e6e4105bcc..4339256d5a8b 100644 --- a/js/src/gc/GCTrace.h +++ b/js/src/gc/GCTrace.h @@ -11,7 +11,7 @@ namespace js { -namespace types { struct ObjectGroup; } +class ObjectGroup; namespace gc { @@ -29,7 +29,7 @@ extern void TraceMinorGCEnd(); extern void TraceMajorGCStart(); extern void TraceTenuredFinalize(Cell *thing); extern void TraceMajorGCEnd(); -extern void TraceTypeNewScript(js::types::ObjectGroup *group); +extern void TraceTypeNewScript(js::ObjectGroup *group); #else @@ -45,7 +45,7 @@ inline void TraceMinorGCEnd() {} inline void TraceMajorGCStart() {} inline void TraceTenuredFinalize(Cell *thing) {} inline void TraceMajorGCEnd() {} -inline void TraceTypeNewScript(js::types::ObjectGroup *group) {} +inline void TraceTypeNewScript(js::ObjectGroup *group) {} #endif diff --git a/js/src/gc/Marking.cpp b/js/src/gc/Marking.cpp index 302ff7a2aadb..b8e6fd0101b5 100644 --- a/js/src/gc/Marking.cpp +++ b/js/src/gc/Marking.cpp @@ -87,7 +87,7 @@ static inline void PushMarkStack(GCMarker *gcmarker, JS::Symbol *sym); static inline void -PushMarkStack(GCMarker *gcmarker, types::ObjectGroup *thing); +PushMarkStack(GCMarker *gcmarker, ObjectGroup *thing); namespace js { namespace gc { @@ -98,7 +98,7 @@ static void MarkChildren(JSTracer *trc, JSScript *script); static void MarkChildren(JSTracer *trc, LazyScript *lazy); static void MarkChildren(JSTracer *trc, Shape *shape); static void MarkChildren(JSTracer *trc, BaseShape *base); -static void MarkChildren(JSTracer *trc, types::ObjectGroup *group); +static void MarkChildren(JSTracer *trc, ObjectGroup *group); static void MarkChildren(JSTracer *trc, jit::JitCode *code); } /* namespace gc */ @@ -614,7 +614,7 @@ DeclMarkerImpl(String, JSFlatString) DeclMarkerImpl(String, JSLinearString) DeclMarkerImpl(String, PropertyName) DeclMarkerImpl(Symbol, JS::Symbol) -DeclMarkerImpl(ObjectGroup, js::types::ObjectGroup) +DeclMarkerImpl(ObjectGroup, js::ObjectGroup) } /* namespace gc */ } /* namespace js */ @@ -655,7 +655,7 @@ gc::MarkKind(JSTracer *trc, void **thingp, JSGCTraceKind kind) MarkInternal(trc, reinterpret_cast(thingp)); break; case JSTRACE_OBJECT_GROUP: - MarkInternal(trc, reinterpret_cast(thingp)); + MarkInternal(trc, reinterpret_cast(thingp)); break; default: MOZ_CRASH("Invalid trace kind in MarkKind."); @@ -796,7 +796,7 @@ gc::MarkTypeRoot(JSTracer *trc, types::Type *v, const char *name) MarkInternal(trc, &obj); *v = types::Type::ObjectType(obj); } else if (v->isGroup()) { - types::ObjectGroup *group = v->group(); + ObjectGroup *group = v->group(); MarkInternal(trc, &group); *v = types::Type::ObjectType(group); } @@ -1066,7 +1066,7 @@ PushMarkStack(GCMarker *gcmarker, JSFunction *thing) } static void -PushMarkStack(GCMarker *gcmarker, types::ObjectGroup *thing) +PushMarkStack(GCMarker *gcmarker, ObjectGroup *thing) { JS_COMPARTMENT_ASSERT(gcmarker->runtime(), thing); MOZ_ASSERT(!IsInsideNursery(thing)); @@ -1422,7 +1422,7 @@ gc::MarkCycleCollectorChildren(JSTracer *trc, Shape *shape) } static void -ScanObjectGroup(GCMarker *gcmarker, types::ObjectGroup *group) +ScanObjectGroup(GCMarker *gcmarker, ObjectGroup *group) { unsigned count = group->getPropertyCount(); for (unsigned i = 0; i < count; i++) { @@ -1450,7 +1450,7 @@ ScanObjectGroup(GCMarker *gcmarker, types::ObjectGroup *group) } static void -gc::MarkChildren(JSTracer *trc, types::ObjectGroup *group) +gc::MarkChildren(JSTracer *trc, ObjectGroup *group) { unsigned count = group->getPropertyCount(); for (unsigned i = 0; i < count; i++) { @@ -1533,7 +1533,7 @@ gc::PushArena(GCMarker *gcmarker, ArenaHeader *aheader) break; case JSTRACE_OBJECT_GROUP: - PushArenaTyped(gcmarker, aheader); + PushArenaTyped(gcmarker, aheader); break; default: @@ -1652,7 +1652,7 @@ void GCMarker::processMarkStackOther(uintptr_t tag, uintptr_t addr) { if (tag == GroupTag) { - ScanObjectGroup(this, reinterpret_cast(addr)); + ScanObjectGroup(this, reinterpret_cast(addr)); } else if (tag == SavedValueArrayTag) { MOZ_ASSERT(!(addr & CellMask)); NativeObject *obj = reinterpret_cast(addr); @@ -1802,7 +1802,7 @@ GCMarker::processMarkStackTop(SliceBudget &budget) return; } - types::ObjectGroup *group = obj->groupFromGC(); + ObjectGroup *group = obj->groupFromGC(); PushMarkStack(this, group); Shape *shape = obj->lastProperty(); @@ -1957,7 +1957,7 @@ js::TraceChildren(JSTracer *trc, void *thing, JSGCTraceKind kind) break; case JSTRACE_OBJECT_GROUP: - MarkChildren(trc, (types::ObjectGroup *)thing); + MarkChildren(trc, (ObjectGroup *)thing); break; default: diff --git a/js/src/gc/Marking.h b/js/src/gc/Marking.h index e6ab4653c6e0..8255d46aadc3 100644 --- a/js/src/gc/Marking.h +++ b/js/src/gc/Marking.h @@ -132,7 +132,7 @@ DeclMarker(String, JSFlatString) DeclMarker(String, JSLinearString) DeclMarker(String, PropertyName) DeclMarker(Symbol, JS::Symbol) -DeclMarker(ObjectGroup, types::ObjectGroup) +DeclMarker(ObjectGroup, ObjectGroup) #undef DeclMarker diff --git a/js/src/gc/Nursery.cpp b/js/src/gc/Nursery.cpp index 3c2de6192f50..ada17d475282 100644 --- a/js/src/gc/Nursery.cpp +++ b/js/src/gc/Nursery.cpp @@ -537,7 +537,7 @@ js::Nursery::forwardBufferPointer(HeapSlot **pSlotsElems) // been tenured during a minor collection. struct TenureCount { - types::ObjectGroup *group; + ObjectGroup *group; int count; }; @@ -550,8 +550,8 @@ struct Nursery::TenureCountCache TenureCountCache() { PodZero(this); } - TenureCount &findEntry(types::ObjectGroup *group) { - return entries[PointerHasher::hash(group) % ArrayLength(entries)]; + TenureCount &findEntry(ObjectGroup *group) { + return entries[PointerHasher::hash(group) % ArrayLength(entries)]; } }; diff --git a/js/src/gc/Nursery.h b/js/src/gc/Nursery.h index 6d6bf6634c82..91ab2beb242a 100644 --- a/js/src/gc/Nursery.h +++ b/js/src/gc/Nursery.h @@ -31,6 +31,8 @@ class TypedArrayObject; class ObjectElements; class NativeObject; class HeapSlot; +class ObjectGroup; + void SetGCZeal(JSRuntime *, uint8_t, uint32_t); namespace gc { @@ -39,10 +41,6 @@ class Collector; class MinorCollectionTracer; } /* namespace gc */ -namespace types { -struct ObjectGroup; -} - namespace jit { class CodeGenerator; class MacroAssembler; @@ -117,7 +115,7 @@ class Nursery /* Free a slots array. */ void freeSlots(HeapSlot *slots); - typedef Vector ObjectGroupList; + typedef Vector ObjectGroupList; /* * Do a minor collection, optionally specifying a list to store groups which diff --git a/js/src/gc/RootMarking.cpp b/js/src/gc/RootMarking.cpp index 4bac29030c66..a9d5c872aabf 100644 --- a/js/src/gc/RootMarking.cpp +++ b/js/src/gc/RootMarking.cpp @@ -103,7 +103,7 @@ MarkExactStackRootsAcrossTypes(T context, JSTracer *trc) MarkExactStackRootList(trc, context, "exact-object"); MarkExactStackRootList(trc, context, "exact-shape"); MarkExactStackRootList(trc, context, "exact-baseshape"); - MarkExactStackRootList( + MarkExactStackRootList( trc, context, "exact-objectgroup"); MarkExactStackRootList(trc, context, "exact-string"); MarkExactStackRootList(trc, context, "exact-symbol"); diff --git a/js/src/gc/Rooting.h b/js/src/gc/Rooting.h index 0df33dba79cc..ab38313521c0 100644 --- a/js/src/gc/Rooting.h +++ b/js/src/gc/Rooting.h @@ -20,14 +20,13 @@ class ArrayObject; class PlainObject; class ScriptSourceObject; class Shape; - -namespace types { struct ObjectGroup; } +class ObjectGroup; // These are internal counterparts to the public types such as HandleObject. typedef JS::Handle HandleNativeObject; typedef JS::Handle HandleShape; -typedef JS::Handle HandleObjectGroup; +typedef JS::Handle HandleObjectGroup; typedef JS::Handle HandleAtom; typedef JS::Handle HandleLinearString; typedef JS::Handle HandlePropertyName; @@ -41,7 +40,7 @@ typedef JS::MutableHandle MutableHandleNativeObject; typedef JS::Rooted RootedNativeObject; typedef JS::Rooted RootedShape; -typedef JS::Rooted RootedObjectGroup; +typedef JS::Rooted RootedObjectGroup; typedef JS::Rooted RootedAtom; typedef JS::Rooted RootedLinearString; typedef JS::Rooted RootedPropertyName; diff --git a/js/src/gc/Tracer.h b/js/src/gc/Tracer.h index eca88dc93259..9aa525f02846 100644 --- a/js/src/gc/Tracer.h +++ b/js/src/gc/Tracer.h @@ -16,15 +16,13 @@ namespace js { class NativeObject; class GCMarker; +class ObjectGroup; namespace gc { struct ArenaHeader; } namespace jit { class JitCode; } -namespace types { -struct ObjectGroup; -} static const size_t NON_INCREMENTAL_MARK_STACK_BASE_CAPACITY = 4096; static const size_t INCREMENTAL_MARK_STACK_BASE_CAPACITY = 32768; @@ -143,7 +141,7 @@ class GCMarker : public JSTracer pushTaggedPtr(ObjectTag, obj); } - void pushType(types::ObjectGroup *group) { + void pushType(ObjectGroup *group) { pushTaggedPtr(GroupTag, group); } diff --git a/js/src/gdb/mozilla/JSObject.py b/js/src/gdb/mozilla/JSObject.py index eef0826ccf17..96a720df0bbe 100644 --- a/js/src/gdb/mozilla/JSObject.py +++ b/js/src/gdb/mozilla/JSObject.py @@ -34,8 +34,8 @@ class JSObjectPtrOrRef(prettyprinters.Pointer): def summary(self): shape = deref(self.value['shape_']) baseshape = deref(shape['base_']) - otype = deref(self.value['type_']) - class_name = otype['clasp_']['name'].string() + group = deref(self.value['group_']) + class_name = group['clasp_']['name'].string() flags = baseshape['flags'] is_delegate = bool(flags & self.otc.flag_DELEGATE) name = None diff --git a/js/src/jit/BaselineCompiler.cpp b/js/src/jit/BaselineCompiler.cpp index ada836df6b8e..c950926e07a4 100644 --- a/js/src/jit/BaselineCompiler.cpp +++ b/js/src/jit/BaselineCompiler.cpp @@ -1722,8 +1722,8 @@ BaselineCompiler::emit_JSOP_NEWARRAY() uint32_t length = GET_UINT24(pc); RootedObjectGroup group(cx); - if (!types::UseSingletonForInitializer(script, pc, JSProto_Array)) { - group = types::TypeScript::InitGroup(cx, script, pc, JSProto_Array); + if (!ObjectGroup::useSingletonForAllocationSite(script, pc, JSProto_Array)) { + group = ObjectGroup::allocationSiteGroup(cx, script, pc, JSProto_Array); if (!group) return false; } @@ -1753,7 +1753,7 @@ bool BaselineCompiler::emit_JSOP_NEWARRAY_COPYONWRITE() { RootedScript scriptRoot(cx, script); - JSObject *obj = types::GetOrFixupCopyOnWriteObject(cx, scriptRoot, pc); + JSObject *obj = ObjectGroup::getOrFixupCopyOnWriteObject(cx, scriptRoot, pc); if (!obj) return false; @@ -1797,8 +1797,8 @@ BaselineCompiler::emit_JSOP_NEWOBJECT() frame.syncStack(0); RootedObjectGroup group(cx); - if (!types::UseSingletonForInitializer(script, pc, JSProto_Object)) { - group = types::TypeScript::InitGroup(cx, script, pc, JSProto_Object); + if (!ObjectGroup::useSingletonForAllocationSite(script, pc, JSProto_Object)) { + group = ObjectGroup::allocationSiteGroup(cx, script, pc, JSProto_Object); if (!group) return false; } @@ -1822,8 +1822,8 @@ BaselineCompiler::emit_JSOP_NEWOBJECT() Register objReg = R0.scratchReg(); Register tempReg = R1.scratchReg(); masm.movePtr(ImmGCPtr(group), tempReg); - masm.branchTest32(Assembler::NonZero, Address(tempReg, types::ObjectGroup::offsetOfFlags()), - Imm32(types::OBJECT_FLAG_PRE_TENURE), &slowPath); + masm.branchTest32(Assembler::NonZero, Address(tempReg, ObjectGroup::offsetOfFlags()), + Imm32(OBJECT_FLAG_PRE_TENURE), &slowPath); masm.branchPtr(Assembler::NotEqual, AbsoluteAddress(cx->compartment()->addressOfMetadataCallback()), ImmWord(0), &slowPath); masm.createGCObject(objReg, tempReg, templateObject, gc::DefaultHeap, &slowPath); @@ -1848,8 +1848,8 @@ BaselineCompiler::emit_JSOP_NEWINIT() JSProtoKey key = JSProtoKey(GET_UINT8(pc)); RootedObjectGroup group(cx); - if (!types::UseSingletonForInitializer(script, pc, key)) { - group = types::TypeScript::InitGroup(cx, script, pc, key); + if (!ObjectGroup::useSingletonForAllocationSite(script, pc, key)) { + group = ObjectGroup::allocationSiteGroup(cx, script, pc, key); if (!group) return false; } @@ -3364,7 +3364,7 @@ BaselineCompiler::emit_JSOP_REST() ArrayObject *templateObject = NewDenseUnallocatedArray(cx, 0, nullptr, TenuredObject); if (!templateObject) return false; - types::FixRestArgumentsType(cx, templateObject); + ObjectGroup::fixRestArgumentsGroup(cx, templateObject); // Call IC. ICRest_Fallback::Compiler compiler(cx, templateObject); diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp index 0a55cabf4152..ae63ab99f25e 100644 --- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -2062,7 +2062,7 @@ ICCompare_ObjectWithUndefined::Compiler::generateStubCode(MacroAssembler &masm) Label emulatesUndefined; Register obj = masm.extractObject(objectOperand, ExtractTemp0); masm.loadPtr(Address(obj, JSObject::offsetOfGroup()), obj); - masm.loadPtr(Address(obj, types::ObjectGroup::offsetOfClasp()), obj); + masm.loadPtr(Address(obj, ObjectGroup::offsetOfClasp()), obj); masm.branchTest32(Assembler::NonZero, Address(obj, Class::offsetOfFlags()), Imm32(JSCLASS_EMULATES_UNDEFINED), @@ -4521,7 +4521,7 @@ LoadTypedThingLength(MacroAssembler &masm, TypedThingLayout layout, Register obj case Layout_OutlineTypedObject: case Layout_InlineTypedObject: masm.loadPtr(Address(obj, JSObject::offsetOfGroup()), result); - masm.loadPtr(Address(result, types::ObjectGroup::offsetOfAddendum()), result); + masm.loadPtr(Address(result, ObjectGroup::offsetOfAddendum()), result); masm.unboxInt32(Address(result, ArrayTypeDescr::offsetOfLength()), result); break; default: @@ -8248,13 +8248,10 @@ DoSetPropFallback(JSContext *cx, BaselineFrame *frame, ICSetProp_Fallback *stub_ obj->as().setAliasedVar(cx, ScopeCoordinate(pc), name, rhs); } else { MOZ_ASSERT(op == JSOP_SETPROP || op == JSOP_STRICTSETPROP); - if (op == JSOP_STRICTSETPROP) { - if (!js::SetProperty(cx, obj, id, rhs)) - return false; - } else { - if (!js::SetProperty(cx, obj, id, rhs)) - return false; - } + + RootedValue v(cx, rhs); + if (!SetProperty(cx, obj, obj, id, &v, op == JSOP_STRICTSETPROP)) + return false; } // Leave the RHS on the stack. @@ -8520,7 +8517,7 @@ ICSetPropNativeAddCompiler::generateStubCode(MacroAssembler &masm) // Check if the old group still has a newScript. masm.loadPtr(Address(objReg, JSObject::offsetOfGroup()), scratch); masm.branchPtr(Assembler::Equal, - Address(scratch, types::ObjectGroup::offsetOfAddendum()), + Address(scratch, ObjectGroup::offsetOfAddendum()), ImmWord(0), &noGroupChange); @@ -9049,7 +9046,7 @@ GetTemplateObjectForNative(JSContext *cx, HandleScript script, jsbytecode *pc, if (!res) return false; - types::ObjectGroup *group = types::TypeScript::InitGroup(cx, script, pc, JSProto_Array); + ObjectGroup *group = ObjectGroup::allocationSiteGroup(cx, script, pc, JSProto_Array); if (!group) return false; res->setGroup(group); @@ -9061,7 +9058,7 @@ GetTemplateObjectForNative(JSContext *cx, HandleScript script, jsbytecode *pc, if (!res) return false; - types::ObjectGroup *group = types::TypeScript::InitGroup(cx, script, pc, JSProto_Array); + ObjectGroup *group = ObjectGroup::allocationSiteGroup(cx, script, pc, JSProto_Array); if (!group) return false; res->setGroup(group); @@ -9085,7 +9082,7 @@ GetTemplateObjectForNative(JSContext *cx, HandleScript script, jsbytecode *pc, if (!res) return false; - types::ObjectGroup *group = types::TypeScript::InitGroup(cx, script, pc, JSProto_Array); + ObjectGroup *group = ObjectGroup::allocationSiteGroup(cx, script, pc, JSProto_Array); if (!group) return false; res->setGroup(group); @@ -9487,7 +9484,7 @@ DoCallFallback(JSContext *cx, BaselineFrame *frame, ICCall_Fallback *stub_, uint // Compute construcing and useNewGroup flags. bool constructing = (op == JSOP_NEW); - bool createSingleton = types::UseSingletonForNewObject(cx, script, pc); + bool createSingleton = ObjectGroup::useSingletonForNewObject(cx, script, pc); // Try attaching a call stub. if (!TryAttachCallStub(cx, stub, script, pc, op, argc, vp, constructing, false, createSingleton)) @@ -11609,7 +11606,7 @@ ICSetElem_Dense::ICSetElem_Dense(JitCode *stubCode, HandleShape shape, HandleObj group_(group) { } -ICSetElem_DenseAdd::ICSetElem_DenseAdd(JitCode *stubCode, types::ObjectGroup *group, +ICSetElem_DenseAdd::ICSetElem_DenseAdd(JitCode *stubCode, ObjectGroup *group, size_t protoChainDepth) : ICUpdatedStub(SetElem_DenseAdd, stubCode), group_(group) @@ -12123,7 +12120,7 @@ static bool DoRestFallback(JSContext *cx, ICRest_Fallback *stub, ArrayObject *obj = NewDenseCopiedArray(cx, numRest, rest, nullptr); if (!obj) return false; - types::FixRestArgumentsType(cx, obj); + ObjectGroup::fixRestArgumentsGroup(cx, obj); res.setObject(*obj); return true; } diff --git a/js/src/jit/BaselineIC.h b/js/src/jit/BaselineIC.h index 1c26077b7cf8..675782b07e91 100644 --- a/js/src/jit/BaselineIC.h +++ b/js/src/jit/BaselineIC.h @@ -3576,7 +3576,7 @@ class ICSetElem_DenseAdd : public ICUpdatedStub protected: HeapPtrObjectGroup group_; - ICSetElem_DenseAdd(JitCode *stubCode, types::ObjectGroup *group, size_t protoChainDepth); + ICSetElem_DenseAdd(JitCode *stubCode, ObjectGroup *group, size_t protoChainDepth); public: static size_t offsetOfGroup() { @@ -3611,7 +3611,7 @@ class ICSetElem_DenseAddImpl : public ICSetElem_DenseAdd static const size_t NumShapes = ProtoChainDepth + 1; mozilla::Array shapes_; - ICSetElem_DenseAddImpl(JitCode *stubCode, types::ObjectGroup *group, + ICSetElem_DenseAddImpl(JitCode *stubCode, ObjectGroup *group, const AutoShapeVector *shapes) : ICSetElem_DenseAdd(stubCode, group, ProtoChainDepth) { @@ -3622,7 +3622,7 @@ class ICSetElem_DenseAddImpl : public ICSetElem_DenseAdd public: static inline ICSetElem_DenseAddImpl *New(ICStubSpace *space, JitCode *code, - types::ObjectGroup *group, + ObjectGroup *group, const AutoShapeVector *shapes) { if (!code) @@ -4585,7 +4585,7 @@ class ICGetProp_Unboxed : public ICMonitoredStub public: Compiler(JSContext *cx, ICStub *firstMonitorStub, - types::ObjectGroup *group, uint32_t fieldOffset, JSValueType fieldType) + ObjectGroup *group, uint32_t fieldOffset, JSValueType fieldType) : ICStubCompiler(cx, ICStub::GetProp_Unboxed), firstMonitorStub_(firstMonitorStub), group_(cx, group), @@ -5545,7 +5545,7 @@ class ICSetProp_Unboxed : public ICUpdatedStub } public: - Compiler(JSContext *cx, types::ObjectGroup *group, uint32_t fieldOffset, + Compiler(JSContext *cx, ObjectGroup *group, uint32_t fieldOffset, JSValueType fieldType) : ICStubCompiler(cx, ICStub::SetProp_Unboxed), group_(cx, group), @@ -5635,7 +5635,7 @@ class ICSetProp_TypedObject : public ICUpdatedStub } public: - Compiler(JSContext *cx, Shape *shape, types::ObjectGroup *group, uint32_t fieldOffset, + Compiler(JSContext *cx, Shape *shape, ObjectGroup *group, uint32_t fieldOffset, SimpleTypeDescr *fieldDescr) : ICStubCompiler(cx, ICStub::SetProp_TypedObject), shape_(cx, shape), diff --git a/js/src/jit/BaselineInspector.cpp b/js/src/jit/BaselineInspector.cpp index c6dfef1e797c..ba09bc6509fe 100644 --- a/js/src/jit/BaselineInspector.cpp +++ b/js/src/jit/BaselineInspector.cpp @@ -99,7 +99,7 @@ BaselineInspector::maybeInfoForPropertyOp(jsbytecode *pc, ICStub *stub = entry.firstStub(); while (stub->next()) { Shape *shape = nullptr; - types::ObjectGroup *group = nullptr; + ObjectGroup *group = nullptr; if (stub->isGetProp_Native()) { shape = stub->toGetProp_Native()->shape(); } else if (stub->isSetProp_Native()) { diff --git a/js/src/jit/BaselineInspector.h b/js/src/jit/BaselineInspector.h index 6bf778da77a1..30082cfcd89a 100644 --- a/js/src/jit/BaselineInspector.h +++ b/js/src/jit/BaselineInspector.h @@ -93,7 +93,7 @@ class BaselineInspector public: typedef Vector ShapeVector; - typedef Vector ObjectGroupVector; + typedef Vector ObjectGroupVector; bool maybeInfoForPropertyOp(jsbytecode *pc, ShapeVector &nativeShapes, ObjectGroupVector &unboxedGroups); diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 42e9bdd6a4c4..c4450739e272 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -723,7 +723,7 @@ CodeGenerator::visitFunctionDispatch(LFunctionDispatch *lir) for (size_t i = 0; i < casesWithFallback - 1; i++) { MOZ_ASSERT(i < mir->numCases()); LBlock *target = skipTrivialBlocks(mir->getCaseBlock(i))->lir(); - if (types::ObjectGroup *funcGroup = mir->getCaseObjectGroup(i)) { + if (ObjectGroup *funcGroup = mir->getCaseObjectGroup(i)) { masm.branchPtr(Assembler::Equal, Address(input, JSObject::offsetOfGroup()), ImmGCPtr(funcGroup), target->label()); } else { @@ -764,7 +764,7 @@ CodeGenerator::visitObjectGroupDispatch(LObjectGroupDispatch *lir) if (lastBranch.isInitialized()) lastBranch.emit(masm); - types::ObjectGroup *group = propTable->getObjectGroup(j); + ObjectGroup *group = propTable->getObjectGroup(j); lastBranch = MacroAssembler::BranchGCPtr(Assembler::Equal, temp, ImmGCPtr(group), target->label()); lastBlock = target; @@ -3680,8 +3680,8 @@ CodeGenerator::emitObjectOrStringResultChecks(LInstruction *lir, MDefinition *mi // properties become unknown, so check for this case. masm.loadPtr(Address(output, JSObject::offsetOfGroup()), temp); masm.branchTestPtr(Assembler::NonZero, - Address(temp, types::ObjectGroup::offsetOfFlags()), - Imm32(types::OBJECT_FLAG_UNKNOWN_PROPERTIES), &ok); + Address(temp, ObjectGroup::offsetOfFlags()), + Imm32(OBJECT_FLAG_UNKNOWN_PROPERTIES), &ok); masm.assumeUnreachable("MIR instruction returned object with unexpected type"); @@ -3760,8 +3760,8 @@ CodeGenerator::emitValueResultChecks(LInstruction *lir, MDefinition *mir) Register payload = masm.extractObject(output, temp1); masm.loadPtr(Address(payload, JSObject::offsetOfGroup()), temp1); masm.branchTestPtr(Assembler::NonZero, - Address(temp1, types::ObjectGroup::offsetOfFlags()), - Imm32(types::OBJECT_FLAG_UNKNOWN_PROPERTIES), &ok); + Address(temp1, ObjectGroup::offsetOfFlags()), + Imm32(OBJECT_FLAG_UNKNOWN_PROPERTIES), &ok); masm.bind(&realMiss); masm.assumeUnreachable("MIR instruction returned value with unexpected type"); @@ -3947,7 +3947,7 @@ CodeGenerator::visitNewArrayCallVM(LNewArray *lir) saveLive(lir); JSObject *templateObject = lir->mir()->templateObject(); - types::ObjectGroup *group = + ObjectGroup *group = templateObject->isSingleton() ? nullptr : templateObject->group(); pushArg(Imm32(lir->mir()->allocatingBehaviour())); @@ -4338,7 +4338,7 @@ CodeGenerator::visitSimdUnbox(LSimdUnbox *lir) // Guard that the object has the same representation as the one produced for // SIMD value-type. - Address clasp(temp, types::ObjectGroup::offsetOfClasp()); + Address clasp(temp, ObjectGroup::offsetOfClasp()); static_assert(!SimdTypeDescr::Opaque, "SIMD objects are transparent"); masm.branchPtr(Assembler::NotEqual, clasp, ImmPtr(&InlineTransparentTypedObject::class_), &bail); @@ -4346,7 +4346,7 @@ CodeGenerator::visitSimdUnbox(LSimdUnbox *lir) // obj->type()->typeDescr() // The previous class pointer comparison implies that the addendumKind is // Addendum_TypeDescr. - masm.loadPtr(Address(temp, types::ObjectGroup::offsetOfAddendum()), temp); + masm.loadPtr(Address(temp, ObjectGroup::offsetOfAddendum()), temp); // Check for the /Kind/ reserved slot of the TypeDescr. This is an Int32 // Value which is equivalent to the object class check. @@ -4810,7 +4810,7 @@ CodeGenerator::visitTypedObjectDescr(LTypedObjectDescr *lir) Register out = ToRegister(lir->output()); masm.loadPtr(Address(obj, JSObject::offsetOfGroup()), out); - masm.loadPtr(Address(out, types::ObjectGroup::offsetOfAddendum()), out); + masm.loadPtr(Address(out, ObjectGroup::offsetOfAddendum()), out); } void diff --git a/js/src/jit/Disassembler.h b/js/src/jit/Disassembler.h index c9868c8a1f7d..101dc3941d63 100644 --- a/js/src/jit/Disassembler.h +++ b/js/src/jit/Disassembler.h @@ -7,6 +7,7 @@ #ifndef jit_Disassembler_h #define jit_Disassembler_h +#include "jit/MacroAssembler.h" #include "jit/Registers.h" namespace js { diff --git a/js/src/jit/IonAnalysis.cpp b/js/src/jit/IonAnalysis.cpp index ef090e94ee21..349564a6d54b 100644 --- a/js/src/jit/IonAnalysis.cpp +++ b/js/src/jit/IonAnalysis.cpp @@ -2880,7 +2880,7 @@ jit::ConvertLinearInequality(TempAllocator &alloc, MBasicBlock *block, const Lin } static bool -AnalyzePoppedThis(JSContext *cx, types::ObjectGroup *group, +AnalyzePoppedThis(JSContext *cx, ObjectGroup *group, MDefinition *thisValue, MInstruction *ins, bool definitelyExecuted, HandlePlainObject baseobj, Vector *initializerList, @@ -2896,9 +2896,6 @@ AnalyzePoppedThis(JSContext *cx, types::ObjectGroup *group, if (setprop->object() != thisValue) return true; - // Don't use GetAtomId here, we need to watch for SETPROP on - // integer properties and bail out. We can't mark the aggregate - // JSID_VOID type property as being in a definite slot. if (setprop->name() == cx->names().prototype || setprop->name() == cx->names().proto || setprop->name() == cx->names().constructor) @@ -3016,7 +3013,7 @@ CmpInstructions(const void *a, const void *b) bool jit::AnalyzeNewScriptDefiniteProperties(JSContext *cx, JSFunction *fun, - types::ObjectGroup *group, HandlePlainObject baseobj, + ObjectGroup *group, HandlePlainObject baseobj, Vector *initializerList) { MOZ_ASSERT(cx->zone()->types.activeAnalysis); diff --git a/js/src/jit/IonAnalysis.h b/js/src/jit/IonAnalysis.h index 116d465eeac9..0a4b4bb9f28d 100644 --- a/js/src/jit/IonAnalysis.h +++ b/js/src/jit/IonAnalysis.h @@ -170,7 +170,7 @@ ConvertLinearInequality(TempAllocator &alloc, MBasicBlock *block, const LinearSu bool AnalyzeNewScriptDefiniteProperties(JSContext *cx, JSFunction *fun, - types::ObjectGroup *group, HandlePlainObject baseobj, + ObjectGroup *group, HandlePlainObject baseobj, Vector *initializerList); bool diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index 0e8a28dbcc90..09261341f6d3 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -359,7 +359,7 @@ IonBuilder::getPolyCallTargets(types::TemporaryTypeSet *calleeTypes, bool constr if (obj) { MOZ_ASSERT(obj->isSingleton()); } else { - types::ObjectGroup *group = calleeTypes->getGroup(i); + ObjectGroup *group = calleeTypes->getGroup(i); if (!group) continue; @@ -5322,7 +5322,7 @@ IonBuilder::inlineCalls(CallInfo &callInfo, const ObjectVector &targets, // // Note that guarding is on the original function pointer even // if there is a clone, since cloning occurs at the callsite. - types::ObjectGroup *funcGroup = original->isSingleton() ? nullptr : original->group(); + ObjectGroup *funcGroup = original->isSingleton() ? nullptr : original->group(); dispatch->addCase(original, funcGroup, inlineBlock); MDefinition *retVal = inlineReturnBlock->peek(-1); @@ -5543,7 +5543,7 @@ IonBuilder::createThisScriptedSingleton(JSFunction *target, MDefinition *callee) return nullptr; types::TypeSetObjectKey *templateObjectKey = types::TypeSetObjectKey::get(templateObject->group()); - if (templateObjectKey->hasFlags(constraints(), types::OBJECT_FLAG_NEW_SCRIPT_CLEARED)) + if (templateObjectKey->hasFlags(constraints(), OBJECT_FLAG_NEW_SCRIPT_CLEARED)) return nullptr; types::StackTypeSet *thisTypes = types::TypeScript::ThisTypes(target->nonLazyScript()); @@ -6250,14 +6250,14 @@ IonBuilder::jsop_newarray(uint32_t count) bool IonBuilder::jsop_newarray_copyonwrite() { - ArrayObject *templateObject = types::GetCopyOnWriteObject(script(), pc); + ArrayObject *templateObject = ObjectGroup::getCopyOnWriteObject(script(), pc); // The baseline compiler should have ensured the template object has a type // with the copy on write flag set already. During the arguments usage // analysis the baseline compiler hasn't run yet, however, though in this // case the template object's type doesn't matter. MOZ_ASSERT_IF(info().analysisMode() != Analysis_ArgumentsUsage, - templateObject->group()->hasAnyFlags(types::OBJECT_FLAG_COPY_ON_WRITE)); + templateObject->group()->hasAnyFlags(OBJECT_FLAG_COPY_ON_WRITE)); MNewArrayCopyOnWrite *ins = MNewArrayCopyOnWrite::New(alloc(), constraints(), templateObject, @@ -6326,7 +6326,7 @@ IonBuilder::jsop_initelem_array() } else { types::TypeSetObjectKey *initializer = obj->resultTypeSet()->getObject(0); if (value->type() == MIRType_MagicHole) { - if (!initializer->hasFlags(constraints(), types::OBJECT_FLAG_NON_PACKED)) + if (!initializer->hasFlags(constraints(), OBJECT_FLAG_NON_PACKED)) needStub = true; } else if (!initializer->unknownProperties()) { types::HeapTypeSetKey elemTypes = initializer->property(JSID_VOID); @@ -7629,7 +7629,7 @@ IonBuilder::checkTypedObjectIndexInBounds(int32_t elemSize, // If we are not loading the length from the object itself, only // optimize if the array buffer can't have been neutered. types::TypeSetObjectKey *globalKey = types::TypeSetObjectKey::get(&script()->global()); - if (globalKey->hasFlags(constraints(), types::OBJECT_FLAG_TYPED_OBJECT_NEUTERED)) { + if (globalKey->hasFlags(constraints(), OBJECT_FLAG_TYPED_OBJECT_NEUTERED)) { trackOptimizationOutcome(TrackedOutcome::TypedObjectNeutered); return false; } @@ -9082,7 +9082,7 @@ IonBuilder::jsop_length_fastPath() // Compute the length for array objects. if (objTypes && objTypes->getKnownClass(constraints()) == &ArrayObject::class_ && - !objTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_LENGTH_OVERFLOW)) + !objTypes->hasObjectFlags(constraints(), OBJECT_FLAG_LENGTH_OVERFLOW)) { current->pop(); MElements *elements = MElements::New(alloc(), obj); @@ -9099,7 +9099,7 @@ IonBuilder::jsop_length_fastPath() TypedObjectPrediction prediction = typedObjectPrediction(obj); if (!prediction.isUseless()) { types::TypeSetObjectKey *globalKey = types::TypeSetObjectKey::get(&script()->global()); - if (globalKey->hasFlags(constraints(), types::OBJECT_FLAG_TYPED_OBJECT_NEUTERED)) + if (globalKey->hasFlags(constraints(), OBJECT_FLAG_TYPED_OBJECT_NEUTERED)) return false; MInstruction *length; @@ -9230,7 +9230,7 @@ IonBuilder::getDefiniteSlot(types::TemporaryTypeSet *types, PropertyName *name) if (!key) continue; - if (types::ObjectGroup *group = key->maybeGroup()) { + if (ObjectGroup *group = key->maybeGroup()) { if (group->newScript() && !group->newScript()->analyzed()) { addAbortedNewScriptPropertiesGroup(group); trackOptimizationOutcome(TrackedOutcome::NoAnalysisInfo); @@ -9538,7 +9538,7 @@ IonBuilder::annotateGetPropertyCache(MDefinition *obj, MGetPropertyCache *getPro // Ensure that the relevant property typeset for each group is // is a single-object typeset containing a JSFunction for (unsigned int i = 0; i < objCount; i++) { - types::ObjectGroup *group = objTypes->getGroup(i); + ObjectGroup *group = objTypes->getGroup(i); if (!group) continue; types::TypeSetObjectKey *key = types::TypeSetObjectKey::get(group); @@ -9988,7 +9988,7 @@ IonBuilder::getPropTryScalarPropOfTypedObject(bool *emitted, MDefinition *typedO // Don't optimize if the typed object might be neutered. types::TypeSetObjectKey *globalKey = types::TypeSetObjectKey::get(&script()->global()); - if (globalKey->hasFlags(constraints(), types::OBJECT_FLAG_TYPED_OBJECT_NEUTERED)) + if (globalKey->hasFlags(constraints(), OBJECT_FLAG_TYPED_OBJECT_NEUTERED)) return true; trackOptimizationSuccess(); @@ -10010,7 +10010,7 @@ IonBuilder::getPropTryReferencePropOfTypedObject(bool *emitted, MDefinition *typ ReferenceTypeDescr::Type fieldType = fieldPrediction.referenceType(); types::TypeSetObjectKey *globalKey = types::TypeSetObjectKey::get(&script()->global()); - if (globalKey->hasFlags(constraints(), types::OBJECT_FLAG_TYPED_OBJECT_NEUTERED)) + if (globalKey->hasFlags(constraints(), OBJECT_FLAG_TYPED_OBJECT_NEUTERED)) return true; trackOptimizationSuccess(); @@ -10032,7 +10032,7 @@ IonBuilder::getPropTryComplexPropOfTypedObject(bool *emitted, { // Don't optimize if the typed object might be neutered. types::TypeSetObjectKey *globalKey = types::TypeSetObjectKey::get(&script()->global()); - if (globalKey->hasFlags(constraints(), types::OBJECT_FLAG_TYPED_OBJECT_NEUTERED)) + if (globalKey->hasFlags(constraints(), OBJECT_FLAG_TYPED_OBJECT_NEUTERED)) return true; // OK, perform the optimization @@ -10430,7 +10430,7 @@ IonBuilder::getPropTryInlineAccess(bool *emitted, MDefinition *obj, PropertyName if (nativeShapes.empty() && unboxedGroups.length() == 1) { spew("Inlining monomorphic unboxed GETPROP"); - types::ObjectGroup *group = unboxedGroups[0]; + ObjectGroup *group = unboxedGroups[0]; // Failures in this group guard should be treated the same as a shape guard failure. obj = MGuardObjectGroup::New(alloc(), obj, group, /* bailOnEquality = */ false, @@ -10604,7 +10604,7 @@ IonBuilder::tryInnerizeWindow(MDefinition *obj) // its ObjectGroup as having unknown properties. The type constraint we add // here will invalidate JIT code when this happens. types::TypeSetObjectKey *key = types::TypeSetObjectKey::get(singleton); - if (key->hasFlags(constraints(), types::OBJECT_FLAG_UNKNOWN_PROPERTIES)) + if (key->hasFlags(constraints(), OBJECT_FLAG_UNKNOWN_PROPERTIES)) return obj; obj->setImplicitlyUsedUnchecked(); @@ -10883,7 +10883,7 @@ IonBuilder::setPropTryReferencePropOfTypedObject(bool *emitted, ReferenceTypeDescr::Type fieldType = fieldPrediction.referenceType(); types::TypeSetObjectKey *globalKey = types::TypeSetObjectKey::get(&script()->global()); - if (globalKey->hasFlags(constraints(), types::OBJECT_FLAG_TYPED_OBJECT_NEUTERED)) + if (globalKey->hasFlags(constraints(), OBJECT_FLAG_TYPED_OBJECT_NEUTERED)) return true; LinearSum byteOffset(alloc()); @@ -10912,7 +10912,7 @@ IonBuilder::setPropTryScalarPropOfTypedObject(bool *emitted, // Don't optimize if the typed object might be neutered. types::TypeSetObjectKey *globalKey = types::TypeSetObjectKey::get(&script()->global()); - if (globalKey->hasFlags(constraints(), types::OBJECT_FLAG_TYPED_OBJECT_NEUTERED)) + if (globalKey->hasFlags(constraints(), OBJECT_FLAG_TYPED_OBJECT_NEUTERED)) return true; LinearSum byteOffset(alloc()); @@ -11109,7 +11109,7 @@ IonBuilder::setPropTryInlineAccess(bool *emitted, MDefinition *obj, if (nativeShapes.empty() && unboxedGroups.length() == 1) { spew("Inlining monomorphic unboxed SETPROP"); - types::ObjectGroup *group = unboxedGroups[0]; + ObjectGroup *group = unboxedGroups[0]; // Failures in this group guard should be treated the same as a shape guard failure. obj = MGuardObjectGroup::New(alloc(), obj, group, /* bailOnEquality = */ false, @@ -11244,7 +11244,7 @@ IonBuilder::jsop_regexp(RegExpObject *reobj) bool mustClone = true; types::TypeSetObjectKey *globalKey = types::TypeSetObjectKey::get(&script()->global()); - if (!globalKey->hasFlags(constraints(), types::OBJECT_FLAG_REGEXP_FLAGS_SET)) { + if (!globalKey->hasFlags(constraints(), OBJECT_FLAG_REGEXP_FLAGS_SET)) { #ifdef DEBUG // Only compare the statics if the one on script()->global() has been // instantiated. @@ -11633,7 +11633,7 @@ IonBuilder::hasStaticScopeObject(ScopeCoordinate sc, JSObject **pcall) types::TypeSetObjectKey *funKey = types::TypeSetObjectKey::get(outerScript->functionNonDelazifying()); - if (funKey->hasFlags(constraints(), types::OBJECT_FLAG_RUNONCE_INVALIDATED)) + if (funKey->hasFlags(constraints(), OBJECT_FLAG_RUNONCE_INVALIDATED)) return false; // The script this aliased var operation is accessing will run only once, @@ -12103,7 +12103,7 @@ IonBuilder::typedObjectPrediction(types::TemporaryTypeSet *types) TypedObjectPrediction out; for (uint32_t i = 0; i < types->getObjectCount(); i++) { - types::ObjectGroup *group = types->getGroup(i); + ObjectGroup *group = types->getGroup(i); if (!group || !types::TypeSetObjectKey::get(group)->hasStableClassAndProto(constraints())) return TypedObjectPrediction(); diff --git a/js/src/jit/IonCaches.cpp b/js/src/jit/IonCaches.cpp index d9f27fcbbb8a..88cd9ce87c21 100644 --- a/js/src/jit/IonCaches.cpp +++ b/js/src/jit/IonCaches.cpp @@ -488,7 +488,7 @@ GeneratePrototypeGuards(JSContext *cx, IonScript *ion, MacroAssembler &masm, JSO // 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, types::ObjectGroup::offsetOfProto()); + Address proto(scratchReg, ObjectGroup::offsetOfProto()); masm.branchPtr(Assembler::NotEqual, proto, ImmMaybeNurseryPtr(obj->getProto()), failures); } @@ -1897,7 +1897,7 @@ CheckTypeSetForWrite(MacroAssembler &masm, JSObject *obj, jsid id, Register object, ConstantOrRegister value, Label *failure) { TypedOrValueRegister valReg = value.reg(); - types::ObjectGroup *group = obj->group(); + ObjectGroup *group = obj->group(); if (group->unknownProperties()) return; types::HeapTypeSet *propTypes = group->maybeGetProperty(id); @@ -1929,7 +1929,7 @@ GenerateSetSlot(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &att // just guard that it's already there. // Obtain and guard on the ObjectGroup of the object. - types::ObjectGroup *group = obj->group(); + ObjectGroup *group = obj->group(); masm.branchPtr(Assembler::NotEqual, Address(object, JSObject::offsetOfGroup()), ImmGCPtr(group), &failures); @@ -2463,7 +2463,7 @@ SetPropertyIC::attachCallSetter(JSContext *cx, HandleScript outerScript, IonScri static void GenerateAddSlot(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher, - NativeObject *obj, Shape *oldShape, types::ObjectGroup *oldGroup, + NativeObject *obj, Shape *oldShape, ObjectGroup *oldGroup, Register object, ConstantOrRegister value, bool checkTypeset) { @@ -2520,7 +2520,7 @@ GenerateAddSlot(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &att masm.push(object); masm.loadPtr(Address(object, JSObject::offsetOfGroup()), object); masm.branchPtr(Assembler::Equal, - Address(object, types::ObjectGroup::offsetOfAddendum()), + Address(object, ObjectGroup::offsetOfAddendum()), ImmWord(0), &noTypeChange); masm.pop(object); @@ -2579,7 +2579,7 @@ static bool CanInlineSetPropTypeCheck(JSObject *obj, jsid id, ConstantOrRegister val, bool *checkTypeset) { bool shouldCheck = false; - types::ObjectGroup *group = obj->group(); + ObjectGroup *group = obj->group(); if (!group->unknownProperties()) { types::HeapTypeSet *propTypes = group->maybeGetProperty(id); if (!propTypes) diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp index 2a51d1d2e053..36b9496dc3eb 100644 --- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -506,10 +506,10 @@ IonBuilder::inlineArrayPopShift(CallInfo &callInfo, MArrayPopShift::Mode mode) // Pop and shift are only handled for dense arrays that have never been // used in an iterator: popping elements does not account for suppressing // deleted properties in active iterators. - types::ObjectGroupFlags unhandledFlags = - types::OBJECT_FLAG_SPARSE_INDEXES | - types::OBJECT_FLAG_LENGTH_OVERFLOW | - types::OBJECT_FLAG_ITERATED; + ObjectGroupFlags unhandledFlags = + OBJECT_FLAG_SPARSE_INDEXES | + OBJECT_FLAG_LENGTH_OVERFLOW | + OBJECT_FLAG_ITERATED; MDefinition *obj = callInfo.thisArg(); types::TemporaryTypeSet *thisTypes = obj->resultTypeSet(); @@ -530,7 +530,7 @@ IonBuilder::inlineArrayPopShift(CallInfo &callInfo, MArrayPopShift::Mode mode) obj = addMaybeCopyElementsForWrite(obj); types::TemporaryTypeSet *returnTypes = getInlineReturnTypeSet(); - bool needsHoleCheck = thisTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_NON_PACKED); + bool needsHoleCheck = thisTypes->hasObjectFlags(constraints(), OBJECT_FLAG_NON_PACKED); bool maybeUndefined = returnTypes->hasType(types::Type::UndefinedType()); BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(), @@ -643,8 +643,8 @@ IonBuilder::inlineArrayPush(CallInfo &callInfo) types::TemporaryTypeSet *thisTypes = callInfo.thisArg()->resultTypeSet(); if (!thisTypes || thisTypes->getKnownClass(constraints()) != &ArrayObject::class_) return InliningStatus_NotInlined; - if (thisTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_SPARSE_INDEXES | - types::OBJECT_FLAG_LENGTH_OVERFLOW)) + if (thisTypes->hasObjectFlags(constraints(), OBJECT_FLAG_SPARSE_INDEXES | + OBJECT_FLAG_LENGTH_OVERFLOW)) { trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags); return InliningStatus_NotInlined; @@ -711,8 +711,8 @@ IonBuilder::inlineArrayConcat(CallInfo &callInfo) if (thisTypes->getKnownClass(constraints()) != &ArrayObject::class_) return InliningStatus_NotInlined; - if (thisTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_SPARSE_INDEXES | - types::OBJECT_FLAG_LENGTH_OVERFLOW)) + if (thisTypes->hasObjectFlags(constraints(), OBJECT_FLAG_SPARSE_INDEXES | + OBJECT_FLAG_LENGTH_OVERFLOW)) { trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags); return InliningStatus_NotInlined; @@ -720,8 +720,8 @@ IonBuilder::inlineArrayConcat(CallInfo &callInfo) if (argTypes->getKnownClass(constraints()) != &ArrayObject::class_) return InliningStatus_NotInlined; - if (argTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_SPARSE_INDEXES | - types::OBJECT_FLAG_LENGTH_OVERFLOW)) + if (argTypes->hasObjectFlags(constraints(), OBJECT_FLAG_SPARSE_INDEXES | + OBJECT_FLAG_LENGTH_OVERFLOW)) { trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags); return InliningStatus_NotInlined; @@ -738,7 +738,7 @@ IonBuilder::inlineArrayConcat(CallInfo &callInfo) if (thisTypes->getObjectCount() != 1) return InliningStatus_NotInlined; - types::ObjectGroup *thisGroup = thisTypes->getGroup(0); + ObjectGroup *thisGroup = thisTypes->getGroup(0); if (!thisGroup) return InliningStatus_NotInlined; types::TypeSetObjectKey *thisKey = types::TypeSetObjectKey::get(thisGroup); @@ -747,8 +747,8 @@ IonBuilder::inlineArrayConcat(CallInfo &callInfo) // Don't inline if 'this' is packed and the argument may not be packed // (the result array will reuse the 'this' type). - if (!thisTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_NON_PACKED) && - argTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_NON_PACKED)) + if (!thisTypes->hasObjectFlags(constraints(), OBJECT_FLAG_NON_PACKED) && + argTypes->hasObjectFlags(constraints(), OBJECT_FLAG_NON_PACKED)) { trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags); return InliningStatus_NotInlined; diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp index 7973d19734fd..c853014a0497 100644 --- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -4321,14 +4321,14 @@ bool jit::ElementAccessIsPacked(types::CompilerConstraintList *constraints, MDefinition *obj) { types::TemporaryTypeSet *types = obj->resultTypeSet(); - return types && !types->hasObjectFlags(constraints, types::OBJECT_FLAG_NON_PACKED); + return types && !types->hasObjectFlags(constraints, OBJECT_FLAG_NON_PACKED); } bool jit::ElementAccessMightBeCopyOnWrite(types::CompilerConstraintList *constraints, MDefinition *obj) { types::TemporaryTypeSet *types = obj->resultTypeSet(); - return !types || types->hasObjectFlags(constraints, types::OBJECT_FLAG_COPY_ON_WRITE); + return !types || types->hasObjectFlags(constraints, OBJECT_FLAG_COPY_ON_WRITE); } bool @@ -4337,7 +4337,7 @@ jit::ElementAccessHasExtraIndexedProperty(types::CompilerConstraintList *constra { types::TemporaryTypeSet *types = obj->resultTypeSet(); - if (!types || types->hasObjectFlags(constraints, types::OBJECT_FLAG_LENGTH_OVERFLOW)) + if (!types || types->hasObjectFlags(constraints, OBJECT_FLAG_LENGTH_OVERFLOW)) return true; return types::TypeCanHaveExtraIndexedProperties(constraints, types); diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index 921d78c18955..678c296880d0 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -6319,7 +6319,7 @@ class MStringSplit JSObject *templateObject() const { return &getOperand(2)->toConstant()->value().toObject(); } - types::ObjectGroup *group() const { + ObjectGroup *group() const { return templateObject()->group(); } bool possiblyCalls() const MOZ_OVERRIDE { @@ -7166,7 +7166,7 @@ struct LambdaFunctionInfo ? (gc::Cell *) fun->nonLazyScript() : (gc::Cell *) fun->lazyScript()), singletonType(fun->isSingleton()), - useSingletonForClone(types::UseSingletonForClone(fun)) + useSingletonForClone(ObjectGroup::useSingletonForClone(fun)) {} LambdaFunctionInfo(const LambdaFunctionInfo &info) @@ -7187,7 +7187,7 @@ class MLambda : MBinaryInstruction(scopeChain, cst), info_(&cst->value().toObject().as()) { setResultType(MIRType_Object); - if (!info().fun->isSingleton() && !types::UseSingletonForClone(info().fun)) + if (!info().fun->isSingleton() && !ObjectGroup::useSingletonForClone(info().fun)) setResultTypeSet(MakeSingletonTypeSet(constraints, info().fun)); } @@ -7225,7 +7225,7 @@ class MLambdaArrow : MBinaryInstruction(scopeChain, this_), info_(fun) { setResultType(MIRType_Object); - MOZ_ASSERT(!types::UseSingletonForClone(fun)); + MOZ_ASSERT(!ObjectGroup::useSingletonForClone(fun)); if (!fun->isSingleton()) setResultTypeSet(MakeSingletonTypeSet(constraints, fun)); } @@ -9201,10 +9201,10 @@ typedef Vector BoolVector; class InlinePropertyTable : public TempObject { struct Entry : public TempObject { - AlwaysTenured group; + AlwaysTenured group; AlwaysTenuredFunction func; - Entry(types::ObjectGroup *group, JSFunction *func) + Entry(ObjectGroup *group, JSFunction *func) : group(group), func(func) { } }; @@ -9232,7 +9232,7 @@ class InlinePropertyTable : public TempObject return pc_; } - bool addEntry(TempAllocator &alloc, types::ObjectGroup *group, JSFunction *func) { + bool addEntry(TempAllocator &alloc, ObjectGroup *group, JSFunction *func) { return entries_.append(new(alloc) Entry(group, func)); } @@ -9240,7 +9240,7 @@ class InlinePropertyTable : public TempObject return entries_.length(); } - types::ObjectGroup *getObjectGroup(size_t i) const { + ObjectGroup *getObjectGroup(size_t i) const { MOZ_ASSERT(i < numEntries()); return entries_[i]->group; } @@ -9380,7 +9380,7 @@ class MGetPropertyPolymorphic }; Vector nativeShapes_; - Vector unboxedGroups_; + Vector unboxedGroups_; AlwaysTenuredPropertyName name_; MGetPropertyPolymorphic(TempAllocator &alloc, MDefinition *obj, PropertyName *name) @@ -9415,7 +9415,7 @@ class MGetPropertyPolymorphic entry.shape = shape; return nativeShapes_.append(entry); } - bool addUnboxedGroup(types::ObjectGroup *group) { + bool addUnboxedGroup(ObjectGroup *group) { return unboxedGroups_.append(group); } size_t numShapes() const { @@ -9430,7 +9430,7 @@ class MGetPropertyPolymorphic size_t numUnboxedGroups() const { return unboxedGroups_.length(); } - types::ObjectGroup *unboxedGroup(size_t i) const { + ObjectGroup *unboxedGroup(size_t i) const { return unboxedGroups_[i]; } PropertyName *name() const { @@ -9462,7 +9462,7 @@ class MSetPropertyPolymorphic }; Vector nativeShapes_; - Vector unboxedGroups_; + Vector unboxedGroups_; AlwaysTenuredPropertyName name_; bool needsBarrier_; @@ -9490,7 +9490,7 @@ class MSetPropertyPolymorphic entry.shape = shape; return nativeShapes_.append(entry); } - bool addUnboxedGroup(types::ObjectGroup *group) { + bool addUnboxedGroup(ObjectGroup *group) { return unboxedGroups_.append(group); } size_t numShapes() const { @@ -9505,7 +9505,7 @@ class MSetPropertyPolymorphic size_t numUnboxedGroups() const { return unboxedGroups_.length(); } - types::ObjectGroup *unboxedGroup(size_t i) const { + ObjectGroup *unboxedGroup(size_t i) const { return unboxedGroups_[i]; } PropertyName *name() const { @@ -9539,10 +9539,10 @@ class MDispatchInstruction // If |func| has a singleton group, |funcGroup| is null. Otherwise, // |funcGroup| holds the ObjectGroup for |func|, and dispatch guards // on the group instead of directly on the function. - types::ObjectGroup *funcGroup; + ObjectGroup *funcGroup; MBasicBlock *block; - Entry(JSFunction *func, types::ObjectGroup *funcGroup, MBasicBlock *block) + Entry(JSFunction *func, ObjectGroup *funcGroup, MBasicBlock *block) : func(func), funcGroup(funcGroup), block(block) { } }; @@ -9611,7 +9611,7 @@ class MDispatchInstruction } public: - void addCase(JSFunction *func, types::ObjectGroup *funcGroup, MBasicBlock *block) { + void addCase(JSFunction *func, ObjectGroup *funcGroup, MBasicBlock *block) { map_.append(Entry(func, funcGroup, block)); } uint32_t numCases() const { @@ -9620,7 +9620,7 @@ class MDispatchInstruction JSFunction *getCase(uint32_t i) const { return map_[i].func; } - types::ObjectGroup *getCaseObjectGroup(uint32_t i) const { + ObjectGroup *getCaseObjectGroup(uint32_t i) const { return map_[i].funcGroup; } MBasicBlock *getCaseBlock(uint32_t i) const { @@ -9858,11 +9858,11 @@ class MGuardObjectGroup : public MUnaryInstruction, public SingleObjectPolicy::Data { - AlwaysTenured group_; + AlwaysTenured group_; bool bailOnEquality_; BailoutKind bailoutKind_; - MGuardObjectGroup(MDefinition *obj, types::ObjectGroup *group, bool bailOnEquality, + MGuardObjectGroup(MDefinition *obj, ObjectGroup *group, bool bailOnEquality, BailoutKind bailoutKind) : MUnaryInstruction(obj), group_(group), @@ -9877,7 +9877,7 @@ class MGuardObjectGroup public: INSTRUCTION_HEADER(GuardObjectGroup) - static MGuardObjectGroup *New(TempAllocator &alloc, MDefinition *obj, types::ObjectGroup *group, + static MGuardObjectGroup *New(TempAllocator &alloc, MDefinition *obj, ObjectGroup *group, bool bailOnEquality, BailoutKind bailoutKind) { return new(alloc) MGuardObjectGroup(obj, group, bailOnEquality, bailoutKind); } @@ -9885,7 +9885,7 @@ class MGuardObjectGroup MDefinition *obj() const { return getOperand(0); } - const types::ObjectGroup *group() const { + const ObjectGroup *group() const { return group_; } bool bailOnEquality() const { diff --git a/js/src/jit/MIRGenerator.h b/js/src/jit/MIRGenerator.h index e6dd38400780..dac1fcef865f 100644 --- a/js/src/jit/MIRGenerator.h +++ b/js/src/jit/MIRGenerator.h @@ -154,7 +154,7 @@ class MIRGenerator return modifiesFrameArguments_; } - typedef Vector ObjectGroupVector; + typedef Vector ObjectGroupVector; // When abortReason() == AbortReason_NewScriptProperties, all types which // the new script properties analysis hasn't been performed on yet. @@ -199,7 +199,7 @@ class MIRGenerator // CodeGenerator::link). ObjectVector nurseryObjects_; - void addAbortedNewScriptPropertiesGroup(types::ObjectGroup *type); + void addAbortedNewScriptPropertiesGroup(ObjectGroup *type); void setForceAbort() { shouldForceAbort_ = true; } diff --git a/js/src/jit/MIRGraph.cpp b/js/src/jit/MIRGraph.cpp index 36e3c3faca9b..49f8b187747c 100644 --- a/js/src/jit/MIRGraph.cpp +++ b/js/src/jit/MIRGraph.cpp @@ -93,7 +93,7 @@ MIRGenerator::abort(const char *message, ...) } void -MIRGenerator::addAbortedNewScriptPropertiesGroup(types::ObjectGroup *group) +MIRGenerator::addAbortedNewScriptPropertiesGroup(ObjectGroup *group) { for (size_t i = 0; i < abortedNewScriptPropertiesGroups_.length(); i++) { if (group == abortedNewScriptPropertiesGroups_[i]) diff --git a/js/src/jit/MacroAssembler.cpp b/js/src/jit/MacroAssembler.cpp index 525646870239..27e0f278a84e 100644 --- a/js/src/jit/MacroAssembler.cpp +++ b/js/src/jit/MacroAssembler.cpp @@ -60,7 +60,7 @@ class TypeWrapper { return t_.singletonNoBarrier(); return nullptr; } - inline types::ObjectGroup *getGroupNoBarrier(unsigned) const { + inline ObjectGroup *getGroupNoBarrier(unsigned) const { if (t_.isGroup()) return t_.groupNoBarrier(); return nullptr; @@ -146,8 +146,8 @@ MacroAssembler::guardTypeSet(const Source &address, const TypeSet *types, Barrie extractObject(address, scratch); loadPtr(Address(obj, JSObject::offsetOfGroup()), scratch); branchTestPtr(Assembler::NonZero, - Address(scratch, types::ObjectGroup::offsetOfFlags()), - Imm32(types::OBJECT_FLAG_UNKNOWN_PROPERTIES), &matched); + Address(scratch, ObjectGroup::offsetOfFlags()), + Imm32(OBJECT_FLAG_UNKNOWN_PROPERTIES), &matched); assumeUnreachable("Unexpected object type"); #endif @@ -210,7 +210,7 @@ MacroAssembler::guardObjectType(Register obj, const TypeSet *types, if (lastBranch.isInitialized()) lastBranch.emit(*this); - types::ObjectGroup *group = types->getGroupNoBarrier(i); + ObjectGroup *group = types->getGroupNoBarrier(i); lastBranch = BranchGCPtr(Equal, scratch, ImmGCPtr(group), &matched); } } diff --git a/js/src/jit/MacroAssembler.h b/js/src/jit/MacroAssembler.h index 2f92bfd37b90..ddb8eae3ebaa 100644 --- a/js/src/jit/MacroAssembler.h +++ b/js/src/jit/MacroAssembler.h @@ -290,12 +290,12 @@ class MacroAssembler : public MacroAssemblerSpecific } void loadObjClass(Register objReg, Register dest) { loadPtr(Address(objReg, JSObject::offsetOfGroup()), dest); - loadPtr(Address(dest, types::ObjectGroup::offsetOfClasp()), dest); + loadPtr(Address(dest, ObjectGroup::offsetOfClasp()), dest); } void branchTestObjClass(Condition cond, Register obj, Register scratch, const js::Class *clasp, Label *label) { loadPtr(Address(obj, JSObject::offsetOfGroup()), scratch); - branchPtr(cond, Address(scratch, types::ObjectGroup::offsetOfClasp()), ImmPtr(clasp), label); + branchPtr(cond, Address(scratch, ObjectGroup::offsetOfClasp()), ImmPtr(clasp), label); } void branchTestObjShape(Condition cond, Register obj, const Shape *shape, Label *label) { branchPtr(cond, Address(obj, JSObject::offsetOfShape()), ImmGCPtr(shape), label); @@ -348,7 +348,7 @@ class MacroAssembler : public MacroAssemblerSpecific void loadObjProto(Register obj, Register dest) { loadPtr(Address(obj, JSObject::offsetOfGroup()), dest); - loadPtr(Address(dest, types::ObjectGroup::offsetOfProto()), dest); + loadPtr(Address(dest, ObjectGroup::offsetOfProto()), dest); } void loadStringLength(Register str, Register dest) { diff --git a/js/src/jit/OptimizationTracking.cpp b/js/src/jit/OptimizationTracking.cpp index 7cfa5d551d9b..a5c3aed54015 100644 --- a/js/src/jit/OptimizationTracking.cpp +++ b/js/src/jit/OptimizationTracking.cpp @@ -818,7 +818,7 @@ MaybeConstructorFromType(types::Type ty) { if (ty.isUnknown() || ty.isAnyObject() || !ty.isGroup()) return nullptr; - types::ObjectGroup *obj = ty.group(); + ObjectGroup *obj = ty.group(); types::TypeNewScript *newScript = obj->newScript(); if (!newScript && obj->maybeUnboxedLayout()) newScript = obj->unboxedLayout().newScript(); @@ -954,7 +954,7 @@ jit::WriteIonTrackedOptimizationsTable(JSContext *cx, CompactBufferWriter &write } else { JSScript *script; uint32_t offset; - if (cx->findAllocationSiteForType(ty, &script, &offset)) { + if (ObjectGroup::findAllocationSiteForType(cx, ty, &script, &offset)) { if (!allTypes->append(IonTrackedTypeWithAddendum(ty, script, offset))) return false; SpewAllocationSite(ty, script, offset); diff --git a/js/src/jit/Recover.h b/js/src/jit/Recover.h index dd94f9701951..abaf68f54fc5 100644 --- a/js/src/jit/Recover.h +++ b/js/src/jit/Recover.h @@ -11,6 +11,7 @@ #include "jsarray.h" +#include "jit/MIR.h" #include "jit/Snapshots.h" struct JSContext; diff --git a/js/src/jit/ScalarReplacement.cpp b/js/src/jit/ScalarReplacement.cpp index 61bbfe36b384..6a82c13bc8ad 100644 --- a/js/src/jit/ScalarReplacement.cpp +++ b/js/src/jit/ScalarReplacement.cpp @@ -13,6 +13,7 @@ #include "jit/MIR.h" #include "jit/MIRGenerator.h" #include "jit/MIRGraph.h" +#include "vm/UnboxedObject.h" namespace js { namespace jit { diff --git a/js/src/jit/VMFunctions.cpp b/js/src/jit/VMFunctions.cpp index da87c369c33e..7f637041f910 100644 --- a/js/src/jit/VMFunctions.cpp +++ b/js/src/jit/VMFunctions.cpp @@ -1288,7 +1288,7 @@ MarkShapeFromIon(JSRuntime *rt, Shape **shapep) } void -MarkObjectGroupFromIon(JSRuntime *rt, types::ObjectGroup **groupp) +MarkObjectGroupFromIon(JSRuntime *rt, ObjectGroup **groupp) { gc::MarkObjectGroupUnbarriered(&rt->gc.marker, groupp, "write barrier"); } diff --git a/js/src/jit/VMFunctions.h b/js/src/jit/VMFunctions.h index 187846326de8..420a5765672e 100644 --- a/js/src/jit/VMFunctions.h +++ b/js/src/jit/VMFunctions.h @@ -345,7 +345,7 @@ template <> struct TypeToArgProperties { static const uint32_t result = TypeToArgProperties::result | VMFunction::ByRef; }; template <> struct TypeToArgProperties { - static const uint32_t result = TypeToArgProperties::result | VMFunction::ByRef; + static const uint32_t result = TypeToArgProperties::result | VMFunction::ByRef; }; // Convert argument type to whether or not it should be passed in a float @@ -759,7 +759,7 @@ void MarkValueFromIon(JSRuntime *rt, Value *vp); void MarkStringFromIon(JSRuntime *rt, JSString **stringp); void MarkObjectFromIon(JSRuntime *rt, JSObject **objp); void MarkShapeFromIon(JSRuntime *rt, Shape **shapep); -void MarkObjectGroupFromIon(JSRuntime *rt, types::ObjectGroup **groupp); +void MarkObjectGroupFromIon(JSRuntime *rt, ObjectGroup **groupp); // Helper for generatePreBarrier. inline void * diff --git a/js/src/jit/shared/CodeGenerator-x86-shared.cpp b/js/src/jit/shared/CodeGenerator-x86-shared.cpp index 52d4528fb8d5..c5a6f4b837cf 100644 --- a/js/src/jit/shared/CodeGenerator-x86-shared.cpp +++ b/js/src/jit/shared/CodeGenerator-x86-shared.cpp @@ -1991,7 +1991,7 @@ CodeGeneratorX86Shared::visitGuardClass(LGuardClass *guard) Register tmp = ToRegister(guard->tempInt()); masm.loadPtr(Address(obj, JSObject::offsetOfGroup()), tmp); - masm.cmpPtr(Operand(tmp, types::ObjectGroup::offsetOfClasp()), ImmPtr(guard->mir()->getClass())); + masm.cmpPtr(Operand(tmp, ObjectGroup::offsetOfClasp()), ImmPtr(guard->mir()->getClass())); bailoutIf(Assembler::NotEqual, guard->snapshot()); } diff --git a/js/src/jsapi-tests/moz.build b/js/src/jsapi-tests/moz.build index 9babe2350778..0f2c4d65ab49 100644 --- a/js/src/jsapi-tests/moz.build +++ b/js/src/jsapi-tests/moz.build @@ -43,6 +43,7 @@ UNIFIED_SOURCES += [ 'testGCNursery.cpp', 'testGCOutOfMemory.cpp', 'testGCStoreBufferRemoval.cpp', + 'testGetPropertyDescriptor.cpp', 'testHashTable.cpp', 'testHashTableInit.cpp', 'testIndexToString.cpp', diff --git a/js/src/jsapi-tests/testGetPropertyDescriptor.cpp b/js/src/jsapi-tests/testGetPropertyDescriptor.cpp new file mode 100644 index 000000000000..3cc4def91a87 --- /dev/null +++ b/js/src/jsapi-tests/testGetPropertyDescriptor.cpp @@ -0,0 +1,32 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "jsapi-tests/tests.h" + +BEGIN_TEST(test_GetPropertyDescriptor) +{ + JS::RootedValue v(cx); + EVAL("({ somename : 123 })", &v); + CHECK(v.isObject()); + + JS::RootedObject obj(cx, &v.toObject()); + JS::Rooted desc(cx); + + CHECK(JS_GetPropertyDescriptor(cx, obj, "somename", &desc)); + CHECK_EQUAL(desc.object(), obj); + CHECK_SAME(desc.value(), JS::Int32Value(123)); + + CHECK(JS_GetPropertyDescriptor(cx, obj, "not-here", &desc)); + CHECK_EQUAL(desc.object(), nullptr); + + CHECK(JS_GetPropertyDescriptor(cx, obj, "toString", &desc)); + JS::RootedObject objectProto(cx, JS_GetObjectPrototype(cx, obj)); + CHECK(objectProto); + CHECK_EQUAL(desc.object(), objectProto); + CHECK(desc.value().isObject()); + CHECK(JS::IsCallable(&desc.value().toObject())); + + return true; +} +END_TEST(test_GetPropertyDescriptor) diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 8c003eb47dbd..d8978e6ca012 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -2624,46 +2624,6 @@ JS::ParsePropertyDescriptorObject(JSContext *cx, return true; } -static bool -GetPropertyDescriptorById(JSContext *cx, HandleObject obj, HandleId id, - MutableHandle desc) -{ - RootedObject obj2(cx); - RootedShape shape(cx); - - if (!LookupProperty(cx, obj, id, &obj2, &shape)) - return false; - - desc.clear(); - if (!shape) - return true; - - desc.object().set(obj2); - if (obj2->isNative()) { - if (IsImplicitDenseOrTypedArrayElement(shape)) { - desc.setEnumerable(); - desc.value().set(obj2->as().getDenseOrTypedArrayElement(JSID_TO_INT(id))); - } else { - desc.setAttributes(shape->attributes()); - desc.setGetter(shape->getter()); - desc.setSetter(shape->setter()); - MOZ_ASSERT(desc.value().isUndefined()); - if (shape->hasSlot()) - desc.value().set(obj2->as().getSlot(shape->slot())); - } - - return true; - } - - // When we hit a proxy during lookup, the property might be - // on the prototype of the proxy, thus use getPropertyDescriptor. - if (obj2->is()) - return Proxy::getPropertyDescriptor(cx, obj2, id, desc); - - // Assume other non-natives (i.e. TypedObjects) behave in a sane way. - return GetOwnPropertyDescriptor(cx, obj2, id, desc); -} - JS_PUBLIC_API(bool) JS_GetOwnPropertyDescriptorById(JSContext *cx, HandleObject obj, HandleId id, MutableHandle desc) @@ -2689,7 +2649,7 @@ JS_PUBLIC_API(bool) JS_GetPropertyDescriptorById(JSContext *cx, HandleObject obj, HandleId id, MutableHandle desc) { - return GetPropertyDescriptorById(cx, obj, id, desc); + return GetPropertyDescriptor(cx, obj, id, desc); } JS_PUBLIC_API(bool) diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index 6c6e79655ebc..8f99d7f54130 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -1247,7 +1247,7 @@ InitArrayElements(JSContext *cx, HandleObject obj, uint32_t start, uint32_t coun if (count == 0) return true; - types::ObjectGroup *group = obj->getGroup(cx); + ObjectGroup *group = obj->getGroup(cx); if (!group) return false; if (updateTypes && !InitArrayTypes(cx, group, vector, count)) @@ -2326,7 +2326,7 @@ TryReuseArrayGroup(JSObject *obj, ArrayObject *narr) * as obj. This can only be performed if the original object is an array * and has the same prototype. */ - MOZ_ASSERT(narr->getProto()->hasNewGroup(&ArrayObject::class_, narr->group())); + MOZ_ASSERT(ObjectGroup::hasDefaultNewGroup(narr->getProto(), &ArrayObject::class_, narr->group())); if (obj->is() && !obj->isSingleton() && obj->getProto() == narr->getProto()) narr->setGroup(obj->group()); @@ -2363,7 +2363,7 @@ CanOptimizeForDenseStorage(HandleObject arr, uint32_t startingIndex, uint32_t co * case can't happen, because any dense array used as the prototype of * another object is first slowified, for type inference's sake. */ - types::ObjectGroup *arrGroup = arr->getGroup(cx); + ObjectGroup *arrGroup = arr->getGroup(cx); if (MOZ_UNLIKELY(!arrGroup || arrGroup->hasAllFlags(OBJECT_FLAG_ITERATED))) return false; @@ -2982,7 +2982,7 @@ array_filter(JSContext *cx, unsigned argc, Value *vp) RootedObject arr(cx, NewDenseFullyAllocatedArray(cx, 0)); if (!arr) return false; - ObjectGroup *newGroup = GetCallerInitGroup(cx, JSProto_Array); + ObjectGroup *newGroup = ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array); if (!newGroup) return false; arr->setGroup(newGroup); @@ -3082,7 +3082,7 @@ array_of(JSContext *cx, unsigned argc, Value *vp) if (IsArrayConstructor(args.thisv()) || !IsConstructor(args.thisv())) { // IsArrayConstructor(this) will usually be true in practice. This is // the most common path. - RootedObjectGroup group(cx, GetCallerInitGroup(cx, JSProto_Array)); + RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array)); if (!group) return false; return ArrayFromCallArgs(cx, group, args); @@ -3189,7 +3189,7 @@ bool js_Array(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); - RootedObjectGroup group(cx, GetCallerInitGroup(cx, JSProto_Array)); + RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array)); if (!group) return false; @@ -3251,7 +3251,8 @@ CreateArrayPrototype(JSContext *cx, JSProtoKey key) if (!proto) return nullptr; - RootedObjectGroup group(cx, cx->getNewGroup(&ArrayObject::class_, TaggedProto(proto))); + RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &ArrayObject::class_, + TaggedProto(proto))); if (!group) return nullptr; @@ -3379,7 +3380,8 @@ NewArray(ExclusiveContext *cxArg, uint32_t length, if (!proto && !GetBuiltinPrototype(cxArg, JSProto_Array, &proto)) return nullptr; - RootedObjectGroup group(cxArg, cxArg->getNewGroup(&ArrayObject::class_, TaggedProto(proto))); + RootedObjectGroup group(cxArg, ObjectGroup::defaultNewGroup(cxArg, &ArrayObject::class_, + TaggedProto(proto))); if (!group) return nullptr; diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index 08ca0bde81a8..9e55b9481479 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -116,7 +116,7 @@ js::ExistingCloneFunctionAtCallsite(const CallsiteCloneTable &table, JSFunction { MOZ_ASSERT(fun->nonLazyScript()->shouldCloneAtCallsite()); MOZ_ASSERT(!fun->nonLazyScript()->enclosingStaticScope()); - MOZ_ASSERT(types::UseSingletonForClone(fun)); + MOZ_ASSERT(ObjectGroup::useSingletonForClone(fun)); /* * If we start allocating function objects in the nursery, then the callsite @@ -253,7 +253,7 @@ js::DestroyContext(JSContext *cx, DestroyContextMode mode) * This printing depends on atoms still existing. */ for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) - c->types.print(cx, false); + types::PrintTypes(cx, c, false); } if (mode == DCM_FORCE_GC) { MOZ_ASSERT(!rt->isHeapBusy()); diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 46b9bc70c841..d155a09987c5 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -301,13 +301,6 @@ class ExclusiveContext : public ContextFriendFields, } // Zone local methods that can be used freely from an ExclusiveContext. - types::ObjectGroup *getNewGroup(const Class *clasp, TaggedProto proto, - JSObject *associated = nullptr); - types::ObjectGroup *getLazySingletonGroup(const Class *clasp, TaggedProto proto); - - // Returns false if not found. - bool findAllocationSiteForType(types::Type ty, JSScript **script, uint32_t *offset) const; - inline js::LifoAlloc &typeLifoAlloc(); // Current global. This is only safe to use within the scope of the diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index 892d6eebb5c9..40d5034edeb7 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -544,13 +544,6 @@ JSCompartment::sweepInnerViews() innerViews.sweep(runtimeFromAnyThread()); } -void -JSCompartment::sweepObjectGroupTables() -{ - sweepNewObjectGroupTable(newObjectGroups); - sweepNewObjectGroupTable(lazyObjectGroups); -} - void JSCompartment::sweepSavedStacks() { @@ -652,10 +645,9 @@ JSCompartment::sweepCrossCompartmentWrappers() void JSCompartment::fixupAfterMovingGC() { fixupGlobal(); - fixupNewObjectGroupTable(newObjectGroups); - fixupNewObjectGroupTable(lazyObjectGroups); fixupInitialShapeTable(); fixupBaseShapeTable(); + objectGroups.fixupTablesAfterMovingGC(); } void @@ -688,15 +680,11 @@ JSCompartment::clearTables() MOZ_ASSERT(enumerators->next() == enumerators); MOZ_ASSERT(regExps.empty()); - types.clearTables(); + objectGroups.clearTables(); if (baseShapes.initialized()) baseShapes.clear(); if (initialShapes.initialized()) initialShapes.clear(); - if (newObjectGroups.initialized()) - newObjectGroups.clear(); - if (lazyObjectGroups.initialized()) - lazyObjectGroups.clear(); if (savedStacks_.initialized()) savedStacks_.clear(); } @@ -816,12 +804,11 @@ JSCompartment::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, size_t *savedStacksSet) { *compartmentObject += mallocSizeOf(this); - types.addSizeOfExcludingThis(mallocSizeOf, tiAllocationSiteTables, - tiArrayTypeTables, tiObjectTypeTables); + objectGroups.addSizeOfExcludingThis(mallocSizeOf, tiAllocationSiteTables, + tiArrayTypeTables, tiObjectTypeTables, + compartmentTables); *compartmentTables += baseShapes.sizeOfExcludingThis(mallocSizeOf) - + initialShapes.sizeOfExcludingThis(mallocSizeOf) - + newObjectGroups.sizeOfExcludingThis(mallocSizeOf) - + lazyObjectGroups.sizeOfExcludingThis(mallocSizeOf); + + initialShapes.sizeOfExcludingThis(mallocSizeOf); *innerViewsArg += innerViews.sizeOfExcludingThis(mallocSizeOf); if (lazyArrayBuffers) *lazyArrayBuffersArg += lazyArrayBuffers->sizeOfIncludingThis(mallocSizeOf); diff --git a/js/src/jscompartment.h b/js/src/jscompartment.h index 76a9af73ad7b..ff887ca10aee 100644 --- a/js/src/jscompartment.h +++ b/js/src/jscompartment.h @@ -220,9 +220,6 @@ struct JSCompartment inline void initGlobal(js::GlobalObject &global); public: - /* Type information about the scripts and objects in this compartment. */ - js::types::TypeCompartment types; - void *data; private: @@ -276,14 +273,10 @@ struct JSCompartment js::InitialShapeSet initialShapes; void sweepInitialShapeTable(); - /* Set of default 'new' or lazy groups in the compartment. */ - js::types::NewObjectGroupTable newObjectGroups; - js::types::NewObjectGroupTable lazyObjectGroups; - void sweepNewObjectGroupTable(js::types::NewObjectGroupTable &table); + // Object group tables and other state in the compartment. + js::ObjectGroupCompartment objectGroups; #ifdef JSGC_HASH_TABLE_CHECKS - void checkObjectGroupTablesAfterMovingGC(); - void checkObjectGroupTableAfterMovingGC(js::types::NewObjectGroupTable &table); void checkInitialShapesTableAfterMovingGC(); void checkWrapperMapAfterMovingGC(); void checkBaseShapeTableAfterMovingGC(); @@ -309,6 +302,9 @@ struct JSCompartment // Map from typed objects to array buffers lazily created for them. js::LazyArrayBufferTable *lazyArrayBuffers; + // All unboxed layouts in the compartment. + mozilla::LinkedList unboxedLayouts; + /* During GC, stores the index of this compartment in rt->compartments. */ unsigned gcIndex; @@ -386,7 +382,6 @@ struct JSCompartment void sweepInnerViews(); void sweepCrossCompartmentWrappers(); - void sweepObjectGroupTables(); void sweepSavedStacks(); void sweepGlobalObject(js::FreeOp *fop); void sweepSelfHostingScriptSource(); @@ -400,7 +395,6 @@ struct JSCompartment void clearTables(); void fixupInitialShapeTable(); - void fixupNewObjectGroupTable(js::types::NewObjectGroupTable &table); void fixupAfterMovingGC(); void fixupGlobal(); void fixupBaseShapeTable(); diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp index 498bfa05c0d7..746f361ff2e1 100644 --- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -64,14 +64,6 @@ js::ForgetSourceHook(JSRuntime *rt) return Move(rt->sourceHook); } -#ifdef NIGHTLY_BUILD -JS_FRIEND_API(void) -js::SetAssertOnScriptEntryHook(JSRuntime *rt, AssertOnScriptEntryHook hook) -{ - rt->assertOnScriptEntryHook_ = hook; -} -#endif - JS_FRIEND_API(void) JS_SetGrayGCRootsTracer(JSRuntime *rt, JSTraceDataOp traceOp, void *data) { diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index 7638d2c9bf27..c4373ce6e37e 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -409,13 +409,6 @@ SetSourceHook(JSRuntime *rt, mozilla::UniquePtr hook); extern JS_FRIEND_API(mozilla::UniquePtr) ForgetSourceHook(JSRuntime *rt); -#ifdef NIGHTLY_BUILD -typedef void (*AssertOnScriptEntryHook)(JSContext *cx, JS::HandleScript script); - -extern JS_FRIEND_API(void) -SetAssertOnScriptEntryHook(JSRuntime *rt, AssertOnScriptEntryHook hook); -#endif - extern JS_FRIEND_API(JS::Zone *) GetCompartmentZone(JSCompartment *comp); diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index a07afaf6854c..909bebd67b20 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -873,7 +873,7 @@ CreateFunctionPrototype(JSContext *cx, JSProtoKey key) return nullptr; functionProto->initScript(script); - types::ObjectGroup* protoGroup = functionProto->getGroup(cx); + ObjectGroup* protoGroup = functionProto->getGroup(cx); if (!protoGroup) return nullptr; @@ -2037,7 +2037,7 @@ js::CloneFunctionObjectUseSameScript(JSCompartment *compartment, HandleFunction { return compartment == fun->compartment() && !fun->isSingleton() && - !types::UseSingletonForClone(fun); + !ObjectGroup::useSingletonForClone(fun); } JSFunction * diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index aa7d001738b0..024e12c23f8f 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -286,7 +286,7 @@ const uint32_t Arena::ThingSizes[] = CHECK_MIN_THING_SIZE( sizeof(Shape), /* FINALIZE_SHAPE */ sizeof(AccessorShape), /* FINALIZE_ACCESSOR_SHAPE */ sizeof(BaseShape), /* FINALIZE_BASE_SHAPE */ - sizeof(types::ObjectGroup), /* FINALIZE_OBJECT_GROUP */ + sizeof(ObjectGroup), /* FINALIZE_OBJECT_GROUP */ sizeof(JSFatInlineString), /* FINALIZE_FAT_INLINE_STRING */ sizeof(JSString), /* FINALIZE_STRING */ sizeof(JSExternalString), /* FINALIZE_EXTERNAL_STRING */ @@ -317,7 +317,7 @@ const uint32_t Arena::FirstThingOffsets[] = { OFFSET(Shape), /* FINALIZE_SHAPE */ OFFSET(AccessorShape), /* FINALIZE_ACCESSOR_SHAPE */ OFFSET(BaseShape), /* FINALIZE_BASE_SHAPE */ - OFFSET(types::ObjectGroup), /* FINALIZE_OBJECT_GROUP */ + OFFSET(ObjectGroup), /* FINALIZE_OBJECT_GROUP */ OFFSET(JSFatInlineString), /* FINALIZE_FAT_INLINE_STRING */ OFFSET(JSString), /* FINALIZE_STRING */ OFFSET(JSExternalString), /* FINALIZE_EXTERNAL_STRING */ @@ -609,7 +609,7 @@ FinalizeArenas(FreeOp *fop, case FINALIZE_BASE_SHAPE: return FinalizeTypedArenas(fop, src, dest, thingKind, budget, keepArenas); case FINALIZE_OBJECT_GROUP: - return FinalizeTypedArenas(fop, src, dest, thingKind, budget, keepArenas); + return FinalizeTypedArenas(fop, src, dest, thingKind, budget, keepArenas); case FINALIZE_STRING: return FinalizeTypedArenas(fop, src, dest, thingKind, budget, keepArenas); case FINALIZE_FAT_INLINE_STRING: @@ -2226,7 +2226,7 @@ GCRuntime::sweepTypesAfterCompacting(Zone *zone) } for (ZoneCellIterUnderGC i(zone, FINALIZE_OBJECT_GROUP); !i.done(); i.next()) { - types::ObjectGroup *group = i.get(); + ObjectGroup *group = i.get(); group->maybeSweep(&oom); } @@ -2247,7 +2247,7 @@ GCRuntime::sweepZoneAfterCompacting(Zone *zone) c->sweepCrossCompartmentWrappers(); c->sweepBaseShapeTable(); c->sweepInitialShapeTable(); - c->sweepObjectGroupTables(); + c->objectGroups.sweep(fop); c->sweepRegExps(); c->sweepCallsiteClones(); c->sweepSavedStacks(); @@ -2311,7 +2311,7 @@ UpdateCellPointers(MovingTracer *trc, ArenaHeader *arena) UpdateCellPointersTyped(trc, arena, traceKind); return; case FINALIZE_OBJECT_GROUP: - UpdateCellPointersTyped(trc, arena, traceKind); + UpdateCellPointersTyped(trc, arena, traceKind); return; case FINALIZE_JITCODE: UpdateCellPointersTyped(trc, arena, traceKind); @@ -4872,7 +4872,7 @@ SweepInitialShapesTask::run() SweepObjectGroupsTask::run() { for (GCCompartmentGroupIter c(runtime); !c.done(); c.next()) - c->sweepObjectGroupTables(); + c->objectGroups.sweep(runtime->defaultFreeOp()); } /* virtual */ void @@ -5197,7 +5197,7 @@ SweepThing(JSScript *script, types::AutoClearTypeInferenceStateOnOOM *oom) } static void -SweepThing(types::ObjectGroup *group, types::AutoClearTypeInferenceStateOnOOM *oom) +SweepThing(ObjectGroup *group, types::AutoClearTypeInferenceStateOnOOM *oom) { group->maybeSweep(oom); } @@ -5250,7 +5250,7 @@ GCRuntime::sweepPhase(SliceBudget &sliceBudget) if (!SweepArenaList(&al.gcScriptArenasToUpdate, sliceBudget, &oom)) return NotFinished; - if (!SweepArenaList( + if (!SweepArenaList( &al.gcObjectGroupArenasToUpdate, sliceBudget, &oom)) { return NotFinished; @@ -6542,7 +6542,7 @@ gc::MergeCompartments(JSCompartment *source, JSCompartment *target) } for (ZoneCellIter iter(source->zone(), FINALIZE_OBJECT_GROUP); !iter.done(); iter.next()) { - types::ObjectGroup *group = iter.get(); + ObjectGroup *group = iter.get(); group->setGeneration(target->zone()->types.generation); } @@ -6967,7 +6967,7 @@ js::gc::CheckHashTablesAfterMovingGC(JSRuntime *rt) * that have been moved. */ for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) { - c->checkObjectGroupTablesAfterMovingGC(); + c->objectGroups.checkTablesAfterMovingGC(); c->checkInitialShapesTableAfterMovingGC(); c->checkWrapperMapAfterMovingGC(); c->checkBaseShapeTableAfterMovingGC(); @@ -7123,7 +7123,7 @@ JS::IncrementalReferenceBarrier(GCCellPtr thing) case JSTRACE_BASE_SHAPE: return BaseShape::writeBarrierPre(static_cast(thing.asCell())); case JSTRACE_OBJECT_GROUP: - return types::ObjectGroup::writeBarrierPre(static_cast(thing.asCell())); + return ObjectGroup::writeBarrierPre(static_cast(thing.asCell())); default: MOZ_CRASH("Invalid trace kind in IncrementalReferenceBarrier."); } diff --git a/js/src/jsgc.h b/js/src/jsgc.h index 575bcffebc93..2e40ad6f6fb1 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -68,7 +68,7 @@ template <> struct MapTypeToFinalizeKind { static const Alloc template <> struct MapTypeToFinalizeKind { static const AllocKind kind = FINALIZE_SHAPE; }; template <> struct MapTypeToFinalizeKind { static const AllocKind kind = FINALIZE_ACCESSOR_SHAPE; }; template <> struct MapTypeToFinalizeKind { static const AllocKind kind = FINALIZE_BASE_SHAPE; }; -template <> struct MapTypeToFinalizeKind { static const AllocKind kind = FINALIZE_OBJECT_GROUP; }; +template <> struct MapTypeToFinalizeKind { static const AllocKind kind = FINALIZE_OBJECT_GROUP; }; template <> struct MapTypeToFinalizeKind { static const AllocKind kind = FINALIZE_FAT_INLINE_STRING; }; template <> struct MapTypeToFinalizeKind { static const AllocKind kind = FINALIZE_STRING; }; template <> struct MapTypeToFinalizeKind { static const AllocKind kind = FINALIZE_EXTERNAL_STRING; }; diff --git a/js/src/jsgcinlines.h b/js/src/jsgcinlines.h index 7c200dd658eb..cbc37c41bd1f 100644 --- a/js/src/jsgcinlines.h +++ b/js/src/jsgcinlines.h @@ -614,10 +614,10 @@ NewJitCode(js::ExclusiveContext *cx) } inline -types::ObjectGroup * +ObjectGroup * NewObjectGroup(js::ExclusiveContext *cx) { - return gc::AllocateNonObject(cx); + return gc::AllocateNonObject(cx); } template diff --git a/js/src/jshashutil.h b/js/src/jshashutil.h index 3889c80c73e8..443b42809975 100644 --- a/js/src/jshashutil.h +++ b/js/src/jshashutil.h @@ -9,6 +9,8 @@ #include "jscntxt.h" +#include "gc/Zone.h" + namespace js { /* diff --git a/js/src/jsinfer.cpp b/js/src/jsinfer.cpp index bf61065b1956..7124c8227461 100644 --- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -225,15 +225,6 @@ types::ObjectGroupString(ObjectGroup *group) return TypeString(Type::ObjectType(group)); } -unsigned JSScript::id() { - if (!id_) { - id_ = ++compartment()->types.scriptCount; - InferSpew(ISpewOps, "script #%u: %p %s:%d", - id_, this, filename() ? filename() : "", lineno()); - } - return id_; -} - void types::InferSpew(SpewChannel channel, const char *fmt, ...) { @@ -308,7 +299,7 @@ types::TypeFailure(JSContext *cx, const char *fmt, ...) JS_snprintf(msgbuf, sizeof(msgbuf), "[infer failure] %s", errbuf); /* Dump type state, even if INFERFLAGS is unset. */ - cx->compartment()->types.print(cx, true); + PrintTypes(cx, cx->compartment(), true); MOZ_ReportAssertionFailure(msgbuf, __FILE__, __LINE__); MOZ_CRASH(); @@ -2260,182 +2251,6 @@ TemporaryTypeSet::propertyNeedsBarrier(CompilerConstraintList *constraints, jsid return false; } -///////////////////////////////////////////////////////////////////// -// TypeCompartment -///////////////////////////////////////////////////////////////////// - -TypeCompartment::TypeCompartment() -{ - PodZero(this); -} - -ObjectGroup * -TypeCompartment::newObjectGroup(ExclusiveContext *cx, const Class *clasp, Handle proto, - ObjectGroupFlags initialFlags) -{ - MOZ_ASSERT_IF(proto.isObject(), cx->isInsideCurrentCompartment(proto.toObject())); - - if (cx->isJSContext()) { - if (proto.isObject() && IsInsideNursery(proto.toObject())) - initialFlags |= OBJECT_FLAG_NURSERY_PROTO; - } - - ObjectGroup *group = js::NewObjectGroup(cx); - if (!group) - return nullptr; - new(group) ObjectGroup(clasp, proto, initialFlags); - - return group; -} - -ObjectGroup * -TypeCompartment::addAllocationSiteObjectGroup(JSContext *cx, AllocationSiteKey key) -{ - AutoEnterAnalysis enter(cx); - - if (!allocationSiteTable) { - allocationSiteTable = cx->new_(); - if (!allocationSiteTable || !allocationSiteTable->init()) { - js_delete(allocationSiteTable); - allocationSiteTable = nullptr; - return nullptr; - } - } - - AllocationSiteTable::AddPtr p = allocationSiteTable->lookupForAdd(key); - MOZ_ASSERT(!p); - - ObjectGroup *res = nullptr; - - jsbytecode *pc = key.script->offsetToPC(key.offset); - RootedScript keyScript(cx, key.script); - - if (!res) { - RootedObject proto(cx); - if (key.kind != JSProto_Null && !GetBuiltinPrototype(cx, key.kind, &proto)) - return nullptr; - - Rooted tagged(cx, TaggedProto(proto)); - res = newObjectGroup(cx, GetClassForProtoKey(key.kind), tagged, OBJECT_FLAG_FROM_ALLOCATION_SITE); - if (!res) - return nullptr; - key.script = keyScript; - } - - if (JSOp(*pc) == JSOP_NEWOBJECT) { - /* - * This object is always constructed the same way and will not be - * observed by other code before all properties have been added. Mark - * all the properties as definite properties of the object. - */ - RootedObject baseobj(cx, key.script->getObject(GET_UINT32_INDEX(pc))); - - if (!res->addDefiniteProperties(cx, baseobj->lastProperty())) - return nullptr; - } - - if (!allocationSiteTable->add(p, key, res)) - return nullptr; - - return res; -} - -static inline jsid -GetAtomId(JSContext *cx, JSScript *script, const jsbytecode *pc, unsigned offset) -{ - PropertyName *name = script->getName(GET_UINT32_INDEX(pc + offset)); - return IdToTypeId(NameToId(name)); -} - -bool -types::UseSingletonForNewObject(JSContext *cx, JSScript *script, jsbytecode *pc) -{ - /* - * Make a heuristic guess at a use of JSOP_NEW that the constructed object - * should have a fresh group. We do this when the NEW is immediately - * followed by a simple assignment to an object's .prototype field. - * This is designed to catch common patterns for subclassing in JS: - * - * function Super() { ... } - * function Sub1() { ... } - * function Sub2() { ... } - * - * Sub1.prototype = new Super(); - * Sub2.prototype = new Super(); - * - * Using distinct groups for the particular prototypes of Sub1 and - * Sub2 lets us continue to distinguish the two subclasses and any extra - * properties added to those prototype objects. - */ - if (script->isGenerator()) - return false; - if (JSOp(*pc) != JSOP_NEW) - return false; - pc += JSOP_NEW_LENGTH; - if (JSOp(*pc) == JSOP_SETPROP) { - jsid id = GetAtomId(cx, script, pc, 0); - if (id == id_prototype(cx)) - return true; - } - - return false; -} - -NewObjectKind -types::UseSingletonForInitializer(JSScript *script, jsbytecode *pc, JSProtoKey key) -{ - // The return value of this method can either be tested like a boolean or - // passed to a NewObject method. - JS_STATIC_ASSERT(GenericObject == 0); - - /* - * Objects created outside loops in global and eval scripts should have - * singleton types. For now this is only done for plain objects and typed - * arrays, but not normal arrays. - */ - - if (script->functionNonDelazifying() && !script->treatAsRunOnce()) - return GenericObject; - - if (key != JSProto_Object && - !(key >= JSProto_Int8Array && key <= JSProto_Uint8ClampedArray) && - !(key >= JSProto_SharedInt8Array && key <= JSProto_SharedUint8ClampedArray)) - { - return GenericObject; - } - - /* - * All loops in the script will have a JSTRY_ITER or JSTRY_LOOP try note - * indicating their boundary. - */ - - if (!script->hasTrynotes()) - return SingletonObject; - - unsigned offset = script->pcToOffset(pc); - - JSTryNote *tn = script->trynotes()->vector; - JSTryNote *tnlimit = tn + script->trynotes()->length; - for (; tn < tnlimit; tn++) { - if (tn->kind != JSTRY_ITER && tn->kind != JSTRY_LOOP) - continue; - - unsigned startOffset = script->mainOffset() + tn->start; - unsigned endOffset = startOffset + tn->length; - - if (offset >= startOffset && offset < endOffset) - return GenericObject; - } - - return SingletonObject; -} - -NewObjectKind -types::UseSingletonForInitializer(JSScript *script, jsbytecode *pc, const Class *clasp) -{ - return UseSingletonForInitializer(script, pc, JSCLASS_CACHED_PROTO_KEY(clasp)); -} - static inline bool ClassCanHaveExtraProperties(const Class *clasp) { @@ -2485,7 +2300,7 @@ types::TypeCanHaveExtraIndexedProperties(CompilerConstraintList *constraints, if (!clasp || (ClassCanHaveExtraProperties(clasp) && !IsAnyTypedArrayClass(clasp))) return true; - if (types->hasObjectFlags(constraints, types::OBJECT_FLAG_SPARSE_INDEXES)) + if (types->hasObjectFlags(constraints, OBJECT_FLAG_SPARSE_INDEXES)) return true; JSObject *proto = types->getCommonPrototype(constraints); @@ -2554,13 +2369,13 @@ TypeZone::addPendingRecompile(JSContext *cx, JSScript *script) } void -TypeCompartment::print(JSContext *cx, bool force) +types::PrintTypes(JSContext *cx, JSCompartment *comp, bool force) { #ifdef DEBUG gc::AutoSuppressGC suppressGC(cx); JSAutoRequest request(cx); - Zone *zone = compartment()->zone(); + Zone *zone = comp->zone(); AutoEnterAnalysis enter(nullptr, zone); if (!force && !InferSpewActive(ISpewResult)) @@ -2579,397 +2394,10 @@ TypeCompartment::print(JSContext *cx, bool force) #endif } -///////////////////////////////////////////////////////////////////// -// TypeCompartment tables -///////////////////////////////////////////////////////////////////// - -/* - * The arrayTypeTable and objectTypeTable are per-compartment tables for making - * common groups to model the contents of large script singletons and JSON - * objects. These are vanilla Arrays and native Objects, so we distinguish the - * types of different ones by looking at the types of their properties. - * - * All singleton/JSON arrays which have the same prototype, are homogenous and - * of the same element type will share a group. All singleton/JSON objects - * which have the same shape and property types will also share a group. - * We don't try to collate arrays or objects that have type mismatches. - */ - -static inline bool -NumberTypes(Type a, Type b) -{ - return (a.isPrimitive(JSVAL_TYPE_INT32) || a.isPrimitive(JSVAL_TYPE_DOUBLE)) - && (b.isPrimitive(JSVAL_TYPE_INT32) || b.isPrimitive(JSVAL_TYPE_DOUBLE)); -} - -/* - * As for GetValueType, but requires object types to be non-singletons with - * their default prototype. These are the only values that should appear in - * arrays and objects whose type can be fixed. - */ -static inline Type -GetValueTypeForTable(const Value &v) -{ - Type type = GetValueType(v); - MOZ_ASSERT(!type.isSingleton()); - return type; -} - -struct types::ArrayTableKey : public DefaultHasher -{ - Type type; - JSObject *proto; - - ArrayTableKey() - : type(Type::UndefinedType()), proto(nullptr) - {} - - ArrayTableKey(Type type, JSObject *proto) - : type(type), proto(proto) - {} - - static inline uint32_t hash(const ArrayTableKey &v) { - return (uint32_t) (v.type.raw() ^ ((uint32_t)(size_t)v.proto >> 2)); - } - - static inline bool match(const ArrayTableKey &v1, const ArrayTableKey &v2) { - return v1.type == v2.type && v1.proto == v2.proto; - } - - bool operator==(const ArrayTableKey& other) { - return type == other.type && proto == other.proto; - } - - bool operator!=(const ArrayTableKey& other) { - return !(*this == other); - } -}; - -void -TypeCompartment::setTypeToHomogenousArray(ExclusiveContext *cx, - JSObject *obj, Type elementType) -{ - MOZ_ASSERT(cx->zone()->types.activeAnalysis); - - if (!arrayTypeTable) { - arrayTypeTable = cx->new_(); - if (!arrayTypeTable || !arrayTypeTable->init()) { - arrayTypeTable = nullptr; - return; - } - } - - ArrayTableKey key(elementType, obj->getProto()); - DependentAddPtr p(cx, *arrayTypeTable, key); - if (p) { - obj->setGroup(p->value()); - } else { - /* Make a new type to use for future arrays with the same elements. */ - RootedObject objProto(cx, obj->getProto()); - Rooted taggedProto(cx, TaggedProto(objProto)); - ObjectGroup *group = newObjectGroup(cx, &ArrayObject::class_, taggedProto); - if (!group) - return; - obj->setGroup(group); - - AddTypePropertyId(cx, group, JSID_VOID, elementType); - - key.proto = objProto; - (void) p.add(cx, *arrayTypeTable, key, group); - } -} - -void -TypeCompartment::fixArrayGroup(ExclusiveContext *cx, ArrayObject *obj) -{ - AutoEnterAnalysis enter(cx); - - /* - * If the array is of homogenous type, pick a group which will be - * shared with all other singleton/JSON arrays of the same type. - * If the array is heterogenous, keep the existing group, which has - * unknown properties. - */ - - unsigned len = obj->getDenseInitializedLength(); - if (len == 0) - return; - - Type type = GetValueTypeForTable(obj->getDenseElement(0)); - - for (unsigned i = 1; i < len; i++) { - Type ntype = GetValueTypeForTable(obj->getDenseElement(i)); - if (ntype != type) { - if (NumberTypes(type, ntype)) - type = Type::DoubleType(); - else - return; - } - } - - setTypeToHomogenousArray(cx, obj, type); -} - -void -types::FixRestArgumentsType(ExclusiveContext *cx, ArrayObject *obj) -{ - cx->compartment()->types.fixRestArgumentsType(cx, obj); -} - -void -TypeCompartment::fixRestArgumentsType(ExclusiveContext *cx, ArrayObject *obj) -{ - AutoEnterAnalysis enter(cx); - - /* - * Tracking element types for rest argument arrays is not worth it, but we - * still want it to be known that it's a dense array. - */ - setTypeToHomogenousArray(cx, obj, Type::UnknownType()); -} - -/* - * N.B. We could also use the initial shape of the object (before its type is - * fixed) as the key in the object table, but since all references in the table - * are weak the hash entries would usually be collected on GC even if objects - * with the new type/shape are still live. - */ -struct types::ObjectTableKey -{ - jsid *properties; - uint32_t nproperties; - uint32_t nfixed; - - struct Lookup { - IdValuePair *properties; - uint32_t nproperties; - uint32_t nfixed; - - Lookup(IdValuePair *properties, uint32_t nproperties, uint32_t nfixed) - : properties(properties), nproperties(nproperties), nfixed(nfixed) - {} - }; - - static inline HashNumber hash(const Lookup &lookup) { - return (HashNumber) (JSID_BITS(lookup.properties[lookup.nproperties - 1].id) ^ - lookup.nproperties ^ - lookup.nfixed); - } - - static inline bool match(const ObjectTableKey &v, const Lookup &lookup) { - if (lookup.nproperties != v.nproperties || lookup.nfixed != v.nfixed) - return false; - for (size_t i = 0; i < lookup.nproperties; i++) { - if (lookup.properties[i].id != v.properties[i]) - return false; - } - return true; - } -}; - -struct types::ObjectTableEntry -{ - ReadBarrieredObjectGroup group; - ReadBarrieredShape shape; - Type *types; -}; - -static inline void -UpdateObjectTableEntryTypes(ExclusiveContext *cx, ObjectTableEntry &entry, - IdValuePair *properties, size_t nproperties) -{ - if (entry.group->unknownProperties()) - return; - for (size_t i = 0; i < nproperties; i++) { - Type type = entry.types[i]; - Type ntype = GetValueTypeForTable(properties[i].value); - if (ntype == type) - continue; - if (ntype.isPrimitive(JSVAL_TYPE_INT32) && - type.isPrimitive(JSVAL_TYPE_DOUBLE)) - { - /* The property types already reflect 'int32'. */ - } else { - if (ntype.isPrimitive(JSVAL_TYPE_DOUBLE) && - type.isPrimitive(JSVAL_TYPE_INT32)) - { - /* Include 'double' in the property types to avoid the update below later. */ - entry.types[i] = Type::DoubleType(); - } - AddTypePropertyId(cx, entry.group, IdToTypeId(properties[i].id), ntype); - } - } -} - -void -TypeCompartment::fixObjectGroup(ExclusiveContext *cx, PlainObject *obj) -{ - AutoEnterAnalysis enter(cx); - - if (!objectTypeTable) { - objectTypeTable = cx->new_(); - if (!objectTypeTable || !objectTypeTable->init()) { - js_delete(objectTypeTable); - objectTypeTable = nullptr; - return; - } - } - - /* - * Use the same group for all singleton/JSON objects with the same - * base shape, i.e. the same fields written in the same order. - * - * Exclude some objects we can't readily associate common types for based on their - * shape. Objects with metadata are excluded so that the metadata does not need to - * be included in the table lookup (the metadata object might be in the nursery). - */ - if (obj->slotSpan() == 0 || obj->inDictionaryMode() || !obj->hasEmptyElements() || obj->getMetadata()) - return; - - Vector properties(cx); - if (!properties.resize(obj->slotSpan())) - return; - - Shape *shape = obj->lastProperty(); - while (!shape->isEmptyShape()) { - IdValuePair &entry = properties[shape->slot()]; - entry.id = shape->propid(); - entry.value = obj->getSlot(shape->slot()); - shape = shape->previous(); - } - - ObjectTableKey::Lookup lookup(properties.begin(), properties.length(), obj->numFixedSlots()); - ObjectTypeTable::AddPtr p = objectTypeTable->lookupForAdd(lookup); - - if (p) { - MOZ_ASSERT(obj->getProto() == p->value().group->proto().toObject()); - MOZ_ASSERT(obj->lastProperty() == p->value().shape); - - UpdateObjectTableEntryTypes(cx, p->value(), properties.begin(), properties.length()); - obj->setGroup(p->value().group); - return; - } - - /* Make a new type to use for the object and similar future ones. */ - Rooted objProto(cx, obj->getTaggedProto()); - ObjectGroup *group = newObjectGroup(cx, &PlainObject::class_, objProto); - if (!group || !group->addDefiniteProperties(cx, obj->lastProperty())) - return; - - if (obj->isIndexed()) - group->setFlags(cx, OBJECT_FLAG_SPARSE_INDEXES); - - ScopedJSFreePtr ids(group->zone()->pod_calloc(properties.length())); - if (!ids) - return; - - ScopedJSFreePtr types(group->zone()->pod_calloc(properties.length())); - if (!types) - return; - - for (size_t i = 0; i < properties.length(); i++) { - ids[i] = properties[i].id; - types[i] = GetValueTypeForTable(obj->getSlot(i)); - AddTypePropertyId(cx, group, IdToTypeId(ids[i]), types[i]); - } - - ObjectTableKey key; - key.properties = ids; - key.nproperties = properties.length(); - key.nfixed = obj->numFixedSlots(); - MOZ_ASSERT(ObjectTableKey::match(key, lookup)); - - ObjectTableEntry entry; - entry.group.set(group); - entry.shape.set(obj->lastProperty()); - entry.types = types; - - obj->setGroup(group); - - p = objectTypeTable->lookupForAdd(lookup); - if (objectTypeTable->add(p, key, entry)) { - ids.forget(); - types.forget(); - } -} - -JSObject * -TypeCompartment::newTypedObject(JSContext *cx, IdValuePair *properties, size_t nproperties) -{ - AutoEnterAnalysis enter(cx); - - if (!objectTypeTable) { - objectTypeTable = cx->new_(); - if (!objectTypeTable || !objectTypeTable->init()) { - js_delete(objectTypeTable); - objectTypeTable = nullptr; - return nullptr; - } - } - - /* - * Use the object group table to allocate an object with the specified - * properties, filling in its final type and shape and failing if no cache - * entry could be found for the properties. - */ - - /* - * Filter out a few cases where we don't want to use the object group table. - * Note that if the properties contain any duplicates or dense indexes, - * the lookup below will fail as such arrays of properties cannot be stored - * in the object group table --- fixObjectGroup populates the table with - * properties read off its input object, which cannot be duplicates, and - * ignores objects with dense indexes. - */ - if (!nproperties || nproperties >= PropertyTree::MAX_HEIGHT) - return nullptr; - - gc::AllocKind allocKind = gc::GetGCObjectKind(nproperties); - size_t nfixed = gc::GetGCKindSlots(allocKind, &PlainObject::class_); - - ObjectTableKey::Lookup lookup(properties, nproperties, nfixed); - ObjectTypeTable::AddPtr p = objectTypeTable->lookupForAdd(lookup); - - if (!p) - return nullptr; - - RootedPlainObject obj(cx, NewBuiltinClassInstance(cx, allocKind)); - if (!obj) { - cx->clearPendingException(); - return nullptr; - } - MOZ_ASSERT(obj->getProto() == p->value().group->proto().toObject()); - - RootedShape shape(cx, p->value().shape); - if (!NativeObject::setLastProperty(cx, obj, shape)) { - cx->clearPendingException(); - return nullptr; - } - - UpdateObjectTableEntryTypes(cx, p->value(), properties, nproperties); - - for (size_t i = 0; i < nproperties; i++) - obj->setSlot(i, properties[i].value); - - obj->setGroup(p->value().group); - return obj; -} - ///////////////////////////////////////////////////////////////////// // ObjectGroup ///////////////////////////////////////////////////////////////////// -void -ObjectGroup::setProto(JSContext *cx, TaggedProto proto) -{ - MOZ_ASSERT(singleton()); - - if (proto.isObject() && IsInsideNursery(proto.toObject())) - addFlags(OBJECT_FLAG_NURSERY_PROTO); - - setProtoUnchecked(proto); -} - static inline void UpdatePropertyType(ExclusiveContext *cx, HeapTypeSet *types, NativeObject *obj, Shape *shape, bool indexed) @@ -3311,12 +2739,8 @@ ObjectGroup::detachNewScript(bool writeBarrier) MOZ_ASSERT(newScript); if (newScript->analyzed()) { - NewObjectGroupTable &newObjectGroups = newScript->function()->compartment()->newObjectGroups; - NewObjectGroupTable::Ptr p = - newObjectGroups.lookup(NewObjectGroupTable::Lookup(nullptr, proto(), newScript->function())); - MOZ_ASSERT(p->group == this); - - newObjectGroups.remove(p); + newScript->function()->compartment()->objectGroups.removeDefaultNewGroup(nullptr, proto(), + newScript->function()); } if (this->newScript()) @@ -3636,50 +3060,6 @@ types::FillBytecodeTypeMap(JSScript *script, uint32_t *bytecodeMap) MOZ_ASSERT(added == script->nTypeSets()); } -ArrayObject * -types::GetOrFixupCopyOnWriteObject(JSContext *cx, HandleScript script, jsbytecode *pc) -{ - // Make sure that the template object for script/pc has a type indicating - // that the object and its copies have copy on write elements. - RootedArrayObject obj(cx, &script->getObject(GET_UINT32_INDEX(pc))->as()); - MOZ_ASSERT(obj->denseElementsAreCopyOnWrite()); - - if (obj->group()->fromAllocationSite()) { - MOZ_ASSERT(obj->group()->hasAnyFlags(OBJECT_FLAG_COPY_ON_WRITE)); - return obj; - } - - RootedObjectGroup group(cx, TypeScript::InitGroup(cx, script, pc, JSProto_Array)); - if (!group) - return nullptr; - - group->addFlags(OBJECT_FLAG_COPY_ON_WRITE); - - // Update type information in the initializer object group. - MOZ_ASSERT(obj->slotSpan() == 0); - for (size_t i = 0; i < obj->getDenseInitializedLength(); i++) { - const Value &v = obj->getDenseElement(i); - AddTypePropertyId(cx, group, JSID_VOID, v); - } - - obj->setGroup(group); - return obj; -} - -ArrayObject * -types::GetCopyOnWriteObject(JSScript *script, jsbytecode *pc) -{ - // GetOrFixupCopyOnWriteObject should already have been called for - // script/pc, ensuring that the template object has a group with the - // COPY_ON_WRITE flag. We don't assert this here, due to a corner case - // where this property doesn't hold. See jsop_newarray_copyonwrite in - // IonBuilder. - ArrayObject *obj = &script->getObject(GET_UINT32_INDEX(pc))->as(); - MOZ_ASSERT(obj->denseElementsAreCopyOnWrite()); - - return obj; -} - void types::TypeMonitorResult(JSContext *cx, JSScript *script, jsbytecode *pc, const js::Value &rval) { @@ -3697,66 +3077,11 @@ types::TypeMonitorResult(JSContext *cx, JSScript *script, jsbytecode *pc, const if (types->hasType(type)) return; - InferSpew(ISpewOps, "bytecodeType: #%u:%05u: %s", - script->id(), script->pcToOffset(pc), TypeString(type)); + InferSpew(ISpewOps, "bytecodeType: %p %05u: %s", + script, script->pcToOffset(pc), TypeString(type)); types->addType(cx, type); } -bool -types::UseSingletonForClone(JSFunction *fun) -{ - if (!fun->isInterpreted()) - return false; - - if (fun->hasScript() && fun->nonLazyScript()->shouldCloneAtCallsite()) - return true; - - if (fun->isArrow()) - return false; - - if (fun->isSingleton()) - return false; - - /* - * When a function is being used as a wrapper for another function, it - * improves precision greatly to distinguish between different instances of - * the wrapper; otherwise we will conflate much of the information about - * the wrapped functions. - * - * An important example is the Class.create function at the core of the - * Prototype.js library, which looks like: - * - * var Class = { - * create: function() { - * return function() { - * this.initialize.apply(this, arguments); - * } - * } - * }; - * - * Each instance of the innermost function will have a different wrapped - * initialize method. We capture this, along with similar cases, by looking - * for short scripts which use both .apply and arguments. For such scripts, - * whenever creating a new instance of the function we both give that - * instance a singleton type and clone the underlying script. - */ - - uint32_t begin, end; - if (fun->hasScript()) { - if (!fun->nonLazyScript()->usesArgumentsApplyAndThis()) - return false; - begin = fun->nonLazyScript()->sourceStart(); - end = fun->nonLazyScript()->sourceEnd(); - } else { - if (!fun->lazyScript()->usesArgumentsApplyAndThis()) - return false; - begin = fun->lazyScript()->begin(); - end = fun->lazyScript()->end(); - } - - return end - begin <= 100; -} - ///////////////////////////////////////////////////////////////////// // TypeScript ///////////////////////////////////////////////////////////////////// @@ -3781,20 +3106,20 @@ JSScript::makeTypes(JSContext *cx) #ifdef DEBUG StackTypeSet *typeArray = typeScript->typeArray(); for (unsigned i = 0; i < nTypeSets(); i++) { - InferSpew(ISpewOps, "typeSet: %sT%p%s bytecode%u #%u", + InferSpew(ISpewOps, "typeSet: %sT%p%s bytecode%u %p", InferSpewColor(&typeArray[i]), &typeArray[i], InferSpewColorReset(), - i, id()); + i, this); } TypeSet *thisTypes = TypeScript::ThisTypes(this); - InferSpew(ISpewOps, "typeSet: %sT%p%s this #%u", + InferSpew(ISpewOps, "typeSet: %sT%p%s this %p", InferSpewColor(thisTypes), thisTypes, InferSpewColorReset(), - id()); + this); unsigned nargs = functionNonDelazifying() ? functionNonDelazifying()->nargs() : 0; for (unsigned i = 0; i < nargs; i++) { TypeSet *types = TypeScript::ArgTypes(this, i); - InferSpew(ISpewOps, "typeSet: %sT%p%s arg%u #%u", + InferSpew(ISpewOps, "typeSet: %sT%p%s arg%u %p", InferSpewColor(types), types, InferSpewColorReset(), - i, id()); + i, this); } #endif @@ -3811,8 +3136,8 @@ JSFunction::setTypeForScriptedFunction(ExclusiveContext *cx, HandleFunction fun, } else { RootedObject funProto(cx, fun->getProto()); Rooted taggedProto(cx, TaggedProto(funProto)); - ObjectGroup *group = - cx->compartment()->types.newObjectGroup(cx, &JSFunction::class_, taggedProto); + ObjectGroup *group = ObjectGroupCompartment::makeGroup(cx, &JSFunction::class_, + taggedProto); if (!group) return false; @@ -4212,8 +3537,8 @@ TypeNewScript::maybeAnalyze(JSContext *cx, ObjectGroup *group, bool *regenerate, ObjectGroupFlags initialFlags = group->flags() & OBJECT_FLAG_DYNAMIC_MASK; Rooted protoRoot(cx, group->proto()); - ObjectGroup *initialGroup = - cx->compartment()->types.newObjectGroup(cx, group->clasp(), protoRoot, initialFlags); + ObjectGroup *initialGroup = ObjectGroupCompartment::makeGroup(cx, group->clasp(), protoRoot, + initialFlags); if (!initialGroup) return false; @@ -4222,12 +3547,8 @@ TypeNewScript::maybeAnalyze(JSContext *cx, ObjectGroup *group, bool *regenerate, if (!group->addDefiniteProperties(cx, prefixShape)) return false; - NewObjectGroupTable &table = cx->compartment()->newObjectGroups; - NewObjectGroupTable::Lookup lookup(nullptr, group->proto(), function()); - - MOZ_ASSERT(table.lookup(lookup)->group == group); - table.remove(lookup); - table.putNew(lookup, NewObjectGroupEntry(initialGroup, function())); + cx->compartment()->objectGroups.replaceDefaultNewGroup(nullptr, group->proto(), function(), + initialGroup); templateObject()->setGroup(initialGroup); @@ -4367,381 +3688,6 @@ TypeNewScript::sweep() preliminaryObjects->sweep(); } -///////////////////////////////////////////////////////////////////// -// JSObject -///////////////////////////////////////////////////////////////////// - -bool -JSObject::shouldSplicePrototype(JSContext *cx) -{ - /* - * During bootstrapping, if inference is enabled we need to make sure not - * to splice a new prototype in for Function.prototype or the global - * object if their __proto__ had previously been set to null, as this - * will change the prototype for all other objects with the same type. - */ - if (getProto() != nullptr) - return false; - return isSingleton(); -} - -bool -JSObject::splicePrototype(JSContext *cx, const Class *clasp, Handle proto) -{ - MOZ_ASSERT(cx->compartment() == compartment()); - - RootedObject self(cx, this); - - /* - * For singleton groups representing only a single JSObject, the proto - * can be rearranged as needed without destroying type information for - * the old or new types. - */ - MOZ_ASSERT(self->isSingleton()); - - // Inner objects may not appear on prototype chains. - MOZ_ASSERT_IF(proto.isObject(), !proto.toObject()->getClass()->ext.outerObject); - - if (proto.isObject() && !proto.toObject()->setDelegate(cx)) - return false; - - // Force type instantiation when splicing lazy group. - RootedObjectGroup group(cx, self->getGroup(cx)); - if (!group) - return false; - RootedObjectGroup protoGroup(cx, nullptr); - if (proto.isObject()) { - protoGroup = proto.toObject()->getGroup(cx); - if (!protoGroup) - return false; - } - - group->setClasp(clasp); - group->setProto(cx, proto); - return true; -} - -/* static */ ObjectGroup * -JSObject::makeLazyGroup(JSContext *cx, HandleObject obj) -{ - MOZ_ASSERT(obj->hasLazyGroup()); - MOZ_ASSERT(cx->compartment() == obj->compartment()); - - /* De-lazification of functions can GC, so we need to do it up here. */ - if (obj->is() && obj->as().isInterpretedLazy()) { - RootedFunction fun(cx, &obj->as()); - if (!fun->getOrCreateScript(cx)) - return nullptr; - } - - // Find flags which need to be specified immediately on the object. - // Don't track whether singletons are packed. - ObjectGroupFlags initialFlags = OBJECT_FLAG_NON_PACKED; - - if (obj->lastProperty()->hasObjectFlag(BaseShape::ITERATED_SINGLETON)) - initialFlags |= OBJECT_FLAG_ITERATED; - - if (obj->isIndexed()) - initialFlags |= OBJECT_FLAG_SPARSE_INDEXES; - - if (obj->is() && obj->as().length() > INT32_MAX) - initialFlags |= OBJECT_FLAG_LENGTH_OVERFLOW; - - Rooted proto(cx, obj->getTaggedProto()); - ObjectGroup *group = cx->compartment()->types.newObjectGroup(cx, obj->getClass(), proto, - initialFlags); - if (!group) - return nullptr; - - AutoEnterAnalysis enter(cx); - - /* Fill in the type according to the state of this object. */ - - group->initSingleton(obj); - - if (obj->is() && obj->as().isInterpreted()) - group->setInterpretedFunction(&obj->as()); - - obj->group_ = group; - - return group; -} - -/* static */ inline HashNumber -NewObjectGroupEntry::hash(const Lookup &lookup) -{ - return PointerHasher::hash(lookup.hashProto.raw()) ^ - PointerHasher::hash(lookup.clasp) ^ - PointerHasher::hash(lookup.associated); -} - -/* static */ inline bool -NewObjectGroupEntry::match(const NewObjectGroupEntry &key, const Lookup &lookup) -{ - return key.group->proto() == lookup.matchProto && - (!lookup.clasp || key.group->clasp() == lookup.clasp) && - key.associated == lookup.associated; -} - -#ifdef DEBUG -bool -JSObject::hasNewGroup(const Class *clasp, ObjectGroup *group) -{ - NewObjectGroupTable &table = compartment()->newObjectGroups; - - if (!table.initialized()) - return false; - - NewObjectGroupTable::Ptr p = - table.lookup(NewObjectGroupTable::Lookup(clasp, TaggedProto(this), nullptr)); - return p && p->group == group; -} -#endif /* DEBUG */ - -/* static */ bool -JSObject::setNewGroupUnknown(JSContext *cx, const Class *clasp, HandleObject obj) -{ - if (!obj->setFlag(cx, js::BaseShape::NEW_GROUP_UNKNOWN)) - return false; - - // If the object already has a new group, mark that group as unknown. - NewObjectGroupTable &table = cx->compartment()->newObjectGroups; - if (table.initialized()) { - Rooted taggedProto(cx, TaggedProto(obj)); - NewObjectGroupTable::Ptr p = - table.lookup(NewObjectGroupTable::Lookup(clasp, taggedProto, nullptr)); - if (p) - MarkObjectGroupUnknownProperties(cx, p->group); - } - - return true; -} - -/* - * This class is used to add a post barrier on the newObjectGroups set, as the - * key is calculated from a prototype object which may be moved by generational - * GC. - */ -class NewObjectGroupsSetRef : public BufferableRef -{ - NewObjectGroupTable *set; - const Class *clasp; - JSObject *proto; - JSObject *associated; - - public: - NewObjectGroupsSetRef(NewObjectGroupTable *s, const Class *clasp, JSObject *proto, JSObject *associated) - : set(s), clasp(clasp), proto(proto), associated(associated) - {} - - void mark(JSTracer *trc) { - JSObject *prior = proto; - trc->setTracingLocation(&*prior); - Mark(trc, &proto, "newObjectGroups set prototype"); - if (prior == proto) - return; - - NewObjectGroupTable::Ptr p = - set->lookup(NewObjectGroupTable::Lookup(clasp, TaggedProto(prior), TaggedProto(proto), - associated)); - if (!p) - return; - - set->rekeyAs(NewObjectGroupTable::Lookup(clasp, TaggedProto(prior), TaggedProto(proto), associated), - NewObjectGroupTable::Lookup(clasp, TaggedProto(proto), associated), *p); - } -}; - -static void -ObjectGroupTablePostBarrier(ExclusiveContext *cx, NewObjectGroupTable *table, - const Class *clasp, TaggedProto proto, JSObject *associated) -{ - MOZ_ASSERT_IF(associated, !IsInsideNursery(associated)); - - if (!proto.isObject()) - return; - - if (!cx->isJSContext()) { - MOZ_ASSERT(!IsInsideNursery(proto.toObject())); - return; - } - - if (IsInsideNursery(proto.toObject())) { - StoreBuffer &sb = cx->asJSContext()->runtime()->gc.storeBuffer; - sb.putGeneric(NewObjectGroupsSetRef(table, clasp, proto.toObject(), associated)); - } -} - -ObjectGroup * -ExclusiveContext::getNewGroup(const Class *clasp, TaggedProto proto, JSObject *associated) -{ - MOZ_ASSERT_IF(associated, proto.isObject()); - MOZ_ASSERT_IF(associated, associated->is() || associated->is()); - MOZ_ASSERT_IF(proto.isObject(), isInsideCurrentCompartment(proto.toObject())); - - // A null lookup clasp is used for 'new' groups with an associated - // function. The group starts out as a plain object but might mutate into an - // unboxed plain object. - MOZ_ASSERT(!clasp == (associated && associated->is())); - - NewObjectGroupTable &newObjectGroups = compartment()->newObjectGroups; - - if (!newObjectGroups.initialized() && !newObjectGroups.init()) - return nullptr; - - if (associated && associated->is()) { - MOZ_ASSERT(!clasp); - - // Canonicalize new functions to use the original one associated with its script. - JSFunction *fun = &associated->as(); - if (fun->hasScript()) - associated = fun->nonLazyScript()->functionNonDelazifying(); - else if (fun->isInterpretedLazy() && !fun->isSelfHostedBuiltin()) - associated = fun->lazyScript()->functionNonDelazifying(); - else - associated = nullptr; - - // If we have previously cleared the 'new' script information for this - // function, don't try to construct another one. - if (associated && associated->wasNewScriptCleared()) - associated = nullptr; - - if (!associated) - clasp = &PlainObject::class_; - } - - NewObjectGroupTable::AddPtr p = - newObjectGroups.lookupForAdd(NewObjectGroupTable::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_); - MOZ_ASSERT(group->proto() == proto); - return group; - } - - AutoEnterAnalysis enter(this); - - if (proto.isObject() && !proto.toObject()->setDelegate(this)) - return nullptr; - - ObjectGroupFlags initialFlags = 0; - if (!proto.isObject() || proto.toObject()->isNewGroupUnknown()) - initialFlags = OBJECT_FLAG_DYNAMIC_MASK; - - Rooted protoRoot(this, proto); - ObjectGroup *group = compartment()->types.newObjectGroup(this, - clasp ? clasp : &PlainObject::class_, - protoRoot, initialFlags); - if (!group) - return nullptr; - - if (!newObjectGroups.add(p, NewObjectGroupEntry(group, associated))) - return nullptr; - - ObjectGroupTablePostBarrier(this, &newObjectGroups, clasp, proto, associated); - - if (proto.isObject()) { - RootedObject obj(this, proto.toObject()); - - if (associated) { - if (associated->is()) - TypeNewScript::make(asJSContext(), group, &associated->as()); - else - group->setTypeDescr(&associated->as()); - } - - /* - * Some builtin objects have slotful native properties baked in at - * creation via the Shape::{insert,get}initialShape mechanism. Since - * these properties are never explicitly defined on new objects, update - * the type information for them here. - */ - - if (obj->is()) { - AddTypePropertyId(this, group, NameToId(names().source), Type::StringType()); - AddTypePropertyId(this, group, NameToId(names().global), Type::BooleanType()); - AddTypePropertyId(this, group, NameToId(names().ignoreCase), Type::BooleanType()); - AddTypePropertyId(this, group, NameToId(names().multiline), Type::BooleanType()); - AddTypePropertyId(this, group, NameToId(names().sticky), Type::BooleanType()); - AddTypePropertyId(this, group, NameToId(names().lastIndex), Type::Int32Type()); - } - - if (obj->is()) - AddTypePropertyId(this, group, NameToId(names().length), Type::Int32Type()); - - if (obj->is()) { - AddTypePropertyId(this, group, NameToId(names().fileName), Type::StringType()); - AddTypePropertyId(this, group, NameToId(names().lineNumber), Type::Int32Type()); - AddTypePropertyId(this, group, NameToId(names().columnNumber), Type::Int32Type()); - AddTypePropertyId(this, group, NameToId(names().stack), Type::StringType()); - } - } - - return group; -} - -ObjectGroup * -ExclusiveContext::getLazySingletonGroup(const Class *clasp, TaggedProto proto) -{ - MOZ_ASSERT_IF(proto.isObject(), compartment() == proto.toObject()->compartment()); - - AutoEnterAnalysis enter(this); - - NewObjectGroupTable &table = compartment()->lazyObjectGroups; - - if (!table.initialized() && !table.init()) - return nullptr; - - NewObjectGroupTable::AddPtr p = table.lookupForAdd(NewObjectGroupTable::Lookup(clasp, proto, nullptr)); - if (p) { - ObjectGroup *group = p->group; - MOZ_ASSERT(group->lazy()); - - return group; - } - - Rooted protoRoot(this, proto); - ObjectGroup *group = compartment()->types.newObjectGroup(this, clasp, protoRoot); - if (!group) - return nullptr; - - if (!table.add(p, NewObjectGroupEntry(group, nullptr))) - return nullptr; - - ObjectGroupTablePostBarrier(this, &table, clasp, proto, nullptr); - - group->initSingleton((JSObject *) ObjectGroup::LAZY_SINGLETON); - MOZ_ASSERT(group->singleton(), "created group must be a proper singleton"); - - return group; -} - -bool -ExclusiveContext::findAllocationSiteForType(Type type, JSScript **script, uint32_t *offset) const -{ - *script = nullptr; - *offset = 0; - - if (type.isUnknown() || type.isAnyObject() || !type.isGroup()) - return false; - ObjectGroup *obj = type.group(); - - const AllocationSiteTable *table = compartment()->types.allocationSiteTable; - if (!table) - return false; - - for (AllocationSiteTable::Range r = table->all(); !r.empty(); r.popFront()) { - if (obj == r.front().value()) { - *script = r.front().key().script; - *offset = r.front().key().offset; - return true; - } - } - return false; -} - ///////////////////////////////////////////////////////////////////// // Tracing ///////////////////////////////////////////////////////////////////// @@ -4955,209 +3901,6 @@ ObjectGroup::maybeSweep(AutoClearTypeInferenceStateOnOOM *oom) } } -void -TypeCompartment::clearTables() -{ - if (allocationSiteTable && allocationSiteTable->initialized()) - allocationSiteTable->clear(); - if (arrayTypeTable && arrayTypeTable->initialized()) - arrayTypeTable->clear(); - if (objectTypeTable && objectTypeTable->initialized()) - objectTypeTable->clear(); -} - -void -TypeCompartment::sweep(FreeOp *fop) -{ - /* - * Iterate through the array/object group tables and remove all entries - * referencing collected data. These tables only hold weak references. - */ - - if (arrayTypeTable) { - for (ArrayTypeTable::Enum e(*arrayTypeTable); !e.empty(); e.popFront()) { - ArrayTableKey key = e.front().key(); - MOZ_ASSERT(key.type.isUnknown() || !key.type.isSingleton()); - - bool remove = false; - if (!key.type.isUnknown() && key.type.isGroup()) { - ObjectGroup *group = key.type.groupNoBarrier(); - if (IsObjectGroupAboutToBeFinalized(&group)) - remove = true; - else - key.type = Type::ObjectType(group); - } - if (key.proto && key.proto != TaggedProto::LazyProto && - IsObjectAboutToBeFinalized(&key.proto)) - { - remove = true; - } - if (IsObjectGroupAboutToBeFinalized(e.front().value().unsafeGet())) - remove = true; - - if (remove) - e.removeFront(); - else if (key != e.front().key()) - e.rekeyFront(key); - } - } - - if (objectTypeTable) { - for (ObjectTypeTable::Enum e(*objectTypeTable); !e.empty(); e.popFront()) { - const ObjectTableKey &key = e.front().key(); - ObjectTableEntry &entry = e.front().value(); - - bool remove = false; - if (IsObjectGroupAboutToBeFinalized(entry.group.unsafeGet())) - remove = true; - if (IsShapeAboutToBeFinalized(entry.shape.unsafeGet())) - remove = true; - for (unsigned i = 0; !remove && i < key.nproperties; i++) { - if (JSID_IS_STRING(key.properties[i])) { - JSString *str = JSID_TO_STRING(key.properties[i]); - if (IsStringAboutToBeFinalized(&str)) - remove = true; - MOZ_ASSERT(AtomToId((JSAtom *)str) == key.properties[i]); - } else if (JSID_IS_SYMBOL(key.properties[i])) { - JS::Symbol *sym = JSID_TO_SYMBOL(key.properties[i]); - if (IsSymbolAboutToBeFinalized(&sym)) - remove = true; - } - - MOZ_ASSERT(!entry.types[i].isSingleton()); - ObjectGroup *group = nullptr; - if (entry.types[i].isGroup()) { - group = entry.types[i].groupNoBarrier(); - if (IsObjectGroupAboutToBeFinalized(&group)) - remove = true; - else if (group != entry.types[i].groupNoBarrier()) - entry.types[i] = Type::ObjectType(group); - } - } - - if (remove) { - js_free(key.properties); - js_free(entry.types); - e.removeFront(); - } - } - } - - if (allocationSiteTable) { - for (AllocationSiteTable::Enum e(*allocationSiteTable); !e.empty(); e.popFront()) { - AllocationSiteKey key = e.front().key(); - bool keyDying = IsScriptAboutToBeFinalized(&key.script); - bool valDying = IsObjectGroupAboutToBeFinalized(e.front().value().unsafeGet()); - if (keyDying || valDying) - e.removeFront(); - else if (key.script != e.front().key().script) - e.rekeyFront(key); - } - } -} - -void -JSCompartment::sweepNewObjectGroupTable(NewObjectGroupTable &table) -{ - MOZ_ASSERT(zone()->runtimeFromAnyThread()->isHeapCollecting()); - if (table.initialized()) { - for (NewObjectGroupTable::Enum e(table); !e.empty(); e.popFront()) { - NewObjectGroupEntry entry = e.front(); - if (IsObjectGroupAboutToBeFinalizedFromAnyThread(entry.group.unsafeGet()) || - (entry.associated && IsObjectAboutToBeFinalizedFromAnyThread(&entry.associated))) - { - e.removeFront(); - } else { - /* Any rekeying necessary is handled by fixupNewObjectGroupTable() below. */ - MOZ_ASSERT(entry.group.unbarrieredGet() == e.front().group.unbarrieredGet()); - MOZ_ASSERT(entry.associated == e.front().associated); - } - } - } -} - -void -JSCompartment::fixupNewObjectGroupTable(NewObjectGroupTable &table) -{ - /* - * Each entry's hash depends on the object's prototype and we can't tell - * whether that has been moved or not in sweepNewObjectGroupTable(). - */ - MOZ_ASSERT(zone()->isCollecting()); - if (table.initialized()) { - for (NewObjectGroupTable::Enum e(table); !e.empty(); e.popFront()) { - NewObjectGroupEntry entry = e.front(); - bool needRekey = false; - if (IsForwarded(entry.group.get())) { - entry.group.set(Forwarded(entry.group.get())); - needRekey = true; - } - TaggedProto proto = entry.group->proto(); - if (proto.isObject() && IsForwarded(proto.toObject())) { - proto = TaggedProto(Forwarded(proto.toObject())); - needRekey = true; - } - if (entry.associated && IsForwarded(entry.associated)) { - entry.associated = Forwarded(entry.associated); - needRekey = true; - } - if (needRekey) { - const Class *clasp = entry.group->clasp(); - if (entry.associated && entry.associated->is()) - clasp = nullptr; - NewObjectGroupTable::Lookup lookup(clasp, proto, entry.associated); - e.rekeyFront(lookup, entry); - } - } - } -} - -#ifdef JSGC_HASH_TABLE_CHECKS - -void -JSCompartment::checkObjectGroupTablesAfterMovingGC() -{ - checkObjectGroupTableAfterMovingGC(newObjectGroups); - checkObjectGroupTableAfterMovingGC(lazyObjectGroups); -} - -void -JSCompartment::checkObjectGroupTableAfterMovingGC(NewObjectGroupTable &table) -{ - /* - * Assert that nothing points into the nursery or needs to be relocated, and - * that the hash table entries are discoverable. - */ - if (!table.initialized()) - return; - - for (NewObjectGroupTable::Enum e(table); !e.empty(); e.popFront()) { - NewObjectGroupEntry entry = e.front(); - CheckGCThingAfterMovingGC(entry.group.get()); - TaggedProto proto = entry.group->proto(); - if (proto.isObject()) - CheckGCThingAfterMovingGC(proto.toObject()); - CheckGCThingAfterMovingGC(entry.associated); - - const Class *clasp = entry.group->clasp(); - if (entry.associated && entry.associated->is()) - clasp = nullptr; - - NewObjectGroupTable::Lookup lookup(clasp, proto, entry.associated); - NewObjectGroupTable::Ptr ptr = table.lookup(lookup); - MOZ_ASSERT(ptr.found() && &*ptr == &e.front()); - } -} - -#endif // JSGC_HASH_TABLE_CHECKS - -TypeCompartment::~TypeCompartment() -{ - js_delete(arrayTypeTable); - js_delete(objectTypeTable); - js_delete(allocationSiteTable); -} - /* static */ void JSScript::maybeSweepTypes(AutoClearTypeInferenceStateOnOOM *oom) { @@ -5221,45 +3964,6 @@ Zone::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, } } -void -TypeCompartment::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, - size_t *allocationSiteTables, - size_t *arrayTypeTables, - size_t *objectTypeTables) -{ - if (allocationSiteTable) - *allocationSiteTables += allocationSiteTable->sizeOfIncludingThis(mallocSizeOf); - - if (arrayTypeTable) - *arrayTypeTables += arrayTypeTable->sizeOfIncludingThis(mallocSizeOf); - - if (objectTypeTable) { - *objectTypeTables += objectTypeTable->sizeOfIncludingThis(mallocSizeOf); - - for (ObjectTypeTable::Enum e(*objectTypeTable); - !e.empty(); - e.popFront()) - { - const ObjectTableKey &key = e.front().key(); - const ObjectTableEntry &value = e.front().value(); - - /* key.ids and values.types have the same length. */ - *objectTypeTables += mallocSizeOf(key.properties) + mallocSizeOf(value.types); - } - } -} - -size_t -ObjectGroup::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const -{ - size_t n = 0; - if (TypeNewScript *newScript = newScriptDontCheckGeneration()) - n += newScript->sizeOfIncludingThis(mallocSizeOf); - if (UnboxedLayout *layout = maybeUnboxedLayoutDontCheckGeneration()) - n += layout->sizeOfIncludingThis(mallocSizeOf); - return n; -} - TypeZone::TypeZone(Zone *zone) : zone_(zone), typeLifoAlloc(TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE), @@ -5325,9 +4029,6 @@ TypeZone::beginSweep(FreeOp *fop, bool releaseTypes, AutoClearTypeInferenceState // compiler outputs list later during the sweep. Don't worry about overflow // here, since stale indexes will persist only until the sweep finishes. generation++; - - for (CompartmentsInZoneIter comp(zone()); !comp.done(); comp.next()) - comp->types.sweep(fop); } void @@ -5379,7 +4080,7 @@ TypeScript::printTypes(JSContext *cx, HandleScript script) const fprintf(stderr, "Eval"); else fprintf(stderr, "Main"); - fprintf(stderr, " #%u %s:%d ", script->id(), script->filename(), (int) script->lineno()); + fprintf(stderr, " %p %s:%d ", script.get(), script->filename(), (int) script->lineno()); if (script->functionNonDelazifying()) { if (js::PropertyName *name = script->functionNonDelazifying()->name()) @@ -5400,7 +4101,7 @@ TypeScript::printTypes(JSContext *cx, HandleScript script) const for (jsbytecode *pc = script->code(); pc < script->codeEnd(); pc += GetBytecodeLength(pc)) { { - fprintf(stderr, "#%u:", script->id()); + fprintf(stderr, "%p:", script.get()); Sprinter sprinter(cx); if (!sprinter.init()) return; @@ -5419,23 +4120,3 @@ TypeScript::printTypes(JSContext *cx, HandleScript script) const fprintf(stderr, "\n"); } #endif /* DEBUG */ - -void -ObjectGroup::setAddendum(AddendumKind kind, void *addendum, bool writeBarrier /* = true */) -{ - MOZ_ASSERT(!needsSweep()); - MOZ_ASSERT(kind <= (OBJECT_FLAG_ADDENDUM_MASK >> OBJECT_FLAG_ADDENDUM_SHIFT)); - - if (writeBarrier) { - // Manually trigger barriers if we are clearing a TypeNewScript. Other - // kinds of addendums are immutable. - if (newScript()) - TypeNewScript::writeBarrierPre(newScript()); - else - MOZ_ASSERT(addendumKind() == Addendum_None || addendumKind() == kind); - } - - flags_ &= ~OBJECT_FLAG_ADDENDUM_MASK; - flags_ |= kind << OBJECT_FLAG_ADDENDUM_SHIFT; - addendum_ = addendum; -} diff --git a/js/src/jsinfer.h b/js/src/jsinfer.h index 406e12a3a21c..a90c49e6c876 100644 --- a/js/src/jsinfer.h +++ b/js/src/jsinfer.h @@ -23,100 +23,10 @@ #include "js/UbiNode.h" #include "js/Utility.h" #include "js/Vector.h" +#include "vm/ObjectGroup.h" namespace js { -class TypeDescr; -class UnboxedLayout; - -class TaggedProto -{ - public: - static JSObject * const LazyProto; - - TaggedProto() : proto(nullptr) {} - explicit TaggedProto(JSObject *proto) : proto(proto) {} - - uintptr_t toWord() const { return uintptr_t(proto); } - - bool isLazy() const { - return proto == LazyProto; - } - bool isObject() const { - /* Skip nullptr and LazyProto. */ - return uintptr_t(proto) > uintptr_t(TaggedProto::LazyProto); - } - JSObject *toObject() const { - MOZ_ASSERT(isObject()); - return proto; - } - JSObject *toObjectOrNull() const { - MOZ_ASSERT(!proto || isObject()); - return proto; - } - JSObject *raw() const { return proto; } - - bool operator ==(const TaggedProto &other) { return proto == other.proto; } - bool operator !=(const TaggedProto &other) { return proto != other.proto; } - - private: - JSObject *proto; -}; - -template <> -struct RootKind -{ - static ThingRootKind rootKind() { return THING_ROOT_OBJECT; } -}; - -template <> struct GCMethods -{ - static TaggedProto initial() { return TaggedProto(); } - static bool poisoned(const TaggedProto &v) { return IsPoisonedPtr(v.raw()); } -}; - -template <> struct GCMethods -{ - static TaggedProto initial() { return TaggedProto(); } - static bool poisoned(const TaggedProto &v) { return IsPoisonedPtr(v.raw()); } -}; - -template -class TaggedProtoOperations -{ - const TaggedProto *value() const { - return static_cast(this)->extract(); - } - - public: - uintptr_t toWord() const { return value()->toWord(); } - inline bool isLazy() const { return value()->isLazy(); } - inline bool isObject() const { return value()->isObject(); } - inline JSObject *toObject() const { return value()->toObject(); } - inline JSObject *toObjectOrNull() const { return value()->toObjectOrNull(); } - JSObject *raw() const { return value()->raw(); } -}; - -template <> -class HandleBase : public TaggedProtoOperations > -{ - friend class TaggedProtoOperations >; - const TaggedProto * extract() const { - return static_cast*>(this)->address(); - } -}; - -template <> -class RootedBase : public TaggedProtoOperations > -{ - friend class TaggedProtoOperations >; - const TaggedProto *extract() const { - return static_cast *>(this)->address(); - } -}; - -class CallObject; - namespace jit { struct IonScript; class JitAllocPolicy; @@ -351,85 +261,6 @@ enum : uint32_t { }; typedef uint32_t TypeFlags; -/* Flags and other state stored in ObjectGroup::flags */ -enum : uint32_t { - /* Whether this group is associated with some allocation site. */ - OBJECT_FLAG_FROM_ALLOCATION_SITE = 0x1, - - /* - * If set, the object's prototype might be in the nursery and can't be - * used during Ion compilation (which may be occurring off thread). - */ - OBJECT_FLAG_NURSERY_PROTO = 0x2, - - /* Mask/shift for the number of properties in propertySet */ - OBJECT_FLAG_PROPERTY_COUNT_MASK = 0xfff8, - OBJECT_FLAG_PROPERTY_COUNT_SHIFT = 3, - OBJECT_FLAG_PROPERTY_COUNT_LIMIT = - OBJECT_FLAG_PROPERTY_COUNT_MASK >> OBJECT_FLAG_PROPERTY_COUNT_SHIFT, - - /* Whether any objects this represents may have sparse indexes. */ - OBJECT_FLAG_SPARSE_INDEXES = 0x00010000, - - /* Whether any objects this represents may not have packed dense elements. */ - OBJECT_FLAG_NON_PACKED = 0x00020000, - - /* - * Whether any objects this represents may be arrays whose length does not - * fit in an int32. - */ - OBJECT_FLAG_LENGTH_OVERFLOW = 0x00040000, - - /* Whether any objects have been iterated over. */ - OBJECT_FLAG_ITERATED = 0x00080000, - - /* For a global object, whether flags were set on the RegExpStatics. */ - OBJECT_FLAG_REGEXP_FLAGS_SET = 0x00100000, - - /* - * For the function on a run-once script, whether the function has actually - * run multiple times. - */ - OBJECT_FLAG_RUNONCE_INVALIDATED = 0x00200000, - - /* - * For a global object, whether any array buffers in this compartment with - * typed object views have been neutered. - */ - OBJECT_FLAG_TYPED_OBJECT_NEUTERED = 0x00400000, - - /* - * Whether objects with this type should be allocated directly in the - * tenured heap. - */ - OBJECT_FLAG_PRE_TENURE = 0x00800000, - - /* Whether objects with this type might have copy on write elements. */ - OBJECT_FLAG_COPY_ON_WRITE = 0x01000000, - - /* Whether this type has had its 'new' script cleared in the past. */ - OBJECT_FLAG_NEW_SCRIPT_CLEARED = 0x02000000, - - /* - * Whether all properties of this object are considered unknown. - * If set, all other flags in DYNAMIC_MASK will also be set. - */ - OBJECT_FLAG_UNKNOWN_PROPERTIES = 0x04000000, - - /* Flags which indicate dynamic properties of represented objects. */ - OBJECT_FLAG_DYNAMIC_MASK = 0x07ff0000, - - // Mask/shift for the kind of addendum attached to this group. - OBJECT_FLAG_ADDENDUM_MASK = 0x38000000, - OBJECT_FLAG_ADDENDUM_SHIFT = 27, - - // Mask/shift for this group's generation. If out of sync with the - // TypeZone's generation, this group hasn't been swept yet. - OBJECT_FLAG_GENERATION_MASK = 0x40000000, - OBJECT_FLAG_GENERATION_SHIFT = 30, -}; -typedef uint32_t ObjectGroupFlags; - class StackTypeSet; class HeapTypeSet; class TemporaryTypeSet; @@ -647,8 +478,6 @@ class HeapTypeSet : public ConstraintTypeSet inline void setNonConstantProperty(ExclusiveContext *cx); }; -class CompilerConstraintList; - CompilerConstraintList * NewCompilerConstraintList(jit::TempAllocator &alloc); @@ -773,30 +602,6 @@ bool AddClearDefiniteFunctionUsesInScript(JSContext *cx, ObjectGroup *group, JSScript *script, JSScript *calleeScript); -/* Is this a reasonable PC to be doing inlining on? */ -inline bool isInlinableCall(jsbytecode *pc); - -/* Type information about a property. */ -struct Property -{ - /* Identifier for this property, JSID_VOID for the aggregate integer index property. */ - HeapId id; - - /* Possible types for this property, including types inherited from prototypes. */ - HeapTypeSet types; - - explicit Property(jsid id) - : id(id) - {} - - Property(const Property &o) - : id(o.id.get()), types(o.types) - {} - - static uint32_t keyBits(jsid id) { return uint32_t(JSID_BITS(id)); } - static jsid getKey(Property *p) { return p->id; } -}; - // For types where only a small number of objects have been allocated, this // structure keeps track of all objects with the type in existence. Once // COUNT objects have been allocated, this structure is cleared and the objects @@ -964,420 +769,68 @@ class TypeNewScript size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const; }; -/* - * 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. - * - * 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 - * in the VM) will treat these properties like those of any other object group. - */ - -/* Type information about an object accessed by a script. */ -struct ObjectGroup : public gc::TenuredCell -{ - private: - /* Class shared by objects in this group. */ - const Class *clasp_; - - /* Prototype shared by objects in this group. */ - HeapPtrObject proto_; - - /* - * Whether there is a singleton JS object with this group. That JS object - * must appear in type sets instead of this; we include the back reference - * here to allow reverting the JS object to a lazy group. - */ - HeapPtrObject singleton_; - - public: - - const Class *clasp() const { - return clasp_; - } - - void setClasp(const Class *clasp) { - clasp_ = clasp; - } - - TaggedProto proto() const { - return TaggedProto(proto_); - } - - JSObject *singleton() const { - return singleton_; - } - - // For use during marking, don't call otherwise. - HeapPtrObject &protoRaw() { return proto_; } - HeapPtrObject &singletonRaw() { return singleton_; } - - void setProto(JSContext *cx, TaggedProto proto); - void setProtoUnchecked(TaggedProto proto) { - proto_ = proto.raw(); - } - - void initSingleton(JSObject *singleton) { - singleton_ = singleton; - } - - /* - * Value held by singleton if this is a standin group for a singleton JS - * object whose group has not been constructed yet. - */ - static const size_t LAZY_SINGLETON = 1; - bool lazy() const { return singleton() == (JSObject *) LAZY_SINGLETON; } - - private: - /* Flags for this group. */ - ObjectGroupFlags flags_; - - // Kinds of addendums which can be attached to ObjectGroups. - enum AddendumKind { - Addendum_None, - - // When used by interpreted function, the addendum stores the - // canonical JSFunction object. - Addendum_InterpretedFunction, - - // When used by the 'new' group when constructing an interpreted - // function, the addendum stores a TypeNewScript. - Addendum_NewScript, - - // When objects in this group have an unboxed representation, the - // addendum stores an UnboxedLayout (which might have a TypeNewScript - // as well, if the group is also constructed using 'new'). - Addendum_UnboxedLayout, - - // When used by typed objects, the addendum stores a TypeDescr. - Addendum_TypeDescr - }; - - // If non-null, holds additional information about this object, whose - // format is indicated by the object's addendum kind. - void *addendum_; - - void setAddendum(AddendumKind kind, void *addendum, bool writeBarrier = true); - - AddendumKind addendumKind() const { - return (AddendumKind) - ((flags_ & OBJECT_FLAG_ADDENDUM_MASK) >> OBJECT_FLAG_ADDENDUM_SHIFT); - } - - TypeNewScript *newScriptDontCheckGeneration() const { - if (addendumKind() == Addendum_NewScript) - return reinterpret_cast(addendum_); - return nullptr; - } - - UnboxedLayout *maybeUnboxedLayoutDontCheckGeneration() const { - if (addendumKind() == Addendum_UnboxedLayout) - return reinterpret_cast(addendum_); - return nullptr; - } - - TypeNewScript *anyNewScript(); - void detachNewScript(bool writeBarrier); - - public: - - ObjectGroupFlags flags() { - maybeSweep(nullptr); - return flags_; - } - - void addFlags(ObjectGroupFlags flags) { - maybeSweep(nullptr); - flags_ |= flags; - } - - void clearFlags(ObjectGroupFlags flags) { - maybeSweep(nullptr); - flags_ &= ~flags; - } - - TypeNewScript *newScript() { - maybeSweep(nullptr); - return newScriptDontCheckGeneration(); - } - - void setNewScript(TypeNewScript *newScript) { - setAddendum(Addendum_NewScript, newScript); - } - - UnboxedLayout *maybeUnboxedLayout() { - maybeSweep(nullptr); - return maybeUnboxedLayoutDontCheckGeneration(); - } - - UnboxedLayout &unboxedLayout() { - MOZ_ASSERT(addendumKind() == Addendum_UnboxedLayout); - return *maybeUnboxedLayout(); - } - - void setUnboxedLayout(UnboxedLayout *layout) { - setAddendum(Addendum_UnboxedLayout, layout); - } - - TypeDescr *maybeTypeDescr() { - // Note: there is no need to sweep when accessing the type descriptor - // of an object, as it is strongly held and immutable. - if (addendumKind() == Addendum_TypeDescr) - return reinterpret_cast(addendum_); - return nullptr; - } - - TypeDescr &typeDescr() { - MOZ_ASSERT(addendumKind() == Addendum_TypeDescr); - return *maybeTypeDescr(); - } - - void setTypeDescr(TypeDescr *descr) { - setAddendum(Addendum_TypeDescr, descr); - } - - JSFunction *maybeInterpretedFunction() { - // Note: as with type descriptors, there is no need to sweep when - // accessing the interpreted function associated with an object. - if (addendumKind() == Addendum_InterpretedFunction) - return reinterpret_cast(addendum_); - return nullptr; - } - - void setInterpretedFunction(JSFunction *fun) { - setAddendum(Addendum_InterpretedFunction, fun); - } - - private: - /* - * Properties of this object. This may contain JSID_VOID, representing the - * types of all integer indexes of the object, and/or JSID_EMPTY, holding - * constraints listening to changes to the object's state. - * - * The type sets in the properties of a group describe the possible values - * that can be read out of that property in actual JS objects. In native - * objects, property types account for plain data properties (those with a - * slot and no getter or setter hook) and dense elements. In typed objects - * and unboxed objects, property types account for object and value - * properties and elements in the object. - * - * For accesses on these properties, the correspondence is as follows: - * - * 1. If the group has unknownProperties(), the possible properties and - * value types for associated JSObjects are unknown. - * - * 2. Otherwise, for any |obj| in |group|, and any |id| which is a property - * in |obj|, before obj->getProperty(id) the property in |group| for - * |id| must reflect the result of the getProperty. - * - * There are several exceptions to this: - * - * 1. For properties of global JS objects which are undefined at the point - * where the property was (lazily) generated, the property type set will - * remain empty, and the 'undefined' type will only be added after a - * subsequent assignment or deletion. After these properties have been - * assigned a defined value, the only way they can become undefined - * again is after such an assign or deletion. - * - * 2. Array lengths are special cased by the compiler and VM and are not - * reflected in property types. - * - * 3. In typed objects (but not unboxed objects), the initial values of - * properties (null pointers and undefined values) are not reflected in - * the property types. These values are always possible when reading the - * property. - * - * We establish these by using write barriers on calls to setProperty and - * defineProperty which are on native properties, and on any jitcode which - * might update the property with a new type. - */ - Property **propertySet; - public: - - inline ObjectGroup(const Class *clasp, TaggedProto proto, ObjectGroupFlags initialFlags); - - bool hasAnyFlags(ObjectGroupFlags flags) { - MOZ_ASSERT((flags & OBJECT_FLAG_DYNAMIC_MASK) == flags); - return !!(this->flags() & flags); - } - bool hasAllFlags(ObjectGroupFlags flags) { - MOZ_ASSERT((flags & OBJECT_FLAG_DYNAMIC_MASK) == flags); - return (this->flags() & flags) == flags; - } - - bool unknownProperties() { - MOZ_ASSERT_IF(flags() & OBJECT_FLAG_UNKNOWN_PROPERTIES, - hasAllFlags(OBJECT_FLAG_DYNAMIC_MASK)); - return !!(flags() & OBJECT_FLAG_UNKNOWN_PROPERTIES); - } - - bool shouldPreTenure() { - return hasAnyFlags(OBJECT_FLAG_PRE_TENURE) && !unknownProperties(); - } - - bool hasTenuredProto() { - return !(flags() & OBJECT_FLAG_NURSERY_PROTO); - } - - gc::InitialHeap initialHeap(CompilerConstraintList *constraints); - - bool canPreTenure() { - return !unknownProperties(); - } - - bool fromAllocationSite() { - return flags() & OBJECT_FLAG_FROM_ALLOCATION_SITE; - } - - void setShouldPreTenure(ExclusiveContext *cx) { - MOZ_ASSERT(canPreTenure()); - setFlags(cx, OBJECT_FLAG_PRE_TENURE); - } - - /* - * Get or create a property of this object. Only call this for properties which - * a script accesses explicitly. - */ - inline HeapTypeSet *getProperty(ExclusiveContext *cx, jsid id); - - /* Get a property only if it already exists. */ - inline HeapTypeSet *maybeGetProperty(jsid id); - - inline unsigned getPropertyCount(); - inline Property *getProperty(unsigned i); - - /* Helpers */ - - void updateNewPropertyTypes(ExclusiveContext *cx, jsid id, HeapTypeSet *types); - bool addDefiniteProperties(ExclusiveContext *cx, Shape *shape); - bool matchDefiniteProperties(HandleObject obj); - void markPropertyNonData(ExclusiveContext *cx, jsid id); - void markPropertyNonWritable(ExclusiveContext *cx, jsid id); - void markStateChange(ExclusiveContext *cx); - void setFlags(ExclusiveContext *cx, ObjectGroupFlags flags); - void markUnknown(ExclusiveContext *cx); - void maybeClearNewScriptOnOOM(); - void clearNewScript(ExclusiveContext *cx); - bool isPropertyNonData(jsid id); - bool isPropertyNonWritable(jsid id); - - void print(); - - inline void clearProperties(); - void maybeSweep(AutoClearTypeInferenceStateOnOOM *oom); - - private: -#ifdef DEBUG - bool needsSweep(); -#endif - - uint32_t generation() { - return (flags_ & OBJECT_FLAG_GENERATION_MASK) >> OBJECT_FLAG_GENERATION_SHIFT; - } - - public: - void setGeneration(uint32_t generation) { - MOZ_ASSERT(generation <= (OBJECT_FLAG_GENERATION_MASK >> OBJECT_FLAG_GENERATION_SHIFT)); - flags_ &= ~OBJECT_FLAG_GENERATION_MASK; - flags_ |= generation << OBJECT_FLAG_GENERATION_SHIFT; - } - - size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const; - - inline void finalize(FreeOp *fop); - void fixupAfterMovingGC() {} - - static inline ThingRootKind rootKind() { return THING_ROOT_OBJECT_GROUP; } - - static inline uint32_t offsetOfClasp() { - return offsetof(ObjectGroup, clasp_); - } - - static inline uint32_t offsetOfProto() { - return offsetof(ObjectGroup, proto_); - } - - static inline uint32_t offsetOfAddendum() { - return offsetof(ObjectGroup, addendum_); - } - - static inline uint32_t offsetOfFlags() { - return offsetof(ObjectGroup, flags_); - } - - private: - inline uint32_t basePropertyCount(); - inline void setBasePropertyCount(uint32_t count); - - static void staticAsserts() { - JS_STATIC_ASSERT(offsetof(ObjectGroup, proto_) == offsetof(js::shadow::ObjectGroup, proto)); - } -}; +/* Is this a reasonable PC to be doing inlining on? */ +inline bool isInlinableCall(jsbytecode *pc); /* - * Entries for the per-compartment set of groups which are the default - * types to use for some prototype. An optional associated object is used which - * allows multiple groups to be created with the same prototype. The - * associated object may be a function (for types constructed with 'new') or a - * type descriptor (for typed objects). These entries are also used for the set - * of lazy groups in the compartment, which use a null associated object - * (though there are only a few of these per compartment). + * Type information about a property. + * + * The type sets in the properties of a group describe the possible values + * that can be read out of that property in actual JS objects. In native + * objects, property types account for plain data properties (those with a + * slot and no getter or setter hook) and dense elements. In typed objects + * and unboxed objects, property types account for object and value + * properties and elements in the object. + * + * For accesses on these properties, the correspondence is as follows: + * + * 1. If the group has unknownProperties(), the possible properties and + * value types for associated JSObjects are unknown. + * + * 2. Otherwise, for any |obj| in |group|, and any |id| which is a property + * in |obj|, before obj->getProperty(id) the property in |group| for + * |id| must reflect the result of the getProperty. + * + * There are several exceptions to this: + * + * 1. For properties of global JS objects which are undefined at the point + * where the property was (lazily) generated, the property type set will + * remain empty, and the 'undefined' type will only be added after a + * subsequent assignment or deletion. After these properties have been + * assigned a defined value, the only way they can become undefined + * again is after such an assign or deletion. + * + * 2. Array lengths are special cased by the compiler and VM and are not + * reflected in property types. + * + * 3. In typed objects (but not unboxed objects), the initial values of + * properties (null pointers and undefined values) are not reflected in + * the property types. These values are always possible when reading the + * property. + * + * We establish these by using write barriers on calls to setProperty and + * defineProperty which are on native properties, and on any jitcode which + * might update the property with a new type. */ -struct NewObjectGroupEntry +struct Property { - ReadBarrieredObjectGroup group; + /* Identifier for this property, JSID_VOID for the aggregate integer index property. */ + HeapId id; - // Note: This pointer is only used for equality and does not need a read barrier. - JSObject *associated; + /* Possible types for this property, including types inherited from prototypes. */ + HeapTypeSet types; - NewObjectGroupEntry(ObjectGroup *group, JSObject *associated) - : group(group), associated(associated) + explicit Property(jsid id) + : id(id) {} - struct Lookup { - const Class *clasp; - TaggedProto hashProto; - TaggedProto matchProto; - JSObject *associated; + Property(const Property &o) + : id(o.id.get()), types(o.types) + {} - Lookup(const Class *clasp, TaggedProto proto, JSObject *associated) - : clasp(clasp), hashProto(proto), matchProto(proto), associated(associated) - {} - - /* - * For use by generational post barriers only. Look up an entry whose - * proto has been moved, but was hashed with the original value. - */ - Lookup(const Class *clasp, TaggedProto hashProto, TaggedProto matchProto, JSObject *associated) - : clasp(clasp), hashProto(hashProto), matchProto(matchProto), associated(associated) - {} - - }; - - static inline HashNumber hash(const Lookup &lookup); - static inline bool match(const NewObjectGroupEntry &key, const Lookup &lookup); - static void rekey(NewObjectGroupEntry &k, const NewObjectGroupEntry& newKey) { k = newKey; } + static uint32_t keyBits(jsid id) { return uint32_t(JSID_BITS(id)); } + static jsid getKey(Property *p) { return p->id; } }; -typedef HashSet NewObjectGroupTable; - -// Whether to make a singleton when calling 'new' at script/pc. -bool -UseSingletonForNewObject(JSContext *cx, JSScript *script, jsbytecode *pc); - -// Whether to make a deep cloned singleton when cloning fun. -bool -UseSingletonForClone(JSFunction *fun); /* * Whether Array.prototype, or an object on its proto chain, has an @@ -1426,10 +879,6 @@ class TypeScript static inline TYPESET *BytecodeTypes(JSScript *script, jsbytecode *pc, uint32_t *bytecodeMap, uint32_t *hint, TYPESET *typeArray); - /* Get a group for an allocation site in this script. */ - static inline ObjectGroup *InitGroup(JSContext *cx, JSScript *script, jsbytecode *pc, - JSProtoKey kind); - /* * Monitor a bytecode pushing any value. This must be called for any opcode * which is JOF_TYPESET, and where either the script has not been analyzed @@ -1477,12 +926,6 @@ class TypeScript void FillBytecodeTypeMap(JSScript *script, uint32_t *bytecodeMap); -ArrayObject * -GetOrFixupCopyOnWriteObject(JSContext *cx, HandleScript script, jsbytecode *pc); - -ArrayObject * -GetCopyOnWriteObject(JSScript *script, jsbytecode *pc); - class RecompileInfo; // Allocate a CompilerOutput for a finished compilation and generate the type @@ -1497,22 +940,6 @@ FinishCompilation(JSContext *cx, HandleScript script, CompilerConstraintList *co void FinishDefinitePropertiesAnalysis(JSContext *cx, CompilerConstraintList *constraints); -struct ArrayTableKey; -typedef HashMap ArrayTypeTable; - -struct ObjectTableKey; -struct ObjectTableEntry; -typedef HashMap ObjectTypeTable; - -struct AllocationSiteKey; -typedef HashMap AllocationSiteTable; - class HeapTypeSetKey; // Type set entry for either a JSObject with singleton type or a non-singleton ObjectGroup. @@ -1683,55 +1110,6 @@ class RecompileInfo typedef Vector RecompileInfoVector; -/* Type information for a compartment. */ -struct TypeCompartment -{ - /* Number of scripts in this compartment. */ - unsigned scriptCount; - - /* Table for referencing types of objects keyed to an allocation site. */ - AllocationSiteTable *allocationSiteTable; - - /* Tables for determining types of singleton/JSON objects. */ - ArrayTypeTable *arrayTypeTable; - ObjectTypeTable *objectTypeTable; - - private: - void setTypeToHomogenousArray(ExclusiveContext *cx, JSObject *obj, Type type); - - public: - void fixArrayGroup(ExclusiveContext *cx, ArrayObject *obj); - void fixObjectGroup(ExclusiveContext *cx, PlainObject *obj); - void fixRestArgumentsType(ExclusiveContext *cx, ArrayObject *obj); - - JSObject *newTypedObject(JSContext *cx, IdValuePair *properties, size_t nproperties); - - TypeCompartment(); - ~TypeCompartment(); - - inline JSCompartment *compartment(); - - // Prints results of this compartment if spew is enabled or force is set. - void print(JSContext *cx, bool force); - - ObjectGroup *newObjectGroup(ExclusiveContext *cx, const Class *clasp, Handle proto, - ObjectGroupFlags initialFlags = 0); - - // Get or make a group for an allocation site, and add to the allocation site table. - ObjectGroup *addAllocationSiteObjectGroup(JSContext *cx, AllocationSiteKey key); - - void clearTables(); - void sweep(FreeOp *fop); - void finalizeObjects(); - - void addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, - size_t *allocationSiteTables, - size_t *arrayTypeTables, - size_t *objectTypeTables); -}; - -void FixRestArgumentsType(ExclusiveContext *cxArg, ArrayObject *obj); - struct AutoEnterAnalysis; struct TypeZone @@ -1819,6 +1197,10 @@ inline const char * ObjectGroupString(ObjectGroup *group) { return nullptr; } /* Print a warning, dump state and abort the program. */ MOZ_NORETURN MOZ_COLD void TypeFailure(JSContext *cx, const char *fmt, ...); +// Prints type information for a context if spew is enabled or force is set. +void +PrintTypes(JSContext *cx, JSCompartment *comp, bool force); + } /* namespace types */ } /* namespace js */ @@ -1826,7 +1208,7 @@ MOZ_NORETURN MOZ_COLD void TypeFailure(JSContext *cx, const char *fmt, ...); // with no associated compartment. namespace JS { namespace ubi { -template<> struct Concrete : TracerConcrete { }; +template<> struct Concrete : TracerConcrete { }; } } diff --git a/js/src/jsinferinlines.h b/js/src/jsinferinlines.h index d56d83ff1a65..e2100c156565 100644 --- a/js/src/jsinferinlines.h +++ b/js/src/jsinferinlines.h @@ -323,87 +323,6 @@ struct AutoEnterAnalysis // Interface functions ///////////////////////////////////////////////////////////////////// -inline const Class * -GetClassForProtoKey(JSProtoKey key) -{ - switch (key) { - case JSProto_Null: - case JSProto_Object: - return &PlainObject::class_; - case JSProto_Array: - return &ArrayObject::class_; - - case JSProto_Number: - return &NumberObject::class_; - case JSProto_Boolean: - return &BooleanObject::class_; - case JSProto_String: - return &StringObject::class_; - case JSProto_Symbol: - return &SymbolObject::class_; - case JSProto_RegExp: - return &RegExpObject::class_; - - case JSProto_Int8Array: - case JSProto_Uint8Array: - case JSProto_Int16Array: - case JSProto_Uint16Array: - case JSProto_Int32Array: - case JSProto_Uint32Array: - case JSProto_Float32Array: - case JSProto_Float64Array: - case JSProto_Uint8ClampedArray: - return &TypedArrayObject::classes[key - JSProto_Int8Array]; - - case JSProto_SharedInt8Array: - case JSProto_SharedUint8Array: - case JSProto_SharedInt16Array: - case JSProto_SharedUint16Array: - case JSProto_SharedInt32Array: - case JSProto_SharedUint32Array: - case JSProto_SharedFloat32Array: - case JSProto_SharedFloat64Array: - case JSProto_SharedUint8ClampedArray: - return &SharedTypedArrayObject::classes[key - JSProto_SharedInt8Array]; - - case JSProto_ArrayBuffer: - return &ArrayBufferObject::class_; - - case JSProto_SharedArrayBuffer: - return &SharedArrayBufferObject::class_; - - case JSProto_DataView: - return &DataViewObject::class_; - - default: - MOZ_CRASH("Bad proto key"); - } -} - -/* - * Get the default 'new' group for a given standard class, per the currently - * active global. - */ -inline ObjectGroup * -GetNewObjectGroup(JSContext *cx, JSProtoKey key) -{ - RootedObject proto(cx); - if (key != JSProto_Null && !GetBuiltinPrototype(cx, key, &proto)) - return nullptr; - return cx->getNewGroup(GetClassForProtoKey(key), TaggedProto(proto.get())); -} - -/* Get a group for the immediate allocation site within a native. */ -inline ObjectGroup * -GetCallerInitGroup(JSContext *cx, JSProtoKey key) -{ - jsbytecode *pc; - RootedScript script(cx, cx->currentScript(&pc)); - if (script) - return TypeScript::InitGroup(cx, script, pc, key); - return GetNewObjectGroup(cx, key); -} - void MarkIteratorUnknownSlow(JSContext *cx); void TypeMonitorCallSlow(JSContext *cx, JSObject *callee, const CallArgs &args, @@ -569,23 +488,6 @@ MarkObjectStateChange(ExclusiveContext *cx, JSObject *obj) obj->group()->markStateChange(cx); } -/* - * For an array or object which has not yet escaped and been referenced elsewhere, - * pick a new type based on the object's current contents. - */ - -inline void -FixArrayGroup(ExclusiveContext *cx, ArrayObject *obj) -{ - cx->compartment()->types.fixArrayGroup(cx, obj); -} - -inline void -FixObjectGroup(ExclusiveContext *cx, PlainObject *obj) -{ - cx->compartment()->types.fixObjectGroup(cx, obj); -} - /* Interface helpers for JSScript*. */ extern void TypeMonitorResult(JSContext *cx, JSScript *script, jsbytecode *pc, const js::Value &rval); @@ -679,85 +581,6 @@ TypeScript::BytecodeTypes(JSScript *script, jsbytecode *pc) hint, types->typeArray()); } -struct AllocationSiteKey : public DefaultHasher { - JSScript *script; - - uint32_t offset : 24; - JSProtoKey kind : 8; - - static const uint32_t OFFSET_LIMIT = (1 << 23); - - AllocationSiteKey() { mozilla::PodZero(this); } - - static inline uint32_t hash(AllocationSiteKey key) { - return uint32_t(size_t(key.script->offsetToPC(key.offset)) ^ key.kind); - } - - static inline bool match(const AllocationSiteKey &a, const AllocationSiteKey &b) { - return a.script == b.script && a.offset == b.offset && a.kind == b.kind; - } -}; - -/* Whether to use a singleton kind for an initializer opcode at script/pc. */ -js::NewObjectKind -UseSingletonForInitializer(JSScript *script, jsbytecode *pc, JSProtoKey key); - -js::NewObjectKind -UseSingletonForInitializer(JSScript *script, jsbytecode *pc, const Class *clasp); - -/* static */ inline ObjectGroup * -TypeScript::InitGroup(JSContext *cx, JSScript *script, jsbytecode *pc, JSProtoKey kind) -{ - MOZ_ASSERT(!UseSingletonForInitializer(script, pc, kind)); - - uint32_t offset = script->pcToOffset(pc); - - if (offset >= AllocationSiteKey::OFFSET_LIMIT) - return GetNewObjectGroup(cx, kind); - - AllocationSiteKey key; - key.script = script; - key.offset = offset; - key.kind = kind; - - if (!cx->compartment()->types.allocationSiteTable) - return cx->compartment()->types.addAllocationSiteObjectGroup(cx, key); - - AllocationSiteTable::Ptr p = cx->compartment()->types.allocationSiteTable->lookup(key); - - if (p) - return p->value(); - return cx->compartment()->types.addAllocationSiteObjectGroup(cx, key); -} - -/* Set the group to use for obj according to the site it was allocated at. */ -static inline bool -SetInitializerObjectGroup(JSContext *cx, HandleScript script, jsbytecode *pc, HandleObject obj, - NewObjectKind kind) -{ - JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(obj->getClass()); - MOZ_ASSERT(key != JSProto_Null); - MOZ_ASSERT(kind == UseSingletonForInitializer(script, pc, key)); - - if (kind == SingletonObject) { - MOZ_ASSERT(obj->isSingleton()); - - /* - * Inference does not account for types of run-once initializer - * objects, as these may not be created until after the script - * has been analyzed. - */ - TypeScript::Monitor(cx, script, pc, ObjectValue(*obj)); - } else { - types::ObjectGroup *group = TypeScript::InitGroup(cx, script, pc, key); - if (!group) - return false; - obj->uninlinedSetGroup(group); - } - - return true; -} - /* static */ inline void TypeScript::Monitor(JSContext *cx, JSScript *script, jsbytecode *pc, const js::Value &rval) { @@ -808,8 +631,8 @@ TypeScript::SetThis(JSContext *cx, JSScript *script, Type type) if (!types->hasType(type)) { AutoEnterAnalysis enter(cx); - InferSpew(ISpewOps, "externalType: setThis #%u: %s", - script->id(), TypeString(type)); + InferSpew(ISpewOps, "externalType: setThis %p: %s", + script, TypeString(type)); types->addType(cx, type); } } @@ -830,8 +653,8 @@ TypeScript::SetArgument(JSContext *cx, JSScript *script, unsigned arg, Type type if (!types->hasType(type)) { AutoEnterAnalysis enter(cx); - InferSpew(ISpewOps, "externalType: setArg #%u %u: %s", - script->id(), arg, TypeString(type)); + InferSpew(ISpewOps, "externalType: setArg %p %u: %s", + script, arg, TypeString(type)); types->addType(cx, type); } } @@ -843,16 +666,6 @@ TypeScript::SetArgument(JSContext *cx, JSScript *script, unsigned arg, const js: SetArgument(cx, script, arg, type); } -///////////////////////////////////////////////////////////////////// -// TypeCompartment -///////////////////////////////////////////////////////////////////// - -inline JSCompartment * -TypeCompartment::compartment() -{ - return (JSCompartment *)((char *)this - offsetof(JSCompartment, types)); -} - ///////////////////////////////////////////////////////////////////// // TypeSet ///////////////////////////////////////////////////////////////////// @@ -1189,33 +1002,26 @@ TypeSet::getObjectClass(unsigned i) const } ///////////////////////////////////////////////////////////////////// -// ObjectGroup +// TypeNewScript ///////////////////////////////////////////////////////////////////// -inline -ObjectGroup::ObjectGroup(const Class *clasp, TaggedProto proto, ObjectGroupFlags initialFlags) -{ - mozilla::PodZero(this); - - /* Inner objects may not appear on prototype chains. */ - MOZ_ASSERT_IF(proto.isObject(), !proto.toObject()->getClass()->ext.outerObject); - - this->clasp_ = clasp; - this->proto_ = proto.raw(); - this->flags_ = initialFlags; - - setGeneration(zone()->types.generation); - - InferSpew(ISpewOps, "newGroup: %s", ObjectGroupString(this)); -} - inline void -ObjectGroup::finalize(FreeOp *fop) +TypeNewScript::writeBarrierPre(TypeNewScript *newScript) { - fop->delete_(newScriptDontCheckGeneration()); - fop->delete_(maybeUnboxedLayoutDontCheckGeneration()); + if (!newScript->function()->runtimeFromAnyThread()->needsIncrementalBarrier()) + return; + + JS::Zone *zone = newScript->function()->zoneFromAnyThread(); + if (zone->needsIncrementalBarrier()) + newScript->trace(zone->barrierTracer()); } +} // namespace types + +///////////////////////////////////////////////////////////////////// +// ObjectGroup +///////////////////////////////////////////////////////////////////// + inline uint32_t ObjectGroup::basePropertyCount() { @@ -1231,24 +1037,24 @@ ObjectGroup::setBasePropertyCount(uint32_t count) | (count << OBJECT_FLAG_PROPERTY_COUNT_SHIFT); } -inline HeapTypeSet * +inline types::HeapTypeSet * ObjectGroup::getProperty(ExclusiveContext *cx, jsid id) { MOZ_ASSERT(JSID_IS_VOID(id) || JSID_IS_EMPTY(id) || JSID_IS_STRING(id) || JSID_IS_SYMBOL(id)); - MOZ_ASSERT_IF(!JSID_IS_EMPTY(id), id == IdToTypeId(id)); + MOZ_ASSERT_IF(!JSID_IS_EMPTY(id), id == types::IdToTypeId(id)); MOZ_ASSERT(!unknownProperties()); - if (HeapTypeSet *types = maybeGetProperty(id)) + if (types::HeapTypeSet *types = maybeGetProperty(id)) return types; - Property *base = cx->typeLifoAlloc().new_(id); + types::Property *base = cx->typeLifoAlloc().new_(id); if (!base) { markUnknown(cx); return nullptr; } uint32_t propertyCount = basePropertyCount(); - Property **pprop = HashSetInsert + types::Property **pprop = types::HashSetInsert (cx->typeLifoAlloc(), propertySet, propertyCount, id); if (!pprop) { markUnknown(cx); @@ -1272,14 +1078,14 @@ ObjectGroup::getProperty(ExclusiveContext *cx, jsid id) return &base->types; } -inline HeapTypeSet * +inline types::HeapTypeSet * ObjectGroup::maybeGetProperty(jsid id) { MOZ_ASSERT(JSID_IS_VOID(id) || JSID_IS_EMPTY(id) || JSID_IS_STRING(id) || JSID_IS_SYMBOL(id)); - MOZ_ASSERT_IF(!JSID_IS_EMPTY(id), id == IdToTypeId(id)); + MOZ_ASSERT_IF(!JSID_IS_EMPTY(id), id == types::IdToTypeId(id)); MOZ_ASSERT(!unknownProperties()); - Property *prop = HashSetLookup + types::Property *prop = types::HashSetLookup (propertySet, basePropertyCount(), id); return prop ? &prop->types : nullptr; @@ -1289,50 +1095,22 @@ inline unsigned ObjectGroup::getPropertyCount() { uint32_t count = basePropertyCount(); - if (count > SET_ARRAY_SIZE) - return HashSetCapacity(count); + if (count > types::SET_ARRAY_SIZE) + return types::HashSetCapacity(count); return count; } -inline Property * +inline types::Property * ObjectGroup::getProperty(unsigned i) { MOZ_ASSERT(i < getPropertyCount()); if (basePropertyCount() == 1) { MOZ_ASSERT(i == 0); - return (Property *) propertySet; + return (types::Property *) propertySet; } return propertySet[i]; } -inline void -TypeNewScript::writeBarrierPre(TypeNewScript *newScript) -{ - if (!newScript->function()->runtimeFromAnyThread()->needsIncrementalBarrier()) - return; - - JS::Zone *zone = newScript->function()->zoneFromAnyThread(); - if (zone->needsIncrementalBarrier()) - newScript->trace(zone->barrierTracer()); -} - -} } /* namespace js::types */ - -inline js::types::TypeScript * -JSScript::types() -{ - maybeSweepTypes(nullptr); - return types_; -} - -inline bool -JSScript::ensureHasTypes(JSContext *cx) -{ - return types() || makeTypes(cx); -} - -namespace js { - template <> struct GCMethods { @@ -1355,4 +1133,17 @@ struct GCMethods } // namespace js +inline js::types::TypeScript * +JSScript::types() +{ + maybeSweepTypes(nullptr); + return types_; +} + +inline bool +JSScript::ensureHasTypes(JSContext *cx) +{ + return types() || makeTypes(cx); +} + #endif /* jsinferinlines_h */ diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp index 4ebc885c2844..a6632f9a6be2 100644 --- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -483,8 +483,8 @@ static inline PropertyIteratorObject * NewPropertyIteratorObject(JSContext *cx, unsigned flags) { if (flags & JSITER_ENUMERATE) { - RootedObjectGroup group(cx, cx->getNewGroup(&PropertyIteratorObject::class_, - TaggedProto(nullptr))); + RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &PropertyIteratorObject::class_, + TaggedProto(nullptr))); if (!group) return nullptr; @@ -580,7 +580,7 @@ VectorToKeyIterator(JSContext *cx, HandleObject obj, unsigned flags, AutoIdVecto if (obj->isSingleton() && !obj->setIteratedSingleton(cx)) return false; - types::MarkObjectGroupFlags(cx, obj, types::OBJECT_FLAG_ITERATED); + types::MarkObjectGroupFlags(cx, obj, OBJECT_FLAG_ITERATED); Rooted iterobj(cx, NewPropertyIteratorObject(cx, flags)); if (!iterobj) @@ -623,7 +623,7 @@ VectorToValueIterator(JSContext *cx, HandleObject obj, unsigned flags, AutoIdVec if (obj->isSingleton() && !obj->setIteratedSingleton(cx)) return false; - types::MarkObjectGroupFlags(cx, obj, types::OBJECT_FLAG_ITERATED); + types::MarkObjectGroupFlags(cx, obj, OBJECT_FLAG_ITERATED); Rooted iterobj(cx, NewPropertyIteratorObject(cx, flags)); if (!iterobj) @@ -1173,7 +1173,7 @@ SuppressDeletedPropertyHelper(JSContext *cx, HandleObject obj, StringPredicate p return false; Rooted desc(cx); - if (!JS_GetPropertyDescriptorById(cx, proto, id, &desc)) + if (!GetPropertyDescriptor(cx, proto, id, &desc)) return false; if (desc.object()) { @@ -1183,7 +1183,7 @@ SuppressDeletedPropertyHelper(JSContext *cx, HandleObject obj, StringPredicate p } /* - * If JS_GetPropertyDescriptorById above removed a property from + * If GetPropertyDescriptorById above removed a property from * ni, start over. */ if (props_end != ni->props_end || props_cursor != ni->props_cursor) diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 651cb9948d91..5ff797bc5c8c 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -1014,64 +1014,15 @@ js::DefineProperties(JSContext *cx, HandleObject obj, HandleObject props) if (!ReadPropertyDescriptors(cx, props, true, &ids, &descs)) return false; - if (obj->is()) { - bool dummy; - Rooted arr(cx, &obj->as()); - for (size_t i = 0, len = ids.length(); i < len; i++) { - if (!DefinePropertyOnArray(cx, arr, ids[i], descs[i], true, &dummy)) - return false; - } - return true; - } - - if (IsAnyTypedArray(obj)) { - bool dummy; - for (size_t i = 0, len = ids.length(); i < len; i++) { - if (!DefinePropertyOnTypedArray(cx, obj, ids[i], descs[i], true, &dummy)) - return false; - } - return true; - } - - if (obj->is() && !obj->as().convertToNative(cx)) - return false; - - if (obj->getOps()->lookupProperty) { - if (obj->is()) { - Rooted pd(cx); - for (size_t i = 0, len = ids.length(); i < len; i++) { - descs[i].populatePropertyDescriptor(obj, &pd); - if (!Proxy::defineProperty(cx, obj, ids[i], &pd)) - return false; - } - return true; - } - bool dummy; - return Reject(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE, true, &dummy); - } - bool dummy; for (size_t i = 0, len = ids.length(); i < len; i++) { - if (!DefinePropertyOnObject(cx, obj.as(), ids[i], descs[i], true, &dummy)) + if (!StandardDefineProperty(cx, obj, ids[i], descs[i], true, &dummy)) return false; } return true; } -js::types::ObjectGroup* -JSObject::uninlinedGetGroup(JSContext *cx) -{ - return getGroup(cx); -} - -void -JSObject::uninlinedSetGroup(js::types::ObjectGroup *group) -{ - setGroup(group); -} - - /*** Seal and freeze *****************************************************************************/ static unsigned @@ -1268,7 +1219,7 @@ NewObjectGCKind(const js::Class *clasp) } static inline JSObject * -NewObject(ExclusiveContext *cx, types::ObjectGroup *groupArg, JSObject *parent, gc::AllocKind kind, +NewObject(ExclusiveContext *cx, ObjectGroup *groupArg, JSObject *parent, gc::AllocKind kind, NewObjectKind newKind) { const Class *clasp = groupArg->clasp(); @@ -1367,7 +1318,7 @@ js::NewObjectWithGivenProto(ExclusiveContext *cxArg, const js::Class *clasp, Rooted proto(cxArg, protoArg); RootedObject parent(cxArg, parentArg); - types::ObjectGroup *group = cxArg->getNewGroup(clasp, proto, nullptr); + ObjectGroup *group = ObjectGroup::defaultNewGroup(cxArg, clasp, proto, nullptr); if (!group) return nullptr; @@ -1549,7 +1500,7 @@ js::NewObjectWithClassProtoCommon(ExclusiveContext *cxArg, return nullptr; Rooted taggedProto(cxArg, TaggedProto(proto)); - types::ObjectGroup *group = cxArg->getNewGroup(clasp, taggedProto); + ObjectGroup *group = ObjectGroup::defaultNewGroup(cxArg, clasp, taggedProto); if (!group) return nullptr; @@ -1618,16 +1569,16 @@ js::NewObjectScriptedCall(JSContext *cx, MutableHandleObject pobj) jsbytecode *pc; RootedScript script(cx, cx->currentScript(&pc)); gc::AllocKind allocKind = NewObjectGCKind(&PlainObject::class_); - NewObjectKind newKind = script - ? UseSingletonForInitializer(script, pc, &PlainObject::class_) - : GenericObject; + NewObjectKind newKind = GenericObject; + if (script && ObjectGroup::useSingletonForAllocationSite(script, pc, &PlainObject::class_)) + newKind = SingletonObject; RootedObject obj(cx, NewBuiltinClassInstance(cx, allocKind, newKind)); if (!obj) return false; if (script) { /* Try to specialize the group of the object to the scripted call site. */ - if (!types::SetInitializerObjectGroup(cx, script, pc, obj, newKind)) + if (!ObjectGroup::setAllocationSiteObjectGroup(cx, script, pc, obj, newKind == SingletonObject)) return false; } @@ -1707,8 +1658,8 @@ js::CreateThisForFunctionWithProto(JSContext *cx, HandleObject callee, JSObject RootedObject res(cx); if (proto) { - RootedObjectGroup group(cx, cx->getNewGroup(nullptr, TaggedProto(proto), - &callee->as())); + RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, nullptr, TaggedProto(proto), + &callee->as())); if (!group) return nullptr; @@ -1719,8 +1670,8 @@ js::CreateThisForFunctionWithProto(JSContext *cx, HandleObject callee, JSObject if (regenerate) { // The script was analyzed successfully and may have changed // the new type table, so refetch the group. - group = cx->getNewGroup(nullptr, TaggedProto(proto), - &callee->as()); + group = ObjectGroup::defaultNewGroup(cx, nullptr, TaggedProto(proto), + &callee->as()); MOZ_ASSERT(group && group->newScript()); } } @@ -1980,9 +1931,9 @@ js::DeepCloneObjectLiteral(JSContext *cx, HandleNativeObject obj, NewObjectKind if (!JSObject::setSingleton(cx, clone)) return nullptr; } else if (obj->is()) { - FixArrayGroup(cx, &clone->as()); + ObjectGroup::fixArrayGroup(cx, &clone->as()); } else { - FixObjectGroup(cx, &clone->as()); + ObjectGroup::fixPlainObjectGroup(cx, &clone->as()); } if (obj->is() && obj->denseElementsAreCopyOnWrite()) { @@ -2190,9 +2141,9 @@ js::XDRObjectLiteral(XDRState *xdr, MutableHandleNativeObject obj) if (!JSObject::setSingleton(cx, obj)) return false; } else if (isArray) { - FixArrayGroup(cx, &obj->as()); + ObjectGroup::fixArrayGroup(cx, &obj->as()); } else { - FixObjectGroup(cx, &obj->as()); + ObjectGroup::fixPlainObjectGroup(cx, &obj->as()); } } @@ -2243,7 +2194,8 @@ js::CloneObjectLiteral(JSContext *cx, HandleObject parent, HandleObject srcObj) JSObject *proto = cx->global()->getOrCreateObjectPrototype(cx); if (!proto) return nullptr; - RootedObjectGroup group(cx, cx->getNewGroup(&PlainObject::class_, TaggedProto(proto))); + RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &PlainObject::class_, + TaggedProto(proto))); if (!group) return nullptr; @@ -2748,7 +2700,7 @@ js::SetClassAndProto(JSContext *cx, HandleObject obj, return false; } - ObjectGroup *group = cx->getNewGroup(clasp, proto); + ObjectGroup *group = ObjectGroup::defaultNewGroup(cx, clasp, proto); if (!group) return false; @@ -3338,6 +3290,30 @@ js::SetImmutablePrototype(ExclusiveContext *cx, HandleObject obj, bool *succeede return true; } +bool +js::GetPropertyDescriptor(JSContext *cx, HandleObject obj, HandleId id, + MutableHandle desc) +{ + RootedObject pobj(cx); + + for (pobj = obj; pobj;) { + if (pobj->is()) + return Proxy::getPropertyDescriptor(cx, pobj, id, desc); + + if (!GetOwnPropertyDescriptor(cx, pobj, id, desc)) + return false; + + if (desc.object()) + return true; + + if (!GetPrototype(cx, pobj, &pobj)) + return false; + } + + MOZ_ASSERT(!desc.object()); + return true; +} + bool js::ToPrimitive(JSContext *cx, HandleObject obj, JSType hint, MutableHandleValue vp) { diff --git a/js/src/jsobj.h b/js/src/jsobj.h index 90b93bb1a7c7..9ab325d74d94 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -118,7 +118,7 @@ class JSObject : public js::gc::Cell bool *succeeded); // Make a new group to use for a singleton object. - static js::types::ObjectGroup *makeLazyGroup(JSContext *cx, js::HandleObject obj); + static js::ObjectGroup *makeLazyGroup(JSContext *cx, js::HandleObject obj); public: js::Shape * lastProperty() const { @@ -143,12 +143,12 @@ class JSObject : public js::gc::Cell return &getClass()->ops; } - js::types::ObjectGroup *group() const { + js::ObjectGroup *group() const { MOZ_ASSERT(!hasLazyGroup()); return groupRaw(); } - js::types::ObjectGroup *groupRaw() const { + js::ObjectGroup *groupRaw() const { return group_; } @@ -323,9 +323,7 @@ class JSObject : public js::gc::Cell */ static inline bool setSingleton(js::ExclusiveContext *cx, js::HandleObject obj); - // uninlinedGetGroup() is the same as getGroup(), but not inlined. - inline js::types::ObjectGroup* getGroup(JSContext *cx); - js::types::ObjectGroup* uninlinedGetGroup(JSContext *cx); + inline js::ObjectGroup* getGroup(JSContext *cx); const js::HeapPtrObjectGroup &groupFromGC() const { /* Direct field access for use by GC. */ @@ -355,9 +353,7 @@ class JSObject : public js::gc::Cell JSObject *getProto() const { MOZ_ASSERT(!uninlinedIsProxy()); - JSObject *proto = getTaggedProto().toObjectOrNull(); - MOZ_ASSERT_IF(proto && proto->isNative(), proto->isDelegate()); - return proto; + return getTaggedProto().toObjectOrNull(); } // Normal objects and a subset of proxies have uninteresting [[Prototype]]. @@ -390,13 +386,7 @@ class JSObject : public js::gc::Cell return lastProperty()->hasObjectFlag(js::BaseShape::IMMUTABLE_PROTOTYPE); } - // uninlinedSetGroup() is the same as setGroup(), but not inlined. - inline void setGroup(js::types::ObjectGroup *group); - void uninlinedSetGroup(js::types::ObjectGroup *group); - -#ifdef DEBUG - bool hasNewGroup(const js::Class *clasp, js::types::ObjectGroup *group); -#endif + inline void setGroup(js::ObjectGroup *group); /* * Mark an object that has been iterated over and is a singleton. We need @@ -925,6 +915,10 @@ DeleteElement(JSContext *cx, js::HandleObject obj, uint32_t index, bool *succeed extern bool SetImmutablePrototype(js::ExclusiveContext *cx, JS::HandleObject obj, bool *succeeded); +extern bool +GetPropertyDescriptor(JSContext *cx, HandleObject obj, HandleId id, + MutableHandle desc); + /* * Deprecated. A version of HasProperty that also returns the object on which * the property was found (but that information is unreliable for proxies), and diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index ba8de8a14879..7f15db094dff 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -75,8 +75,8 @@ JSObject::setSingleton(js::ExclusiveContext *cx, js::HandleObject obj) { MOZ_ASSERT_IF(cx->isJSContext(), !IsInsideNursery(obj)); - js::types::ObjectGroup *group = cx->getLazySingletonGroup(obj->getClass(), - obj->getTaggedProto()); + js::ObjectGroup *group = js::ObjectGroup::lazySingletonGroup(cx, obj->getClass(), + obj->getTaggedProto()); if (!group) return false; @@ -84,7 +84,7 @@ JSObject::setSingleton(js::ExclusiveContext *cx, js::HandleObject obj) return true; } -inline js::types::ObjectGroup* +inline js::ObjectGroup* JSObject::getGroup(JSContext *cx) { MOZ_ASSERT(cx->compartment() == compartment()); @@ -98,7 +98,7 @@ JSObject::getGroup(JSContext *cx) } inline void -JSObject::setGroup(js::types::ObjectGroup *group) +JSObject::setGroup(js::ObjectGroup *group) { MOZ_ASSERT(group); MOZ_ASSERT(!isSingleton()); diff --git a/js/src/jsscript.h b/js/src/jsscript.h index 72ae17a582b1..166afd92407a 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -875,13 +875,6 @@ class JSScript : public js::gc::TenuredCell * Reset if the script's JIT code is forcibly * discarded. */ -#ifdef DEBUG - // Unique identifier within the compartment for this script, used for - // printing analysis information. - uint32_t id_; - uint32_t idpad; -#endif - // 16-bit fields. uint16_t version; /* JS version under which script was compiled */ @@ -1459,12 +1452,6 @@ class JSScript : public js::gc::TenuredCell /* Return whether this script was compiled for 'eval' */ bool isForEval() { return isCachedEval() || isActiveEval(); } -#ifdef DEBUG - unsigned id(); -#else - unsigned id() { return 0; } -#endif - /* Ensure the script has a TypeScript. */ inline bool ensureHasTypes(JSContext *cx); diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index 908e40623ac9..4d11745d25df 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -3785,7 +3785,7 @@ js::str_split(JSContext *cx, unsigned argc, Value *vp) if (!str) return false; - RootedObjectGroup group(cx, GetCallerInitGroup(cx, JSProto_Array)); + RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array)); if (!group) return false; AddTypePropertyId(cx, group, JSID_VOID, Type::StringType()); diff --git a/js/src/moz.build b/js/src/moz.build index 239c858519b9..93330b7028e2 100644 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -253,6 +253,7 @@ UNIFIED_SOURCES += [ 'vm/MemoryMetrics.cpp', 'vm/Monitor.cpp', 'vm/NativeObject.cpp', + 'vm/ObjectGroup.cpp', 'vm/PIC.cpp', 'vm/Probes.cpp', 'vm/ProxyObject.cpp', diff --git a/js/src/proxy/DirectProxyHandler.cpp b/js/src/proxy/DirectProxyHandler.cpp index 29bd91fc9e1b..533f21a4c066 100644 --- a/js/src/proxy/DirectProxyHandler.cpp +++ b/js/src/proxy/DirectProxyHandler.cpp @@ -20,7 +20,7 @@ DirectProxyHandler::getPropertyDescriptor(JSContext *cx, HandleObject proxy, Han assertEnteredPolicy(cx, proxy, id, GET | SET | GET_PROPERTY_DESCRIPTOR); MOZ_ASSERT(!hasPrototype()); // Should never be called if there's a prototype. RootedObject target(cx, proxy->as().target()); - return JS_GetPropertyDescriptorById(cx, target, id, desc); + return GetPropertyDescriptor(cx, target, id, desc); } bool diff --git a/js/src/proxy/Proxy.cpp b/js/src/proxy/Proxy.cpp index 0093f513a23f..7ccdead5398a 100644 --- a/js/src/proxy/Proxy.cpp +++ b/js/src/proxy/Proxy.cpp @@ -111,7 +111,7 @@ Proxy::getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, return false; if (desc.object()) return true; - INVOKE_ON_PROTOTYPE(cx, handler, proxy, JS_GetPropertyDescriptorById(cx, proto, id, desc)); + INVOKE_ON_PROTOTYPE(cx, handler, proxy, GetPropertyDescriptor(cx, proto, id, desc)); } bool diff --git a/js/src/proxy/ScriptedDirectProxyHandler.cpp b/js/src/proxy/ScriptedDirectProxyHandler.cpp index 6b9e17d25b5a..aaa43913704c 100644 --- a/js/src/proxy/ScriptedDirectProxyHandler.cpp +++ b/js/src/proxy/ScriptedDirectProxyHandler.cpp @@ -429,7 +429,7 @@ ScriptedDirectProxyHandler::getPropertyDescriptor(JSContext *cx, HandleObject pr MOZ_ASSERT(!desc.object()); return true; } - return JS_GetPropertyDescriptorById(cx, proto, id, desc); + return GetPropertyDescriptor(cx, proto, id, desc); } // ES6 (5 April 2014) 9.5.5 Proxy.[[GetOwnProperty]](P) diff --git a/js/src/vm/ArgumentsObject.cpp b/js/src/vm/ArgumentsObject.cpp index f78218907a90..1d9ff6c822a3 100644 --- a/js/src/vm/ArgumentsObject.cpp +++ b/js/src/vm/ArgumentsObject.cpp @@ -167,7 +167,7 @@ ArgumentsObject::create(JSContext *cx, HandleScript script, HandleFunction calle bool strict = callee->strict(); const Class *clasp = strict ? &StrictArgumentsObject::class_ : &NormalArgumentsObject::class_; - RootedObjectGroup group(cx, cx->getNewGroup(clasp, TaggedProto(proto.get()))); + RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, clasp, TaggedProto(proto.get()))); if (!group) return nullptr; diff --git a/js/src/vm/ArrayBufferObject.cpp b/js/src/vm/ArrayBufferObject.cpp index cb57b746ed77..9befbec20bec 100644 --- a/js/src/vm/ArrayBufferObject.cpp +++ b/js/src/vm/ArrayBufferObject.cpp @@ -523,7 +523,7 @@ ArrayBufferObject::neuter(JSContext *cx, Handle buffer, // flag change will be observed. if (!cx->global()->getGroup(cx)) CrashAtUnhandlableOOM("ArrayBufferObject::neuter"); - types::MarkObjectGroupFlags(cx, cx->global(), types::OBJECT_FLAG_TYPED_OBJECT_NEUTERED); + types::MarkObjectGroupFlags(cx, cx->global(), OBJECT_FLAG_TYPED_OBJECT_NEUTERED); cx->compartment()->neuteredTypedObjects = 1; } diff --git a/js/src/vm/ArrayObject-inl.h b/js/src/vm/ArrayObject-inl.h index eb2a75a93321..793656fda281 100644 --- a/js/src/vm/ArrayObject-inl.h +++ b/js/src/vm/ArrayObject-inl.h @@ -22,7 +22,7 @@ ArrayObject::setLength(ExclusiveContext *cx, uint32_t length) if (length > INT32_MAX) { /* Track objects with overflowing lengths in type information. */ - types::MarkObjectGroupFlags(cx, this, types::OBJECT_FLAG_LENGTH_OVERFLOW); + types::MarkObjectGroupFlags(cx, this, OBJECT_FLAG_LENGTH_OVERFLOW); } getElementsHeader()->length = length; diff --git a/js/src/vm/HelperThreads.cpp b/js/src/vm/HelperThreads.cpp index a022b33e6579..5004630bd38b 100644 --- a/js/src/vm/HelperThreads.cpp +++ b/js/src/vm/HelperThreads.cpp @@ -845,6 +845,15 @@ LeaveParseTaskZone(JSRuntime *rt, ParseTask *task) rt->clearUsedByExclusiveThread(task->cx->zone()); } +static bool +EnsureConstructor(JSContext *cx, Handle global, JSProtoKey key) +{ + if (!GlobalObject::ensureConstructor(cx, global, key)) + return false; + + return global->getPrototype(key).toObject().setDelegate(cx); +} + JSScript * GlobalHelperThreadState::finishParseTask(JSContext *maybecx, JSRuntime *rt, void *token) { @@ -876,11 +885,11 @@ GlobalHelperThreadState::finishParseTask(JSContext *maybecx, JSRuntime *rt, void // Make sure we have all the constructors we need for the prototype // remapping below, since we can't GC while that's happening. Rooted global(cx, &cx->global()->as()); - if (!GlobalObject::ensureConstructor(cx, global, JSProto_Object) || - !GlobalObject::ensureConstructor(cx, global, JSProto_Array) || - !GlobalObject::ensureConstructor(cx, global, JSProto_Function) || - !GlobalObject::ensureConstructor(cx, global, JSProto_RegExp) || - !GlobalObject::ensureConstructor(cx, global, JSProto_Iterator)) + if (!EnsureConstructor(cx, global, JSProto_Object) || + !EnsureConstructor(cx, global, JSProto_Array) || + !EnsureConstructor(cx, global, JSProto_Function) || + !EnsureConstructor(cx, global, JSProto_RegExp) || + !EnsureConstructor(cx, global, JSProto_Iterator)) { LeaveParseTaskZone(rt, parseTask); return nullptr; @@ -896,7 +905,7 @@ GlobalHelperThreadState::finishParseTask(JSContext *maybecx, JSRuntime *rt, void !iter.done(); iter.next()) { - types::ObjectGroup *group = iter.get(); + ObjectGroup *group = iter.get(); TaggedProto proto(group->proto()); if (!proto.isObject()) continue; diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index ce60266a18aa..ee4c37708880 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -411,11 +411,6 @@ js::RunScript(JSContext *cx, RunState &state) { JS_CHECK_RECURSION(cx, return false); -#ifdef NIGHTLY_BUILD - if (AssertOnScriptEntryHook hook = cx->runtime()->assertOnScriptEntryHook_) - (*hook)(cx, state.script()); -#endif - SPSEntryMarker marker(cx->runtime(), state.script()); state.script()->ensureNonLazyCanonicalFunction(cx); @@ -509,7 +504,7 @@ js::Invoke(JSContext *cx, CallArgs args, MaybeConstruct construct) if (!iter.done() && iter.hasScript()) { JSScript *script = iter.script(); jsbytecode *pc = iter.pc(); - if (UseSingletonForNewObject(cx, script, pc)) + if (ObjectGroup::useSingletonForNewObject(cx, script, pc)) state.setCreateSingleton(); } } @@ -2564,7 +2559,7 @@ CASE(JSOP_FUNCALL) } InitialFrameFlags initial = construct ? INITIAL_CONSTRUCT : INITIAL_NONE; - bool createSingleton = UseSingletonForNewObject(cx, script, REGS.pc); + bool createSingleton = ObjectGroup::useSingletonForNewObject(cx, script, REGS.pc); TypeMonitorCall(cx, args, construct); @@ -3086,17 +3081,22 @@ CASE(JSOP_NEWINIT) MOZ_ASSERT(i == JSProto_Array || i == JSProto_Object); RootedObject &obj = rootObject0; - NewObjectKind newKind; + NewObjectKind newKind = GenericObject; if (i == JSProto_Array) { - newKind = UseSingletonForInitializer(script, REGS.pc, &ArrayObject::class_); + if (ObjectGroup::useSingletonForAllocationSite(script, REGS.pc, &ArrayObject::class_)) + newKind = SingletonObject; obj = NewDenseEmptyArray(cx, nullptr, newKind); } else { gc::AllocKind allocKind = GuessObjectGCKind(0); - newKind = UseSingletonForInitializer(script, REGS.pc, &PlainObject::class_); + if (ObjectGroup::useSingletonForAllocationSite(script, REGS.pc, &PlainObject::class_)) + newKind = SingletonObject; obj = NewBuiltinClassInstance(cx, allocKind, newKind); } - if (!obj || !SetInitializerObjectGroup(cx, script, REGS.pc, obj, newKind)) + if (!obj || !ObjectGroup::setAllocationSiteObjectGroup(cx, script, REGS.pc, obj, + newKind == SingletonObject)) + { goto error; + } PUSH_OBJECT(*obj); } @@ -3106,10 +3106,15 @@ CASE(JSOP_NEWARRAY) { unsigned count = GET_UINT24(REGS.pc); RootedObject &obj = rootObject0; - NewObjectKind newKind = UseSingletonForInitializer(script, REGS.pc, &ArrayObject::class_); + NewObjectKind newKind = GenericObject; + if (ObjectGroup::useSingletonForAllocationSite(script, REGS.pc, &ArrayObject::class_)) + newKind = SingletonObject; obj = NewDenseFullyAllocatedArray(cx, count, nullptr, newKind); - if (!obj || !SetInitializerObjectGroup(cx, script, REGS.pc, obj, newKind)) + if (!obj || !ObjectGroup::setAllocationSiteObjectGroup(cx, script, REGS.pc, obj, + newKind == SingletonObject)) + { goto error; + } PUSH_OBJECT(*obj); } @@ -3118,7 +3123,7 @@ END_CASE(JSOP_NEWARRAY) CASE(JSOP_NEWARRAY_COPYONWRITE) { RootedObject &baseobj = rootObject0; - baseobj = types::GetOrFixupCopyOnWriteObject(cx, script, REGS.pc); + baseobj = ObjectGroup::getOrFixupCopyOnWriteObject(cx, script, REGS.pc); if (!baseobj) goto error; @@ -3137,10 +3142,15 @@ CASE(JSOP_NEWOBJECT) baseobj = script->getObject(REGS.pc); RootedObject &obj = rootObject1; - NewObjectKind newKind = UseSingletonForInitializer(script, REGS.pc, baseobj->getClass()); + NewObjectKind newKind = GenericObject; + if (ObjectGroup::useSingletonForAllocationSite(script, REGS.pc, baseobj->getClass())) + newKind = SingletonObject; obj = CopyInitializerObject(cx, baseobj.as(), newKind); - if (!obj || !SetInitializerObjectGroup(cx, script, REGS.pc, obj, newKind)) + if (!obj || !ObjectGroup::setAllocationSiteObjectGroup(cx, script, REGS.pc, obj, + newKind == SingletonObject)) + { goto error; + } PUSH_OBJECT(*obj); } @@ -3776,17 +3786,6 @@ js::GetAndClearException(JSContext *cx, MutableHandleValue res) return CheckForInterrupt(cx); } -template -bool -js::SetProperty(JSContext *cx, HandleObject obj, HandleId id, const Value &value) -{ - RootedValue v(cx, value); - return SetProperty(cx, obj, obj, id, &v, strict); -} - -template bool js::SetProperty (JSContext *cx, HandleObject obj, HandleId id, const Value &value); -template bool js::SetProperty(JSContext *cx, HandleObject obj, HandleId id, const Value &value); - template bool js::DeleteProperty(JSContext *cx, HandleValue v, HandlePropertyName name, bool *bp) @@ -3965,7 +3964,7 @@ js::RunOnceScriptPrologue(JSContext *cx, HandleScript script) return false; types::MarkObjectGroupFlags(cx, script->functionNonDelazifying(), - types::OBJECT_FLAG_RUNONCE_INVALIDATED); + OBJECT_FLAG_RUNONCE_INVALIDATED); return true; } diff --git a/js/src/vm/Interpreter.h b/js/src/vm/Interpreter.h index cec256e9410d..269a5762341d 100644 --- a/js/src/vm/Interpreter.h +++ b/js/src/vm/Interpreter.h @@ -341,10 +341,6 @@ ModValues(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, Mutable bool UrshValues(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, MutableHandleValue res); -template -bool -SetProperty(JSContext *cx, HandleObject obj, HandleId id, const Value &value); - template bool DeleteProperty(JSContext *ctx, HandleValue val, HandlePropertyName name, bool *bv); diff --git a/js/src/vm/JSONParser.cpp b/js/src/vm/JSONParser.cpp index ccf5808a2ca6..a8e892f382d2 100644 --- a/js/src/vm/JSONParser.cpp +++ b/js/src/vm/JSONParser.cpp @@ -581,12 +581,12 @@ JSObject * JSONParserBase::createFinishedObject(PropertyVector &properties) { /* - * Look for an existing cached type and shape for objects with this set of + * Look for an existing cached group and shape for objects with this set of * properties. */ { - JSObject *obj = cx->compartment()->types.newTypedObject(cx, properties.begin(), - properties.length()); + JSObject *obj = ObjectGroup::newPlainObject(cx, properties.begin(), + properties.length()); if (obj) return obj; } @@ -611,11 +611,11 @@ JSONParserBase::createFinishedObject(PropertyVector &properties) } /* - * Try to assign a new type to the object with type information for its + * Try to assign a new group to the object with type information for its * properties, and update the initializer object group cache with this * object's final shape. */ - cx->compartment()->types.fixObjectGroup(cx, obj); + ObjectGroup::fixPlainObjectGroup(cx, obj); return obj; } @@ -645,8 +645,8 @@ JSONParserBase::finishArray(MutableHandleValue vp, ElementVector &elements) if (!obj) return false; - /* Try to assign a new type to the array according to its elements. */ - cx->compartment()->types.fixArrayGroup(cx, obj); + /* Try to assign a new group to the array according to its elements. */ + ObjectGroup::fixArrayGroup(cx, obj); vp.setObject(*obj); if (!freeElements.append(&elements)) diff --git a/js/src/vm/MemoryMetrics.cpp b/js/src/vm/MemoryMetrics.cpp index f93d49bb24e9..450c47d9fbb8 100644 --- a/js/src/vm/MemoryMetrics.cpp +++ b/js/src/vm/MemoryMetrics.cpp @@ -550,7 +550,7 @@ StatsCellCallback(JSRuntime *rt, void *data, void *thing, JSGCTraceKind traceKin } case JSTRACE_OBJECT_GROUP: { - types::ObjectGroup *group = static_cast(thing); + ObjectGroup *group = static_cast(thing); zStats->objectGroupsGCHeap += thingSize; zStats->objectGroupsMallocHeap += group->sizeOfExcludingThis(rtStats->mallocSizeOf_); break; diff --git a/js/src/vm/NativeObject-inl.h b/js/src/vm/NativeObject-inl.h index 0146d2375416..4652d213011f 100644 --- a/js/src/vm/NativeObject-inl.h +++ b/js/src/vm/NativeObject-inl.h @@ -99,7 +99,7 @@ NativeObject::initDenseElementWithType(ExclusiveContext *cx, uint32_t index, inline void NativeObject::setDenseElementHole(ExclusiveContext *cx, uint32_t index) { - types::MarkObjectGroupFlags(cx, this, types::OBJECT_FLAG_NON_PACKED); + types::MarkObjectGroupFlags(cx, this, OBJECT_FLAG_NON_PACKED); setDenseElement(index, MagicValue(JS_ELEMENTS_HOLE)); } @@ -108,8 +108,8 @@ NativeObject::removeDenseElementForSparseIndex(ExclusiveContext *cx, HandleNativeObject obj, uint32_t index) { types::MarkObjectGroupFlags(cx, obj, - types::OBJECT_FLAG_NON_PACKED | - types::OBJECT_FLAG_SPARSE_INDEXES); + OBJECT_FLAG_NON_PACKED | + OBJECT_FLAG_SPARSE_INDEXES); if (obj->containsDenseElement(index)) obj->setDenseElement(index, MagicValue(JS_ELEMENTS_HOLE)); } @@ -124,7 +124,7 @@ inline void NativeObject::markDenseElementsNotPacked(ExclusiveContext *cx) { MOZ_ASSERT(isNative()); - MarkObjectGroupFlags(cx, this, types::OBJECT_FLAG_NON_PACKED); + types::MarkObjectGroupFlags(cx, this, OBJECT_FLAG_NON_PACKED); } inline void diff --git a/js/src/vm/ObjectGroup.cpp b/js/src/vm/ObjectGroup.cpp new file mode 100644 index 000000000000..f1713e4b73fd --- /dev/null +++ b/js/src/vm/ObjectGroup.cpp @@ -0,0 +1,1565 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "vm/ObjectGroup.h" + +#include "jshashutil.h" +#include "jsobj.h" + +#include "gc/StoreBuffer.h" +#include "gc/Zone.h" +#include "vm/ArrayObject.h" +#include "vm/UnboxedObject.h" + +#include "jsgcinlines.h" +#include "jsinferinlines.h" +#include "jsobjinlines.h" + +using namespace js; +using namespace js::types; + +using mozilla::PodZero; + +///////////////////////////////////////////////////////////////////// +// ObjectGroup +///////////////////////////////////////////////////////////////////// + +ObjectGroup::ObjectGroup(const Class *clasp, TaggedProto proto, ObjectGroupFlags initialFlags) +{ + PodZero(this); + + /* Inner objects may not appear on prototype chains. */ + MOZ_ASSERT_IF(proto.isObject(), !proto.toObject()->getClass()->ext.outerObject); + + this->clasp_ = clasp; + this->proto_ = proto.raw(); + this->flags_ = initialFlags; + + setGeneration(zone()->types.generation); +} + +void +ObjectGroup::finalize(FreeOp *fop) +{ + fop->delete_(newScriptDontCheckGeneration()); + fop->delete_(maybeUnboxedLayoutDontCheckGeneration()); +} + +void +ObjectGroup::setProtoUnchecked(TaggedProto proto) +{ + proto_ = proto.raw(); + MOZ_ASSERT_IF(proto_ && proto_->isNative(), proto_->isDelegate()); +} + +void +ObjectGroup::setProto(JSContext *cx, TaggedProto proto) +{ + MOZ_ASSERT(singleton()); + + if (proto.isObject() && IsInsideNursery(proto.toObject())) + addFlags(OBJECT_FLAG_NURSERY_PROTO); + + setProtoUnchecked(proto); +} + +size_t +ObjectGroup::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const +{ + size_t n = 0; + if (TypeNewScript *newScript = newScriptDontCheckGeneration()) + n += newScript->sizeOfIncludingThis(mallocSizeOf); + if (UnboxedLayout *layout = maybeUnboxedLayoutDontCheckGeneration()) + n += layout->sizeOfIncludingThis(mallocSizeOf); + return n; +} + +void +ObjectGroup::setAddendum(AddendumKind kind, void *addendum, bool writeBarrier /* = true */) +{ + MOZ_ASSERT(!needsSweep()); + MOZ_ASSERT(kind <= (OBJECT_FLAG_ADDENDUM_MASK >> OBJECT_FLAG_ADDENDUM_SHIFT)); + + if (writeBarrier) { + // Manually trigger barriers if we are clearing a TypeNewScript. Other + // kinds of addendums are immutable. + if (newScript()) + TypeNewScript::writeBarrierPre(newScript()); + else + MOZ_ASSERT(addendumKind() == Addendum_None || addendumKind() == kind); + } + + flags_ &= ~OBJECT_FLAG_ADDENDUM_MASK; + flags_ |= kind << OBJECT_FLAG_ADDENDUM_SHIFT; + addendum_ = addendum; +} + +/* static */ bool +ObjectGroup::useSingletonForClone(JSFunction *fun) +{ + if (!fun->isInterpreted()) + return false; + + if (fun->hasScript() && fun->nonLazyScript()->shouldCloneAtCallsite()) + return true; + + if (fun->isArrow()) + return false; + + if (fun->isSingleton()) + return false; + + /* + * When a function is being used as a wrapper for another function, it + * improves precision greatly to distinguish between different instances of + * the wrapper; otherwise we will conflate much of the information about + * the wrapped functions. + * + * An important example is the Class.create function at the core of the + * Prototype.js library, which looks like: + * + * var Class = { + * create: function() { + * return function() { + * this.initialize.apply(this, arguments); + * } + * } + * }; + * + * Each instance of the innermost function will have a different wrapped + * initialize method. We capture this, along with similar cases, by looking + * for short scripts which use both .apply and arguments. For such scripts, + * whenever creating a new instance of the function we both give that + * instance a singleton type and clone the underlying script. + */ + + uint32_t begin, end; + if (fun->hasScript()) { + if (!fun->nonLazyScript()->usesArgumentsApplyAndThis()) + return false; + begin = fun->nonLazyScript()->sourceStart(); + end = fun->nonLazyScript()->sourceEnd(); + } else { + if (!fun->lazyScript()->usesArgumentsApplyAndThis()) + return false; + begin = fun->lazyScript()->begin(); + end = fun->lazyScript()->end(); + } + + return end - begin <= 100; +} + +/* static */ bool +ObjectGroup::useSingletonForNewObject(JSContext *cx, JSScript *script, jsbytecode *pc) +{ + /* + * Make a heuristic guess at a use of JSOP_NEW that the constructed object + * should have a fresh group. We do this when the NEW is immediately + * followed by a simple assignment to an object's .prototype field. + * This is designed to catch common patterns for subclassing in JS: + * + * function Super() { ... } + * function Sub1() { ... } + * function Sub2() { ... } + * + * Sub1.prototype = new Super(); + * Sub2.prototype = new Super(); + * + * Using distinct groups for the particular prototypes of Sub1 and + * Sub2 lets us continue to distinguish the two subclasses and any extra + * properties added to those prototype objects. + */ + if (script->isGenerator()) + return false; + if (JSOp(*pc) != JSOP_NEW) + return false; + pc += JSOP_NEW_LENGTH; + if (JSOp(*pc) == JSOP_SETPROP) { + if (script->getName(pc) == cx->names().prototype) + return true; + } + return false; +} + +/* static */ bool +ObjectGroup::useSingletonForAllocationSite(JSScript *script, jsbytecode *pc, JSProtoKey key) +{ + // The return value of this method can either be tested like a boolean or + // passed to a NewObject method. + JS_STATIC_ASSERT(GenericObject == 0); + + /* + * Objects created outside loops in global and eval scripts should have + * singleton types. For now this is only done for plain objects and typed + * arrays, but not normal arrays. + */ + + if (script->functionNonDelazifying() && !script->treatAsRunOnce()) + return GenericObject; + + if (key != JSProto_Object && + !(key >= JSProto_Int8Array && key <= JSProto_Uint8ClampedArray) && + !(key >= JSProto_SharedInt8Array && key <= JSProto_SharedUint8ClampedArray)) + { + return GenericObject; + } + + /* + * All loops in the script will have a JSTRY_ITER or JSTRY_LOOP try note + * indicating their boundary. + */ + + if (!script->hasTrynotes()) + return SingletonObject; + + unsigned offset = script->pcToOffset(pc); + + JSTryNote *tn = script->trynotes()->vector; + JSTryNote *tnlimit = tn + script->trynotes()->length; + for (; tn < tnlimit; tn++) { + if (tn->kind != JSTRY_ITER && tn->kind != JSTRY_LOOP) + continue; + + unsigned startOffset = script->mainOffset() + tn->start; + unsigned endOffset = startOffset + tn->length; + + if (offset >= startOffset && offset < endOffset) + return GenericObject; + } + + return SingletonObject; +} + +/* static */ bool +ObjectGroup::useSingletonForAllocationSite(JSScript *script, jsbytecode *pc, const Class *clasp) +{ + return useSingletonForAllocationSite(script, pc, JSCLASS_CACHED_PROTO_KEY(clasp)); +} + +///////////////////////////////////////////////////////////////////// +// JSObject +///////////////////////////////////////////////////////////////////// + +bool +JSObject::shouldSplicePrototype(JSContext *cx) +{ + /* + * During bootstrapping, if inference is enabled we need to make sure not + * to splice a new prototype in for Function.prototype or the global + * object if their __proto__ had previously been set to null, as this + * will change the prototype for all other objects with the same type. + */ + if (getProto() != nullptr) + return false; + return isSingleton(); +} + +bool +JSObject::splicePrototype(JSContext *cx, const Class *clasp, Handle proto) +{ + MOZ_ASSERT(cx->compartment() == compartment()); + + RootedObject self(cx, this); + + /* + * For singleton groups representing only a single JSObject, the proto + * can be rearranged as needed without destroying type information for + * the old or new types. + */ + MOZ_ASSERT(self->isSingleton()); + + // Inner objects may not appear on prototype chains. + MOZ_ASSERT_IF(proto.isObject(), !proto.toObject()->getClass()->ext.outerObject); + + if (proto.isObject() && !proto.toObject()->setDelegate(cx)) + return false; + + // Force type instantiation when splicing lazy group. + RootedObjectGroup group(cx, self->getGroup(cx)); + if (!group) + return false; + RootedObjectGroup protoGroup(cx, nullptr); + if (proto.isObject()) { + protoGroup = proto.toObject()->getGroup(cx); + if (!protoGroup) + return false; + } + + group->setClasp(clasp); + group->setProto(cx, proto); + return true; +} + +/* static */ ObjectGroup * +JSObject::makeLazyGroup(JSContext *cx, HandleObject obj) +{ + MOZ_ASSERT(obj->hasLazyGroup()); + MOZ_ASSERT(cx->compartment() == obj->compartment()); + + /* De-lazification of functions can GC, so we need to do it up here. */ + if (obj->is() && obj->as().isInterpretedLazy()) { + RootedFunction fun(cx, &obj->as()); + if (!fun->getOrCreateScript(cx)) + return nullptr; + } + + // Find flags which need to be specified immediately on the object. + // Don't track whether singletons are packed. + ObjectGroupFlags initialFlags = OBJECT_FLAG_NON_PACKED; + + if (obj->lastProperty()->hasObjectFlag(BaseShape::ITERATED_SINGLETON)) + initialFlags |= OBJECT_FLAG_ITERATED; + + if (obj->isIndexed()) + initialFlags |= OBJECT_FLAG_SPARSE_INDEXES; + + if (obj->is() && obj->as().length() > INT32_MAX) + initialFlags |= OBJECT_FLAG_LENGTH_OVERFLOW; + + Rooted proto(cx, obj->getTaggedProto()); + ObjectGroup *group = ObjectGroupCompartment::makeGroup(cx, obj->getClass(), proto, + initialFlags); + if (!group) + return nullptr; + + AutoEnterAnalysis enter(cx); + + /* Fill in the type according to the state of this object. */ + + group->initSingleton(obj); + + if (obj->is() && obj->as().isInterpreted()) + group->setInterpretedFunction(&obj->as()); + + obj->group_ = group; + + return group; +} + +/* static */ bool +JSObject::setNewGroupUnknown(JSContext *cx, const js::Class *clasp, JS::HandleObject obj) +{ + ObjectGroup::setDefaultNewGroupUnknown(cx, clasp, obj); + return obj->setFlag(cx, BaseShape::NEW_GROUP_UNKNOWN); +} + +///////////////////////////////////////////////////////////////////// +// ObjectGroupCompartment NewTable +///////////////////////////////////////////////////////////////////// + +/* + * Entries for the per-compartment set of groups which are the default + * types to use for some prototype. An optional associated object is used which + * allows multiple groups to be created with the same prototype. The + * associated object may be a function (for types constructed with 'new') or a + * type descriptor (for typed objects). These entries are also used for the set + * of lazy groups in the compartment, which use a null associated object + * (though there are only a few of these per compartment). + */ +struct ObjectGroupCompartment::NewEntry +{ + ReadBarrieredObjectGroup group; + + // Note: This pointer is only used for equality and does not need a read barrier. + JSObject *associated; + + NewEntry(ObjectGroup *group, JSObject *associated) + : group(group), associated(associated) + {} + + struct Lookup { + const Class *clasp; + TaggedProto hashProto; + TaggedProto matchProto; + JSObject *associated; + + Lookup(const Class *clasp, TaggedProto proto, JSObject *associated) + : clasp(clasp), hashProto(proto), matchProto(proto), associated(associated) + {} + + /* + * For use by generational post barriers only. Look up an entry whose + * proto has been moved, but was hashed with the original value. + */ + Lookup(const Class *clasp, TaggedProto hashProto, TaggedProto matchProto, JSObject *associated) + : clasp(clasp), hashProto(hashProto), matchProto(matchProto), associated(associated) + {} + }; + + static inline HashNumber hash(const Lookup &lookup) { + return PointerHasher::hash(lookup.hashProto.raw()) ^ + PointerHasher::hash(lookup.clasp) ^ + PointerHasher::hash(lookup.associated); + } + + static inline bool match(const NewEntry &key, const Lookup &lookup) { + return key.group->proto() == lookup.matchProto && + (!lookup.clasp || key.group->clasp() == lookup.clasp) && + key.associated == lookup.associated; + } + + static void rekey(NewEntry &k, const NewEntry& newKey) { k = newKey; } +}; + +// This class is used to add a post barrier on a NewTable entry, as the key is +// calculated from a prototype object which may be moved by generational GC. +class ObjectGroupCompartment::NewTableRef : public gc::BufferableRef +{ + NewTable *table; + const Class *clasp; + JSObject *proto; + JSObject *associated; + + public: + NewTableRef(NewTable *table, const Class *clasp, JSObject *proto, JSObject *associated) + : table(table), clasp(clasp), proto(proto), associated(associated) + {} + + void mark(JSTracer *trc) { + JSObject *prior = proto; + trc->setTracingLocation(&*prior); + Mark(trc, &proto, "newObjectGroups set prototype"); + if (prior == proto) + return; + + NewTable::Ptr p = table->lookup(NewTable::Lookup(clasp, TaggedProto(prior), + TaggedProto(proto), + associated)); + if (!p) + return; + + table->rekeyAs(NewTable::Lookup(clasp, TaggedProto(prior), TaggedProto(proto), associated), + NewTable::Lookup(clasp, TaggedProto(proto), associated), *p); + } +}; + +/* static */ void +ObjectGroupCompartment::newTablePostBarrier(ExclusiveContext *cx, NewTable *table, + const Class *clasp, TaggedProto proto, + JSObject *associated) +{ + MOZ_ASSERT_IF(associated, !IsInsideNursery(associated)); + + if (!proto.isObject()) + return; + + if (!cx->isJSContext()) { + MOZ_ASSERT(!IsInsideNursery(proto.toObject())); + return; + } + + if (IsInsideNursery(proto.toObject())) { + gc::StoreBuffer &sb = cx->asJSContext()->runtime()->gc.storeBuffer; + sb.putGeneric(NewTableRef(table, clasp, proto.toObject(), associated)); + } +} + +/* static */ ObjectGroup * +ObjectGroup::defaultNewGroup(ExclusiveContext *cx, const Class *clasp, + TaggedProto proto, JSObject *associated) +{ + MOZ_ASSERT_IF(associated, proto.isObject()); + MOZ_ASSERT_IF(associated, associated->is() || associated->is()); + MOZ_ASSERT_IF(proto.isObject(), cx->isInsideCurrentCompartment(proto.toObject())); + + // A null lookup clasp is used for 'new' groups with an associated + // function. The group starts out as a plain object but might mutate into an + // unboxed plain object. + MOZ_ASSERT(!clasp == (associated && associated->is())); + + ObjectGroupCompartment::NewTable *&table = cx->compartment()->objectGroups.defaultNewTable; + + if (!table) { + table = cx->new_(); + if (!table || !table->init()) { + js_delete(table); + table = nullptr; + return nullptr; + } + } + + if (associated && associated->is()) { + MOZ_ASSERT(!clasp); + + // Canonicalize new functions to use the original one associated with its script. + JSFunction *fun = &associated->as(); + if (fun->hasScript()) + associated = fun->nonLazyScript()->functionNonDelazifying(); + else if (fun->isInterpretedLazy() && !fun->isSelfHostedBuiltin()) + associated = fun->lazyScript()->functionNonDelazifying(); + else + associated = nullptr; + + // If we have previously cleared the 'new' script information for this + // function, don't try to construct another one. + if (associated && associated->wasNewScriptCleared()) + associated = nullptr; + + if (!associated) + clasp = &PlainObject::class_; + } + + 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_); + MOZ_ASSERT(group->proto() == proto); + return group; + } + + AutoEnterAnalysis enter(cx); + + if (proto.isObject() && !proto.toObject()->setDelegate(cx)) + return nullptr; + + ObjectGroupFlags initialFlags = 0; + if (!proto.isObject() || proto.toObject()->isNewGroupUnknown()) + initialFlags = OBJECT_FLAG_DYNAMIC_MASK; + + Rooted protoRoot(cx, proto); + ObjectGroup *group = ObjectGroupCompartment::makeGroup(cx, clasp ? clasp : &PlainObject::class_, + protoRoot, initialFlags); + if (!group) + return nullptr; + + if (!table->add(p, ObjectGroupCompartment::NewEntry(group, associated))) + return nullptr; + + ObjectGroupCompartment::newTablePostBarrier(cx, table, clasp, proto, associated); + + if (proto.isObject()) { + RootedObject obj(cx, proto.toObject()); + + if (associated) { + if (associated->is()) + TypeNewScript::make(cx->asJSContext(), group, &associated->as()); + else + group->setTypeDescr(&associated->as()); + } + + /* + * Some builtin objects have slotful native properties baked in at + * creation via the Shape::{insert,get}initialShape mechanism. Since + * these properties are never explicitly defined on new objects, update + * the type information for them here. + */ + + const JSAtomState &names = cx->names(); + + if (obj->is()) { + AddTypePropertyId(cx, group, NameToId(names.source), Type::StringType()); + AddTypePropertyId(cx, group, NameToId(names.global), Type::BooleanType()); + AddTypePropertyId(cx, group, NameToId(names.ignoreCase), Type::BooleanType()); + AddTypePropertyId(cx, group, NameToId(names.multiline), Type::BooleanType()); + AddTypePropertyId(cx, group, NameToId(names.sticky), Type::BooleanType()); + AddTypePropertyId(cx, group, NameToId(names.lastIndex), Type::Int32Type()); + } + + if (obj->is()) + AddTypePropertyId(cx, group, NameToId(names.length), Type::Int32Type()); + + if (obj->is()) { + AddTypePropertyId(cx, group, NameToId(names.fileName), Type::StringType()); + AddTypePropertyId(cx, group, NameToId(names.lineNumber), Type::Int32Type()); + AddTypePropertyId(cx, group, NameToId(names.columnNumber), Type::Int32Type()); + AddTypePropertyId(cx, group, NameToId(names.stack), Type::StringType()); + } + } + + return group; +} + +/* static */ ObjectGroup * +ObjectGroup::lazySingletonGroup(ExclusiveContext *cx, const Class *clasp, TaggedProto proto) +{ + MOZ_ASSERT_IF(proto.isObject(), cx->compartment() == proto.toObject()->compartment()); + + ObjectGroupCompartment::NewTable *&table = cx->compartment()->objectGroups.lazyTable; + + if (!table) { + table = cx->new_(); + if (!table || !table->init()) { + js_delete(table); + table = nullptr; + return nullptr; + } + } + + ObjectGroupCompartment::NewTable::AddPtr p = + table->lookupForAdd(ObjectGroupCompartment::NewEntry::Lookup(clasp, proto, nullptr)); + if (p) { + ObjectGroup *group = p->group; + MOZ_ASSERT(group->lazy()); + + return group; + } + + AutoEnterAnalysis enter(cx); + + Rooted protoRoot(cx, proto); + ObjectGroup *group = ObjectGroupCompartment::makeGroup(cx, clasp, protoRoot); + if (!group) + return nullptr; + + if (!table->add(p, ObjectGroupCompartment::NewEntry(group, nullptr))) + return nullptr; + + ObjectGroupCompartment::newTablePostBarrier(cx, table, clasp, proto, nullptr); + + group->initSingleton((JSObject *) ObjectGroup::LAZY_SINGLETON); + MOZ_ASSERT(group->singleton(), "created group must be a proper singleton"); + + return group; +} + +/* static */ void +ObjectGroup::setDefaultNewGroupUnknown(JSContext *cx, const Class *clasp, HandleObject obj) +{ + // If the object already has a new group, mark that group as unknown. + ObjectGroupCompartment::NewTable *table = cx->compartment()->objectGroups.defaultNewTable; + if (table) { + Rooted taggedProto(cx, TaggedProto(obj)); + ObjectGroupCompartment::NewTable::Ptr p = + table->lookup(ObjectGroupCompartment::NewEntry::Lookup(clasp, taggedProto, nullptr)); + if (p) + MarkObjectGroupUnknownProperties(cx, p->group); + } +} + +#ifdef DEBUG +/* static */ bool +ObjectGroup::hasDefaultNewGroup(JSObject *proto, const Class *clasp, ObjectGroup *group) +{ + ObjectGroupCompartment::NewTable *table = proto->compartment()->objectGroups.defaultNewTable; + + if (table) { + ObjectGroupCompartment::NewTable::Ptr p = + table->lookup(ObjectGroupCompartment::NewEntry::Lookup(clasp, TaggedProto(proto), nullptr)); + return p && p->group == group; + } + return false; +} +#endif /* DEBUG */ + +inline const Class * +GetClassForProtoKey(JSProtoKey key) +{ + switch (key) { + case JSProto_Null: + case JSProto_Object: + return &PlainObject::class_; + case JSProto_Array: + return &ArrayObject::class_; + + case JSProto_Number: + return &NumberObject::class_; + case JSProto_Boolean: + return &BooleanObject::class_; + case JSProto_String: + return &StringObject::class_; + case JSProto_Symbol: + return &SymbolObject::class_; + case JSProto_RegExp: + return &RegExpObject::class_; + + case JSProto_Int8Array: + case JSProto_Uint8Array: + case JSProto_Int16Array: + case JSProto_Uint16Array: + case JSProto_Int32Array: + case JSProto_Uint32Array: + case JSProto_Float32Array: + case JSProto_Float64Array: + case JSProto_Uint8ClampedArray: + return &TypedArrayObject::classes[key - JSProto_Int8Array]; + + case JSProto_SharedInt8Array: + case JSProto_SharedUint8Array: + case JSProto_SharedInt16Array: + case JSProto_SharedUint16Array: + case JSProto_SharedInt32Array: + case JSProto_SharedUint32Array: + case JSProto_SharedFloat32Array: + case JSProto_SharedFloat64Array: + case JSProto_SharedUint8ClampedArray: + return &SharedTypedArrayObject::classes[key - JSProto_SharedInt8Array]; + + case JSProto_ArrayBuffer: + return &ArrayBufferObject::class_; + + case JSProto_SharedArrayBuffer: + return &SharedArrayBufferObject::class_; + + case JSProto_DataView: + return &DataViewObject::class_; + + default: + MOZ_CRASH("Bad proto key"); + } +} + +/* static */ ObjectGroup * +ObjectGroup::defaultNewGroup(JSContext *cx, JSProtoKey key) +{ + RootedObject proto(cx); + if (key != JSProto_Null && !GetBuiltinPrototype(cx, key, &proto)) + return nullptr; + return defaultNewGroup(cx, GetClassForProtoKey(key), TaggedProto(proto.get())); +} + +///////////////////////////////////////////////////////////////////// +// ObjectGroupCompartment ArrayObjectTable +///////////////////////////////////////////////////////////////////// + +struct ObjectGroupCompartment::ArrayObjectKey : public DefaultHasher +{ + Type type; + JSObject *proto; + + ArrayObjectKey() + : type(Type::UndefinedType()), proto(nullptr) + {} + + ArrayObjectKey(Type type, JSObject *proto) + : type(type), proto(proto) + {} + + static inline uint32_t hash(const ArrayObjectKey &v) { + return (uint32_t) (v.type.raw() ^ ((uint32_t)(size_t)v.proto >> 2)); + } + + static inline bool match(const ArrayObjectKey &v1, const ArrayObjectKey &v2) { + return v1.type == v2.type && v1.proto == v2.proto; + } + + bool operator==(const ArrayObjectKey& other) { + return type == other.type && proto == other.proto; + } + + bool operator!=(const ArrayObjectKey& other) { + return !(*this == other); + } +}; + +static inline bool +NumberTypes(Type a, Type b) +{ + return (a.isPrimitive(JSVAL_TYPE_INT32) || a.isPrimitive(JSVAL_TYPE_DOUBLE)) + && (b.isPrimitive(JSVAL_TYPE_INT32) || b.isPrimitive(JSVAL_TYPE_DOUBLE)); +} + +/* + * As for GetValueType, but requires object types to be non-singletons with + * their default prototype. These are the only values that should appear in + * arrays and objects whose type can be fixed. + */ +static inline Type +GetValueTypeForTable(const Value &v) +{ + Type type = GetValueType(v); + MOZ_ASSERT(!type.isSingleton()); + return type; +} + +/* static */ void +ObjectGroup::fixArrayGroup(ExclusiveContext *cx, ArrayObject *obj) +{ + AutoEnterAnalysis enter(cx); + + /* + * If the array is of homogenous type, pick a group which will be + * shared with all other singleton/JSON arrays of the same type. + * If the array is heterogenous, keep the existing group, which has + * unknown properties. + */ + + unsigned len = obj->getDenseInitializedLength(); + if (len == 0) + return; + + Type type = GetValueTypeForTable(obj->getDenseElement(0)); + + for (unsigned i = 1; i < len; i++) { + Type ntype = GetValueTypeForTable(obj->getDenseElement(i)); + if (ntype != type) { + if (NumberTypes(type, ntype)) + type = Type::DoubleType(); + else + return; + } + } + + setGroupToHomogenousArray(cx, obj, type); +} + +/* static */ void +ObjectGroup::fixRestArgumentsGroup(ExclusiveContext *cx, ArrayObject *obj) +{ + AutoEnterAnalysis enter(cx); + + // Tracking element types for rest argument arrays is not worth it, but we + // still want it to be known that it's a dense array. + setGroupToHomogenousArray(cx, obj, Type::UnknownType()); +} + +/* static */ void +ObjectGroup::setGroupToHomogenousArray(ExclusiveContext *cx, JSObject *obj, Type elementType) +{ + MOZ_ASSERT(cx->zone()->types.activeAnalysis); + + ObjectGroupCompartment::ArrayObjectTable *&table = + cx->compartment()->objectGroups.arrayObjectTable; + + if (!table) { + table = cx->new_(); + if (!table || !table->init()) { + js_delete(table); + table = nullptr; + return; + } + } + + ObjectGroupCompartment::ArrayObjectKey key(elementType, obj->getProto()); + DependentAddPtr p(cx, *table, key); + if (p) { + obj->setGroup(p->value()); + } else { + // Make a new group to use for future arrays with the same elements. + RootedObject objProto(cx, obj->getProto()); + Rooted taggedProto(cx, TaggedProto(objProto)); + ObjectGroup *group = ObjectGroupCompartment::makeGroup(cx, &ArrayObject::class_, taggedProto); + if (!group) + return; + obj->setGroup(group); + + AddTypePropertyId(cx, group, JSID_VOID, elementType); + + key.proto = objProto; + (void) p.add(cx, *table, key, group); + } +} + +///////////////////////////////////////////////////////////////////// +// ObjectGroupCompartment PlainObjectTable +///////////////////////////////////////////////////////////////////// + +/* + * N.B. We could also use the initial shape of the object (before its type is + * fixed) as the key in the object table, but since all references in the table + * are weak the hash entries would usually be collected on GC even if objects + * with the new type/shape are still live. + */ +struct ObjectGroupCompartment::PlainObjectKey +{ + jsid *properties; + uint32_t nproperties; + uint32_t nfixed; + + struct Lookup { + IdValuePair *properties; + uint32_t nproperties; + uint32_t nfixed; + + Lookup(IdValuePair *properties, uint32_t nproperties, uint32_t nfixed) + : properties(properties), nproperties(nproperties), nfixed(nfixed) + {} + }; + + static inline HashNumber hash(const Lookup &lookup) { + return (HashNumber) (JSID_BITS(lookup.properties[lookup.nproperties - 1].id) ^ + lookup.nproperties ^ + lookup.nfixed); + } + + static inline bool match(const PlainObjectKey &v, const Lookup &lookup) { + if (lookup.nproperties != v.nproperties || lookup.nfixed != v.nfixed) + return false; + for (size_t i = 0; i < lookup.nproperties; i++) { + if (lookup.properties[i].id != v.properties[i]) + return false; + } + return true; + } +}; + +struct ObjectGroupCompartment::PlainObjectEntry +{ + ReadBarrieredObjectGroup group; + ReadBarrieredShape shape; + Type *types; +}; + +/* static */ void +ObjectGroupCompartment::updatePlainObjectEntryTypes(ExclusiveContext *cx, PlainObjectEntry &entry, + IdValuePair *properties, size_t nproperties) +{ + if (entry.group->unknownProperties()) + return; + for (size_t i = 0; i < nproperties; i++) { + Type type = entry.types[i]; + Type ntype = GetValueTypeForTable(properties[i].value); + if (ntype == type) + continue; + if (ntype.isPrimitive(JSVAL_TYPE_INT32) && + type.isPrimitive(JSVAL_TYPE_DOUBLE)) + { + /* The property types already reflect 'int32'. */ + } else { + if (ntype.isPrimitive(JSVAL_TYPE_DOUBLE) && + type.isPrimitive(JSVAL_TYPE_INT32)) + { + /* Include 'double' in the property types to avoid the update below later. */ + entry.types[i] = Type::DoubleType(); + } + AddTypePropertyId(cx, entry.group, IdToTypeId(properties[i].id), ntype); + } + } +} + +/* static */ void +ObjectGroup::fixPlainObjectGroup(ExclusiveContext *cx, PlainObject *obj) +{ + AutoEnterAnalysis enter(cx); + + ObjectGroupCompartment::PlainObjectTable *&table = + cx->compartment()->objectGroups.plainObjectTable; + + if (!table) { + table = cx->new_(); + if (!table || !table->init()) { + js_delete(table); + table = nullptr; + return; + } + } + + /* + * Use the same group for all singleton/JSON objects with the same + * base shape, i.e. the same fields written in the same order. + * + * Exclude some objects we can't readily associate common types for based on their + * shape. Objects with metadata are excluded so that the metadata does not need to + * be included in the table lookup (the metadata object might be in the nursery). + */ + if (obj->slotSpan() == 0 || obj->inDictionaryMode() || !obj->hasEmptyElements() || obj->getMetadata()) + return; + + Vector properties(cx); + if (!properties.resize(obj->slotSpan())) + return; + + Shape *shape = obj->lastProperty(); + while (!shape->isEmptyShape()) { + IdValuePair &entry = properties[shape->slot()]; + entry.id = shape->propid(); + entry.value = obj->getSlot(shape->slot()); + shape = shape->previous(); + } + + ObjectGroupCompartment::PlainObjectKey::Lookup lookup(properties.begin(), properties.length(), + obj->numFixedSlots()); + ObjectGroupCompartment::PlainObjectTable::AddPtr p = table->lookupForAdd(lookup); + + if (p) { + MOZ_ASSERT(obj->getProto() == p->value().group->proto().toObject()); + MOZ_ASSERT(obj->lastProperty() == p->value().shape); + + ObjectGroupCompartment::updatePlainObjectEntryTypes(cx, p->value(), + properties.begin(), + properties.length()); + obj->setGroup(p->value().group); + return; + } + + /* Make a new type to use for the object and similar future ones. */ + Rooted objProto(cx, obj->getTaggedProto()); + ObjectGroup *group = ObjectGroupCompartment::makeGroup(cx, &PlainObject::class_, objProto); + if (!group || !group->addDefiniteProperties(cx, obj->lastProperty())) + return; + + if (obj->isIndexed()) + group->setFlags(cx, OBJECT_FLAG_SPARSE_INDEXES); + + ScopedJSFreePtr ids(group->zone()->pod_calloc(properties.length())); + if (!ids) + return; + + ScopedJSFreePtr types(group->zone()->pod_calloc(properties.length())); + if (!types) + return; + + for (size_t i = 0; i < properties.length(); i++) { + ids[i] = properties[i].id; + types[i] = GetValueTypeForTable(obj->getSlot(i)); + AddTypePropertyId(cx, group, IdToTypeId(ids[i]), types[i]); + } + + ObjectGroupCompartment::PlainObjectKey key; + key.properties = ids; + key.nproperties = properties.length(); + key.nfixed = obj->numFixedSlots(); + MOZ_ASSERT(ObjectGroupCompartment::PlainObjectKey::match(key, lookup)); + + ObjectGroupCompartment::PlainObjectEntry entry; + entry.group.set(group); + entry.shape.set(obj->lastProperty()); + entry.types = types; + + obj->setGroup(group); + + p = table->lookupForAdd(lookup); + if (table->add(p, key, entry)) { + ids.forget(); + types.forget(); + } +} + +/* static */ PlainObject * +ObjectGroup::newPlainObject(JSContext *cx, IdValuePair *properties, size_t nproperties) +{ + AutoEnterAnalysis enter(cx); + + ObjectGroupCompartment::PlainObjectTable *table = + cx->compartment()->objectGroups.plainObjectTable; + + if (!table) + return nullptr; + + /* + * Use the object group table to allocate an object with the specified + * properties, filling in its final group and shape and failing if no table + * entry could be found for the properties. + */ + + /* + * Filter out a few cases where we don't want to use the object group table. + * Note that if the properties contain any duplicates or dense indexes, + * the lookup below will fail as such arrays of properties cannot be stored + * in the object group table --- fixObjectGroup populates the table with + * properties read off its input object, which cannot be duplicates, and + * ignores objects with dense indexes. + */ + if (!nproperties || nproperties >= PropertyTree::MAX_HEIGHT) + return nullptr; + + gc::AllocKind allocKind = gc::GetGCObjectKind(nproperties); + size_t nfixed = gc::GetGCKindSlots(allocKind, &PlainObject::class_); + + ObjectGroupCompartment::PlainObjectKey::Lookup lookup(properties, nproperties, nfixed); + ObjectGroupCompartment::PlainObjectTable::Ptr p = table->lookupForAdd(lookup); + + if (!p) + return nullptr; + + RootedPlainObject obj(cx, NewBuiltinClassInstance(cx, allocKind)); + if (!obj) { + cx->clearPendingException(); + return nullptr; + } + MOZ_ASSERT(obj->getProto() == p->value().group->proto().toObject()); + + RootedShape shape(cx, p->value().shape); + if (!NativeObject::setLastProperty(cx, obj, shape)) { + cx->clearPendingException(); + return nullptr; + } + + ObjectGroupCompartment::updatePlainObjectEntryTypes(cx, p->value(), properties, nproperties); + + for (size_t i = 0; i < nproperties; i++) + obj->setSlot(i, properties[i].value); + + obj->setGroup(p->value().group); + return obj; +} + +///////////////////////////////////////////////////////////////////// +// ObjectGroupCompartment AllocationSiteTable +///////////////////////////////////////////////////////////////////// + +struct ObjectGroupCompartment::AllocationSiteKey : public DefaultHasher { + JSScript *script; + + uint32_t offset : 24; + JSProtoKey kind : 8; + + static const uint32_t OFFSET_LIMIT = (1 << 23); + + AllocationSiteKey() { mozilla::PodZero(this); } + + static inline uint32_t hash(AllocationSiteKey key) { + return uint32_t(size_t(key.script->offsetToPC(key.offset)) ^ key.kind); + } + + static inline bool match(const AllocationSiteKey &a, const AllocationSiteKey &b) { + return a.script == b.script && a.offset == b.offset && a.kind == b.kind; + } +}; + +/* static */ ObjectGroup * +ObjectGroup::allocationSiteGroup(JSContext *cx, JSScript *script, jsbytecode *pc, + JSProtoKey kind) +{ + MOZ_ASSERT(!useSingletonForAllocationSite(script, pc, kind)); + + uint32_t offset = script->pcToOffset(pc); + + if (offset >= ObjectGroupCompartment::AllocationSiteKey::OFFSET_LIMIT) + return defaultNewGroup(cx, kind); + + ObjectGroupCompartment::AllocationSiteKey key; + key.script = script; + key.offset = offset; + key.kind = kind; + + ObjectGroupCompartment::AllocationSiteTable *&table = + cx->compartment()->objectGroups.allocationSiteTable; + + if (!table) { + table = cx->new_(); + if (!table || !table->init()) { + js_delete(table); + table = nullptr; + return nullptr; + } + } + + ObjectGroupCompartment::AllocationSiteTable::AddPtr p = table->lookupForAdd(key); + if (p) + return p->value(); + + AutoEnterAnalysis enter(cx); + + RootedObject proto(cx); + if (kind != JSProto_Null && !GetBuiltinPrototype(cx, kind, &proto)) + return nullptr; + + Rooted tagged(cx, TaggedProto(proto)); + ObjectGroup *res = ObjectGroupCompartment::makeGroup(cx, GetClassForProtoKey(kind), tagged, + OBJECT_FLAG_FROM_ALLOCATION_SITE); + if (!res) + return nullptr; + + if (JSOp(*pc) == JSOP_NEWOBJECT) { + // This object is always constructed the same way and will not be + // observed by other code before all properties have been added. Mark + // all the properties as definite properties of the object. + JSObject *baseobj = script->getObject(GET_UINT32_INDEX(pc)); + if (!res->addDefiniteProperties(cx, baseobj->lastProperty())) + return nullptr; + } + + if (!table->add(p, key, res)) + return nullptr; + + return res; +} + +/* static */ ObjectGroup * +ObjectGroup::callingAllocationSiteGroup(JSContext *cx, JSProtoKey key) +{ + jsbytecode *pc; + RootedScript script(cx, cx->currentScript(&pc)); + if (script) + return allocationSiteGroup(cx, script, pc, key); + return defaultNewGroup(cx, key); +} + +/* static */ bool +ObjectGroup::setAllocationSiteObjectGroup(JSContext *cx, + HandleScript script, jsbytecode *pc, + HandleObject obj, bool singleton) +{ + JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(obj->getClass()); + MOZ_ASSERT(key != JSProto_Null); + MOZ_ASSERT(singleton == useSingletonForAllocationSite(script, pc, key)); + + if (singleton) { + MOZ_ASSERT(obj->isSingleton()); + + /* + * Inference does not account for types of run-once initializer + * objects, as these may not be created until after the script + * has been analyzed. + */ + TypeScript::Monitor(cx, script, pc, ObjectValue(*obj)); + } else { + ObjectGroup *group = allocationSiteGroup(cx, script, pc, key); + if (!group) + return false; + obj->setGroup(group); + } + + return true; +} + +/* static */ ArrayObject * +ObjectGroup::getOrFixupCopyOnWriteObject(JSContext *cx, HandleScript script, jsbytecode *pc) +{ + // Make sure that the template object for script/pc has a type indicating + // that the object and its copies have copy on write elements. + RootedArrayObject obj(cx, &script->getObject(GET_UINT32_INDEX(pc))->as()); + MOZ_ASSERT(obj->denseElementsAreCopyOnWrite()); + + if (obj->group()->fromAllocationSite()) { + MOZ_ASSERT(obj->group()->hasAnyFlags(OBJECT_FLAG_COPY_ON_WRITE)); + return obj; + } + + RootedObjectGroup group(cx, allocationSiteGroup(cx, script, pc, JSProto_Array)); + if (!group) + return nullptr; + + group->addFlags(OBJECT_FLAG_COPY_ON_WRITE); + + // Update type information in the initializer object group. + MOZ_ASSERT(obj->slotSpan() == 0); + for (size_t i = 0; i < obj->getDenseInitializedLength(); i++) { + const Value &v = obj->getDenseElement(i); + AddTypePropertyId(cx, group, JSID_VOID, v); + } + + obj->setGroup(group); + return obj; +} + +/* static */ ArrayObject * +ObjectGroup::getCopyOnWriteObject(JSScript *script, jsbytecode *pc) +{ + // getOrFixupCopyOnWriteObject should already have been called for + // script/pc, ensuring that the template object has a group with the + // COPY_ON_WRITE flag. We don't assert this here, due to a corner case + // where this property doesn't hold. See jsop_newarray_copyonwrite in + // IonBuilder. + ArrayObject *obj = &script->getObject(GET_UINT32_INDEX(pc))->as(); + MOZ_ASSERT(obj->denseElementsAreCopyOnWrite()); + + return obj; +} + +/* static */ bool +ObjectGroup::findAllocationSiteForType(JSContext *cx, Type type, + JSScript **script, uint32_t *offset) +{ + *script = nullptr; + *offset = 0; + + if (type.isUnknown() || type.isAnyObject() || !type.isGroup()) + return false; + ObjectGroup *obj = type.group(); + + const ObjectGroupCompartment::AllocationSiteTable *table = + cx->compartment()->objectGroups.allocationSiteTable; + + if (!table) + return false; + + for (ObjectGroupCompartment::AllocationSiteTable::Range r = table->all(); + !r.empty(); + r.popFront()) + { + if (obj == r.front().value()) { + *script = r.front().key().script; + *offset = r.front().key().offset; + return true; + } + } + + return false; +} + +///////////////////////////////////////////////////////////////////// +// ObjectGroupCompartment +///////////////////////////////////////////////////////////////////// + +ObjectGroupCompartment::ObjectGroupCompartment() +{ + PodZero(this); +} + +ObjectGroupCompartment::~ObjectGroupCompartment() +{ + js_delete(defaultNewTable); + js_delete(lazyTable); + js_delete(arrayObjectTable); + js_delete(plainObjectTable); + js_delete(allocationSiteTable); +} + +void +ObjectGroupCompartment::removeDefaultNewGroup(const Class *clasp, TaggedProto proto, + JSObject *associated) +{ + NewTable::Ptr p = defaultNewTable->lookup(NewEntry::Lookup(clasp, proto, associated)); + MOZ_ASSERT(p); + + defaultNewTable->remove(p); +} + +void +ObjectGroupCompartment::replaceDefaultNewGroup(const Class *clasp, TaggedProto proto, + JSObject *associated, ObjectGroup *group) +{ + NewEntry::Lookup lookup(clasp, proto, associated); + + NewTable::Ptr p = defaultNewTable->lookup(lookup); + MOZ_ASSERT(p); + defaultNewTable->remove(p); + defaultNewTable->putNew(lookup, NewEntry(group, associated)); +} + +/* static */ +ObjectGroup * +ObjectGroupCompartment::makeGroup(ExclusiveContext *cx, const Class *clasp, + Handle proto, + ObjectGroupFlags initialFlags /* = 0 */) +{ + MOZ_ASSERT_IF(proto.isObject(), cx->isInsideCurrentCompartment(proto.toObject())); + + if (cx->isJSContext()) { + if (proto.isObject() && IsInsideNursery(proto.toObject())) + initialFlags |= OBJECT_FLAG_NURSERY_PROTO; + } + + ObjectGroup *group = NewObjectGroup(cx); + if (!group) + return nullptr; + new(group) ObjectGroup(clasp, proto, initialFlags); + + return group; +} + +void +ObjectGroupCompartment::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, + size_t *allocationSiteTables, + size_t *arrayObjectGroupTables, + size_t *plainObjectGroupTables, + size_t *compartmentTables) +{ + if (allocationSiteTable) + *allocationSiteTables += allocationSiteTable->sizeOfIncludingThis(mallocSizeOf); + + if (arrayObjectTable) + *arrayObjectGroupTables += arrayObjectTable->sizeOfIncludingThis(mallocSizeOf); + + if (plainObjectTable) { + *plainObjectGroupTables += plainObjectTable->sizeOfIncludingThis(mallocSizeOf); + + for (PlainObjectTable::Enum e(*plainObjectTable); + !e.empty(); + e.popFront()) + { + const PlainObjectKey &key = e.front().key(); + const PlainObjectEntry &value = e.front().value(); + + /* key.ids and values.types have the same length. */ + *plainObjectGroupTables += mallocSizeOf(key.properties) + mallocSizeOf(value.types); + } + } + + if (defaultNewTable) + *compartmentTables += defaultNewTable->sizeOfIncludingThis(mallocSizeOf); + + if (lazyTable) + *compartmentTables += lazyTable->sizeOfIncludingThis(mallocSizeOf); +} + +void +ObjectGroupCompartment::clearTables() +{ + if (allocationSiteTable && allocationSiteTable->initialized()) + allocationSiteTable->clear(); + if (arrayObjectTable && arrayObjectTable->initialized()) + arrayObjectTable->clear(); + if (plainObjectTable && plainObjectTable->initialized()) + plainObjectTable->clear(); + if (defaultNewTable && defaultNewTable->initialized()) + defaultNewTable->clear(); + if (lazyTable && lazyTable->initialized()) + lazyTable->clear(); +} + +void +ObjectGroupCompartment::sweep(FreeOp *fop) +{ + /* + * Iterate through the array/object group tables and remove all entries + * referencing collected data. These tables only hold weak references. + */ + + if (arrayObjectTable) { + for (ArrayObjectTable::Enum e(*arrayObjectTable); !e.empty(); e.popFront()) { + ArrayObjectKey key = e.front().key(); + MOZ_ASSERT(key.type.isUnknown() || !key.type.isSingleton()); + + bool remove = false; + if (!key.type.isUnknown() && key.type.isGroup()) { + ObjectGroup *group = key.type.groupNoBarrier(); + if (IsObjectGroupAboutToBeFinalizedFromAnyThread(&group)) + remove = true; + else + key.type = Type::ObjectType(group); + } + if (key.proto && key.proto != TaggedProto::LazyProto && + IsObjectAboutToBeFinalizedFromAnyThread(&key.proto)) + { + remove = true; + } + if (IsObjectGroupAboutToBeFinalizedFromAnyThread(e.front().value().unsafeGet())) + remove = true; + + if (remove) + e.removeFront(); + else if (key != e.front().key()) + e.rekeyFront(key); + } + } + + if (plainObjectTable) { + for (PlainObjectTable::Enum e(*plainObjectTable); !e.empty(); e.popFront()) { + const PlainObjectKey &key = e.front().key(); + PlainObjectEntry &entry = e.front().value(); + + bool remove = false; + if (IsObjectGroupAboutToBeFinalizedFromAnyThread(entry.group.unsafeGet())) + remove = true; + if (IsShapeAboutToBeFinalizedFromAnyThread(entry.shape.unsafeGet())) + remove = true; + for (unsigned i = 0; !remove && i < key.nproperties; i++) { + if (JSID_IS_STRING(key.properties[i])) { + JSString *str = JSID_TO_STRING(key.properties[i]); + if (IsStringAboutToBeFinalizedFromAnyThread(&str)) + remove = true; + MOZ_ASSERT(AtomToId((JSAtom *)str) == key.properties[i]); + } else if (JSID_IS_SYMBOL(key.properties[i])) { + JS::Symbol *sym = JSID_TO_SYMBOL(key.properties[i]); + if (IsSymbolAboutToBeFinalizedFromAnyThread(&sym)) + remove = true; + } + + MOZ_ASSERT(!entry.types[i].isSingleton()); + ObjectGroup *group = nullptr; + if (entry.types[i].isGroup()) { + group = entry.types[i].groupNoBarrier(); + if (IsObjectGroupAboutToBeFinalizedFromAnyThread(&group)) + remove = true; + else if (group != entry.types[i].groupNoBarrier()) + entry.types[i] = Type::ObjectType(group); + } + } + + if (remove) { + js_free(key.properties); + js_free(entry.types); + e.removeFront(); + } + } + } + + if (allocationSiteTable) { + for (AllocationSiteTable::Enum e(*allocationSiteTable); !e.empty(); e.popFront()) { + AllocationSiteKey key = e.front().key(); + bool keyDying = IsScriptAboutToBeFinalizedFromAnyThread(&key.script); + bool valDying = IsObjectGroupAboutToBeFinalizedFromAnyThread(e.front().value().unsafeGet()); + if (keyDying || valDying) + e.removeFront(); + else if (key.script != e.front().key().script) + e.rekeyFront(key); + } + } + + sweepNewTable(defaultNewTable); + sweepNewTable(lazyTable); +} + +void +ObjectGroupCompartment::sweepNewTable(NewTable *table) +{ + if (table && table->initialized()) { + for (NewTable::Enum e(*table); !e.empty(); e.popFront()) { + NewEntry entry = e.front(); + if (IsObjectGroupAboutToBeFinalizedFromAnyThread(entry.group.unsafeGet()) || + (entry.associated && IsObjectAboutToBeFinalizedFromAnyThread(&entry.associated))) + { + e.removeFront(); + } else { + /* Any rekeying necessary is handled by fixupNewObjectGroupTable() below. */ + MOZ_ASSERT(entry.group.unbarrieredGet() == e.front().group.unbarrieredGet()); + MOZ_ASSERT(entry.associated == e.front().associated); + } + } + } +} + +void +ObjectGroupCompartment::fixupNewTableAfterMovingGC(NewTable *table) +{ + /* + * Each entry's hash depends on the object's prototype and we can't tell + * whether that has been moved or not in sweepNewObjectGroupTable(). + */ + if (table && table->initialized()) { + for (NewTable::Enum e(*table); !e.empty(); e.popFront()) { + NewEntry entry = e.front(); + bool needRekey = false; + if (IsForwarded(entry.group.get())) { + entry.group.set(Forwarded(entry.group.get())); + needRekey = true; + } + TaggedProto proto = entry.group->proto(); + if (proto.isObject() && IsForwarded(proto.toObject())) { + proto = TaggedProto(Forwarded(proto.toObject())); + needRekey = true; + } + if (entry.associated && IsForwarded(entry.associated)) { + entry.associated = Forwarded(entry.associated); + needRekey = true; + } + if (needRekey) { + const Class *clasp = entry.group->clasp(); + if (entry.associated && entry.associated->is()) + clasp = nullptr; + NewEntry::Lookup lookup(clasp, proto, entry.associated); + e.rekeyFront(lookup, entry); + } + } + } +} + +#ifdef JSGC_HASH_TABLE_CHECKS + +void +ObjectGroupCompartment::checkNewTableAfterMovingGC(NewTable *table) +{ + /* + * Assert that nothing points into the nursery or needs to be relocated, and + * that the hash table entries are discoverable. + */ + if (!table || !table->initialized()) + return; + + for (NewTable::Enum e(*table); !e.empty(); e.popFront()) { + NewEntry entry = e.front(); + CheckGCThingAfterMovingGC(entry.group.get()); + TaggedProto proto = entry.group->proto(); + if (proto.isObject()) + CheckGCThingAfterMovingGC(proto.toObject()); + CheckGCThingAfterMovingGC(entry.associated); + + const Class *clasp = entry.group->clasp(); + if (entry.associated && entry.associated->is()) + clasp = nullptr; + + NewEntry::Lookup lookup(clasp, proto, entry.associated); + NewTable::Ptr ptr = table->lookup(lookup); + MOZ_ASSERT(ptr.found() && &*ptr == &e.front()); + } +} + +#endif // JSGC_HASH_TABLE_CHECKS diff --git a/js/src/vm/ObjectGroup.h b/js/src/vm/ObjectGroup.h new file mode 100644 index 000000000000..6dcb6b9b26df --- /dev/null +++ b/js/src/vm/ObjectGroup.h @@ -0,0 +1,684 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef vm_ObjectGroup_h +#define vm_ObjectGroup_h + +#include "jsbytecode.h" +#include "jsfriendapi.h" + +#include "ds/IdValuePair.h" +#include "gc/Barrier.h" + +namespace js { + +class TypeDescr; +class UnboxedLayout; + +namespace types { + +class Type; +class TypeNewScript; +class HeapTypeSet; +struct Property; +class AutoClearTypeInferenceStateOnOOM; +class CompilerConstraintList; + +} // namespace types + +// Information about an object prototype, which can be either a particular +// object, null, or a lazily generated object. The latter is only used by +// certain kinds of proxies. +class TaggedProto +{ + public: + static JSObject * const LazyProto; + + TaggedProto() : proto(nullptr) {} + explicit TaggedProto(JSObject *proto) : proto(proto) {} + + uintptr_t toWord() const { return uintptr_t(proto); } + + bool isLazy() const { + return proto == LazyProto; + } + bool isObject() const { + /* Skip nullptr and LazyProto. */ + return uintptr_t(proto) > uintptr_t(TaggedProto::LazyProto); + } + JSObject *toObject() const { + MOZ_ASSERT(isObject()); + return proto; + } + JSObject *toObjectOrNull() const { + MOZ_ASSERT(!proto || isObject()); + return proto; + } + JSObject *raw() const { return proto; } + + bool operator ==(const TaggedProto &other) { return proto == other.proto; } + bool operator !=(const TaggedProto &other) { return proto != other.proto; } + + private: + JSObject *proto; +}; + +template <> +struct RootKind +{ + static ThingRootKind rootKind() { return THING_ROOT_OBJECT; } +}; + +template <> struct GCMethods +{ + static TaggedProto initial() { return TaggedProto(); } + static bool poisoned(const TaggedProto &v) { return IsPoisonedPtr(v.raw()); } +}; + +template <> struct GCMethods +{ + static TaggedProto initial() { return TaggedProto(); } + static bool poisoned(const TaggedProto &v) { return IsPoisonedPtr(v.raw()); } +}; + +template +class TaggedProtoOperations +{ + const TaggedProto *value() const { + return static_cast(this)->extract(); + } + + public: + uintptr_t toWord() const { return value()->toWord(); } + inline bool isLazy() const { return value()->isLazy(); } + inline bool isObject() const { return value()->isObject(); } + inline JSObject *toObject() const { return value()->toObject(); } + inline JSObject *toObjectOrNull() const { return value()->toObjectOrNull(); } + JSObject *raw() const { return value()->raw(); } +}; + +template <> +class HandleBase : public TaggedProtoOperations > +{ + friend class TaggedProtoOperations >; + const TaggedProto * extract() const { + return static_cast*>(this)->address(); + } +}; + +template <> +class RootedBase : public TaggedProtoOperations > +{ + friend class TaggedProtoOperations >; + const TaggedProto *extract() const { + return static_cast *>(this)->address(); + } +}; + +/* Flags and other state stored in ObjectGroupFlags */ +enum : uint32_t { + /* Whether this group is associated with some allocation site. */ + OBJECT_FLAG_FROM_ALLOCATION_SITE = 0x1, + + /* + * If set, the object's prototype might be in the nursery and can't be + * used during Ion compilation (which may be occurring off thread). + */ + OBJECT_FLAG_NURSERY_PROTO = 0x2, + + /* Mask/shift for the number of properties in propertySet */ + OBJECT_FLAG_PROPERTY_COUNT_MASK = 0xfff8, + OBJECT_FLAG_PROPERTY_COUNT_SHIFT = 3, + OBJECT_FLAG_PROPERTY_COUNT_LIMIT = + OBJECT_FLAG_PROPERTY_COUNT_MASK >> OBJECT_FLAG_PROPERTY_COUNT_SHIFT, + + /* Whether any objects this represents may have sparse indexes. */ + OBJECT_FLAG_SPARSE_INDEXES = 0x00010000, + + /* Whether any objects this represents may not have packed dense elements. */ + OBJECT_FLAG_NON_PACKED = 0x00020000, + + /* + * Whether any objects this represents may be arrays whose length does not + * fit in an int32. + */ + OBJECT_FLAG_LENGTH_OVERFLOW = 0x00040000, + + /* Whether any objects have been iterated over. */ + OBJECT_FLAG_ITERATED = 0x00080000, + + /* For a global object, whether flags were set on the RegExpStatics. */ + OBJECT_FLAG_REGEXP_FLAGS_SET = 0x00100000, + + /* + * For the function on a run-once script, whether the function has actually + * run multiple times. + */ + OBJECT_FLAG_RUNONCE_INVALIDATED = 0x00200000, + + /* + * For a global object, whether any array buffers in this compartment with + * typed object views have been neutered. + */ + OBJECT_FLAG_TYPED_OBJECT_NEUTERED = 0x00400000, + + /* + * Whether objects with this type should be allocated directly in the + * tenured heap. + */ + OBJECT_FLAG_PRE_TENURE = 0x00800000, + + /* Whether objects with this type might have copy on write elements. */ + OBJECT_FLAG_COPY_ON_WRITE = 0x01000000, + + /* Whether this type has had its 'new' script cleared in the past. */ + OBJECT_FLAG_NEW_SCRIPT_CLEARED = 0x02000000, + + /* + * Whether all properties of this object are considered unknown. + * If set, all other flags in DYNAMIC_MASK will also be set. + */ + OBJECT_FLAG_UNKNOWN_PROPERTIES = 0x04000000, + + /* Flags which indicate dynamic properties of represented objects. */ + OBJECT_FLAG_DYNAMIC_MASK = 0x07ff0000, + + // Mask/shift for the kind of addendum attached to this group. + OBJECT_FLAG_ADDENDUM_MASK = 0x38000000, + OBJECT_FLAG_ADDENDUM_SHIFT = 27, + + // Mask/shift for this group's generation. If out of sync with the + // TypeZone's generation, this group hasn't been swept yet. + OBJECT_FLAG_GENERATION_MASK = 0x40000000, + OBJECT_FLAG_GENERATION_SHIFT = 30, +}; +typedef uint32_t ObjectGroupFlags; + +/* + * 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. + * + * 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 + * in the VM) will treat these properties like those of any other object group. + */ + +/* Type information about an object accessed by a script. */ +class ObjectGroup : public gc::TenuredCell +{ + /* Class shared by objects in this group. */ + const Class *clasp_; + + /* Prototype shared by objects in this group. */ + HeapPtrObject proto_; + + /* + * Whether there is a singleton JS object with this group. That JS object + * must appear in type sets instead of this; we include the back reference + * here to allow reverting the JS object to a lazy group. + */ + HeapPtrObject singleton_; + + public: + + const Class *clasp() const { + return clasp_; + } + + void setClasp(const Class *clasp) { + clasp_ = clasp; + } + + TaggedProto proto() const { + return TaggedProto(proto_); + } + + JSObject *singleton() const { + return singleton_; + } + + // For use during marking, don't call otherwise. + HeapPtrObject &protoRaw() { return proto_; } + HeapPtrObject &singletonRaw() { return singleton_; } + + void setProto(JSContext *cx, TaggedProto proto); + void setProtoUnchecked(TaggedProto proto); + + void initSingleton(JSObject *singleton) { + singleton_ = singleton; + } + + /* + * Value held by singleton if this is a standin group for a singleton JS + * object whose group has not been constructed yet. + */ + static const size_t LAZY_SINGLETON = 1; + bool lazy() const { return singleton() == (JSObject *) LAZY_SINGLETON; } + + private: + /* Flags for this group. */ + ObjectGroupFlags flags_; + + // Kinds of addendums which can be attached to ObjectGroups. + enum AddendumKind { + Addendum_None, + + // When used by interpreted function, the addendum stores the + // canonical JSFunction object. + Addendum_InterpretedFunction, + + // When used by the 'new' group when constructing an interpreted + // function, the addendum stores a TypeNewScript. + Addendum_NewScript, + + // When objects in this group have an unboxed representation, the + // addendum stores an UnboxedLayout (which might have a TypeNewScript + // as well, if the group is also constructed using 'new'). + Addendum_UnboxedLayout, + + // When used by typed objects, the addendum stores a TypeDescr. + Addendum_TypeDescr + }; + + // If non-null, holds additional information about this object, whose + // format is indicated by the object's addendum kind. + void *addendum_; + + void setAddendum(AddendumKind kind, void *addendum, bool writeBarrier = true); + + AddendumKind addendumKind() const { + return (AddendumKind) + ((flags_ & OBJECT_FLAG_ADDENDUM_MASK) >> OBJECT_FLAG_ADDENDUM_SHIFT); + } + + types::TypeNewScript *newScriptDontCheckGeneration() const { + if (addendumKind() == Addendum_NewScript) + return reinterpret_cast(addendum_); + return nullptr; + } + + UnboxedLayout *maybeUnboxedLayoutDontCheckGeneration() const { + if (addendumKind() == Addendum_UnboxedLayout) + return reinterpret_cast(addendum_); + return nullptr; + } + + types::TypeNewScript *anyNewScript(); + void detachNewScript(bool writeBarrier); + + public: + + ObjectGroupFlags flags() { + maybeSweep(nullptr); + return flags_; + } + + void addFlags(ObjectGroupFlags flags) { + maybeSweep(nullptr); + flags_ |= flags; + } + + void clearFlags(ObjectGroupFlags flags) { + maybeSweep(nullptr); + flags_ &= ~flags; + } + + types::TypeNewScript *newScript() { + maybeSweep(nullptr); + return newScriptDontCheckGeneration(); + } + + void setNewScript(types::TypeNewScript *newScript) { + setAddendum(Addendum_NewScript, newScript); + } + + UnboxedLayout *maybeUnboxedLayout() { + maybeSweep(nullptr); + return maybeUnboxedLayoutDontCheckGeneration(); + } + + UnboxedLayout &unboxedLayout() { + MOZ_ASSERT(addendumKind() == Addendum_UnboxedLayout); + return *maybeUnboxedLayout(); + } + + void setUnboxedLayout(UnboxedLayout *layout) { + setAddendum(Addendum_UnboxedLayout, layout); + } + + TypeDescr *maybeTypeDescr() { + // Note: there is no need to sweep when accessing the type descriptor + // of an object, as it is strongly held and immutable. + if (addendumKind() == Addendum_TypeDescr) + return reinterpret_cast(addendum_); + return nullptr; + } + + TypeDescr &typeDescr() { + MOZ_ASSERT(addendumKind() == Addendum_TypeDescr); + return *maybeTypeDescr(); + } + + void setTypeDescr(TypeDescr *descr) { + setAddendum(Addendum_TypeDescr, descr); + } + + JSFunction *maybeInterpretedFunction() { + // Note: as with type descriptors, there is no need to sweep when + // accessing the interpreted function associated with an object. + if (addendumKind() == Addendum_InterpretedFunction) + return reinterpret_cast(addendum_); + return nullptr; + } + + void setInterpretedFunction(JSFunction *fun) { + setAddendum(Addendum_InterpretedFunction, fun); + } + + private: + // Properties of this object. This may contain JSID_VOID, representing the + // types of all integer indexes of the object, and/or JSID_EMPTY, holding + // constraints listening to changes to the object's state. + // + // See types::Property for more detail about the types represented here. + types::Property **propertySet; + public: + + inline ObjectGroup(const Class *clasp, TaggedProto proto, ObjectGroupFlags initialFlags); + + inline bool hasAnyFlags(ObjectGroupFlags flags) { + MOZ_ASSERT((flags & OBJECT_FLAG_DYNAMIC_MASK) == flags); + return !!(this->flags() & flags); + } + bool hasAllFlags(ObjectGroupFlags flags) { + MOZ_ASSERT((flags & OBJECT_FLAG_DYNAMIC_MASK) == flags); + return (this->flags() & flags) == flags; + } + + bool unknownProperties() { + MOZ_ASSERT_IF(flags() & OBJECT_FLAG_UNKNOWN_PROPERTIES, + hasAllFlags(OBJECT_FLAG_DYNAMIC_MASK)); + return !!(flags() & OBJECT_FLAG_UNKNOWN_PROPERTIES); + } + + bool shouldPreTenure() { + return hasAnyFlags(OBJECT_FLAG_PRE_TENURE) && !unknownProperties(); + } + + bool hasTenuredProto() { + return !(flags() & OBJECT_FLAG_NURSERY_PROTO); + } + + gc::InitialHeap initialHeap(types::CompilerConstraintList *constraints); + + bool canPreTenure() { + return !unknownProperties(); + } + + bool fromAllocationSite() { + return flags() & OBJECT_FLAG_FROM_ALLOCATION_SITE; + } + + void setShouldPreTenure(ExclusiveContext *cx) { + MOZ_ASSERT(canPreTenure()); + setFlags(cx, OBJECT_FLAG_PRE_TENURE); + } + + /* + * Get or create a property of this object. Only call this for properties which + * a script accesses explicitly. + */ + inline types::HeapTypeSet *getProperty(ExclusiveContext *cx, jsid id); + + /* Get a property only if it already exists. */ + inline types::HeapTypeSet *maybeGetProperty(jsid id); + + inline unsigned getPropertyCount(); + inline types::Property *getProperty(unsigned i); + + /* Helpers */ + + void updateNewPropertyTypes(ExclusiveContext *cx, jsid id, types::HeapTypeSet *types); + bool addDefiniteProperties(ExclusiveContext *cx, Shape *shape); + bool matchDefiniteProperties(HandleObject obj); + void markPropertyNonData(ExclusiveContext *cx, jsid id); + void markPropertyNonWritable(ExclusiveContext *cx, jsid id); + void markStateChange(ExclusiveContext *cx); + void setFlags(ExclusiveContext *cx, ObjectGroupFlags flags); + void markUnknown(ExclusiveContext *cx); + void maybeClearNewScriptOnOOM(); + void clearNewScript(ExclusiveContext *cx); + bool isPropertyNonData(jsid id); + bool isPropertyNonWritable(jsid id); + + void print(); + + inline void clearProperties(); + void maybeSweep(types::AutoClearTypeInferenceStateOnOOM *oom); + + private: +#ifdef DEBUG + bool needsSweep(); +#endif + + uint32_t generation() { + return (flags_ & OBJECT_FLAG_GENERATION_MASK) >> OBJECT_FLAG_GENERATION_SHIFT; + } + + public: + void setGeneration(uint32_t generation) { + MOZ_ASSERT(generation <= (OBJECT_FLAG_GENERATION_MASK >> OBJECT_FLAG_GENERATION_SHIFT)); + flags_ &= ~OBJECT_FLAG_GENERATION_MASK; + flags_ |= generation << OBJECT_FLAG_GENERATION_SHIFT; + } + + size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const; + + void finalize(FreeOp *fop); + void fixupAfterMovingGC() {} + + static inline ThingRootKind rootKind() { return THING_ROOT_OBJECT_GROUP; } + + static inline uint32_t offsetOfClasp() { + return offsetof(ObjectGroup, clasp_); + } + + static inline uint32_t offsetOfProto() { + return offsetof(ObjectGroup, proto_); + } + + static inline uint32_t offsetOfAddendum() { + return offsetof(ObjectGroup, addendum_); + } + + static inline uint32_t offsetOfFlags() { + return offsetof(ObjectGroup, flags_); + } + + private: + inline uint32_t basePropertyCount(); + inline void setBasePropertyCount(uint32_t count); + + static void staticAsserts() { + JS_STATIC_ASSERT(offsetof(ObjectGroup, proto_) == offsetof(js::shadow::ObjectGroup, proto)); + } + + public: + // Whether to make a deep cloned singleton when cloning fun. + static bool useSingletonForClone(JSFunction *fun); + + // Whether to make a singleton when calling 'new' at script/pc. + static bool useSingletonForNewObject(JSContext *cx, JSScript *script, jsbytecode *pc); + + // Whether to make a singleton object at an allocation site. + static bool useSingletonForAllocationSite(JSScript *script, jsbytecode *pc, + JSProtoKey key); + static bool useSingletonForAllocationSite(JSScript *script, jsbytecode *pc, + const Class *clasp); + + // Static accessors for ObjectGroupCompartment NewTable. + + static ObjectGroup *defaultNewGroup(ExclusiveContext *cx, const Class *clasp, + TaggedProto proto, + JSObject *associated = nullptr); + static ObjectGroup *lazySingletonGroup(ExclusiveContext *cx, const Class *clasp, + TaggedProto proto); + + static void setDefaultNewGroupUnknown(JSContext *cx, const js::Class *clasp, JS::HandleObject obj); + +#ifdef DEBUG + static bool hasDefaultNewGroup(JSObject *proto, const Class *clasp, ObjectGroup *group); +#endif + + // Static accessors for ObjectGroupCompartment ArrayObjectTable and PlainObjectTable. + + // Update the group of a freshly created array or plain object according to + // the object's current contents. + static void fixArrayGroup(ExclusiveContext *cx, ArrayObject *obj); + static void fixPlainObjectGroup(ExclusiveContext *cx, PlainObject *obj); + + // Update the group of a freshly created 'rest' arguments object. + static void fixRestArgumentsGroup(ExclusiveContext *cx, ArrayObject *obj); + + static PlainObject *newPlainObject(JSContext *cx, IdValuePair *properties, size_t nproperties); + + // Static accessors for ObjectGroupCompartment AllocationSiteTable. + + // Get a non-singleton group to use for objects created at the specified + // allocation site. + static ObjectGroup *allocationSiteGroup(JSContext *cx, JSScript *script, jsbytecode *pc, + JSProtoKey key); + + // Get a non-singleton group to use for objects created in a JSNative call. + static ObjectGroup *callingAllocationSiteGroup(JSContext *cx, JSProtoKey key); + + // Set the group or singleton-ness of an object created for an allocation site. + static bool + setAllocationSiteObjectGroup(JSContext *cx, HandleScript script, jsbytecode *pc, + HandleObject obj, bool singleton); + + static ArrayObject *getOrFixupCopyOnWriteObject(JSContext *cx, HandleScript script, + jsbytecode *pc); + static ArrayObject *getCopyOnWriteObject(JSScript *script, jsbytecode *pc); + + // Returns false if not found. + static bool findAllocationSiteForType(JSContext *cx, types::Type type, + JSScript **script, uint32_t *offset); + + private: + static ObjectGroup *defaultNewGroup(JSContext *cx, JSProtoKey key); + static void setGroupToHomogenousArray(ExclusiveContext *cx, JSObject *obj, types::Type type); +}; + +// Structure used to manage the groups in a compartment. +class ObjectGroupCompartment +{ + friend class ObjectGroup; + + struct NewEntry; + typedef HashSet NewTable; + class NewTableRef; + + // Set of default 'new' or lazy groups in the compartment. + NewTable *defaultNewTable; + NewTable *lazyTable; + + struct ArrayObjectKey; + typedef HashMap ArrayObjectTable; + + struct PlainObjectKey; + struct PlainObjectEntry; + typedef HashMap PlainObjectTable; + + // Tables for managing groups common to the contents of large script + // singleton objects and JSON objects. These are vanilla ArrayObjects and + // PlainObjects, so we distinguish the groups of different ones by looking + // at the types of their properties. + // + // All singleton/JSON arrays which have the same prototype, are homogenous + // and of the same element type will share a group. All singleton/JSON + // objects which have the same shape and property types will also share a + // group. We don't try to collate arrays or objects with type mismatches. + ArrayObjectTable *arrayObjectTable; + PlainObjectTable *plainObjectTable; + + struct AllocationSiteKey; + typedef HashMap AllocationSiteTable; + + // Table for referencing types of objects keyed to an allocation site. + AllocationSiteTable *allocationSiteTable; + + public: + ObjectGroupCompartment(); + ~ObjectGroupCompartment(); + + void removeDefaultNewGroup(const Class *clasp, TaggedProto proto, JSObject *associated); + void replaceDefaultNewGroup(const Class *clasp, TaggedProto proto, JSObject *associated, + ObjectGroup *group); + + static ObjectGroup *makeGroup(ExclusiveContext *cx, const Class *clasp, + Handle proto, + ObjectGroupFlags initialFlags = 0); + + void addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, + size_t *allocationSiteTables, + size_t *arrayGroupTables, + size_t *plainObjectGroupTables, + size_t *compartmentTables); + + void clearTables(); + + void sweep(FreeOp *fop); + +#ifdef JSGC_HASH_TABLE_CHECKS + void checkTablesAfterMovingGC() { + checkNewTableAfterMovingGC(defaultNewTable); + checkNewTableAfterMovingGC(lazyTable); + } +#endif + + void fixupTablesAfterMovingGC() { + fixupNewTableAfterMovingGC(defaultNewTable); + fixupNewTableAfterMovingGC(lazyTable); + } + + private: +#ifdef JSGC_HASH_TABLE_CHECKS + void checkNewTableAfterMovingGC(NewTable *table); +#endif + + void sweepNewTable(NewTable *table); + void fixupNewTableAfterMovingGC(NewTable *table); + + static void newTablePostBarrier(ExclusiveContext *cx, NewTable *table, + const Class *clasp, TaggedProto proto, JSObject *associated); + static void updatePlainObjectEntryTypes(ExclusiveContext *cx, PlainObjectEntry &entry, + IdValuePair *properties, size_t nproperties); +}; + +} // namespace js + +#endif /* vm_ObjectGroup_h */ diff --git a/js/src/vm/ProxyObject.cpp b/js/src/vm/ProxyObject.cpp index 80c1424110d1..324b5810ef7c 100644 --- a/js/src/vm/ProxyObject.cpp +++ b/js/src/vm/ProxyObject.cpp @@ -66,7 +66,7 @@ ProxyObject::New(JSContext *cx, const BaseProxyHandler *handler, HandleValue pri /* Don't track types of properties of non-DOM and non-singleton proxies. */ if (newKind != SingletonObject && !clasp->isDOMClass()) - MarkObjectGroupUnknownProperties(cx, proxy->group()); + types::MarkObjectGroupUnknownProperties(cx, proxy->group()); return proxy; } diff --git a/js/src/vm/RegExpObject.cpp b/js/src/vm/RegExpObject.cpp index 108d8fc83373..3d6b4a4557d7 100644 --- a/js/src/vm/RegExpObject.cpp +++ b/js/src/vm/RegExpObject.cpp @@ -708,8 +708,7 @@ RegExpCompartment::createMatchResultTemplateObject(JSContext *cx) // Create a new group for the template. Rooted proto(cx, templateObject->getTaggedProto()); - types::ObjectGroup *group = - cx->compartment()->types.newObjectGroup(cx, templateObject->getClass(), proto); + ObjectGroup *group = ObjectGroupCompartment::makeGroup(cx, templateObject->getClass(), proto); if (!group) return matchResultTemplateObject_; // = nullptr templateObject->setGroup(group); diff --git a/js/src/vm/RegExpStatics.cpp b/js/src/vm/RegExpStatics.cpp index e856b4b87ad8..4097fd9eb3af 100644 --- a/js/src/vm/RegExpStatics.cpp +++ b/js/src/vm/RegExpStatics.cpp @@ -76,7 +76,7 @@ RegExpStatics::markFlagsSet(JSContext *cx) // always be performed). MOZ_ASSERT_IF(cx->global()->hasRegExpStatics(), this == cx->global()->getRegExpStatics(cx)); - types::MarkObjectGroupFlags(cx, cx->global(), types::OBJECT_FLAG_REGEXP_FLAGS_SET); + types::MarkObjectGroupFlags(cx, cx->global(), OBJECT_FLAG_REGEXP_FLAGS_SET); } bool diff --git a/js/src/vm/Runtime-inl.h b/js/src/vm/Runtime-inl.h index 1b6d279222d2..ddd850f7b067 100644 --- a/js/src/vm/Runtime-inl.h +++ b/js/src/vm/Runtime-inl.h @@ -52,7 +52,7 @@ NewObjectCache::newObjectFromHit(JSContext *cx, EntryIndex entry_, js::gc::Initi // Do an end run around JSObject::group() to avoid doing AutoUnprotectCell // on the templateObj, which is not a GC thing and can't use runtimeFromAnyThread. - types::ObjectGroup *group = templateObj->group_; + ObjectGroup *group = templateObj->group_; if (group->shouldPreTenure()) heap = gc::TenuredHeap; diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp index 69986ff8515a..3c740baa6b9e 100644 --- a/js/src/vm/Runtime.cpp +++ b/js/src/vm/Runtime.cpp @@ -164,9 +164,6 @@ JSRuntime::JSRuntime(JSRuntime *parentRuntime) negativeInfinityValue(DoubleValue(NegativeInfinity())), positiveInfinityValue(DoubleValue(PositiveInfinity())), emptyString(nullptr), -#ifdef NIGHTLY_BUILD - assertOnScriptEntryHook_(nullptr), -#endif spsProfiler(thisFromCtor()), profilingScripts(false), suppressProfilerSampling(false), diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h index 6aca87bf5089..1a9152196f72 100644 --- a/js/src/vm/Runtime.h +++ b/js/src/vm/Runtime.h @@ -287,7 +287,7 @@ class NewObjectCache inline bool lookupGlobal(const Class *clasp, js::GlobalObject *global, gc::AllocKind kind, EntryIndex *pentry); - bool lookupGroup(js::types::ObjectGroup *group, gc::AllocKind kind, EntryIndex *pentry) { + bool lookupGroup(js::ObjectGroup *group, gc::AllocKind kind, EntryIndex *pentry) { return lookup(group->clasp(), group, kind, pentry); } @@ -306,7 +306,7 @@ class NewObjectCache inline void fillGlobal(EntryIndex entry, const Class *clasp, js::GlobalObject *global, gc::AllocKind kind, NativeObject *obj); - void fillGroup(EntryIndex entry, js::types::ObjectGroup *group, gc::AllocKind kind, + void fillGroup(EntryIndex entry, js::ObjectGroup *group, gc::AllocKind kind, NativeObject *obj) { MOZ_ASSERT(obj->group() == group); @@ -347,7 +347,7 @@ class NewObjectCache static void copyCachedToObject(JSObject *dst, JSObject *src, gc::AllocKind kind) { js_memcpy(dst, src, gc::Arena::thingSize(kind)); Shape::writeBarrierPost(dst->shape_, &dst->shape_); - types::ObjectGroup::writeBarrierPost(dst->group_, &dst->group_); + ObjectGroup::writeBarrierPost(dst->group_, &dst->group_); } }; @@ -987,10 +987,6 @@ struct JSRuntime : public JS::shadow::Runtime, mozilla::UniquePtr sourceHook; -#ifdef NIGHTLY_BUILD - js::AssertOnScriptEntryHook assertOnScriptEntryHook_; -#endif - /* SPS profiling metadata */ js::SPSProfiler spsProfiler; diff --git a/js/src/vm/ScopeObject.cpp b/js/src/vm/ScopeObject.cpp index d4ec087c9de1..97f473150977 100644 --- a/js/src/vm/ScopeObject.cpp +++ b/js/src/vm/ScopeObject.cpp @@ -153,7 +153,7 @@ CallObject::createSingleton(JSContext *cx, HandleShape shape, uint32_t lexicalBe MOZ_ASSERT(CanBeFinalizedInBackground(kind, &CallObject::class_)); kind = gc::GetBackgroundAllocKind(kind); - RootedObjectGroup group(cx, cx->getLazySingletonGroup(&class_, TaggedProto(nullptr))); + RootedObjectGroup group(cx, ObjectGroup::lazySingletonGroup(cx, &class_, TaggedProto(nullptr))); if (!group) return nullptr; RootedObject obj(cx, JSObject::create(cx, kind, gc::TenuredHeap, shape, group)); @@ -178,7 +178,7 @@ CallObject::createTemplateObject(JSContext *cx, HandleScript script, gc::Initial RootedShape shape(cx, script->bindings.callObjShape()); MOZ_ASSERT(shape->getObjectClass() == &class_); - RootedObjectGroup group(cx, cx->getNewGroup(&class_, TaggedProto(nullptr))); + RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &class_, TaggedProto(nullptr))); if (!group) return nullptr; @@ -326,7 +326,7 @@ DeclEnvObject::createTemplateObject(JSContext *cx, HandleFunction fun, gc::Initi { MOZ_ASSERT(IsNurseryAllocable(FINALIZE_KIND)); - RootedObjectGroup group(cx, cx->getNewGroup(&class_, TaggedProto(nullptr))); + RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &class_, TaggedProto(nullptr))); if (!group) return nullptr; @@ -400,7 +400,7 @@ js::XDRStaticWithObject(XDRState *, HandleObject, MutableHandlegetNewGroup(&class_, TaggedProto(nullptr))); + RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &class_, TaggedProto(nullptr))); if (!group) return nullptr; @@ -433,7 +433,8 @@ DynamicWithObject::create(JSContext *cx, HandleObject object, HandleObject enclo HandleObject staticWith, WithKind kind) { MOZ_ASSERT(staticWith->is()); - RootedObjectGroup group(cx, cx->getNewGroup(&class_, TaggedProto(staticWith.get()))); + RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &class_, + TaggedProto(staticWith.get()))); if (!group) return nullptr; @@ -563,7 +564,7 @@ const Class DynamicWithObject::class_ = { /* static */ StaticEvalObject * StaticEvalObject::create(JSContext *cx, HandleObject enclosing) { - RootedObjectGroup group(cx, cx->getNewGroup(&class_, TaggedProto(nullptr))); + RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &class_, TaggedProto(nullptr))); if (!group) return nullptr; @@ -596,7 +597,8 @@ ClonedBlockObject::create(JSContext *cx, Handle block, Hand { MOZ_ASSERT(block->getClass() == &BlockObject::class_); - RootedObjectGroup group(cx, cx->getNewGroup(&BlockObject::class_, TaggedProto(block.get()))); + RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &BlockObject::class_, + TaggedProto(block.get()))); if (!group) return nullptr; @@ -668,7 +670,8 @@ ClonedBlockObject::copyUnaliasedValues(AbstractFramePtr frame) StaticBlockObject * StaticBlockObject::create(ExclusiveContext *cx) { - RootedObjectGroup group(cx, cx->getNewGroup(&BlockObject::class_, TaggedProto(nullptr))); + RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &BlockObject::class_, + TaggedProto(nullptr))); if (!group) return nullptr; @@ -880,7 +883,7 @@ js::CloneNestedScopeObject(JSContext *cx, HandleObject enclosingScope, HandlegetNewGroup(&class_, TaggedProto(nullptr))); + RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &class_, TaggedProto(nullptr))); if (!group) return nullptr; diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp index 81ce1fe76145..817b307c2781 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -324,7 +324,7 @@ js::intrinsic_NewDenseArray(JSContext *cx, unsigned argc, Value *vp) if (!buffer) return false; - types::ObjectGroup *newgroup = types::GetCallerInitGroup(cx, JSProto_Array); + ObjectGroup *newgroup = ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array); if (!newgroup) return false; buffer->setGroup(newgroup); @@ -507,7 +507,7 @@ js::intrinsic_IsPackedArray(JSContext *cx, unsigned argc, Value *vp) JSObject *obj = &args[0].toObject(); bool isPacked = obj->is() && !obj->hasLazyGroup() && - !obj->group()->hasAllFlags(types::OBJECT_FLAG_NON_PACKED) && + !obj->group()->hasAllFlags(OBJECT_FLAG_NON_PACKED) && obj->as().getDenseInitializedLength() == obj->as().length(); diff --git a/js/src/vm/Shape-inl.h b/js/src/vm/Shape-inl.h index 49a6483b464a..3892cb2377ec 100644 --- a/js/src/vm/Shape-inl.h +++ b/js/src/vm/Shape-inl.h @@ -41,10 +41,11 @@ Shape::search(ExclusiveContext *cx, jsid id) } inline bool -Shape::set(JSContext* cx, HandleObject obj, HandleObject receiver, bool strict, +Shape::set(JSContext* cx, HandleNativeObject obj, HandleObject receiver, bool strict, MutableHandleValue vp) { MOZ_ASSERT_IF(hasDefaultSetter(), hasGetterValue()); + MOZ_ASSERT(!obj->is()); // See bug 1128681. if (attrs & JSPROP_SETTER) { Value fval = setterValue(); @@ -58,16 +59,6 @@ Shape::set(JSContext* cx, HandleObject obj, HandleObject receiver, bool strict, return true; RootedId id(cx, propid()); - - /* - * |with (it) color='red';| ends up here. - * Avoid exposing the With object to native setters. - */ - if (obj->is()) { - RootedObject nobj(cx, &obj->as().object()); - return CallJSPropertyOpSetter(cx, setterOp(), nobj, id, strict, vp); - } - return CallJSPropertyOpSetter(cx, setterOp(), obj, id, strict, vp); } diff --git a/js/src/vm/Shape.cpp b/js/src/vm/Shape.cpp index f1e28749b397..821dfb9b9411 100644 --- a/js/src/vm/Shape.cpp +++ b/js/src/vm/Shape.cpp @@ -1724,7 +1724,7 @@ NewObjectCache::invalidateEntriesForShape(JSContext *cx, HandleShape shape, Hand kind = GetBackgroundAllocKind(kind); Rooted global(cx, &shape->getObjectParent()->global()); - RootedObjectGroup group(cx, cx->getNewGroup(clasp, TaggedProto(proto))); + RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, clasp, TaggedProto(proto))); EntryIndex entry; if (lookupGlobal(clasp, global, kind, &entry)) diff --git a/js/src/vm/Shape.h b/js/src/vm/Shape.h index 8b9da498ce97..1a8041848e45 100644 --- a/js/src/vm/Shape.h +++ b/js/src/vm/Shape.h @@ -16,7 +16,6 @@ #include "jsapi.h" #include "jsfriendapi.h" -#include "jsinfer.h" #include "jspropertytree.h" #include "jstypes.h" #include "NamespaceImports.h" @@ -29,6 +28,7 @@ #include "js/MemoryMetrics.h" #include "js/RootingAPI.h" #include "js/UbiNode.h" +#include "vm/ObjectGroup.h" #include "vm/PropDesc.h" #ifdef _MSC_VER @@ -962,7 +962,8 @@ class Shape : public gc::TenuredCell setter() == rawSetter; } - bool set(JSContext* cx, HandleObject obj, HandleObject receiver, bool strict, MutableHandleValue vp); + bool set(JSContext* cx, HandleNativeObject obj, HandleObject receiver, bool strict, + MutableHandleValue vp); BaseShape *base() const { return base_.get(); } diff --git a/js/src/vm/SharedTypedArrayObject.cpp b/js/src/vm/SharedTypedArrayObject.cpp index ee9118aef0dd..26a6ed3119d8 100644 --- a/js/src/vm/SharedTypedArrayObject.cpp +++ b/js/src/vm/SharedTypedArrayObject.cpp @@ -135,7 +135,7 @@ class SharedTypedArrayObjectTemplate : public SharedTypedArrayObject if (!obj) return nullptr; - types::ObjectGroup *group = cx->getNewGroup(obj->getClass(), TaggedProto(proto.get())); + ObjectGroup *group = ObjectGroup::defaultNewGroup(cx, obj->getClass(), TaggedProto(proto.get())); if (!group) return nullptr; obj->setGroup(group); @@ -156,16 +156,17 @@ class SharedTypedArrayObjectTemplate : public SharedTypedArrayObject jsbytecode *pc; RootedScript script(cx, cx->currentScript(&pc)); - NewObjectKind newKind = script - ? UseSingletonForInitializer(script, pc, instanceClass()) - : GenericObject; + NewObjectKind newKind = GenericObject; + if (script && ObjectGroup::useSingletonForAllocationSite(script, pc, instanceClass())) + newKind = SingletonObject; RootedObject obj(cx, NewBuiltinClassInstance(cx, instanceClass(), allocKind, newKind)); if (!obj) return nullptr; - if (script) { - if (!types::SetInitializerObjectGroup(cx, script, pc, obj, newKind)) - return nullptr; + if (script && !ObjectGroup::setAllocationSiteObjectGroup(cx, script, pc, obj, + newKind == SingletonObject)) + { + return nullptr; } return &obj->as(); diff --git a/js/src/vm/Stack.cpp b/js/src/vm/Stack.cpp index 34d147c0799a..48c7c33282a1 100644 --- a/js/src/vm/Stack.cpp +++ b/js/src/vm/Stack.cpp @@ -135,7 +135,7 @@ InterpreterFrame::createRestParameter(JSContext *cx) ArrayObject *obj = NewDenseCopiedArray(cx, nrest, restvp, nullptr); if (!obj) return nullptr; - types::FixRestArgumentsType(cx, obj); + ObjectGroup::fixRestArgumentsGroup(cx, obj); return obj; } diff --git a/js/src/vm/Stack.h b/js/src/vm/Stack.h index 66e22bded0e8..d7d222aa254e 100644 --- a/js/src/vm/Stack.h +++ b/js/src/vm/Stack.h @@ -26,6 +26,7 @@ namespace js { class ArgumentsObject; class AsmJSModule; class InterpreterRegs; +class CallObject; class ScopeObject; class ScriptFrameIter; class SPSProfiler; diff --git a/js/src/vm/TypedArrayObject.cpp b/js/src/vm/TypedArrayObject.cpp index 6c6db9c9ba9d..8d48a1825d4d 100644 --- a/js/src/vm/TypedArrayObject.cpp +++ b/js/src/vm/TypedArrayObject.cpp @@ -305,7 +305,8 @@ class TypedArrayObjectTemplate : public TypedArrayObject if (!obj) return nullptr; - types::ObjectGroup *group = cx->getNewGroup(obj->getClass(), TaggedProto(proto.get())); + ObjectGroup *group = ObjectGroup::defaultNewGroup(cx, obj->getClass(), + TaggedProto(proto.get())); if (!group) return nullptr; obj->setGroup(group); @@ -326,16 +327,17 @@ class TypedArrayObjectTemplate : public TypedArrayObject jsbytecode *pc; RootedScript script(cx, cx->currentScript(&pc)); - NewObjectKind newKind = script - ? UseSingletonForInitializer(script, pc, clasp) - : GenericObject; + NewObjectKind newKind = GenericObject; + if (script && ObjectGroup::useSingletonForAllocationSite(script, pc, clasp)) + newKind = SingletonObject; RootedObject obj(cx, NewBuiltinClassInstance(cx, clasp, allocKind, newKind)); if (!obj) return nullptr; - if (script) { - if (!types::SetInitializerObjectGroup(cx, script, pc, obj, newKind)) - return nullptr; + if (script && !ObjectGroup::setAllocationSiteObjectGroup(cx, script, pc, obj, + newKind == SingletonObject)) + { + return nullptr; } return &obj->as(); @@ -972,9 +974,9 @@ DataViewNewObjectKind(JSContext *cx, uint32_t byteLength, JSObject *proto) return SingletonObject; jsbytecode *pc; JSScript *script = cx->currentScript(&pc); - if (!script) - return GenericObject; - return types::UseSingletonForInitializer(script, pc, &DataViewObject::class_); + if (script && ObjectGroup::useSingletonForAllocationSite(script, pc, &DataViewObject::class_)) + return SingletonObject; + return GenericObject; } inline DataViewObject * @@ -1000,7 +1002,7 @@ DataViewObject::create(JSContext *cx, uint32_t byteOffset, uint32_t byteLength, return nullptr; if (proto) { - types::ObjectGroup *group = cx->getNewGroup(&class_, TaggedProto(proto)); + ObjectGroup *group = ObjectGroup::defaultNewGroup(cx, &class_, TaggedProto(proto)); if (!group) return nullptr; obj->setGroup(group); @@ -1009,9 +1011,10 @@ DataViewObject::create(JSContext *cx, uint32_t byteOffset, uint32_t byteLength, } else { jsbytecode *pc; RootedScript script(cx, cx->currentScript(&pc)); - if (script) { - if (!types::SetInitializerObjectGroup(cx, script, pc, obj, newKind)) - return nullptr; + if (script && !ObjectGroup::setAllocationSiteObjectGroup(cx, script, pc, obj, + newKind == SingletonObject)) + { + return nullptr; } } diff --git a/js/src/vm/UbiNode.cpp b/js/src/vm/UbiNode.cpp index 5d3863ab95e8..2bbef9a0e0d9 100644 --- a/js/src/vm/UbiNode.cpp +++ b/js/src/vm/UbiNode.cpp @@ -60,15 +60,15 @@ Concrete::size(mozilla::MallocSizeOf mallocSizeof) const Node::Node(JSGCTraceKind kind, void *ptr) { switch (kind) { - case JSTRACE_OBJECT: construct(static_cast(ptr)); break; - case JSTRACE_SCRIPT: construct(static_cast(ptr)); break; - case JSTRACE_STRING: construct(static_cast(ptr)); break; - case JSTRACE_SYMBOL: construct(static_cast(ptr)); break; - case JSTRACE_BASE_SHAPE: construct(static_cast(ptr)); break; - case JSTRACE_JITCODE: construct(static_cast(ptr)); break; - case JSTRACE_LAZY_SCRIPT: construct(static_cast(ptr)); break; - case JSTRACE_SHAPE: construct(static_cast(ptr)); break; - case JSTRACE_OBJECT_GROUP: construct(static_cast(ptr)); break; + case JSTRACE_OBJECT: construct(static_cast(ptr)); break; + case JSTRACE_SCRIPT: construct(static_cast(ptr)); break; + case JSTRACE_STRING: construct(static_cast(ptr)); break; + case JSTRACE_SYMBOL: construct(static_cast(ptr)); break; + case JSTRACE_BASE_SHAPE: construct(static_cast(ptr)); break; + case JSTRACE_JITCODE: construct(static_cast(ptr)); break; + case JSTRACE_LAZY_SCRIPT: construct(static_cast(ptr)); break; + case JSTRACE_SHAPE: construct(static_cast(ptr)); break; + case JSTRACE_OBJECT_GROUP: construct(static_cast(ptr)); break; default: MOZ_CRASH("bad JSGCTraceKind passed to JS::ubi::Node::Node"); @@ -239,8 +239,8 @@ template<> const char16_t TracerConcrete::concreteTypeName[] = MOZ_UTF16("js::Shape"); template<> const char16_t TracerConcrete::concreteTypeName[] = MOZ_UTF16("js::BaseShape"); -template<> const char16_t TracerConcrete::concreteTypeName[] = - MOZ_UTF16("js::types::ObjectGroup"); +template<> const char16_t TracerConcrete::concreteTypeName[] = + MOZ_UTF16("js::ObjectGroup"); // Instantiate all the TracerConcrete and templates here, where @@ -255,7 +255,7 @@ template class TracerConcrete; template class TracerConcrete; template class TracerConcreteWithCompartment; template class TracerConcreteWithCompartment; -template class TracerConcrete; +template class TracerConcrete; } } diff --git a/js/src/vm/UnboxedObject.cpp b/js/src/vm/UnboxedObject.cpp index ba03c3ff56c4..15dfe30ba064 100644 --- a/js/src/vm/UnboxedObject.cpp +++ b/js/src/vm/UnboxedObject.cpp @@ -14,6 +14,7 @@ using mozilla::ArrayLength; using mozilla::DebugOnly; using mozilla::PodCopy; +using mozilla::UniquePtr; using namespace js; @@ -417,9 +418,29 @@ UnboxedTypeIncludes(JSValueType supertype, JSValueType subtype) return false; } +// Return whether the property names and types in layout are a subset of the +// specified vector. +static bool +PropertiesAreSuperset(const UnboxedLayout::PropertyVector &properties, UnboxedLayout *layout) +{ + for (size_t i = 0; i < layout->properties().length(); i++) { + const UnboxedLayout::Property &layoutProperty = layout->properties()[i]; + bool found = false; + for (size_t j = 0; j < properties.length(); j++) { + if (layoutProperty.name == properties[j].name) { + found = (layoutProperty.type == properties[j].type); + break; + } + } + if (!found) + return false; + } + return true; +} + bool js::TryConvertToUnboxedLayout(JSContext *cx, Shape *templateShape, - types::ObjectGroup *group, types::PreliminaryObjectArray *objects) + ObjectGroup *group, types::PreliminaryObjectArray *objects) { if (!cx->runtime()->options().unboxedObjects()) return true; @@ -487,43 +508,83 @@ js::TryConvertToUnboxedLayout(JSContext *cx, Shape *templateShape, properties[slot].name = JSID_TO_ATOM(r.front().propid())->asPropertyName(); } - // Fill in all the unboxed object's property offsets, ordering fields from the - // largest down to avoid alignment issues. + // Fill in all the unboxed object's property offsets. uint32_t offset = 0; + // Search for an existing unboxed layout which is a subset of this one. + // If there are multiple such layouts, use the largest one. If we're able + // to find such a layout, use the same property offsets for the shared + // properties, which will allow us to generate better code if the objects + // have a subtype/supertype relation and are accessed at common sites. + UnboxedLayout *bestExisting = nullptr; + for (UnboxedLayout *existing = cx->compartment()->unboxedLayouts.getFirst(); + existing; + existing = existing->getNext()) + { + if (PropertiesAreSuperset(properties, existing)) { + if (!bestExisting || + existing->properties().length() > bestExisting->properties().length()) + { + bestExisting = existing; + } + } + } + if (bestExisting) { + for (size_t i = 0; i < bestExisting->properties().length(); i++) { + const UnboxedLayout::Property &existingProperty = bestExisting->properties()[i]; + for (size_t j = 0; j < templateShape->slotSpan(); j++) { + if (existingProperty.name == properties[j].name) { + MOZ_ASSERT(existingProperty.type == properties[j].type); + properties[j].offset = existingProperty.offset; + } + } + } + offset = bestExisting->size(); + } + + // Order remaining properties from the largest down for the best space + // utilization. static const size_t typeSizes[] = { 8, 4, 1 }; - Vector objectOffsets, stringOffsets; - - DebugOnly addedProperties = 0; for (size_t i = 0; i < ArrayLength(typeSizes); i++) { size_t size = typeSizes[i]; for (size_t j = 0; j < templateShape->slotSpan(); j++) { + if (properties[j].offset != UINT32_MAX) + continue; JSValueType type = properties[j].type; if (UnboxedTypeSize(type) == size) { - if (type == JSVAL_TYPE_OBJECT) { - if (!objectOffsets.append(offset)) - return false; - } else if (type == JSVAL_TYPE_STRING) { - if (!stringOffsets.append(offset)) - return false; - } - addedProperties++; + offset = JS_ROUNDUP(offset, size); properties[j].offset = offset; offset += size; } } } - MOZ_ASSERT(addedProperties == templateShape->slotSpan()); // The entire object must be allocatable inline. if (sizeof(JSObject) + offset > JSObject::MAX_BYTE_SIZE) return true; - UnboxedLayout *layout = group->zone()->new_(properties, offset); + UniquePtr > layout; + layout.reset(group->zone()->new_(properties, offset)); if (!layout) return false; + cx->compartment()->unboxedLayouts.insertFront(layout.get()); + + // Figure out the offsets of any objects or string properties. + Vector objectOffsets, stringOffsets; + for (size_t i = 0; i < templateShape->slotSpan(); i++) { + MOZ_ASSERT(properties[i].offset != UINT32_MAX); + JSValueType type = properties[i].type; + if (type == JSVAL_TYPE_OBJECT) { + if (!objectOffsets.append(properties[i].offset)) + return false; + } else if (type == JSVAL_TYPE_STRING) { + if (!stringOffsets.append(properties[i].offset)) + return false; + } + } + // Construct the layout's trace list. if (!objectOffsets.empty() || !stringOffsets.empty()) { Vector entries; @@ -582,7 +643,7 @@ js::TryConvertToUnboxedLayout(JSContext *cx, Shape *templateShape, layout->setNewScript(newScript); group->setClasp(&UnboxedPlainObject::class_); - group->setUnboxedLayout(layout); + group->setUnboxedLayout(layout.get()); size_t valueCursor = 0; for (size_t i = 0; i < types::PreliminaryObjectArray::COUNT; i++) { @@ -597,5 +658,6 @@ js::TryConvertToUnboxedLayout(JSContext *cx, Shape *templateShape, } MOZ_ASSERT(valueCursor == values.length()); + layout.release(); return true; } diff --git a/js/src/vm/UnboxedObject.h b/js/src/vm/UnboxedObject.h index 32f833728030..75b926106bf9 100644 --- a/js/src/vm/UnboxedObject.h +++ b/js/src/vm/UnboxedObject.h @@ -35,7 +35,7 @@ UnboxedTypeNeedsPreBarrier(JSValueType type) } // Class describing the layout of an UnboxedPlainObject. -class UnboxedLayout +class UnboxedLayout : public mozilla::LinkedListElement { public: struct Property { @@ -44,7 +44,7 @@ class UnboxedLayout JSValueType type; Property() - : name(nullptr), offset(0), type(JSVAL_TYPE_MAGIC) + : name(nullptr), offset(UINT32_MAX), type(JSVAL_TYPE_MAGIC) {} }; @@ -182,7 +182,7 @@ class UnboxedPlainObject : public JSObject // preliminary objects and their group to the new unboxed representation. bool TryConvertToUnboxedLayout(JSContext *cx, Shape *templateShape, - types::ObjectGroup *group, types::PreliminaryObjectArray *objects); + ObjectGroup *group, types::PreliminaryObjectArray *objects); inline gc::AllocKind UnboxedLayout::getAllocKind() const diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index ab7e16fa2605..82f389ee7d0e 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -1153,7 +1153,7 @@ PresShell::Destroy() mUpdateImageVisibilityEvent.Revoke(); - ClearVisibleImagesList(); + ClearVisibleImagesList(nsIImageLoadingContent::ON_NONVISIBLE_REQUEST_DISCARD); if (mCaret) { mCaret->Terminate(); @@ -5820,7 +5820,8 @@ PresShell::MarkImagesInListVisible(const nsDisplayList& aList) static PLDHashOperator DecrementVisibleCount(nsRefPtrHashKey* aEntry, void*) { - aEntry->GetKey()->DecrementVisibleCount(); + aEntry->GetKey()->DecrementVisibleCount( + nsIImageLoadingContent::ON_NONVISIBLE_NO_ACTION); return PL_DHASH_NEXT; } @@ -5844,7 +5845,8 @@ PresShell::ClearImageVisibilityVisited(nsView* aView, bool aClear) if (aClear) { PresShell* presShell = static_cast(vm->GetPresShell()); if (!presShell->mImageVisibilityVisited) { - presShell->ClearVisibleImagesList(); + presShell->ClearVisibleImagesList( + nsIImageLoadingContent::ON_NONVISIBLE_NO_ACTION); } presShell->mImageVisibilityVisited = false; } @@ -5853,10 +5855,23 @@ PresShell::ClearImageVisibilityVisited(nsView* aView, bool aClear) } } -void -PresShell::ClearVisibleImagesList() +static PLDHashOperator +DecrementVisibleCountAndDiscard(nsRefPtrHashKey* aEntry, + void*) { - mVisibleImages.EnumerateEntries(DecrementVisibleCount, nullptr); + aEntry->GetKey()->DecrementVisibleCount( + nsIImageLoadingContent::ON_NONVISIBLE_REQUEST_DISCARD); + return PL_DHASH_NEXT; +} + +void +PresShell::ClearVisibleImagesList(uint32_t aNonvisibleAction) +{ + auto enumerator + = aNonvisibleAction == nsIImageLoadingContent::ON_NONVISIBLE_REQUEST_DISCARD + ? DecrementVisibleCountAndDiscard + : DecrementVisibleCount; + mVisibleImages.EnumerateEntries(enumerator, nullptr); mVisibleImages.Clear(); } @@ -5986,7 +6001,8 @@ PresShell::UpdateImageVisibility() // call update on that frame nsIFrame* rootFrame = GetRootFrame(); if (!rootFrame) { - ClearVisibleImagesList(); + ClearVisibleImagesList( + nsIImageLoadingContent::ON_NONVISIBLE_REQUEST_DISCARD); return; } @@ -6145,7 +6161,8 @@ PresShell::RemoveImageFromVisibleList(nsIImageLoadingContent* aImage) mVisibleImages.RemoveEntry(aImage); if (mVisibleImages.Count() < count) { // aImage was in the hashtable, so we need to decrement its visible count - aImage->DecrementVisibleCount(); + aImage->DecrementVisibleCount( + nsIImageLoadingContent::ON_NONVISIBLE_NO_ACTION); } } diff --git a/layout/base/nsPresShell.h b/layout/base/nsPresShell.h index 6080435cda96..26b7fc6fcb03 100644 --- a/layout/base/nsPresShell.h +++ b/layout/base/nsPresShell.h @@ -735,7 +735,7 @@ protected: nsRevocableEventPtr > mUpdateImageVisibilityEvent; - void ClearVisibleImagesList(); + void ClearVisibleImagesList(uint32_t aNonvisibleAction); static void ClearImageVisibilityVisited(nsView* aView, bool aClear); static void MarkImagesInListVisible(const nsDisplayList& aList); void MarkImagesInSubtreeVisible(nsIFrame* aFrame, const nsRect& aRect); diff --git a/layout/reftests/backgrounds/reftest.list b/layout/reftests/backgrounds/reftest.list index 9060bf5cfac9..83549179610d 100644 --- a/layout/reftests/backgrounds/reftest.list +++ b/layout/reftests/backgrounds/reftest.list @@ -155,8 +155,9 @@ fails-if(Android&&AndroidVersion==15) fuzzy-if(!Android||(Android&&AndroidVersio == attachment-local-clipping-image-1.html attachment-local-clipping-image-1-ref.html == attachment-local-clipping-image-2.html attachment-local-clipping-image-1-ref.html # Same ref as the previous test. == attachment-local-clipping-image-3.html attachment-local-clipping-image-3-ref.html -fails-if(Android&&AndroidVersion==15) == attachment-local-clipping-image-4.html attachment-local-clipping-image-4-ref.html #Bug 959165 -fails-if(Android&&AndroidVersion==15) == attachment-local-clipping-image-5.html attachment-local-clipping-image-4-ref.html # Same ref as the previous test. #Bug 959165 +# The next three tests are fuzzy due to bug 1128229. +fails-if(Android&&AndroidVersion==15) fuzzy-if(winWidget||(Android&&AndroidVersion!=15),16,69) == attachment-local-clipping-image-4.html attachment-local-clipping-image-4-ref.html #Bug 959165 +fails-if(Android&&AndroidVersion==15) fuzzy-if(winWidget||(Android&&AndroidVersion!=15),16,69) == attachment-local-clipping-image-5.html attachment-local-clipping-image-4-ref.html # Same ref as the previous test. #Bug 959165 fails-if(Android&&AndroidVersion==15) fuzzy-if(!Android||(Android&&AndroidVersion!=15),80,500) == attachment-local-clipping-image-6.html attachment-local-clipping-image-6-ref.html #Bug 959165 == background-multiple-with-border-radius.html background-multiple-with-border-radius-ref.html diff --git a/layout/reftests/bugs/reftest.list b/layout/reftests/bugs/reftest.list index f50b9d730ec7..061f5cee554f 100644 --- a/layout/reftests/bugs/reftest.list +++ b/layout/reftests/bugs/reftest.list @@ -55,9 +55,9 @@ fails == 25888-2r-block.html 25888-2r-ref.html # Bug 25888 fails == 25888-3l-block.html 25888-3l-ref.html # Bug 25888 fails == 25888-3r-block.html 25888-3r-ref.html # Bug 25888 skip-if(B2G) == 28811-1a.html 28811-1-ref.html -== 28811-1b.html 28811-1-ref.html +fuzzy-if(gtk2Widget,6,26200) == 28811-1b.html 28811-1-ref.html # Bug 1128229 skip-if(B2G) == 28811-2a.html 28811-2-ref.html -== 28811-2b.html 28811-2-ref.html +fuzzy-if(gtk2Widget,6,26200) == 28811-2b.html 28811-2-ref.html # Bug 1128229 == 40596-1a.html 40596-1-ref.html != 40596-1b.html 40596-1-ref.html == 40596-1c.html 40596-1-ref.html @@ -935,10 +935,10 @@ fuzzy-if(Android,13,9) == 407111-1.html 407111-1-ref.html # Bug 1128229 == 409089-1.html 409089-1-ref.html == 409089-2.html 409089-2-ref.html == 409089-3.html 409089-3-ref.html -== 409659-1a.html 409659-1-ref.html +fuzzy-if(winWidget,123,1600) == 409659-1a.html 409659-1-ref.html # Bug 1128229 != 409659-1b.html 409659-1-ref.html fails-if(Android&&AndroidVersion>=15&&smallScreen&&AndroidVersion!=17) != 409659-1c.html 409659-1-ref.html #there is no difference in the visible area of the screen -== 409659-1d.html 409659-1-ref.html +fuzzy-if(winWidget,123,1900) == 409659-1d.html 409659-1-ref.html # Bug 1128229 == 410621-1.html 410621-1-ref.html == 411059-1.html 411059-1-ref.html == 411334-1.xml 411334-1-ref.xml @@ -1196,9 +1196,10 @@ test-pref(dom.use_xbl_scopes_for_remote_xul,true) != 449149-1b.html about:blank == 455280-1.xhtml 455280-1-ref.xhtml skip-if(B2G) == 455826-1.html 455826-1-ref.html skip-if(B2G) fails-if(cocoaWidget) fails-if(Android) == 456147.xul 456147-ref.html # bug 458047 -fuzzy-if(Android,11,40) fuzzy-if(B2G,11,40) fuzzy-if(d2d&&/^Windows\x20NT\x206\.2/.test(http.oscpu),1,69) == 456219-1a.html 456219-1-ref.html # bug 853273 -fuzzy-if(Android,11,40) fuzzy-if(B2G,11,40) fuzzy-if(d2d&&/^Windows\x20NT\x206\.2/.test(http.oscpu),1,69) == 456219-1b.html 456219-1-ref.html # bug 853273 -fuzzy-if(Android,11,40) fuzzy-if(B2G,11,40) fuzzy-if(d2d&&/^Windows\x20NT\x206\.2/.test(http.oscpu),1,69) == 456219-1c.html 456219-1-ref.html # bug 853273 +# The next three tests are fuzzy due to bug 1128229. +fuzzy-if(Android||B2G,11,41) fuzzy-if(d2d&&/^Windows\x20NT\x206\.2/.test(http.oscpu),1,69) == 456219-1a.html 456219-1-ref.html # bug 853273 +fuzzy-if(Android||B2G,11,41) fuzzy-if(d2d&&/^Windows\x20NT\x206\.2/.test(http.oscpu),1,69) == 456219-1b.html 456219-1-ref.html # bug 853273 +fuzzy-if(Android||B2G,11,41) fuzzy-if(d2d&&/^Windows\x20NT\x206\.2/.test(http.oscpu),1,69) == 456219-1c.html 456219-1-ref.html # bug 853273 == 456219-2.html 456219-2-ref.html == 456330-1.gif 456330-1-ref.png == 456484-1.html 456484-1-ref.html diff --git a/layout/reftests/image-rect/reftest.list b/layout/reftests/image-rect/reftest.list index e2a091225b27..a9de1062c4fb 100644 --- a/layout/reftests/image-rect/reftest.list +++ b/layout/reftests/image-rect/reftest.list @@ -8,7 +8,7 @@ asserts(0-4) == background-draw-nothing-malformed-images.html background-draw-no == background-over-size-rect.html background-over-size-rect-ref.html == background-test-parser.html background-test-parser-ref.html fuzzy-if(Android||B2G,113,124) == background-with-other-properties.html background-with-other-properties-ref.html -== background-zoom-1.html background-zoom-1-ref.html +fuzzy-if(Android||B2G,16,22) == background-zoom-1.html background-zoom-1-ref.html # Bug 1128229 == background-zoom-2.html background-zoom-2-ref.html == background-zoom-3.html background-zoom-3-ref.html == background-zoom-4.html background-zoom-4-ref.html diff --git a/layout/reftests/image/reftest.list b/layout/reftests/image/reftest.list index 1d31b824ba82..4e58605809b6 100644 --- a/layout/reftests/image/reftest.list +++ b/layout/reftests/image/reftest.list @@ -2,7 +2,7 @@ fuzzy-if(Android,8,30) == background-image-zoom-1.html background-image-zoom-1-r == background-image-zoom-2.html about:blank == image-seam-1a.html image-seam-1-ref.html == image-seam-1b.html image-seam-1-ref.html -== image-seam-2.html image-seam-2-ref.html +fuzzy-if(Android,255,154) == image-seam-2.html image-seam-2-ref.html # Bug 1128229 skip-if(B2G&&browserIsRemote) == image-zoom-1.html image-zoom-1-ref.html skip-if(B2G&&browserIsRemote) == image-zoom-2.html image-zoom-1-ref.html == invalid-url-image-1.html invalid-url-image-1-ref.html @@ -17,7 +17,7 @@ test-pref(layout.css.object-fit-and-position.enabled,true) == image-object-fit-w test-pref(layout.css.object-fit-and-position.enabled,true) == image-object-fit-with-background-2.html image-object-fit-with-background-2-ref.html test-pref(layout.css.object-fit-and-position.enabled,true) == image-object-position-dyn-1.html image-object-position-dyn-1-ref.html test-pref(layout.css.object-fit-and-position.enabled,true) == image-object-position-with-background-1.html image-object-position-with-background-1-ref.html -test-pref(layout.css.object-fit-and-position.enabled,true) == image-object-position-with-background-2.html image-object-position-with-background-2-ref.html +test-pref(layout.css.object-fit-and-position.enabled,true) fuzzy-if(winWidget,117,374) == image-object-position-with-background-2.html image-object-position-with-background-2-ref.html # Bug 1128229 # Tests for image-orientation used with 'from-image' (note that all # image-orientation tests are fuzzy because the JPEG images do not perfectly diff --git a/layout/svg/SVGFEImageFrame.cpp b/layout/svg/SVGFEImageFrame.cpp index 1b14a7dbb05a..5758ed4681ad 100644 --- a/layout/svg/SVGFEImageFrame.cpp +++ b/layout/svg/SVGFEImageFrame.cpp @@ -82,7 +82,8 @@ SVGFEImageFrame::DestroyFrom(nsIFrame* aDestructRoot) if (imageLoader) { imageLoader->FrameDestroyed(this); - imageLoader->DecrementVisibleCount(); + imageLoader + ->DecrementVisibleCount(nsIImageLoadingContent::ON_NONVISIBLE_NO_ACTION); } SVGFEImageFrameBase::DestroyFrom(aDestructRoot); diff --git a/layout/tools/reftest/runreftest.py b/layout/tools/reftest/runreftest.py index 00dac3613bf6..cfe60ef27afb 100644 --- a/layout/tools/reftest/runreftest.py +++ b/layout/tools/reftest/runreftest.py @@ -24,6 +24,7 @@ sys.path.insert(0, SCRIPT_DIRECTORY) from automationutils import ( dumpScreen, environment, + printstatus, processLeakLog ) import mozcrash @@ -394,13 +395,13 @@ class RefTest(object): return else: try: - proc.kill(sig=signal.SIGABRT) + process.kill(sig=signal.SIGABRT) except OSError: # https://bugzilla.mozilla.org/show_bug.cgi?id=921509 log.info("Can't trigger Breakpad, process no longer exists") return log.info("Can't trigger Breakpad, just killing process") - proc.kill() + process.kill() ### output processing diff --git a/netwerk/base/nsIOService.cpp b/netwerk/base/nsIOService.cpp index d607ab509bd9..d77847db8de9 100644 --- a/netwerk/base/nsIOService.cpp +++ b/netwerk/base/nsIOService.cpp @@ -42,6 +42,7 @@ #include "nsThreadUtils.h" #include "mozilla/LoadInfo.h" #include "mozilla/net/NeckoCommon.h" +#include "mozilla/Telemetry.h" #ifdef MOZ_WIDGET_GONK #include "nsINetworkManager.h" @@ -148,6 +149,8 @@ static const char kNetworkActiveChanged[] = "network-active-changed"; uint32_t nsIOService::gDefaultSegmentSize = 4096; uint32_t nsIOService::gDefaultSegmentCount = 24; +bool nsIOService::sTelemetryEnabled = false; + NS_IMPL_ISUPPORTS(nsAppOfflineInfo, nsIAppOfflineInfo) //////////////////////////////////////////////////////////////////////////////// @@ -222,6 +225,8 @@ nsIOService::Init() else NS_WARNING("failed to get observer service"); + Preferences::AddBoolVarCache(&sTelemetryEnabled, "toolkit.telemetry.enabled", false); + gIOService = this; InitializeNetworkLinkService(); @@ -634,6 +639,28 @@ nsIOService::NewChannelFromURIWithProxyFlagsInternal(nsIURI* aURI, if (NS_FAILED(rv)) return rv; + if (sTelemetryEnabled) { + nsAutoCString path; + aURI->GetPath(path); + + bool endsInExcl = StringEndsWith(path, NS_LITERAL_CSTRING("!")); + int32_t bangSlashPos = path.Find("!/"); + + bool hasBangSlash = bangSlashPos != kNotFound; + bool hasBangDoubleSlash = false; + + if (bangSlashPos != kNotFound) { + nsDependentCSubstring substr(path, bangSlashPos); + hasBangDoubleSlash = StringBeginsWith(substr, NS_LITERAL_CSTRING("!//")); + } + + Telemetry::Accumulate(Telemetry::URL_PATH_ENDS_IN_EXCLAMATION, endsInExcl); + Telemetry::Accumulate(Telemetry::URL_PATH_CONTAINS_EXCLAMATION_SLASH, + hasBangSlash); + Telemetry::Accumulate(Telemetry::URL_PATH_CONTAINS_EXCLAMATION_DOUBLE_SLASH, + hasBangDoubleSlash); + } + nsCOMPtr handler; rv = GetProtocolHandler(scheme.get(), getter_AddRefs(handler)); if (NS_FAILED(rv)) diff --git a/netwerk/base/nsIOService.h b/netwerk/base/nsIOService.h index d91d7919dccc..23b3705437c9 100644 --- a/netwerk/base/nsIOService.h +++ b/netwerk/base/nsIOService.h @@ -154,6 +154,8 @@ private: // Hashtable of (appId, nsIAppOffineInfo::mode) pairs // that is used especially in IsAppOffline nsDataHashtable mAppsOfflineStatus; + + static bool sTelemetryEnabled; public: // Used for all default buffer sizes that necko allocates. static uint32_t gDefaultSegmentSize; diff --git a/netwerk/base/nsSocketTransport2.cpp b/netwerk/base/nsSocketTransport2.cpp index a54fa6b3c5a0..24944b7d7f1a 100644 --- a/netwerk/base/nsSocketTransport2.cpp +++ b/netwerk/base/nsSocketTransport2.cpp @@ -1988,11 +1988,13 @@ NS_IMPL_ISUPPORTS(nsSocketTransport, nsISocketTransport, nsITransport, nsIDNSListener, - nsIClassInfo) + nsIClassInfo, + nsIInterfaceRequestor) NS_IMPL_CI_INTERFACE_GETTER(nsSocketTransport, nsISocketTransport, nsITransport, - nsIDNSListener) + nsIDNSListener, + nsIInterfaceRequestor) NS_IMETHODIMP nsSocketTransport::OpenInputStream(uint32_t flags, @@ -2419,6 +2421,17 @@ nsSocketTransport::OnLookupComplete(nsICancelable *request, return NS_OK; } +// nsIInterfaceRequestor +NS_IMETHODIMP +nsSocketTransport::GetInterface(const nsIID &iid, void **result) +{ + if (iid.Equals(NS_GET_IID(nsIDNSRecord))) { + return mDNSRecord ? + mDNSRecord->QueryInterface(iid, result) : NS_ERROR_NO_INTERFACE; + } + return this->QueryInterface(iid, result); +} + NS_IMETHODIMP nsSocketTransport::GetInterfaces(uint32_t *count, nsIID * **array) { diff --git a/netwerk/base/nsSocketTransport2.h b/netwerk/base/nsSocketTransport2.h index 4210337efb95..78cebd171821 100644 --- a/netwerk/base/nsSocketTransport2.h +++ b/netwerk/base/nsSocketTransport2.h @@ -14,6 +14,7 @@ #include "nsString.h" #include "nsCOMPtr.h" +#include "nsIInterfaceRequestor.h" #include "nsISocketTransport.h" #include "nsIAsyncInputStream.h" #include "nsIAsyncOutputStream.h" @@ -108,6 +109,7 @@ class nsSocketTransport MOZ_FINAL : public nsASocketHandler , public nsISocketTransport , public nsIDNSListener , public nsIClassInfo + , public nsIInterfaceRequestor { typedef mozilla::Mutex Mutex; @@ -117,6 +119,7 @@ public: NS_DECL_NSISOCKETTRANSPORT NS_DECL_NSIDNSLISTENER NS_DECL_NSICLASSINFO + NS_DECL_NSIINTERFACEREQUESTOR nsSocketTransport(); diff --git a/netwerk/dns/DNSRequestChild.cpp b/netwerk/dns/DNSRequestChild.cpp index 25edefa2e199..45a51c11cadf 100644 --- a/netwerk/dns/DNSRequestChild.cpp +++ b/netwerk/dns/DNSRequestChild.cpp @@ -94,6 +94,13 @@ ChildDNSRecord::GetNextAddr(uint16_t port, NetAddr *addr) return NS_OK; } +NS_IMETHODIMP +ChildDNSRecord::GetAddresses(nsTArray & aAddressArray) +{ + aAddressArray = mAddresses; + return NS_OK; +} + // shamelessly copied from nsDNSRecord NS_IMETHODIMP ChildDNSRecord::GetScriptableNextAddr(uint16_t port, nsINetAddr **result) diff --git a/netwerk/dns/nsDNSService2.cpp b/netwerk/dns/nsDNSService2.cpp index a8330c169658..849c8bae8a90 100644 --- a/netwerk/dns/nsDNSService2.cpp +++ b/netwerk/dns/nsDNSService2.cpp @@ -172,6 +172,46 @@ nsDNSRecord::GetNextAddr(uint16_t port, NetAddr *addr) return NS_OK; } +NS_IMETHODIMP +nsDNSRecord::GetAddresses(nsTArray & aAddressArray) +{ + if (mDone) { + return NS_ERROR_NOT_AVAILABLE; + } + + mHostRecord->addr_info_lock.Lock(); + if (mHostRecord->addr_info) { + for (NetAddrElement *iter = mHostRecord->addr_info->mAddresses.getFirst(); + iter; iter = iter->getNext()) { + if (mHostRecord->Blacklisted(&iter->mAddress)) { + continue; + } + NetAddr *addr = aAddressArray.AppendElement(NetAddr()); + memcpy(addr, &iter->mAddress, sizeof(NetAddr)); + if (addr->raw.family == AF_INET) { + addr->inet.port = 0; + } else if (addr->raw.family == AF_INET6) { + addr->inet6.port = 0; + } + } + mHostRecord->addr_info_lock.Unlock(); + } else { + mHostRecord->addr_info_lock.Unlock(); + + if (!mHostRecord->addr) { + return NS_ERROR_NOT_AVAILABLE; + } + NetAddr *addr = aAddressArray.AppendElement(NetAddr()); + memcpy(addr, mHostRecord->addr, sizeof(NetAddr)); + if (addr->raw.family == AF_INET) { + addr->inet.port = 0; + } else if (addr->raw.family == AF_INET6) { + addr->inet6.port = 0; + } + } + return NS_OK; +} + NS_IMETHODIMP nsDNSRecord::GetScriptableNextAddr(uint16_t port, nsINetAddr * *result) { diff --git a/netwerk/dns/nsIDNSRecord.idl b/netwerk/dns/nsIDNSRecord.idl index eb6959a0e24e..4f446d479c19 100644 --- a/netwerk/dns/nsIDNSRecord.idl +++ b/netwerk/dns/nsIDNSRecord.idl @@ -10,8 +10,10 @@ namespace net { union NetAddr; } } +template class nsTArray; %} native NetAddr(mozilla::net::NetAddr); +[ref] native nsNetAddrTArrayRef(nsTArray); interface nsINetAddr; /** @@ -22,7 +24,7 @@ interface nsINetAddr; * like an enumerator, allowing the caller to easily step through the * list of IP addresses. */ -[scriptable, uuid(95ced6f3-44b4-4427-a149-c9a1e033d852)] +[scriptable, uuid(f92228ae-c417-4188-a604-0830a95e7eb9)] interface nsIDNSRecord : nsISupports { /** @@ -45,6 +47,15 @@ interface nsIDNSRecord : nsISupports */ [noscript] NetAddr getNextAddr(in uint16_t aPort); + /** + * this function copies the value of all working members of the RR + * set into the output array. + * + * @param aAddressArray + * The result set + */ + [noscript] void getAddresses(out nsNetAddrTArrayRef aAddressArray); + /** * this function returns the value of the next IP address as a * scriptable address and increments the internal address iterator. diff --git a/netwerk/protocol/http/ConnectionDiagnostics.cpp b/netwerk/protocol/http/ConnectionDiagnostics.cpp index df490de10de3..174fa3fa25ff 100644 --- a/netwerk/protocol/http/ConnectionDiagnostics.cpp +++ b/netwerk/protocol/http/ConnectionDiagnostics.cpp @@ -70,10 +70,10 @@ nsHttpConnectionMgr::PrintDiagnosticsCB(const nsACString &key, ent->mIdleConns.Length()); self->mLogData.AppendPrintf(" Half Opens Length = %u\n", ent->mHalfOpens.Length()); - self->mLogData.AppendPrintf(" Coalescing Key = %s\n", - ent->mCoalescingKey.get()); + self->mLogData.AppendPrintf(" Coalescing Keys Length = %u\n", + ent->mCoalescingKeys.Length()); self->mLogData.AppendPrintf(" Spdy using = %d, tested = %d, preferred = %d\n", - ent->mUsingSpdy, ent->mTestedSpdy, ent->mSpdyPreferred); + ent->mUsingSpdy, ent->mTestedSpdy, ent->mInPreferredHash); self->mLogData.AppendPrintf(" pipelinestate = %d penalty = %d\n", ent->mPipelineState, ent->mPipeliningPenalty); for (i = 0; i < nsAHttpTransaction::CLASS_MAX; ++i) { @@ -96,7 +96,10 @@ nsHttpConnectionMgr::PrintDiagnosticsCB(const nsACString &key, self->mLogData.AppendPrintf(" :: Pending Transaction #%u\n", i); ent->mPendingQ[i]->PrintDiagnostics(self->mLogData); } - + for (i = 0; i < ent->mCoalescingKeys.Length(); ++i) { + self->mLogData.AppendPrintf(" :: Coalescing Key #%u %s\n", + i, ent->mCoalescingKeys[i].get()); + } return PL_DHASH_NEXT; } diff --git a/netwerk/protocol/http/nsHttpConnectionMgr.cpp b/netwerk/protocol/http/nsHttpConnectionMgr.cpp index 498fd64e4118..99ba429ab04c 100644 --- a/netwerk/protocol/http/nsHttpConnectionMgr.cpp +++ b/netwerk/protocol/http/nsHttpConnectionMgr.cpp @@ -26,6 +26,7 @@ #include "mozilla/Telemetry.h" #include "mozilla/net/DashboardTypes.h" #include "NullHttpTransaction.h" +#include "nsIDNSRecord.h" #include "nsITransport.h" #include "nsISocketTransportService.h" #include @@ -569,6 +570,46 @@ nsHttpConnectionMgr::ClearConnectionHistory() return NS_OK; } + +nsHttpConnectionMgr::nsConnectionEntry * +nsHttpConnectionMgr::LookupPreferredHash(nsHttpConnectionMgr::nsConnectionEntry *ent) +{ + nsConnectionEntry *preferred = nullptr; + uint32_t len = ent->mCoalescingKeys.Length(); + for (uint32_t i = 0; !preferred && (i < len); ++i) { + preferred = mSpdyPreferredHash.Get(ent->mCoalescingKeys[i]); + } + return preferred; +} + +void +nsHttpConnectionMgr::StorePreferredHash(nsHttpConnectionMgr::nsConnectionEntry *ent) +{ + if (ent->mCoalescingKeys.IsEmpty()) { + return; + } + + ent->mInPreferredHash = true; + uint32_t len = ent->mCoalescingKeys.Length(); + for (uint32_t i = 0; i < len; ++i) { + mSpdyPreferredHash.Put(ent->mCoalescingKeys[i], ent); + } +} + +void +nsHttpConnectionMgr::RemovePreferredHash(nsHttpConnectionMgr::nsConnectionEntry *ent) +{ + if (!ent->mInPreferredHash || ent->mCoalescingKeys.IsEmpty()) { + return; + } + + ent->mInPreferredHash = false; + uint32_t len = ent->mCoalescingKeys.Length(); + for (uint32_t i = 0; i < len; ++i) { + mSpdyPreferredHash.Remove(ent->mCoalescingKeys[i]); + } +} + // Given a nsHttpConnectionInfo find the connection entry object that // contains either the nshttpconnection or nshttptransaction parameter. // Normally this is done by the hashkey lookup of connectioninfo, @@ -587,13 +628,13 @@ nsHttpConnectionMgr::LookupConnectionEntry(nsHttpConnectionInfo *ci, // If there is no sign of coalescing (or it is disabled) then just // return the primary hash lookup - if (!ent || !ent->mUsingSpdy || ent->mCoalescingKey.IsEmpty()) + if (!ent || !ent->mUsingSpdy || ent->mCoalescingKeys.IsEmpty()) return ent; // If there is no preferred coalescing entry for this host (or the // preferred entry is the one that matched the mCT hash lookup) then // there is only option - nsConnectionEntry *preferred = mSpdyPreferredHash.Get(ent->mCoalescingKey); + nsConnectionEntry *preferred = LookupPreferredHash(ent); if (!preferred || (preferred == ent)) return ent; @@ -674,18 +715,15 @@ nsHttpConnectionMgr::ReportSpdyConnection(nsHttpConnection *conn, // lookup. Filtering on that has to be done at the time of use // rather than the time of registration (i.e. now). nsConnectionEntry *joinedConnection; - nsConnectionEntry *preferred = - mSpdyPreferredHash.Get(ent->mCoalescingKey); + nsConnectionEntry *preferred = LookupPreferredHash(ent); - LOG(("ReportSpdyConnection %s %s ent=%p preferred=%p\n", - ent->mConnInfo->Host(), ent->mCoalescingKey.get(), - ent, preferred)); + LOG(("ReportSpdyConnection %p,%s prefers %p,%s\n", + ent, ent->mConnInfo->Host(), preferred, + preferred ? preferred->mConnInfo->Host() : "")); if (!preferred) { - if (!ent->mCoalescingKey.IsEmpty()) { - mSpdyPreferredHash.Put(ent->mCoalescingKey, ent); - ent->mSpdyPreferred = true; - } + // this becomes the preferred entry + StorePreferredHash(ent); } else if ((preferred != ent) && (joinedConnection = GetSpdyPreferredEnt(ent)) && (joinedConnection != ent)) { @@ -775,11 +813,11 @@ nsHttpConnectionMgr::GetSpdyPreferredEnt(nsConnectionEntry *aOriginalEntry) { if (!gHttpHandler->IsSpdyEnabled() || !gHttpHandler->CoalesceSpdy() || - aOriginalEntry->mCoalescingKey.IsEmpty()) + aOriginalEntry->mCoalescingKeys.IsEmpty()) { return nullptr; + } - nsConnectionEntry *preferred = - mSpdyPreferredHash.Get(aOriginalEntry->mCoalescingKey); + nsConnectionEntry *preferred = LookupPreferredHash(aOriginalEntry); // if there is no redirection no cert validation is required if (preferred == aOriginalEntry) @@ -807,8 +845,7 @@ nsHttpConnectionMgr::GetSpdyPreferredEnt(nsConnectionEntry *aOriginalEntry) if (!activeSpdy) { // remove the preferred status of this entry if it cannot be // used for pooling. - preferred->mSpdyPreferred = false; - RemoveSpdyPreferredEnt(preferred->mCoalescingKey); + RemovePreferredHash(preferred); LOG(("nsHttpConnectionMgr::GetSpdyPreferredConnection " "preferred host mapping %s to %s removed due to inactivity.\n", aOriginalEntry->mConnInfo->Host(), @@ -872,15 +909,6 @@ nsHttpConnectionMgr::GetSpdyPreferredEnt(nsConnectionEntry *aOriginalEntry) return preferred; } -void -nsHttpConnectionMgr::RemoveSpdyPreferredEnt(nsACString &aHashKey) -{ - if (aHashKey.IsEmpty()) - return; - - mSpdyPreferredHash.Remove(aHashKey); -} - //----------------------------------------------------------------------------- // enumeration callbacks @@ -2749,8 +2777,7 @@ nsHttpConnectionMgr::OnMsgUpdateParam(int32_t, void *param) nsHttpConnectionMgr::nsConnectionEntry::~nsConnectionEntry() { MOZ_COUNT_DTOR(nsConnectionEntry); - if (mSpdyPreferred) - gHttpHandler->ConnMgr()->RemoveSpdyPreferredEnt(mCoalescingKey); + gHttpHandler->ConnMgr()->RemovePreferredHash(this); } void @@ -3469,32 +3496,36 @@ nsHttpConnectionMgr::nsHalfOpenSocket::OnTransportStatus(nsITransport *trans, // just completed. We can't do coalescing if using a proxy because the // ip addresses are not available to the client. - if (status == NS_NET_STATUS_CONNECTED_TO && + if (status == NS_NET_STATUS_CONNECTING_TO && gHttpHandler->IsSpdyEnabled() && gHttpHandler->CoalesceSpdy() && mEnt && mEnt->mConnInfo && mEnt->mConnInfo->EndToEndSSL() && !mEnt->mConnInfo->UsingProxy() && - mEnt->mCoalescingKey.IsEmpty()) { + mEnt->mCoalescingKeys.IsEmpty()) { - NetAddr addr; - nsresult rv = mSocketTransport->GetPeerAddr(&addr); - if (NS_SUCCEEDED(rv)) { - mEnt->mCoalescingKey.SetCapacity(kIPv6CStrBufSize + 26); - NetAddrToString(&addr, mEnt->mCoalescingKey.BeginWriting(), kIPv6CStrBufSize); - mEnt->mCoalescingKey.SetLength( - strlen(mEnt->mCoalescingKey.BeginReading())); - - if (mEnt->mConnInfo->GetAnonymous()) - mEnt->mCoalescingKey.AppendLiteral("~A:"); - else - mEnt->mCoalescingKey.AppendLiteral("~.:"); - mEnt->mCoalescingKey.AppendInt(mEnt->mConnInfo->Port()); - - LOG(("nsHttpConnectionMgr::nsHalfOpenSocket::OnTransportStatus " - "STATUS_CONNECTED_TO Established New Coalescing Key for host " - "%s [%s]", mEnt->mConnInfo->Host(), - mEnt->mCoalescingKey.get())); + nsCOMPtr dnsRecord(do_GetInterface(mSocketTransport)); + nsTArray addressSet; + nsresult rv = NS_ERROR_NOT_AVAILABLE; + if (dnsRecord) { + rv = dnsRecord->GetAddresses(addressSet); + } + if (NS_SUCCEEDED(rv) && !addressSet.IsEmpty()) { + for (uint32_t i = 0; i < addressSet.Length(); ++i) { + nsCString *newKey = mEnt->mCoalescingKeys.AppendElement(nsCString()); + newKey->SetCapacity(kIPv6CStrBufSize + 26); + NetAddrToString(&addressSet[i], newKey->BeginWriting(), kIPv6CStrBufSize); + newKey->SetLength(strlen(newKey->BeginReading())); + if (mEnt->mConnInfo->GetAnonymous()) { + newKey->AppendLiteral("~A:"); + } else { + newKey->AppendLiteral("~.:"); + } + newKey->AppendInt(mEnt->mConnInfo->Port()); + LOG(("nsHttpConnectionMgr::nsHalfOpenSocket::OnTransportStatus " + "STATUS_CONNECTING_TO Established New Coalescing Key # %d for host " + "%s [%s]", i, mEnt->mConnInfo->Host(), newKey->get())); + } gHttpHandler->ConnMgr()->ProcessSpdyPendingQ(mEnt); } } @@ -3585,7 +3616,7 @@ nsConnectionEntry::nsConnectionEntry(nsHttpConnectionInfo *ci) , mSpdyCWND(0) , mUsingSpdy(false) , mTestedSpdy(false) - , mSpdyPreferred(false) + , mInPreferredHash(false) , mPreferIPv4(false) , mPreferIPv6(false) { diff --git a/netwerk/protocol/http/nsHttpConnectionMgr.h b/netwerk/protocol/http/nsHttpConnectionMgr.h index 1fd8bff14e2a..ebecebfe8039 100644 --- a/netwerk/protocol/http/nsHttpConnectionMgr.h +++ b/netwerk/protocol/http/nsHttpConnectionMgr.h @@ -364,7 +364,7 @@ private: // mSpdyPreferred. The mapping is maintained in the connection mananger // mSpdyPreferred hash. // - nsCString mCoalescingKey; + nsTArray mCoalescingKeys; // The value of a recevied SPDY settings type 5 previously received // for this connection entry and the time it was set. @@ -382,7 +382,7 @@ private: // minimized so that we can multiplex on a single spdy connection. bool mTestedSpdy; - bool mSpdyPreferred; + bool mInPreferredHash; // Flags to remember our happy-eyeballs decision. // Reset only by Ctrl-F5 reload. @@ -589,7 +589,9 @@ private: // Manage the preferred spdy connection entry for this address nsConnectionEntry *GetSpdyPreferredEnt(nsConnectionEntry *aOriginalEntry); - void RemoveSpdyPreferredEnt(nsACString &aDottedDecimal); + nsConnectionEntry *LookupPreferredHash(nsConnectionEntry *ent); + void StorePreferredHash(nsConnectionEntry *ent); + void RemovePreferredHash(nsConnectionEntry *ent); nsHttpConnection *GetSpdyPreferredConn(nsConnectionEntry *ent); nsDataHashtable mSpdyPreferredHash; nsConnectionEntry *LookupConnectionEntry(nsHttpConnectionInfo *ci, diff --git a/testing/docker/builder/VERSION b/testing/docker/builder/VERSION index 53a75d673557..0d91a54c7d43 100644 --- a/testing/docker/builder/VERSION +++ b/testing/docker/builder/VERSION @@ -1 +1 @@ -0.2.6 +0.3.0 diff --git a/testing/taskcluster/mach_commands.py b/testing/taskcluster/mach_commands.py index 7b46c9943d73..56c4b20f5436 100644 --- a/testing/taskcluster/mach_commands.py +++ b/testing/taskcluster/mach_commands.py @@ -105,6 +105,8 @@ class DecisionTask(object): @CommandArgument('--revision', required=True, help='Revision for this project') + @CommandArgument('--revision-hash', + help='Treeherder revision hash') @CommandArgument('--comment', required=True, help='Commit message for this revision') @@ -121,6 +123,7 @@ class DecisionTask(object): 'comment': params['comment'], 'url': params['url'], 'revision': params['revision'], + 'revision_hash': params.get('revision_hash', ''), 'owner': params['owner'], 'as_slugid': SlugidJar(), 'from_now': json_time_from_now, diff --git a/testing/taskcluster/tasks/branches/base_job_flags.yml b/testing/taskcluster/tasks/branches/base_job_flags.yml index 58136214304a..d67875c34a3e 100644 --- a/testing/taskcluster/tasks/branches/base_job_flags.yml +++ b/testing/taskcluster/tasks/branches/base_job_flags.yml @@ -11,6 +11,8 @@ flags: - linux64-mulet # Firefox desktop - b2g gecko linux 64 bit - macosx64_gecko # b2g desktop osx 64 bit - win32_gecko # b2g desktop win 32 bit + - flame-kk # b2g flame kitkat + - flame-kk-eng # b2g flame eng build tests: - cppunit diff --git a/testing/taskcluster/tasks/builds/b2g_emulator_ics_debug.yml b/testing/taskcluster/tasks/builds/b2g_emulator_ics_debug.yml index 8750c7987c81..8c2d8326d2a4 100644 --- a/testing/taskcluster/tasks/builds/b2g_emulator_ics_debug.yml +++ b/testing/taskcluster/tasks/builds/b2g_emulator_ics_debug.yml @@ -1,6 +1,7 @@ $inherits: from: 'tasks/builds/b2g_emulator_base.yml' task: + workerType: emulator-ics-debug scopes: - 'docker-worker:cache:build-emulator-ics-debug' metadata: diff --git a/testing/taskcluster/tasks/builds/b2g_emulator_ics_opt.yml b/testing/taskcluster/tasks/builds/b2g_emulator_ics_opt.yml index ebeb95679022..6e0a57f296c8 100644 --- a/testing/taskcluster/tasks/builds/b2g_emulator_ics_opt.yml +++ b/testing/taskcluster/tasks/builds/b2g_emulator_ics_opt.yml @@ -1,6 +1,7 @@ $inherits: from: 'tasks/builds/b2g_emulator_base.yml' task: + workerType: emulator-ics scopes: - 'docker-worker:cache:build-emulator-ics-opt' metadata: diff --git a/testing/taskcluster/tasks/builds/b2g_emulator_jb_debug.yml b/testing/taskcluster/tasks/builds/b2g_emulator_jb_debug.yml index cfcbbc0dba76..0158a64df82e 100644 --- a/testing/taskcluster/tasks/builds/b2g_emulator_jb_debug.yml +++ b/testing/taskcluster/tasks/builds/b2g_emulator_jb_debug.yml @@ -1,6 +1,7 @@ $inherits: from: 'tasks/builds/b2g_emulator_base.yml' task: + workerType: emulator-jb-debug scopes: - 'docker-worker:cache:build-emulator-jb-debug' metadata: diff --git a/testing/taskcluster/tasks/builds/b2g_emulator_jb_opt.yml b/testing/taskcluster/tasks/builds/b2g_emulator_jb_opt.yml index a396c4587889..44db1ff35d8c 100644 --- a/testing/taskcluster/tasks/builds/b2g_emulator_jb_opt.yml +++ b/testing/taskcluster/tasks/builds/b2g_emulator_jb_opt.yml @@ -1,6 +1,7 @@ $inherits: from: 'tasks/builds/b2g_emulator_base.yml' task: + workerType: emulator-jb scopes: - 'docker-worker:cache:build-emulator-jb-opt' metadata: diff --git a/testing/taskcluster/tasks/builds/b2g_emulator_kk_debug.yml b/testing/taskcluster/tasks/builds/b2g_emulator_kk_debug.yml index c826e4367194..0d412ea0cdc5 100644 --- a/testing/taskcluster/tasks/builds/b2g_emulator_kk_debug.yml +++ b/testing/taskcluster/tasks/builds/b2g_emulator_kk_debug.yml @@ -1,7 +1,7 @@ $inherits: from: 'tasks/builds/b2g_emulator_base.yml' task: - workerType: b2gbuild-emulator-kk + workerType: emulator-kk-debug scopes: - 'docker-worker:cache:build-emulator-kk-debug' metadata: diff --git a/testing/taskcluster/tasks/builds/b2g_emulator_kk_opt.yml b/testing/taskcluster/tasks/builds/b2g_emulator_kk_opt.yml index 67e05364d221..060e8e3c3cd4 100644 --- a/testing/taskcluster/tasks/builds/b2g_emulator_kk_opt.yml +++ b/testing/taskcluster/tasks/builds/b2g_emulator_kk_opt.yml @@ -1,7 +1,7 @@ $inherits: from: 'tasks/builds/b2g_emulator_base.yml' task: - workerType: b2gbuild-emulator-kk + workerType: emulator-kk scopes: - 'docker-worker:cache:build-emulator-kk-opt' metadata: diff --git a/testing/taskcluster/tasks/decision/branch.yml b/testing/taskcluster/tasks/decision/branch.yml index 50a53b30e2be..48455c8a0e2a 100644 --- a/testing/taskcluster/tasks/decision/branch.yml +++ b/testing/taskcluster/tasks/decision/branch.yml @@ -7,27 +7,11 @@ metadata: source: "{{source}}" scopes: - - "docker-worker:image:quay.io/mozilla/decision:*" - - "queue:define-task:aws-provisioner/gecko-decision" - - "queue:create-task:aws-provisioner/gecko-decision" - - "docker-worker:cache:tc-vcs-public-sources" - - "docker-worker:cache:build-emulator-jb-opt" - - "docker-worker:cache:build-mulet-linux-objects" - - "docker-worker:cache:build-emulator-ics-opt" - - "queue:define-task:aws-provisioner/b2gtest" - - "queue:create-task:aws-provisioner/b2gtest" - - "docker-worker:image:quay.io/mozilla/builder:*" - - "docker-worker:cache:tooltool-cache" - - "queue:define-task:aws-provisioner/b2gbuild" - - "queue:create-task:aws-provisioner/b2gbuild" - - "docker-worker:cache:build-emulator-kk-debug" - - "docker-worker:cache:build-b2g-desktop-objects" - - "docker-worker:cache:build-emulator-kk-opt" - - "docker-worker:cache:build-emulator-jb-debug" - - "docker-worker:cache:tc-vcs" - - "docker-worker:cache:sources-gecko" - - "docker-worker:cache:sources-gaia" - - "docker-worker:cache:build-emulator-ics-debug" + # Note the below scopes are insecure however these get overriden on the server + # side to whatever scopes are set by mozilla-taskcluster. + - queue:* + - docker-worker:* + - scheduler:* tasks: - taskId: '{{#as_slugid}}decision task{{/as_slugid}}' task: @@ -46,7 +30,7 @@ tasks: scopes: - "docker-worker:cache:tc-vcs-public-sources" - - "docker-worker:image:quay.io/mozilla/decision:0.0.3" + - "docker-worker:image:quay.io/mozilla/builder:0.3.0" payload: env: @@ -63,7 +47,7 @@ tasks: # Note: This task is built server side without the context or tooling that # exist in tree so we must hard code the version - image: 'quay.io/mozilla/decision:0.0.3' + image: 'quay.io/mozilla/builder:0.3.0' # Virtually no network or other potentially risky operations happen as part # of the task timeout aside from the initial clone. We intentionally have @@ -75,11 +59,12 @@ tasks: - /bin/bash - -cx - > - source $(which entrypoint) && + source ./bin/decision.sh && ./mach taskcluster-graph --message='{{comment}}' --project='{{project}}' --owner='{{owner}}' + --revision-hash='{{revision_hash}}' --extend-graph > /home/worker/graph.json graphs: - /home/worker/graph.json diff --git a/testing/taskcluster/tasks/test.yml b/testing/taskcluster/tasks/test.yml index 2a76e94753e5..2bc4cf0617c5 100644 --- a/testing/taskcluster/tasks/test.yml +++ b/testing/taskcluster/tasks/test.yml @@ -5,7 +5,7 @@ task: metadata: source: http://todo.com/soon owner: {{owner}} - workerType: test-c4-2xlarge + workerType: b2gtest provisionerId: aws-provisioner schedulerId: task-graph-scheduler diff --git a/testing/testsuite-targets.mk b/testing/testsuite-targets.mk index 3793c3d7595a..ae6b8f402f44 100644 --- a/testing/testsuite-targets.mk +++ b/testing/testsuite-targets.mk @@ -419,7 +419,7 @@ package-tests: ifndef UNIVERSAL_BINARY $(NSINSTALL) -D $(DIST)/$(PKG_PATH) endif - find $(PKG_STAGE) -name '*.pyc' -exec rm {} \; + find -L $(PKG_STAGE) -name '*.pyc' -exec rm {} \; $(MKDIR) -p $(abspath $(DIST))/$(PKG_PATH) && \ cd $(PKG_STAGE) && \ zip -rq9D '$(abspath $(DIST))/$(PKG_PATH)$(TEST_PACKAGE)' \ diff --git a/testing/web-platform/mach_commands.py b/testing/web-platform/mach_commands.py index 51b3bb7b604c..1c4e80344933 100644 --- a/testing/web-platform/mach_commands.py +++ b/testing/web-platform/mach_commands.py @@ -89,7 +89,7 @@ class WebPlatformTestsUpdater(MozbuildObject): if kwargs["config"] is None: kwargs["config"] = os.path.join(self.topsrcdir, 'testing', 'web-platform', 'wptrunner.ini') updatecommandline.check_args(kwargs) - logger = update.setup_logging(kwargs, {}) + logger = update.setup_logging(kwargs, {"mach": sys.stdout}) try: update.run_update(logger, **kwargs) diff --git a/testing/web-platform/update/__init__.py b/testing/web-platform/update/__init__.py index 0dbacf525df9..0cd536d4c353 100644 --- a/testing/web-platform/update/__init__.py +++ b/testing/web-platform/update/__init__.py @@ -27,7 +27,7 @@ def run_update(logger, **kwargs): if __name__ == "__main__": args = updatecommandline.parse_args() - logger = setup_logging(args, {}) + logger = setup_logging(args, {"mach": sys.stdout}) assert structuredlog.get_default_logger() is not None diff --git a/testing/web-platform/update/updatecommandline.py b/testing/web-platform/update/updatecommandline.py index 4966b5ad2a9f..e26ee5845234 100644 --- a/testing/web-platform/update/updatecommandline.py +++ b/testing/web-platform/update/updatecommandline.py @@ -6,8 +6,10 @@ def create_parser(): from wptrunner import wptcommandline parser = wptcommandline.create_parser_update() - parser.add_argument("--upstream", action="store_true", - help="Push local changes to upstream repository") + parser.add_argument("--upstream", dest="upstream", action="store_true", default=None, + help="Push local changes to upstream repository even when not syncing") + parser.add_argument("--no-upstream", dest="upstream", action="store_false", default=None, + help="Dont't push local changes to upstream repository when syncing") parser.add_argument("--token-file", action="store", type=wptcommandline.abs_path, help="Path to file containing github token") parser.add_argument("--token", action="store", help="GitHub token to use") @@ -18,6 +20,8 @@ def check_args(kwargs): from wptrunner import wptcommandline wptcommandline.set_from_config(kwargs) + kwargs["upstream"] = kwargs["upstream"] if kwargs["upstream"] is not None else kwargs["sync"] + if kwargs["upstream"]: if kwargs["rev"]: raise ValueError("Setting --rev with --upstream isn't supported") diff --git a/testing/web-platform/update/upstream.py b/testing/web-platform/update/upstream.py index d509d6ab62f3..a36daae23d9f 100644 --- a/testing/web-platform/update/upstream.py +++ b/testing/web-platform/update/upstream.py @@ -1,4 +1,5 @@ import os +import re import subprocess import sys import urlparse @@ -7,7 +8,7 @@ from wptrunner.update.sync import LoadManifest from wptrunner.update.tree import get_unique_name from wptrunner.update.base import Step, StepRunner, exit_clean, exit_unclean -from .tree import GitTree, Patch +from .tree import Commit, GitTree, Patch import github from .github import GitHub @@ -29,7 +30,7 @@ def rewrite_patch(patch, strip_dir): for line in patch.diff.split("\n"): for start in line_starts: if line.startswith(start): - new_diff.append(line.replace(strip_dir, "")) + new_diff.append(line.replace(strip_dir, "").encode("utf8")) break else: new_diff.append(line) @@ -101,14 +102,20 @@ class CheckoutBranch(Step): class GetLastSyncCommit(Step): """Find the gecko commit at which we last performed a sync with upstream.""" - provides = ["last_sync_commit"] + provides = ["last_sync_path", "last_sync_commit"] def create(self, state): self.logger.info("Looking for last sync commit") - state.last_sync_commit = state.local_tree.commits_by_message(state.test_manifest.rev, - os.path.join(state.local_tree.root, - "testing", - "web-platform"))[-1] + state.last_sync_path = os.path.join(state.metadata_path, "mozilla-sync") + with open(state.last_sync_path) as f: + last_sync_sha1 = f.read().strip() + + state.last_sync_commit = Commit(state.local_tree, last_sync_sha1) + + if not state.local_tree.contains_commit(state.last_sync_commit): + self.logger.error("Could not find last sync commit %s" % last_sync_sha1) + return exit_clean + self.logger.info("Last sync to web-platform-tests happened in %s" % state.last_sync_commit.sha1) @@ -132,14 +139,23 @@ class LoadCommits(Step): state.source_commits = state.local_tree.log(state.last_sync_commit, state.tests_path) - for i, commit in enumerate(state.source_commits): + update_regexp = re.compile("Bug \d+ - Update web-platform-tests to revision [0-9a-f]{40}") + + for i, commit in enumerate(state.source_commits[:]): + if update_regexp.match(commit.message.text): + # This is a previous update commit so ignore it + state.source_commits.remove(commit) + continue + if commit.message.backouts: #TODO: Add support for collapsing backouts raise NotImplementedError("Need to get the Git->Hg commits for backouts and remove the backed out patch") + if not commit.message.bug: self.logger.error("Commit %i (%s) doesn't have an associated bug number." % (i + 1, commit.sha1)) return exit_unclean + self.logger.debug("Source commits: %s" % state.source_commits) class MovePatches(Step): @@ -227,6 +243,17 @@ class MergeUpstream(Step): return rv state.merge_index += 1 +class UpdateLastSyncCommit(Step): + """Update the gecko commit at which we last performed a sync with upstream.""" + + provides = [] + + def create(self, state): + self.logger.info("Updating last sync commit") + with open(state.last_sync_path, "w") as f: + f.write(state.local_tree.rev) + # This gets added to the patch later on + class MergeLocalBranch(Step): """Create a local branch pointing at the commit to upstream""" @@ -305,7 +332,8 @@ class SyncToUpstreamRunner(StepRunner): MovePatches, RebaseCommits, CheckRebase, - MergeUpstream] + MergeUpstream, + UpdateLastSyncCommit] class PRMergeRunner(StepRunner): diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json index 470da09838cc..1b1625809e25 100644 --- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -2431,6 +2431,21 @@ "extended_statistics_ok": true, "description": "How many speculative connections are made needlessly" }, + "URL_PATH_ENDS_IN_EXCLAMATION": { + "expires_in_version": "never", + "kind": "boolean", + "description": "The URL path ends in !" + }, + "URL_PATH_CONTAINS_EXCLAMATION_SLASH": { + "expires_in_version": "never", + "kind": "boolean", + "description": "The URL path contains !/" + }, + "URL_PATH_CONTAINS_EXCLAMATION_DOUBLE_SLASH": { + "expires_in_version": "never", + "kind": "boolean", + "description": "The URL path contains !//" + }, "FIND_PLUGINS": { "alert_emails": ["perf-telemetry-alerts@mozilla.com"], "expires_in_version": "40", @@ -7358,5 +7373,17 @@ "expires_in_version": "45", "kind": "count", "description": "The number of Sync 1.5 migrations completed by Android Sync." + }, + "SLOW_SCRIPT_NOTICE_COUNT": { + "alert_emails": ["perf-telemetry-alerts@mozilla.com"], + "expires_in_version": "never", + "kind": "count", + "description": "Count slow script notices" + }, + "PLUGIN_HANG_NOTICE_COUNT": { + "alert_emails": ["perf-telemetry-alerts@mozilla.com"], + "expires_in_version": "never", + "kind": "count", + "description": "Count plugin hang notices in e10s" } } diff --git a/toolkit/components/telemetry/gen-histogram-enum.py b/toolkit/components/telemetry/gen-histogram-enum.py index 9a473ca03266..821a144602d4 100644 --- a/toolkit/components/telemetry/gen-histogram-enum.py +++ b/toolkit/components/telemetry/gen-histogram-enum.py @@ -16,7 +16,7 @@ def main(argv): filename = argv[0] print banner - print "enum ID {" + print "enum ID : uint32_t {" for histogram in histogram_tools.from_file(filename): cpp_guard = histogram.cpp_guard() if cpp_guard: diff --git a/widget/android/GfxInfo.cpp b/widget/android/GfxInfo.cpp index 95bd0d409cd8..4ab9da311ede 100644 --- a/widget/android/GfxInfo.cpp +++ b/widget/android/GfxInfo.cpp @@ -72,8 +72,8 @@ public: } nsRefPtr gl; - bool requireCompatProfile = true; - gl = gl::GLContextProvider::CreateHeadless(requireCompatProfile); + gl = gl::GLContextProvider::CreateOffscreen(gfxIntSize(16, 16), + gl::SurfaceCaps::ForRGB()); if (!gl) { // Setting mReady to true here means that we won't retry. Everything will diff --git a/xpcom/base/CycleCollectedJSRuntime.cpp b/xpcom/base/CycleCollectedJSRuntime.cpp index cbfedce9aaa9..3094af949952 100644 --- a/xpcom/base/CycleCollectedJSRuntime.cpp +++ b/xpcom/base/CycleCollectedJSRuntime.cpp @@ -1134,7 +1134,7 @@ void IncrementalFinalizeRunnable::ReleaseNow(bool aLimited) { if (mReleasing) { - MOZ_ASSERT(false, "Try to avoid re-entering ReleaseNow!"); + NS_WARNING("Re-entering ReleaseNow"); return; } {