From:  Jan de Mooij <jdemooij@mozilla.com>
Date:  05 Jun 2018 00:25:32 Hong Kong Time
Newsgroup:  news.mozilla.org/mozilla.dev.tech.js-engine.internals
Subject:  

Same-compartment realms

NNTP-Posting-Host:  63.245.214.181

Hi all,

There were some questions about realms and how they relate to compartments
and globals, so this is a brief overview of the JS changes we're making for
bug 1357862 [0].

A JSCompartment used to represent two things (since compartment-per-global):

   1. A global object and data associated with it. The spec calls this a
   realm [1].
   2. Some sort of (security) membrane/sandbox: objects within a
   compartment can reference each other directly, but cross-compartment
   wrappers (proxies) are needed when compartment A wants to reference an
   object in compartment B.

One problem with this is that websites often use iframes and each frame
needs to have its own global object, so we currently end up with multiple
compartments and CCW overhead is a serious performance cliff.

To address this, we're splitting JSCompartment in two different classes:
JS::Compartment (storing the wrapper map and representing the security
concept) and JS::Realm (one realm per global, most of the state that used
to be in JSCompartment is now in JS::Realm). Then it will be possible for
Gecko (and the JS shell, of course [2]) to put multiple realms in the same
compartment and objects in these realms can then just reference each other
directly without wrapper overhead. Wrappers will still be necessary for
realms that live in different compartments.

There will still be a "current" global/realm, cx->realm(): AutoCompartment
has been renamed to AutoRealm and now enters a realm. The interpreter and
the JITs will be able to make cross-realm calls and so they'll have to
update cx->realm_ before/after scripted/native calls. This means all stack
frames within a single Activation will be same-compartment but not
necessarily same-realm.

Each JSScript belongs to a single realm. For objects, things are a bit more
complicated: because cross-compartment wrappers will be shared by all
realms in the compartment, it doesn't make a lot of sense to use
|wrapper->realm()|, |wrapper->global()| or |AutoRealm ar(cx, wrapper)|.
We'll have to do an audit for each of these and change the default behavior
to assert or return nullptr if we have a CCW [3]. We also have to audit all
assertSameCompartment calls and change some of them to assert same-realm
instead [4].

On the Gecko side there will be other changes (for document.domain and
WindowProxy/Location security checks), but this shouldn't really affect
SpiderMonkey.

So we now have the following three concepts:

   - Zone: the GC unit. This typically stores things related to
   allocation/GC. In the browser, there's roughly one zone per tab. Each zone
   has a list of compartments.


   - Compartment: all about security and wrappers (please don't add new
   fields to this unless really necessary, prefer Realm or Zone). Each
   compartment has a list of realms.


   - Realm: a global object and things related to it.

I hope this helps. Let me know if you have any questions/concerns.

Jan

[0] https://bugzilla.mozilla.org/show_bug.cgi?id=1357862
[1] https://tc39.github.io/ecma262/#sec-code-realms
[2] https://bugzilla.mozilla.org/show_bug.cgi?id=1466501
[3] https://bugzilla.mozilla.org/show_bug.cgi?id=1466112
[4] https://bugzilla.mozilla.org/show_bug.cgi?id=1466118