Skip to content

Commit 91d4135

Browse files
committed
Details fleshed out
1 parent 24fb644 commit 91d4135

1 file changed

Lines changed: 125 additions & 26 deletions

File tree

README.md

Lines changed: 125 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,26 @@ Champions:
1010

1111
## Synopsis
1212

13-
Provide a `Global` constructor that produces a new instance of globalThis with `Global`, `eval`, `Function` constructor, and `ModuleSource` (or equivalent) constructor such that execution contexts
14-
generated from the evaluators refer back to this global, and with
15-
virtualized host behavior for dynamic import in script
16-
contexts based on the `importHook` and `importMeta` provided.
17-
Based on the `keys` option (that defaults to all) further fields are copied from the `globalThis` in which the `Global` constructor lives.
18-
19-
All options are optional.
20-
13+
Provide a `globalThis.Global` constructor that produces a new instance of
14+
`globalThis` with a fresh set of evaluators: `eval`, `Function`,
15+
`AsyncFunction`, `GeneratorFunction`, and `AsyncGeneratorFunction` that effect
16+
evaluation with the new `Global` and its associated module map.
17+
The constructor returns the new global with all of the internal slots of
18+
`globalThis` and configurable copies of all the property descriptors from
19+
`globalThis` or just those specified in the array of `keys`.
20+
Dynamic `import` within these new evaluators is bound to the new global.
21+
Dynamic `import` of a `ModuleSource` within these evaluators
22+
instantiates that module in the new global's module map and in the lexical
23+
scope of the new global.
24+
25+
> This proposal picks up from the previous proposal for
26+
> [Evaluators]
27+
> (https://github.com/tc39/proposal-compartments/blob/7e60fdbce66ef2d97370007afeb807192c653333/3-evaluator.md)
28+
> from the [HardenedJS](https://hardenedjs.org) [`Compartment`
29+
> proposal][proposal-compartment].
2130
2231
## Interfaces
32+
2333
```ts
2434
interface Global {
2535
constructor({
@@ -31,7 +41,6 @@ interface Global {
3141
Global: typeof Global,
3242
Function: typeof Function,
3343
eval: typeof eval,
34-
ModuleSource: typeof ModuleSource,
3544

3645
// and ...globalThis[...keys]
3746
}
@@ -45,48 +54,138 @@ new globalThis.Global({
4554
});
4655
```
4756

48-
The `Global` constructor copies values for `keys` (or all entries if `keys` not specified) from the globalThis it originates from.
57+
The `Global` constructor copies values for `keys` (or all entries if `keys` not
58+
specified) from the globalThis it originates from.
4959

5060
Produces a _global_ with fresh:
51-
- `Global` - the same Global constructor but copying values from the new _global_
52-
- `Function` and `eval` - evaluators that execute code with the _global_ as the global scope and `importHook`,`importMeta` used for all imports encountered in the evaluated code
53-
- `ModuleSource` - (tentatively, but we need some way to execute modules with that _global_) TBD
61+
- `Global` - a new `Global` constructor that will use the new _global_ for
62+
purposes of duplicating internal slots, the property descriptors of copied
63+
`keys`, and its `importHook`.
64+
- `Function` and `eval` - evaluators that execute code with the _global_ as the
65+
global scope and `importHook`,`importMeta` used for all imports encountered
66+
in the evaluated code
67+
- All other function constructors, which can be accessed through `eval` and
68+
their corresponding, undeniable syntax, like `global.eval('async () =>
69+
{}').constructor`.
70+
71+
The global does not require a fresh `ModuleSource` because
72+
the source is paired with the global by use of dynamic `import` in global evaluation,
73+
as in `new Global().eval('specifier => import(specifier)')(specifier)`.
74+
75+
Invariants:
5476

55-
Invariants
5677
```js
5778
globalThis.x = {};
5879
const thatGlobal = new globalThis.Global({
59-
keys: Object.keys(globalThis)
80+
keys: Object.keys(globalThis),
6081
});
6182
thatGlobal.eval !== thisGlobal.eval;
6283
thatGlobal.Global !== thisGlobal.Global;
63-
thatGlobal.ModuleSource !== thisGlobal.ModuleSource;
84+
thatGlobal.ModuleSource === thisGlobal.ModuleSource;
6485
thatGlobal.Function !== thisGlobal.Function;
6586
thatGlobal.eval('Object.getPrototypeOf(async () => {})') !== Object.getPrototypeOf(async () => {});
6687
thatGlobal.x === thisGlobal.x;
6788
```
6889

6990
## Motivation
7091

71-
## Design Questions
92+
### Domain Specific Languages
7293

73-
### `keys` default
94+
Tools like Mocha, Jest, and Jasmine install the verbs and nouns of their
95+
domain-specific-language in global scope.
7496

75-
- what about non-enumerable keys?
76-
- what about symbol keys?
77-
- what copying semantics is used?
78-
- would getters be invoked or copied?
97+
Isolating these changes currently requires creation of a new realm,
98+
and creating new realms comes with the hazard of identity discontinuity.
99+
For example, `array instanceof Array` is not as reliable as `Array.isArray`,
100+
and the hazard is not limited to intrinsics that have anticipated this
101+
problem with work-arounds like `Array.isArray` or thenable `Promise` adoption.
79102

80-
structuredClone() is not part of ECMAScript, sadly
103+
Some of these tools work around this problem by using the platforms existing
104+
facility for creating a new `Global`, albeit an iframe or the Node.js `vm`
105+
module.
106+
Then, they are obliged to graft the intrinsics of one realm over the other,
107+
which leaks for the cases of syntactically undeniable Realm-specific intrinsics
108+
like the `AsyncFunction` constructor and prototype, and requires the
109+
implementer to be vigilant to the extent that they graft every intrinsic from
110+
one realm to another.
111+
We have found such arrangements to be fragile and leaky.
81112

82-
### prototype chain in the browser
113+
New `Global` provide an alternate solution: evaluate modules or scripts in a
114+
separate global scope with shared intrinsics.
83115

84-
`globalThis` in the browser has a non-trivial prototype chain for some Window API functionality and events
116+
```js
117+
const dslGlobal = const new Global({
118+
globalThis: {
119+
__proto__: globalThis,
120+
describe,
121+
before,
122+
after,
123+
}
124+
});
125+
dslGlobal.describe = () => {}
126+
dslGlobal.before = () => {}
127+
dslGlobal.after = () => {};
85128

129+
const source = await import.source(entrypoint);
130+
await dslGlobal.eval('s => import(s)')(source);
131+
```
132+
133+
In this example, only the entrypoint module for the DSL sees additional
134+
globals.
135+
The `source` adopts the import hook associatd with `dslGlobal` by
136+
virtue of using the `dslGlobal`'s dynamic `import`.
137+
Current DSLs cannot execute concurrently or depend on dynamic scope to track
138+
the entrypoint that called each DSL verb.
139+
140+
### Enforcing the principle of least authority
141+
142+
On the web, the same origin policy has become sufficiently effective at
143+
preventing cross-site scripting attacks that attackers have been forced to
144+
attack from within the same origin.
145+
Conveniently for attackers, the richness of the JavaScript library ecosystem
146+
has produced ample vectors to enter the same origin.
147+
The vast bulk of a modern web application is its supply chain, including code
148+
that will be eventually incorporated into the scripts that will run in the same
149+
origin, but also the tools that generate those scripts, and the tools that
150+
prepare the developer environment.
151+
152+
The same-origin-policy protects the rapidly deteriorating fiction that
153+
web browsers mediate an interaction between just two parties: the service and
154+
the user.
155+
For modern applications, particularly platforms that mediate interactions among
156+
many parties or simply have a deep supply chain, web application developers
157+
need a mechanism to isolate third-party dependencies and minimize their access
158+
to powerful objects like high resolution timers or network, compute, or storage
159+
capability bearing interfaces.
160+
161+
Some hosts, including a community of embedded systems represented at [ECMA
162+
TC53][tc53], do not have an origin on which to build a same-origin-policy, and
163+
have elected to build their security model on isolated evaluators, through the
164+
high-level Compartment interface.
165+
166+
## Intersection Semantics
167+
168+
### Shared Structs
169+
170+
We expect that the new global, like old globals, would have both its own module
171+
map and also shared struct prototype registry, such that a module executed
172+
within that global would produce its own shared struct prototypes.
173+
This gives platforms a place to stand to ensure that separate globals do not
174+
share any undeniable mutable state.
175+
176+
## Design Questions
177+
178+
### prototype chain in the browser
179+
180+
`globalThis` in the browser has a non-trivial prototype chain for some Window
181+
API functionality and events.
86182

87183
### Backward compatibility and the `constructor` field on a global
88184

89-
`globalThis` already has a constructor in the browser and that constructor is `Window`, an _Illegal constructor_ as one can inform themselves by attempting to invoke it
185+
`globalThis` already has a constructor in the browser and that constructor is
186+
`Window`, an _Illegal constructor_ as one can inform themselves by attempting
187+
to invoke it.
188+
90189
```js
91190
globalThis.constructor === Window
92191
const g1 = new Global()

0 commit comments

Comments
 (0)