Bug 1438121: Part 2: Define the 'bindToAsyncStack' JS shell testing function. r=fitzgen
authorJim Blandy <jimb@mozilla.com>
Sun, 25 Feb 2018 15:43:20 -0800
changeset 763465 4ca86bbdc4090adb24956c783e1b42fd495e0f1f
parent 763464 6f555aced81dac6cde601db7ca6589e42efe5f57
child 763466 f72fd25e822fc7c8f625a0b6f659a7aaee4d4a41
push id101458
push userbmo:rbarker@mozilla.com
push dateMon, 05 Mar 2018 23:59:13 +0000
reviewersfitzgen
bugs1438121
milestone60.0a1
Bug 1438121: Part 2: Define the 'bindToAsyncStack' JS shell testing function. r=fitzgen See JS_FN documentation for details. MozReview-Commit-ID: 6WUaGfdwogX
js/src/shell/js.cpp
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -993,16 +993,91 @@ SetPromiseRejectionTrackerCallback(JSCon
 
     GetShellContext(cx)->promiseRejectionTrackerCallback = args[0];
     JS::SetPromiseRejectionTrackerCallback(cx, ForwardingPromiseRejectionTrackerCallback);
 
     args.rval().setUndefined();
     return true;
 }
 
+static bool
+BoundToAsyncStack(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    RootedFunction function(cx, (&GetFunctionNativeReserved(&args.callee(), 0)
+                                 .toObject().as<JSFunction>()));
+    RootedObject options(cx, &GetFunctionNativeReserved(&args.callee(), 1).toObject());
+
+    RootedSavedFrame stack(cx, nullptr);
+    JSAutoByteString cause;
+    bool isExplicit;
+
+    RootedValue v(cx);
+
+    if (!JS_GetProperty(cx, options, "stack", &v))
+        return false;
+    if (!v.isObject() || !v.toObject().is<SavedFrame>()) {
+        JS_ReportErrorASCII(cx, "The 'stack' property must be a SavedFrame object.");
+        return false;
+    }
+    stack = &v.toObject().as<SavedFrame>();
+
+    if (!JS_GetProperty(cx, options, "cause", &v))
+        return false;
+    RootedString causeString(cx, ToString(cx, v));
+    if (!causeString || !cause.encodeUtf8(cx, causeString)) {
+        MOZ_ASSERT(cx->isExceptionPending());
+        return false;
+    }
+
+    if (!JS_GetProperty(cx, options, "explicit", &v))
+        return false;
+    isExplicit = v.isUndefined() ? true : ToBoolean(v);
+
+    auto kind = (isExplicit
+                 ? JS::AutoSetAsyncStackForNewCalls::AsyncCallKind::EXPLICIT
+                 : JS::AutoSetAsyncStackForNewCalls::AsyncCallKind::IMPLICIT);
+
+    JS::AutoSetAsyncStackForNewCalls asasfnckthxbye(cx, stack, cause.ptr(), kind);
+    return Call(cx, UndefinedHandleValue, function,
+                JS::HandleValueArray::empty(), args.rval());
+}
+
+static bool
+BindToAsyncStack(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    if (args.length() != 2) {
+        JS_ReportErrorASCII(cx, "bindToAsyncStack takes exactly two arguments.");
+        return false;
+    }
+
+    if (!args[0].isObject() || !IsCallable(args[0])) {
+        JS_ReportErrorASCII(cx, "bindToAsyncStack's first argument should be a function.");
+        return false;
+    }
+
+    if (!args[1].isObject()) {
+        JS_ReportErrorASCII(cx, "bindToAsyncStack's second argument should be an object.");
+        return false;
+    }
+
+    RootedFunction bound(cx, NewFunctionWithReserved(cx, BoundToAsyncStack, 0, 0,
+                                                     "bindToAsyncStack thunk"));
+    if (!bound)
+        return false;
+    SetFunctionNativeReserved(bound, 0, args[0]);
+    SetFunctionNativeReserved(bound, 1, args[1]);
+
+    args.rval().setObject(*bound);
+    return true;
+}
+
 #ifdef ENABLE_INTL_API
 static bool
 AddIntlExtras(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     if (!args.get(0).isObject()) {
         JS_ReportErrorASCII(cx, "addIntlExtras must be passed an object");
         return false;
@@ -7218,16 +7293,36 @@ JS_FN_HELP("parseBin", BinParse, 1, 0,
 "Take jobs from the shell's job queue in FIFO order and run them until the\n"
 "queue is empty.\n"),
 
     JS_FN_HELP("setPromiseRejectionTrackerCallback", SetPromiseRejectionTrackerCallback, 1, 0,
 "setPromiseRejectionTrackerCallback()",
 "Sets the callback to be invoked whenever a Promise rejection is unhandled\n"
 "or a previously-unhandled rejection becomes handled."),
 
+    JS_FN_HELP("bindToAsyncStack", BindToAsyncStack, 2, 0,
+"bindToAsyncStack(fn, { stack, cause, explicit })",
+"  Returns a new function that calls 'fn' with no arguments, passing\n"
+"  'undefined' as the 'this' value, and supplies an async stack for the\n"
+"  call as described by the second argument, an object with the following\n"
+"  properties (which are not optional, unless specified otherwise):\n"
+"\n"
+"  stack:    A SavedFrame object, like that returned by 'saveStack'. Stacks\n"
+"            captured during calls to the returned function capture this as\n"
+"            their async stack parent, accessible via a SavedFrame's\n"
+"            'asyncParent' property.\n"
+"\n"
+"  cause:    A string, supplied as the async cause on the top frame of\n"
+"            captured async stacks.\n"
+"\n"
+"  explicit: A boolean value, indicating whether the given 'stack' should\n"
+"            always supplant the returned function's true callers (true),\n"
+"            or only when there are no other JavaScript frames on the stack\n"
+"            below it (false). If omitted, this is treated as 'true'."),
+
 #ifdef ENABLE_INTL_API
     JS_FN_HELP("addIntlExtras", AddIntlExtras, 1, 0,
 "addIntlExtras(obj)",
 "Adds various not-yet-standardized Intl functions as properties on the\n"
 "provided object (this should generally be Intl itself).  The added\n"
 "functions and their behavior are experimental: don't depend upon them\n"
 "unless you're willing to update your code if these experimental APIs change\n"
 "underneath you."),