LCOV - code coverage report
Current view: top level - xpcom/base - CycleCollectedJSContext.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 108 184 58.7 %
Date: 2017-07-14 16:53:18 Functions: 19 27 70.4 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "mozilla/CycleCollectedJSContext.h"
       8             : #include <algorithm>
       9             : #include "mozilla/ArrayUtils.h"
      10             : #include "mozilla/AutoRestore.h"
      11             : #include "mozilla/CycleCollectedJSRuntime.h"
      12             : #include "mozilla/Move.h"
      13             : #include "mozilla/MemoryReporting.h"
      14             : #include "mozilla/Sprintf.h"
      15             : #include "mozilla/Telemetry.h"
      16             : #include "mozilla/TimelineConsumers.h"
      17             : #include "mozilla/TimelineMarker.h"
      18             : #include "mozilla/Unused.h"
      19             : #include "mozilla/DebuggerOnGCRunnable.h"
      20             : #include "mozilla/dom/DOMJSClass.h"
      21             : #include "mozilla/dom/ProfileTimelineMarkerBinding.h"
      22             : #include "mozilla/dom/Promise.h"
      23             : #include "mozilla/dom/PromiseBinding.h"
      24             : #include "mozilla/dom/PromiseDebugging.h"
      25             : #include "mozilla/dom/ScriptSettings.h"
      26             : #include "jsprf.h"
      27             : #include "js/Debug.h"
      28             : #include "js/GCAPI.h"
      29             : #include "js/Utility.h"
      30             : #include "nsContentUtils.h"
      31             : #include "nsCycleCollectionNoteRootCallback.h"
      32             : #include "nsCycleCollectionParticipant.h"
      33             : #include "nsCycleCollector.h"
      34             : #include "nsDOMJSUtils.h"
      35             : #include "nsJSUtils.h"
      36             : #include "nsWrapperCache.h"
      37             : #include "nsStringBuffer.h"
      38             : 
      39             : #ifdef MOZ_CRASHREPORTER
      40             : #include "nsExceptionHandler.h"
      41             : #endif
      42             : 
      43             : #include "nsIException.h"
      44             : #include "nsIPlatformInfo.h"
      45             : #include "nsThread.h"
      46             : #include "nsThreadUtils.h"
      47             : #include "xpcpublic.h"
      48             : 
      49             : using namespace mozilla;
      50             : using namespace mozilla::dom;
      51             : 
      52             : namespace mozilla {
      53             : 
      54           4 : CycleCollectedJSContext::CycleCollectedJSContext()
      55             :   : mIsPrimaryContext(true)
      56             :   , mRuntime(nullptr)
      57             :   , mJSContext(nullptr)
      58             :   , mDoingStableStates(false)
      59           4 :   , mDisableMicroTaskCheckpoint(false)
      60             : {
      61           4 :   MOZ_COUNT_CTOR(CycleCollectedJSContext);
      62           8 :   nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
      63           4 :   mOwningThread = thread.forget().downcast<nsThread>().take();
      64           4 :   MOZ_RELEASE_ASSERT(mOwningThread);
      65           4 : }
      66             : 
      67           0 : CycleCollectedJSContext::~CycleCollectedJSContext()
      68             : {
      69           0 :   MOZ_COUNT_DTOR(CycleCollectedJSContext);
      70             :   // If the allocation failed, here we are.
      71           0 :   if (!mJSContext) {
      72           0 :     return;
      73             :   }
      74             : 
      75           0 :   mRuntime->RemoveContext(this);
      76             : 
      77           0 :   if (mIsPrimaryContext) {
      78           0 :     mRuntime->Shutdown(mJSContext);
      79             :   }
      80             : 
      81             :   // Last chance to process any events.
      82           0 :   ProcessMetastableStateQueue(mBaseRecursionDepth);
      83           0 :   MOZ_ASSERT(mMetastableStateEvents.IsEmpty());
      84             : 
      85           0 :   ProcessStableStateQueue();
      86           0 :   MOZ_ASSERT(mStableStateEvents.IsEmpty());
      87             : 
      88             :   // Clear mPendingException first, since it might be cycle collected.
      89           0 :   mPendingException = nullptr;
      90             : 
      91           0 :   MOZ_ASSERT(mDebuggerPromiseMicroTaskQueue.empty());
      92           0 :   MOZ_ASSERT(mPromiseMicroTaskQueue.empty());
      93             : 
      94           0 :   mUncaughtRejections.reset();
      95           0 :   mConsumedRejections.reset();
      96             : 
      97           0 :   JS_DestroyContext(mJSContext);
      98           0 :   mJSContext = nullptr;
      99             : 
     100           0 :   if (mIsPrimaryContext) {
     101           0 :     nsCycleCollector_forgetJSContext();
     102             :   } else {
     103           0 :     nsCycleCollector_forgetNonPrimaryContext();
     104             :   }
     105             : 
     106           0 :   mozilla::dom::DestroyScriptSettings();
     107             : 
     108           0 :   mOwningThread->SetScriptObserver(nullptr);
     109           0 :   NS_RELEASE(mOwningThread);
     110             : 
     111           0 :   if (mIsPrimaryContext) {
     112           0 :     delete mRuntime;
     113             :   }
     114           0 :   mRuntime = nullptr;
     115           0 : }
     116             : 
     117             : void
     118           4 : CycleCollectedJSContext::InitializeCommon()
     119             : {
     120           4 :   mRuntime->AddContext(this);
     121             : 
     122           4 :   mOwningThread->SetScriptObserver(this);
     123             :   // The main thread has a base recursion depth of 0, workers of 1.
     124           4 :   mBaseRecursionDepth = RecursionDepth();
     125             : 
     126           4 :   NS_GetCurrentThread()->SetCanInvokeJS(true);
     127             : 
     128           4 :   JS::SetGetIncumbentGlobalCallback(mJSContext, GetIncumbentGlobalCallback);
     129             : 
     130           4 :   JS::SetEnqueuePromiseJobCallback(mJSContext, EnqueuePromiseJobCallback, this);
     131           4 :   JS::SetPromiseRejectionTrackerCallback(mJSContext, PromiseRejectionTrackerCallback, this);
     132           4 :   mUncaughtRejections.init(mJSContext, JS::GCVector<JSObject*, 0, js::SystemAllocPolicy>(js::SystemAllocPolicy()));
     133           4 :   mConsumedRejections.init(mJSContext, JS::GCVector<JSObject*, 0, js::SystemAllocPolicy>(js::SystemAllocPolicy()));
     134           4 : }
     135             : 
     136             : nsresult
     137           4 : CycleCollectedJSContext::Initialize(JSRuntime* aParentRuntime,
     138             :                                     uint32_t aMaxBytes,
     139             :                                     uint32_t aMaxNurseryBytes)
     140             : {
     141           4 :   MOZ_ASSERT(!mJSContext);
     142             : 
     143           4 :   mozilla::dom::InitScriptSettings();
     144           4 :   mJSContext = JS_NewContext(aMaxBytes, aMaxNurseryBytes, aParentRuntime);
     145           4 :   if (!mJSContext) {
     146           0 :     return NS_ERROR_OUT_OF_MEMORY;
     147             :   }
     148             : 
     149           4 :   mRuntime = CreateRuntime(mJSContext);
     150             : 
     151           4 :   InitializeCommon();
     152             : 
     153           4 :   nsCycleCollector_registerJSContext(this);
     154             : 
     155           4 :   return NS_OK;
     156             : }
     157             : 
     158             : nsresult
     159           0 : CycleCollectedJSContext::InitializeNonPrimary(CycleCollectedJSContext* aPrimaryContext)
     160             : {
     161           0 :   MOZ_ASSERT(!mJSContext);
     162             : 
     163           0 :   mIsPrimaryContext = false;
     164             : 
     165           0 :   mozilla::dom::InitScriptSettings();
     166           0 :   mJSContext = JS_NewCooperativeContext(aPrimaryContext->mJSContext);
     167           0 :   if (!mJSContext) {
     168           0 :     return NS_ERROR_OUT_OF_MEMORY;
     169             :   }
     170             : 
     171           0 :   mRuntime = aPrimaryContext->mRuntime;
     172             : 
     173           0 :   InitializeCommon();
     174             : 
     175           0 :   nsCycleCollector_registerNonPrimaryContext(this);
     176             : 
     177           0 :   return NS_OK;
     178             : }
     179             : 
     180             : size_t
     181           0 : CycleCollectedJSContext::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
     182             : {
     183           0 :   return 0;
     184             : }
     185             : 
     186             : class PromiseJobRunnable final : public Runnable
     187             : {
     188             : public:
     189         235 :   PromiseJobRunnable(JS::HandleObject aCallback,
     190             :                      JS::HandleObject aAllocationSite,
     191             :                      nsIGlobalObject* aIncumbentGlobal)
     192         235 :     : Runnable("PromiseJobRunnable")
     193             :     , mCallback(
     194         470 :         new PromiseJobCallback(aCallback, aAllocationSite, aIncumbentGlobal))
     195             :   {
     196         235 :   }
     197             : 
     198         470 :   virtual ~PromiseJobRunnable()
     199         235 :   {
     200         705 :   }
     201             : 
     202             : protected:
     203             :   NS_IMETHOD
     204         235 :   Run() override
     205             :   {
     206         235 :     JSObject* callback = mCallback->CallbackPreserveColor();
     207         235 :     nsIGlobalObject* global = callback ? xpc::NativeGlobal(callback) : nullptr;
     208         235 :     if (global && !global->IsDying()) {
     209         235 :       mCallback->Call("promise callback");
     210             :     }
     211         235 :     return NS_OK;
     212             :   }
     213             : 
     214             : private:
     215             :   RefPtr<PromiseJobCallback> mCallback;
     216             : };
     217             : 
     218             : /* static */
     219             : JSObject*
     220         427 : CycleCollectedJSContext::GetIncumbentGlobalCallback(JSContext* aCx)
     221             : {
     222         427 :   nsIGlobalObject* global = mozilla::dom::GetIncumbentGlobal();
     223         427 :   if (global) {
     224         427 :     return global->GetGlobalJSObject();
     225             :   }
     226           0 :   return nullptr;
     227             : }
     228             : 
     229             : /* static */
     230             : bool
     231         235 : CycleCollectedJSContext::EnqueuePromiseJobCallback(JSContext* aCx,
     232             :                                                    JS::HandleObject aJob,
     233             :                                                    JS::HandleObject aAllocationSite,
     234             :                                                    JS::HandleObject aIncumbentGlobal,
     235             :                                                    void* aData)
     236             : {
     237         235 :   CycleCollectedJSContext* self = static_cast<CycleCollectedJSContext*>(aData);
     238         235 :   MOZ_ASSERT(aCx == self->Context());
     239         235 :   MOZ_ASSERT(Get() == self);
     240             : 
     241         235 :   nsIGlobalObject* global = nullptr;
     242         235 :   if (aIncumbentGlobal) {
     243         235 :     global = xpc::NativeGlobal(aIncumbentGlobal);
     244             :   }
     245         705 :   nsCOMPtr<nsIRunnable> runnable = new PromiseJobRunnable(aJob, aAllocationSite, global);
     246         235 :   self->DispatchToMicroTask(runnable.forget());
     247         470 :   return true;
     248             : }
     249             : 
     250             : /* static */
     251             : void
     252           0 : CycleCollectedJSContext::PromiseRejectionTrackerCallback(JSContext* aCx,
     253             :                                                          JS::HandleObject aPromise,
     254             :                                                          PromiseRejectionHandlingState state,
     255             :                                                          void* aData)
     256             : {
     257             : #ifdef DEBUG
     258           0 :   CycleCollectedJSContext* self = static_cast<CycleCollectedJSContext*>(aData);
     259             : #endif // DEBUG
     260           0 :   MOZ_ASSERT(aCx == self->Context());
     261           0 :   MOZ_ASSERT(Get() == self);
     262             : 
     263           0 :   if (state == PromiseRejectionHandlingState::Unhandled) {
     264           0 :     PromiseDebugging::AddUncaughtRejection(aPromise);
     265             :   } else {
     266           0 :     PromiseDebugging::AddConsumedRejection(aPromise);
     267             :   }
     268           0 : }
     269             : 
     270             : already_AddRefed<nsIException>
     271        3957 : CycleCollectedJSContext::GetPendingException() const
     272             : {
     273        3957 :   MOZ_ASSERT(mJSContext);
     274             : 
     275        7914 :   nsCOMPtr<nsIException> out = mPendingException;
     276        7914 :   return out.forget();
     277             : }
     278             : 
     279             : void
     280       15339 : CycleCollectedJSContext::SetPendingException(nsIException* aException)
     281             : {
     282       15339 :   MOZ_ASSERT(mJSContext);
     283       15339 :   mPendingException = aException;
     284       15339 : }
     285             : 
     286             : std::queue<nsCOMPtr<nsIRunnable>>&
     287        1324 : CycleCollectedJSContext::GetPromiseMicroTaskQueue()
     288             : {
     289        1324 :   MOZ_ASSERT(mJSContext);
     290        1324 :   return mPromiseMicroTaskQueue;
     291             : }
     292             : 
     293             : std::queue<nsCOMPtr<nsIRunnable>>&
     294           0 : CycleCollectedJSContext::GetDebuggerPromiseMicroTaskQueue()
     295             : {
     296           0 :   MOZ_ASSERT(mJSContext);
     297           0 :   return mDebuggerPromiseMicroTaskQueue;
     298             : }
     299             : 
     300             : void
     301        1258 : CycleCollectedJSContext::ProcessStableStateQueue()
     302             : {
     303        1258 :   MOZ_ASSERT(mJSContext);
     304        1258 :   MOZ_RELEASE_ASSERT(!mDoingStableStates);
     305        1258 :   mDoingStableStates = true;
     306             : 
     307        1258 :   for (uint32_t i = 0; i < mStableStateEvents.Length(); ++i) {
     308           0 :     nsCOMPtr<nsIRunnable> event = mStableStateEvents[i].forget();
     309           0 :     event->Run();
     310             :   }
     311             : 
     312        1258 :   mStableStateEvents.Clear();
     313        1258 :   mDoingStableStates = false;
     314        1258 : }
     315             : 
     316             : void
     317        1495 : CycleCollectedJSContext::ProcessMetastableStateQueue(uint32_t aRecursionDepth)
     318             : {
     319        1495 :   MOZ_ASSERT(mJSContext);
     320        1495 :   MOZ_RELEASE_ASSERT(!mDoingStableStates);
     321        1495 :   mDoingStableStates = true;
     322             : 
     323        2990 :   nsTArray<RunInMetastableStateData> localQueue = Move(mMetastableStateEvents);
     324             : 
     325        1495 :   for (uint32_t i = 0; i < localQueue.Length(); ++i)
     326             :   {
     327           0 :     RunInMetastableStateData& data = localQueue[i];
     328           0 :     if (data.mRecursionDepth != aRecursionDepth) {
     329           0 :       continue;
     330             :     }
     331             : 
     332             :     {
     333           0 :       nsCOMPtr<nsIRunnable> runnable = data.mRunnable.forget();
     334           0 :       runnable->Run();
     335             :     }
     336             : 
     337           0 :     localQueue.RemoveElementAt(i--);
     338             :   }
     339             : 
     340             :   // If the queue has events in it now, they were added from something we called,
     341             :   // so they belong at the end of the queue.
     342        1495 :   localQueue.AppendElements(mMetastableStateEvents);
     343        1495 :   localQueue.SwapElements(mMetastableStateEvents);
     344        1495 :   mDoingStableStates = false;
     345        1495 : }
     346             : 
     347             : void
     348        1258 : CycleCollectedJSContext::AfterProcessTask(uint32_t aRecursionDepth)
     349             : {
     350        1258 :   MOZ_ASSERT(mJSContext);
     351             : 
     352             :   // See HTML 6.1.4.2 Processing model
     353             : 
     354             :   // Execute any events that were waiting for a microtask to complete.
     355             :   // This is not (yet) in the spec.
     356        1258 :   ProcessMetastableStateQueue(aRecursionDepth);
     357             : 
     358             :   // Step 4.1: Execute microtasks.
     359        1258 :   if (!mDisableMicroTaskCheckpoint) {
     360        1227 :     if (NS_IsMainThread()) {
     361        1227 :       nsContentUtils::PerformMainThreadMicroTaskCheckpoint();
     362        1227 :       Promise::PerformMicroTaskCheckpoint();
     363             :     } else {
     364           0 :       Promise::PerformWorkerMicroTaskCheckpoint();
     365             :     }
     366             :   }
     367             : 
     368             :   // Step 4.2 Execute any events that were waiting for a stable state.
     369        1258 :   ProcessStableStateQueue();
     370        1258 : }
     371             : 
     372             : void
     373         237 : CycleCollectedJSContext::AfterProcessMicrotask()
     374             : {
     375         237 :   MOZ_ASSERT(mJSContext);
     376         237 :   AfterProcessMicrotask(RecursionDepth());
     377         237 : }
     378             : 
     379             : void
     380         237 : CycleCollectedJSContext::AfterProcessMicrotask(uint32_t aRecursionDepth)
     381             : {
     382         237 :   MOZ_ASSERT(mJSContext);
     383             : 
     384             :   // Between microtasks, execute any events that were waiting for a microtask
     385             :   // to complete.
     386         237 :   ProcessMetastableStateQueue(aRecursionDepth);
     387         237 : }
     388             : 
     389             : uint32_t
     390         307 : CycleCollectedJSContext::RecursionDepth()
     391             : {
     392         307 :   return mOwningThread->RecursionDepth();
     393             : }
     394             : 
     395             : void
     396           0 : CycleCollectedJSContext::RunInStableState(already_AddRefed<nsIRunnable>&& aRunnable)
     397             : {
     398           0 :   MOZ_ASSERT(mJSContext);
     399           0 :   mStableStateEvents.AppendElement(Move(aRunnable));
     400           0 : }
     401             : 
     402             : void
     403           0 : CycleCollectedJSContext::RunInMetastableState(already_AddRefed<nsIRunnable>&& aRunnable)
     404             : {
     405           0 :   MOZ_ASSERT(mJSContext);
     406             : 
     407           0 :   RunInMetastableStateData data;
     408           0 :   data.mRunnable = aRunnable;
     409             : 
     410           0 :   MOZ_ASSERT(mOwningThread);
     411           0 :   data.mRecursionDepth = RecursionDepth();
     412             : 
     413             :   // There must be an event running to get here.
     414             : #ifndef MOZ_WIDGET_COCOA
     415           0 :   MOZ_ASSERT(data.mRecursionDepth > mBaseRecursionDepth);
     416             : #else
     417             :   // XXX bug 1261143
     418             :   // Recursion depth should be greater than mBaseRecursionDepth,
     419             :   // or the runnable will stay in the queue forever.
     420             :   if (data.mRecursionDepth <= mBaseRecursionDepth) {
     421             :     data.mRecursionDepth = mBaseRecursionDepth + 1;
     422             :   }
     423             : #endif
     424             : 
     425           0 :   mMetastableStateEvents.AppendElement(Move(data));
     426           0 : }
     427             : 
     428             : void
     429         237 : CycleCollectedJSContext::DispatchToMicroTask(already_AddRefed<nsIRunnable> aRunnable)
     430             : {
     431         474 :   RefPtr<nsIRunnable> runnable(aRunnable);
     432             : 
     433         237 :   MOZ_ASSERT(NS_IsMainThread());
     434         237 :   MOZ_ASSERT(runnable);
     435             : 
     436         237 :   mPromiseMicroTaskQueue.push(runnable.forget());
     437         237 : }
     438             : 
     439             : } // namespace mozilla

Generated by: LCOV version 1.13