Tag Archives: promises

Things I’ve Learned This Week (June 1 – June 5, 2015)

How to get an nsIGlobalObject* from a JSContext*

I’m working on a patch for bug 1116188 to make gathering profiles from subprocesses asynchronous. In order to do that, I’m exposing a new method on nsIProfiler called getProfileDataAsync that is returning a DOM Promise. What’s interesting about this is that I’m returning a DOM Promise from C++! 1

In order to construct a DOM Promise in C++, I need to hand it something that implements nsIGlobalObject. I suspect that this helps the Promise determine which memory region that it belongs to.

My new method gets a JSContext* because I’ve got the [implicit_jscontext] bit about the method definition in the nsIProfiler.idl file… so how do I go about turning that into an nsIGlobalObject?

Here’s the maneuver:

// Where aCX is your JSContext*:
nsIGlobalObject* go = xpc::NativeGlobal(JS::CurrentGlobalOrNull(aCx));

That will, as the name suggests, return either an nsIGlobalObject*, or a nullptr.

Resolving a DOM Promise from C++ with a JS Object

For my patch for bug 1116188, it’s all well and good to create a DOM Promise, but you have to resolve or reject that Promise for it to have any real value.

In my case, I wanted to take a string, parse it into a JS Object, and resolve with that.

Resolving or rejecting a DOM Promise in Javascript is pretty straight-forward – you’re given back resolve / reject function, and you just need to call those with your results and you’re done.

In C++, things get a little hairier. As I discovered in my most recent episode of The Joy of Coding, conditions need to be right in order for this to work out.

Here’s what I ended up doing (I’ve simplified the method somewhat to remove noise):

void
ProfileGatherer::Finish()
{
  AutoJSAPI jsapi;
  jsapi.Init();
  JSContext* cx = jsapi.cx();
  JSAutoCompartment ac(cx, mPromise->GlobalJSObject());

  // Now parse the JSON so that we resolve with a JS Object.
  JS::RootedValue val(cx);
  {
    UniquePtr<char[]> buf = mWriter.WriteFunc()->CopyData();
    NS_ConvertUTF8toUTF16 js_string(nsDependentCString(buf.get()));
    MOZ_ALWAYS_TRUE(JS_ParseJSON(cx, static_cast<const char16_t*>(js_string.get()),
                                 js_string.Length(), &val));
  }
  mPromise->MaybeResolve(val);
}

The key parts here are getting the AutoJSAPI on the stack, initting it, gettings its JSContext, and then putting the JSAutoCompartment on the stack. Note that I had to pass not only the JSContext, but the global JS Object for the Promise as well – I suspect that’s, again, to ensure that the right compartment is being entered. Otherwise, I start failing assertions like crazy.

Note that the code above is by no means perfect – I’m missing error handling functions for when the JSON parsing goes wrong. In that case, I should probably reject the Promise instead. bz pointed me to a good example of that going on here in Fetch.cpp:

      if (!JS_ParseJSON(cx, decoded.get(), decoded.Length(), &json)) {
        if (!JS_IsExceptionPending(cx)) {
          localPromise->MaybeReject(NS_ERROR_DOM_UNKNOWN_ERR);
          return;
        }

        JS::Rooted<JS::Value> exn(cx);
        DebugOnly<bool> gotException = JS_GetPendingException(cx, &exn);
        MOZ_ASSERT(gotException);

        JS_ClearPendingException(cx);
        localPromise->MaybeReject(cx, exn);
        return;
      }

      localPromise->MaybeResolve(cx, json);
      return;

I’ll probably end up doing something similar in the next iteration of my patch.


  1. I learned how to do that a few weeks back

The Joy of Coding (Ep. 17): Frustrations in the Key of C++

In this episode, I gave a quick update on the OS X printing bug we’d been working on a for a few weeks (Spoiler alert – the patch got reviewed and landed!), and then dove into my new problem: getting performance profiles from subprocesses asynchronously.

And, I won’t lie to you, this is probably the most frustrating episode in the series so far. I really didn’t make much headway.

The way I want to solve this problem involves passing a DOM Promise back to the Javascript caller that resolves when all of the profiles have been gathered asynchronously.

If I were writing this in Javascript, it’d be a cinch. Creating, passing around, and resolving Promises is pretty straight-forward in that world.

But the Gecko profiler backend is written entirely in C++, and so that’s where I’d have to create the Promise.

A few weeks back, I posted a “Things I’ve learned this week” about how to create DOM Promises in C++. That’s all well and good, but creating the Promise is only half of the job. You have to resolve (or reject) the Promise in order for it to be useful at all.

The way I wanted to resolve the Promise involved parsing a JSON string and resolving with the resulting object.

That turned out to be a lot harder than I thought it’d be. Watch the video to see why. Suffice it to say, I spend a lot of it asking for help in IRC. It’s a 100% accurate demonstration of what I do when I’m lost, or can’t figure something out, and I need help.

Since I recorded this episode, I’ve figured out what I needed to do – I’ve posted a “Things I’ve learned this week” containing that information. Hopefully that’ll help somebody else in the future!

Oh – also, this episode has sound effects, courtesy of Wacky Morning DJ (which I demonstrated in last week’s episode).

Episode agenda.

References

Bug 1116188 – [e10s] Stop using sync messages for Gecko profilerNotes