|
1 |
| -# Realm API usage |
2 |
| - |
3 |
| -### Example: simple realm |
4 |
| - |
5 |
| -```js |
6 |
| -let g = globalThis; // outer global |
7 |
| -let r = new Realm(); // root realm |
8 |
| - |
9 |
| -let f = r.globalThis.Function("return 17"); |
10 |
| - |
11 |
| -f() === 17 // true |
12 |
| - |
13 |
| -Reflect.getPrototypeOf(f) === g.Function.prototype // false |
14 |
| -Reflect.getPrototypeOf(f) === r.globalThis.Function.prototype // true |
15 |
| -``` |
16 |
| - |
17 |
| -### Example: Importing Module |
18 |
| - |
19 |
| -```js |
20 |
| -let r = new Realm(); |
21 |
| -const { x } = await r.import('/path/to/foo.js'); |
22 |
| -``` |
23 |
| - |
24 |
| -In this example, the new realm will fetch, and evaluate the module, and extract the `x` named export from that module namespace. `Realm.prototype.import` is equivalent to the dynamic import syntax (e.g.: `const { x } = await import('/path/to/foo.js');` from within the realm. In some cases, evaluation will not be available (e.g.: in browsers, CSP might block unsafe-eval), while importing from module is still possible. |
25 |
| - |
26 |
| -### Example: Virtualized contexts |
27 |
| - |
28 |
| -Importing modules allow us to run asynchronous executions with set boundaries for access to global environment contexts. |
29 |
| - |
30 |
| -#### main js file: |
31 |
| - |
32 |
| -```js |
33 |
| -globalThis.DATA = "a global value"; |
34 |
| - |
35 |
| -let r = new Realm(); |
36 |
| - |
37 |
| -// r.import is equivalent to the dynamic import expression |
38 |
| -// It provides asynchronous execution, without creating or relying in a |
39 |
| -// different thread or process. |
40 |
| -r.import("./sandbox.js").then(({test}) => { |
41 |
| - |
42 |
| - // globals in this root realm are not leaked |
43 |
| - test("DATA"); // undefined |
44 |
| - |
45 |
| - let desc = test("Array"); // {writable: true, enumerable: false, configurable: true, value: ƒ} |
46 |
| - let Arr = desc.value; |
47 |
| - |
48 |
| - Arr === r.globalThis.Array; // true |
49 |
| - Arr === Array; // false |
50 |
| - |
51 |
| - // foo and bar are immediately visible as globals here. |
52 |
| -}); |
53 |
| -``` |
54 |
| - |
55 |
| -#### sandbox.js file |
56 |
| - |
57 |
| -```js |
58 |
| -// DATA is not available as a global name here |
59 |
| - |
60 |
| -// Names here are not leaked to the root realm |
61 |
| -var foo = 42; |
62 |
| -globalThis.bar = 39; |
63 |
| - |
64 |
| -export function test(property) { |
65 |
| - |
66 |
| - // Built-ins like `Object` are included. |
67 |
| - return Object.getPropertyDescriptor(globalThis, property); |
68 |
| -} |
69 |
| -``` |
70 |
| - |
71 |
| -### Example: simple subclass |
72 |
| - |
73 |
| -```js |
74 |
| -class EmptyRealm extends Realm { |
75 |
| - constructor(...args) { |
76 |
| - super(...args); |
77 |
| - let globalThis = this.globalThis; |
78 |
| - |
79 |
| - // delete global descriptors: |
80 |
| - delete globalThis.Math; |
81 |
| - ... |
82 |
| - } |
83 |
| -} |
84 |
| -``` |
85 |
| - |
86 |
| -### Example: DOM mocking |
87 |
| - |
88 |
| -```js |
89 |
| -class FakeWindow extends Realm { |
90 |
| - constructor(...args) { |
91 |
| - super(...args); |
92 |
| - let globalThis = this.globalThis; |
93 |
| - |
94 |
| - globalThis.document = new FakeDocument(...); |
95 |
| - globalThis.alert = new Proxy(fakeAlert, { ... }); |
96 |
| - ... |
97 |
| - } |
98 |
| -} |
99 |
| -``` |
100 |
| - |
101 |
| - |
102 |
| - |
103 |
| -## iframes vs realms |
104 |
| - |
105 |
| -If you're using anonymous iframes today to "evaluate" javascript code in a different realm, you can replace it with a new Realm, as a more performant option, e.g.: |
106 |
| - |
107 |
| -```js |
108 |
| -const globalOne = window; |
109 |
| -let iframe = document.createElement('iframe'); |
110 |
| -document.body.appendChild(iframe); |
111 |
| -const globalTwo = iframe.contentWindow; |
112 |
| -``` |
113 |
| - |
114 |
| -will become: |
115 |
| - |
116 |
| -```js |
117 |
| -const globalOne = window; |
118 |
| -const globalTwo = new Realm().globalThis; |
119 |
| -``` |
120 |
| - |
121 |
| -### Indirect evaluation |
122 |
| - |
123 |
| -This operation should be equivalent, in both scenarios: |
124 |
| - |
125 |
| -```js |
126 |
| -globalOne.eval('1 + 2'); // yield 3 |
127 |
| -globalTwo.eval('1 + 2'); // yield 3 |
128 |
| -``` |
129 |
| - |
130 |
| -### Direct evaluation |
131 |
| - |
132 |
| -This operation should be equivalent, in both scenarios: |
133 |
| - |
134 |
| -```js |
135 |
| -globalOne.eval('eval("1 + 2")'); // yield 3 |
136 |
| -globalTwo.eval('eval("1 + 2")'); // yield 3 |
137 |
| -``` |
138 |
| - |
139 |
| -### Identity Discontinuity |
140 |
| - |
141 |
| -Considering that you're creating a brand new realm, with its brand new global variable, |
142 |
| -the identity discontinuity is still present, just like in the iframe example: |
143 |
| - |
144 |
| -```js |
145 |
| -let a1 = globalOne.eval('[1,2,3]'); |
146 |
| -let a2 = globalTwo.eval('[1,2,3]'); |
147 |
| -a1.prototype === a2.prototype; // yield false |
148 |
| -a1 instanceof globalTwo.Array; // yield false |
149 |
| -a2 instanceof globalOne.Array; // yield false |
150 |
| -``` |
151 |
| - |
152 |
| -_Note: There are other solutions to this problem by using proxies and membranes, which has some performance implications. It is not a goal of this proposal to solve this._ |
153 |
| - |
154 |
| -## node's vm objects vs realms |
155 |
| - |
156 |
| -If you're using node's `vm` module today to "evaluate" javascript code in a different realm, you can replace it with a new Realm, e.g.: |
157 |
| - |
158 |
| -```js |
159 |
| -const vm = require('vm'); |
160 |
| -const script = new vm.Script('this'); |
161 |
| -const globalOne = globalThis; |
162 |
| -const globalTwo = script.runInContext(new vm.createContext()); |
163 |
| -``` |
164 |
| - |
165 |
| -will become: |
166 |
| - |
167 |
| -```js |
168 |
| -const globalOne = globalThis; |
169 |
| -const globalTwo = new Realm().globalThis; |
170 |
| -``` |
171 |
| - |
172 |
| -_Note: these two are equivalent in functionality._ |
| 1 | +_all the examples moved to the [Explainer](explainer.md) file_ |
0 commit comments