Bug 1438121: Final Part 1: Change JS::CopyAsyncStack's maxFrameCount to be a Maybe, and use that type throughout. r=tromey
authorJim Blandy <jimb@mozilla.com>
Tue, 06 Mar 2018 21:52:31 -0800
changeset 407432 b621eecdca37582f8bc6872e7ed096be85203334
parent 407431 1976ff94b12a14e32cac357c1ef2cf7caca8ff7e
child 407433 f54675bcf897379cd4f81cad3b80f812b9f2a903
push id100691
push userjblandy@mozilla.com
push dateSat, 10 Mar 2018 02:26:54 +0000
treeherdermozilla-inbound@1057c4246ffa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstromey
bugs1438121
milestone60.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1438121: Final Part 1: Change JS::CopyAsyncStack's maxFrameCount to be a Maybe, and use that type throughout. r=tromey I botched another patch in this series because I was confused about when zero meant "no limit" and when it actually just meant zero, so I figured I'd fix this. MozReview-Commit-ID: 5vgzKGSKL8F
docshell/base/timeline/JavascriptTimelineMarker.h
js/src/jsapi.cpp
js/src/jsapi.h
js/src/vm/SavedStacks.cpp
js/src/vm/SavedStacks.h
--- a/docshell/base/timeline/JavascriptTimelineMarker.h
+++ b/docshell/base/timeline/JavascriptTimelineMarker.h
@@ -3,16 +3,19 @@
 /* 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 mozilla_JavascriptTimelineMarker_h_
 #define mozilla_JavascriptTimelineMarker_h_
 
 #include "TimelineMarker.h"
+
+#include "mozilla/Maybe.h"
+
 #include "mozilla/dom/ProfileTimelineMarkerBinding.h"
 #include "mozilla/dom/RootedDictionary.h"
 #include "mozilla/dom/ToJSValue.h"
 
 namespace mozilla {
 
 class JavascriptTimelineMarker : public TimelineMarker
 {
@@ -58,17 +61,17 @@ public:
         JS::Rooted<JSString*> asyncCause(aCx, JS_NewUCStringCopyN(aCx, mAsyncCause.BeginReading(),
                                                                   mAsyncCause.Length()));
         if (!asyncCause) {
           JS_ClearPendingException(aCx);
           return;
         }
 
         if (JS::IsSavedFrame(asyncStack) &&
-            !JS::CopyAsyncStack(aCx, asyncStack, asyncCause, &parentFrame, 0)) {
+            !JS::CopyAsyncStack(aCx, asyncStack, asyncCause, &parentFrame, mozilla::Nothing())) {
           JS_ClearPendingException(aCx);
         } else {
           stackFrame.mAsyncParent = parentFrame;
         }
       }
 
       JS::Rooted<JS::Value> newStack(aCx);
       if (ToJSValue(aCx, stackFrame, &newStack)) {
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -7756,17 +7756,17 @@ JS::CaptureCurrentStack(JSContext* cx, J
         return false;
     stackp.set(frame.get());
     return true;
 }
 
 JS_PUBLIC_API(bool)
 JS::CopyAsyncStack(JSContext* cx, JS::HandleObject asyncStack,
                    JS::HandleString asyncCause, JS::MutableHandleObject stackp,
-                   unsigned maxFrameCount)
+                   const Maybe<size_t>& maxFrameCount)
 {
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
     MOZ_RELEASE_ASSERT(cx->compartment());
 
     js::AssertObjectIsSavedFrameOrWrapper(cx, asyncStack);
     JSCompartment* compartment = cx->compartment();
     Rooted<SavedFrame*> frame(cx);
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -6502,24 +6502,24 @@ CaptureCurrentStack(JSContext* cx, Mutab
  * by some other object.  This may be used when you need to treat a
  * given stack trace as an async parent.  If you just need to capture
  * the current stack, async parents and all, use CaptureCurrentStack
  * instead.
  *
  * Here |asyncStack| is the async stack to prepare.  It is copied into
  * |cx|'s current compartment, and the newest frame is given
  * |asyncCause| as its asynchronous cause.  If |maxFrameCount| is
- * non-zero, capture at most the youngest |maxFrameCount| frames.  The
+ * |Some(n)|, capture at most the youngest |n| frames.  The
  * new stack object is written to |stackp|.  Returns true on success,
  * or sets an exception and returns |false| on error.
  */
 extern JS_PUBLIC_API(bool)
 CopyAsyncStack(JSContext* cx, HandleObject asyncStack,
                HandleString asyncCause, MutableHandleObject stackp,
-               unsigned maxFrameCount);
+               const mozilla::Maybe<size_t>& maxFrameCount);
 
 /*
  * Accessors for working with SavedFrame JSObjects
  *
  * Each of these functions assert that if their `HandleObject savedFrame`
  * argument is non-null, its JSClass is the SavedFrame class (or it is a
  * cross-compartment or Xray wrapper around an object with the SavedFrame class)
  * and the object is not the SavedFrame.prototype object.
--- a/js/src/vm/SavedStacks.cpp
+++ b/js/src/vm/SavedStacks.cpp
@@ -1185,17 +1185,18 @@ SavedStacks::saveCurrentStack(JSContext*
 
     AutoGeckoProfilerEntry pseudoFrame(cx, "js::SavedStacks::saveCurrentStack");
     FrameIter iter(cx);
     return insertFrames(cx, iter, frame, mozilla::Move(capture));
 }
 
 bool
 SavedStacks::copyAsyncStack(JSContext* cx, HandleObject asyncStack, HandleString asyncCause,
-                            MutableHandleSavedFrame adoptedStack, uint32_t maxFrameCount)
+                            MutableHandleSavedFrame adoptedStack,
+                            const Maybe<size_t>& maxFrameCount)
 {
     MOZ_ASSERT(initialized());
     MOZ_RELEASE_ASSERT(cx->compartment());
     assertSameCompartment(cx, this);
 
     RootedObject asyncStackObj(cx, CheckedUnwrap(asyncStack));
     MOZ_RELEASE_ASSERT(asyncStackObj);
     MOZ_RELEASE_ASSERT(js::SavedFrame::isSavedFrameAndNotProto(*asyncStackObj));
@@ -1406,20 +1407,20 @@ SavedStacks::insertFrames(JSContext* cx,
             capture.as<JS::MaxFrames>().maxFrames--;
     }
 
     // Limit the depth of the async stack, if any, and ensure that the
     // SavedFrame instances we use are stored in the same compartment as the
     // rest of the synchronous stack chain.
     RootedSavedFrame parentFrame(cx, cachedFrame);
     if (asyncStack && !capture.is<JS::FirstSubsumedFrame>()) {
-        uint32_t maxAsyncFrames = capture.is<JS::MaxFrames>()
+        size_t maxAsyncFrames = capture.is<JS::MaxFrames>()
             ? capture.as<JS::MaxFrames>().maxFrames
             : ASYNC_STACK_MAX_FRAME_COUNT;
-        if (!adoptAsyncStack(cx, asyncStack, asyncCause, &parentFrame, maxAsyncFrames))
+        if (!adoptAsyncStack(cx, asyncStack, asyncCause, &parentFrame, Some(maxAsyncFrames)))
             return false;
     }
 
     // Iterate through |stackChain| in reverse order and get or create the
     // actual SavedFrame instances.
     for (size_t i = stackChain->length(); i != 0; i--) {
         SavedFrame::HandleLookup lookup = stackChain[i-1];
         lookup->parent = parentFrame;
@@ -1437,27 +1438,27 @@ SavedStacks::insertFrames(JSContext* cx,
     frame.set(parentFrame);
     return true;
 }
 
 bool
 SavedStacks::adoptAsyncStack(JSContext* cx, HandleSavedFrame asyncStack,
                              HandleString asyncCause,
                              MutableHandleSavedFrame adoptedStack,
-                             uint32_t maxFrameCount)
+                             const Maybe<size_t>& maxFrameCount)
 {
     RootedAtom asyncCauseAtom(cx, AtomizeString(cx, asyncCause));
     if (!asyncCauseAtom)
         return false;
 
-    // If maxFrameCount is zero, the caller asked for an unlimited number of
+    // If maxFrameCount is Nothing, the caller asked for an unlimited number of
     // stack frames, but async stacks are not limited by the available stack
     // memory, so we need to set an arbitrary limit when collecting them. We
     // still don't enforce an upper limit if the caller requested more frames.
-    uint32_t maxFrames = maxFrameCount > 0 ? maxFrameCount : ASYNC_STACK_MAX_FRAME_COUNT;
+    size_t maxFrames = maxFrameCount.valueOr(ASYNC_STACK_MAX_FRAME_COUNT);
 
     // Accumulate the vector of Lookup objects in |stackChain|.
     SavedFrame::AutoLookupVector stackChain(cx);
     SavedFrame* currentSavedFrame = asyncStack;
     SavedFrame* firstSavedFrameParent = nullptr;
     for (uint32_t i = 0; i < maxFrames && currentSavedFrame; i++) {
         if (!stackChain->emplaceBack(*currentSavedFrame)) {
             ReportOutOfMemory(cx);
@@ -1480,17 +1481,17 @@ SavedStacks::adoptAsyncStack(JSContext* 
     if (currentSavedFrame == nullptr &&
         asyncStack->compartment() == cx->compartment()) {
         // If we consumed the full async stack, and the stack is in the same
         // compartment as the one requested, we don't need to rebuild the full
         // chain again using the lookup objects, we can just reference the
         // existing chain and change the asyncCause on the younger frame.
         oldestFramePosition = 1;
         parentFrame = firstSavedFrameParent;
-    } else if (maxFrameCount == 0 &&
+    } else if (maxFrameCount.isNothing() &&
                oldestFramePosition == ASYNC_STACK_MAX_FRAME_COUNT) {
         // If we captured the maximum number of frames and the caller requested
         // no specific limit, we only return half of them. This means that for
         // the next iterations, it's likely we can use the optimization above.
         oldestFramePosition = ASYNC_STACK_MAX_FRAME_COUNT / 2;
     }
 
     // Iterate through |stackChain| in reverse order and get or create the
--- a/js/src/vm/SavedStacks.h
+++ b/js/src/vm/SavedStacks.h
@@ -165,17 +165,17 @@ class SavedStacks {
 
     MOZ_MUST_USE bool init();
     bool initialized() const { return frames.initialized(); }
     MOZ_MUST_USE bool saveCurrentStack(JSContext* cx, MutableHandleSavedFrame frame,
                                        JS::StackCapture&& capture = JS::StackCapture(JS::AllFrames()));
     MOZ_MUST_USE bool copyAsyncStack(JSContext* cx, HandleObject asyncStack,
                                      HandleString asyncCause,
                                      MutableHandleSavedFrame adoptedStack,
-                                     uint32_t maxFrameCount = 0);
+                                     const Maybe<size_t>& maxFrameCount);
     void sweep();
     void trace(JSTracer* trc);
     uint32_t count();
     void clear();
     void chooseSamplingProbability(JSCompartment*);
 
     // Set the sampling random number generator's state to |state0| and
     // |state1|. One or the other must be non-zero. See the comments for
@@ -221,17 +221,17 @@ class SavedStacks {
     };
 
     MOZ_MUST_USE bool insertFrames(JSContext* cx, FrameIter& iter,
                                    MutableHandleSavedFrame frame,
                                    JS::StackCapture&& capture);
     MOZ_MUST_USE bool adoptAsyncStack(JSContext* cx, HandleSavedFrame asyncStack,
                                       HandleString asyncCause,
                                       MutableHandleSavedFrame adoptedStack,
-                                      uint32_t maxFrameCount);
+                                      const Maybe<size_t>& maxFrameCount);
     SavedFrame* getOrCreateSavedFrame(JSContext* cx, SavedFrame::HandleLookup lookup);
     SavedFrame* createFrameFromLookup(JSContext* cx, SavedFrame::HandleLookup lookup);
 
     // Cache for memoizing PCToLineNumber lookups.
 
     struct PCKey {
         PCKey(JSScript* script, jsbytecode* pc) : script(script), pc(pc) { }