deno_core
deno_core copied to clipboard
Support for realms/contexts in `deno_core` (in preparation for `ShadowRealm`)
In JS, code execution is always tied to some realm (or "context" in V8 terminology), which provides a global object and a set of built-ins (Object
, Array
, Error
, etc.) which is different from those of other realms. Multiple realms can share an event loop (= a thread), and those that do share one may be able to access objects from a different realm. In the web, a same-origin <iframe>
is a different realm from the top-level page (iframe.contentWindow.Array !== window.Array
). Currently there is no way to create realms or run code in them purely with JS built-ins, that's something that host environments (the web / Node.js / Deno) would have to provide, so it's fine for Deno to not support them.
But there is currently a stage-3 TC39 proposal called ShadowRealm
that would allow creating realms, as a way of sandboxing untrusted code. This proposal is not close to shipping anywhere, and it's only now starting to get implemented in V8, but currently Deno assumes that there's a single V8 context available at all times, and it's better to start refactoring things to support realms with plenty of time to spare.
Since ShadowRealm
s are meant to be a sandbox primitive, there would be no way to make objects from the parent realm available inside the ShadowRealm
and vice versa. This simplifies to some extent the requirements needed, because that way there is no need to comb through the JS code looking for wrong uses of instanceof
. That said, these are the requirements that seem to be needed for ShadowRealm
(and I'm sure I'm forgetting some):
- [x] Add a Rust API to create V8 contexts associated with a
JsRuntime
and run scripts in them (similar toJsRuntime::execute_script
), to simplify running tests. Modules can be left for later. denoland/deno#14019 - [x] Make sure bindings and extension scripts are initialized correctly in the context, both when using snapshots and when not. This includes making sure the ops cache in different realms can't go out of sync. denoland/deno#14019
- [x] Make sure any object return values of bindings and ops, as well as any exceptions they throw, are objects in the current realm (i.e. their prototype is not, say, a different realm's
Object.prototype
). denoland/deno#14750, denoland/deno#17050. - [x] Make sure async ops don't resolve a promise in the wrong realm. Originally landed on denoland/deno#14734, then reverted, now relanded in denoland/deno#17204.
- [x]
js_promise_reject_cb
,js_uncaught_exception_cb
andjs_wasm_streaming_cb
should probably be realm → callback (weak)maps. Originally landed on denoland/deno#15599, then reverted, now relanded in denoland/deno#17422. - [ ] Modules. Per the HTML spec, every realm has its own module map, although module fetches can be cached across realms. denoland/deno#15760 denoland/deno_core#41
- [x] Add Rust APIs to load modules in a context (similar to
JsRuntime::load_main_module
andJsRuntime::load_side_module
). - [x] There should be a module map per realm, rather than one for the entire
JsRuntime
. (Should there be a single module loader, though? Blobs should probably not be cached across realms, but non-blob fetches probably should, so it's not clear what to do about denoland/deno#12458.) - [x] Make sure module evaluation in realms integrates with the event loop.
- [X] Dynamic imports and
import.meta
.
- [x] Add Rust APIs to load modules in a context (similar to
- [ ] Review every use of
serde_v8::Value
to make sure objects from a realm can't leak into another. - [ ] Implement the
ShadowRealm
constructor. denoland/deno#16211 - [ ] Fix the inspector to properly support multiple realms. denoland/deno_core#353