1 of 27

ECMAScript Realms

Proposal at Stage 2�

TC39 June, 2020� https://github.com/tc39/proposal-realms/

2 of 27

Changes

3 of 27

Changes

  • Simplification of the API�
  • Layering via HostInitializeUserRealm (thanks to @littledan)
  • Iron out the spec text

4 of 27

API Changes

A lot of simplifications, closing all remaining questions about the API:

    • Removed thisValue option and accessor
      1. Removing options bag all together�
    • Renamed global accessor to globalThis
    • Remove Realm.prototype.evaluate() method�
    • Remove Realm.prototype.intrinsics() method�
    • Adding Realm.prototype.import() method

5 of 27

Realm API

6 of 27

Few Clarifications

7 of 27

Important clarification about the terminology

In the Web Platform, both Realm and Global Object are usually associated to Window, Worker, and Worklets semantics. They are also associated to their detachable nature.

This proposal is limited to the semantics specified by ECMA-262 with no extra requirements from the web counterparts.

8 of 27

Important clarification about the global object

  • The Realm's Global Object is an Ordinary Object.

9 of 27

Important clarification about "detachable" realms

  • A Realm Object (and its global object) are not detachable
  • A Realm Object has a lifeline to its incubator Realm

Window or�Worker

Realm A

Realm B

Realm C

10 of 27

Important clarification about evaluation

  • Realm API does not introduce a new way to evaluate code, it reuses the existing evaluation mechanisms:
    • If you disable CSP unsafe-eval, you cannot evaluate code synchronously
    • If you disable CSP default-src, you cannot use Realm.prototype.import()

11 of 27

Important clarification about the module graph

const realm = new Realm();

// imports code that executes within its own environment.

const { doSomething } = await realm.import('./file.js');

doSomething();

  • Every Realm object has its own module graph

12 of 27

Important clarification about compartments

const compartment = new Compartment(options);

const VirtualizedRealm = compartment.globalThis.Realm;

const realm = new VirtualizedRealm();

const { doSomething } = await realm.import('./file.js');

  • A new compartment provides a new Realm constructor
  • A realm object created from a compartment is subject to the compartment's virtualization mechanism

13 of 27

Use Cases

14 of 27

Third Party Scripts

  • We want a quick and simple execution of third party scripts.
  • Not one, not two, not three, but many scripts.
  • We don't need a new host/agent, but just integrity of the expected Intrinsics.
  • Non-blocking async loading and execution via realm.import.
  • No immediate access to application globals (e.g. Window) is also convenient.
  • No immediate access to unforgeables (e.g. window.top, window.location) is great.

15 of 27

Third Party Scripts: Plugins

import { api } from 'plugingFramework';

const realm = new Realm();

realm.globalThis.api = api;

await realm.import('./plugin1.js');

16 of 27

Code Testing

  • While threading is good for testing, the layering from Realms is also great.
  • Test frameworks can use Realms to inject code
  • And also control the order the injections if necessary.

17 of 27

Code Testing: Running tests in a realm

import { test } from 'testFramework';

const realm = new Realm();

realm.globalThis.test = test;

await realm.import('./main-spec.js');

test.report();

18 of 27

Code Testing: Running test FW and tests in a realm

const realm = new Realm();

const [ framework, { tap } ] = await Promise.all([

realm.import('testFramework'),

realm.import('reporters')

]);

framework.use(tap);

await realm.import('./main-spec.js');

19 of 27

Codebase segmentation

  • A big codebase tend to evolve slowly
  • Old code vs new code is a constant struggle
  • Modifying code to resolve a conflict (e.g.: global variables) is non-trivial.
  • With a lightweight mechanism to preserve the integrity of the intrinsics you could isolate libraries, or logical pieces of the codebase

20 of 27

Template Library

  • Code generation should not be subject to pollution (global, prototype, etc.).
  • E.g.: Lodash _.template() uses Function(...) to create a compiled template function, instead it can use a Realm to avoid leaking global variables

var compiled = _.template(

'<% _.forEach(users, function(user) { %><li><%- user %></li><% }); %>'

);��compiled({ users: ['user1', 'user2'] });

21 of 27

DOM Virtualization

  • We still want things to still interact with the DOM
  • We don't want to spend any excessive amount of resources
  • We want to emulate the DOM as best as possible
    • Requiring other libraries to change to meet our requirements is difficult

22 of 27

DOM Virtualization: AMP WorkerDOM Challenge

google.com

AMP

Worker

(Virtual DOM)

???.cdn.ampproject.org

Runs AMP Framework

Runs NYT Scripts

async comm

Problem: Element.getBoundingClientRect() doesn't work over async comm channels (i.e. worker-dom)

Preloaded�

cross-domain iframe

23 of 27

DOM Virtualization

import virtualDocument from 'virtual-document';

const realm = new Realm();

realm.globalThis.document = virtualDocument;

await realm.import('./publisher-amin.js');

24 of 27

Where are we right now?

25 of 27

Open Question 1

  • Implementers concern:
    • "Mechanical concerns about how Realms would work with HTML"
      • DOM Layering is work-in-progress

26 of 27

Open Question 2

  • Implementers concern:
    • "Realms is counter to the current design principles of the web platform"
      • direct access to other "globals" discussion (Issue #238)

27 of 27

Questions?