-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(core): Add support for modules in realms. #15760
Conversation
With these changes, Line 407 in 16dbf4a
That one sender is being kept alive because now a realm's module map is stored in the context slot (rather than in the isolate slot), and because of denoland/rusty_v8#1066 it ends up not getting dropped even after the isolate is dropped. Edit (October 8th): This is fixed now. |
bae389b
to
eb0b5f4
Compare
#16286 changed the module map, that was in a slot in the isolate backed by the |
While starting to look into writing tests for this, I realized that in this PR I'm using a single |
630db54
to
8bd63f1
Compare
This PR got stuck for a while while most of the realm code got reverted because it was blocking performance work. All of the PRs that were preconditions for modules have now been relanded, so I have rewritten this PR from scratch and will continue to work on it now. |
PTAL @bartlomieju @littledivy. Although this is probably not ready for merging, and the comments #15760 (comment) and #15760 (comment) are still things to fix, it would be good to get some initial reviews. |
Sorry Andreu, I was out for most of the week. I'll put it into my TODO list for the next week and provide initial review. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be honest I'm not sure why #16215 got closed without a merge. @littledivy could you give a rationale?
core/modules.rs
Outdated
global_realm | ||
.instantiate_module(runtime.v8_isolate(), mod_b) | ||
.unwrap(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A lot of these changes seem superficial - how about we still keep all of these methods on the JsRuntime
and they still internally call into the "global realm"? I believe in 99% cases we won't be handling multiple realms and it just makes the common path more complicated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using the global realm when a different realm should be used might lead to crossing objects from the global realm into the ShadowRealm
, or vice versa, allowing prototype pollution across the ShadowRealm
boundary. Requiring these methods (as well as handle_scope
, execute_script
, etc.) to be accessed through a JsRealm
forces you to think about which realm you need to work with, and also draws reviewers' attention to it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using the global realm when a different realm should be used might lead to crossing objects from the global realm into the ShadowRealm, or vice versa
How is that possible? I thought that if you try to act on an object from a different context you'd get a V8 panic
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You get a panic for different isolates, not contexts. Although V8 keeps track of in which context an object was created, this isn't really a thing in the spec, and using an object from another context is something you can do just fine in the browser with iframes (iframe.contentWindow.document
), or in Node.js's vm
module (vm.runInNewContext("({})")
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, seems like a valid reason to have separate API then.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for slow review @andreubotella.
The question about ModuleLoader
is an interesting one and I don't have a good answer right now. Does spec say anything about it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also we need to figure out what we're gonna do with snapshotting. I think for the first pass we should not care about additional realms and just make sure that snapshotting still works with a "global realm"
The HTML spec requires a level of "caching" in module fetches between a page's main and shadow realms, such that if a module is imported in the main realm it should not be re-fetched when imported from the shadow realm, and vice versa. I haven't looked at this code in a long time, but last time I checked Deno did some caching for module sources that would already take care of this. But as I was looking at the spec, I realized that import maps were recently merged into the HTML spec, and it's not clear if anyone has looked in any depth at how they interact with So I guess we should probably change
WFM |
Since |
Let's wait on #17648 before we merge this PR. |
e101c91
to
3f7efd2
Compare
This will help make reviews easier for denoland#15760, which moves a number of methods related to module loading from `JsRuntime` into `JsRealm`.
This will help make reviews easier for #15760, which moves a number of methods related to module loading from `JsRuntime` into `JsRealm`.
This change makes realms other than the main one support modules by having a module map associated to each realm, rather than one per module. As part of this, it also: - Adds an argument to `JsRuntime::create_realm` to set the realm's module loader. This allows different realms to have different module loading strategies (different import maps, for example). - Moves all of the methods of `JsRuntime` related to module loading into `JsRealm`. To minimize changing unrelated code, the public and crate-private methods in `JsRuntime` are kept, with their implementation replaced by a call to the corresponding method of the main realm's `JsRealm`. - Removes the `module_map` argument to the `EventLoopPendingState` constructor, instead accessing each realm's corresponding module map as part of the existing iteration. - Changes the parts of `JsRuntime::poll_event_loop` that deal with module evaluation and detecting stalled top-level awaits to support multiple module maps and multiple top-level module evaluations at the same time. - Moves `pending_mod_evaluate` and `pending_dyn_mod_evaluate` from `JsRuntimeState` to `ContextState`. Towards #13239.
9bc24b0
to
a3cfbf2
Compare
I think this PR is now ready for review. The commit message contains a somewhat detailed list of changes. I thought changing the module map to be stored on a context slot would make things slower, but the basic Main branch:
This PR:
|
@andreubotella looking at the benchmarks you provided there's about 0.3ms regression in the startup benchmark - it's something we'll need to address before landing. I'm gonna give a thorough review this week. Thanks for factoring out parts of |
This will help make reviews easier for #15760, which moves a number of methods related to module loading from `JsRuntime` into `JsRealm`.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks great Andreu. I'm still worried about the regression in startup time benchmarks and I believe we should address it before landing.
Could you remind me of the reason why we need global_realm
to be an Option
? If we removed the Option
we could get rid of a lot of clones.
@@ -628,6 +599,15 @@ impl JsRuntime { | |||
})), | |||
); | |||
|
|||
// TODO(andreubotella): Set up ExtModuleLoader. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does that TODO mean that not internal code in currently available in realms?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I haven't tested ESM internal code for non-main realms at all. I was planning to delve into that after this PR landed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good - I think it would be as simple as checking is Deno.core.getPromiseDetails
is defined
self.op_state(), | ||
self.snapshot_options == snapshot_util::SnapshotOptions::Load, | ||
))); | ||
// TODO(andreubotella): Snapshotted data? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you mean by this TODO?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm talking about these lines as the module map is being initialized in the JsRuntime
constructor:
Lines 513 to 518 in 4317055
if let Some(snapshotted_data) = maybe_snapshotted_data { | |
let scope = | |
&mut v8::HandleScope::with_context(&mut isolate, global_context); | |
let mut module_map = module_map_rc.borrow_mut(); | |
module_map.update_with_snapshotted_data(scope, snapshotted_data); | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, so without it I believe Deno.core.*
APIs won't be available
snapshot_util::set_snapshotted_data( | ||
&mut scope, | ||
context, | ||
realm.context().clone(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a bit worried about all the clones that are happening when accessing context. But let's address that in a follow up
Only because |
Maybe we could put an empty |
Appears that's currently not possible, because |
I think I'd be fine with initializing |
Oh, never mind, that by itself would be undefined behavior since |
@piscisaureus suggested other solution. I will test it first on current |
This has now landed on the deno_core repo. Closing. |
Part of denoland/deno_core#911.