Skip to content
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

vm: add module support #17560

Closed
wants to merge 12 commits into from
Closed

Conversation

devsnek
Copy link
Member

@devsnek devsnek commented Dec 9, 2017

Checklist
  • make -j4 test (UNIX), or vcbuild test (Windows) passes
  • tests and/or benchmarks are included
  • documentation is changed or added
  • commit message follows commit guidelines
Affected core subsystem(s)

vm, module

@nodejs-github-bot nodejs-github-bot added the vm Issues and PRs related to the vm subsystem. label Dec 9, 2017
@bnoordhuis
Copy link
Member

Is this a work in progress? I ask because I don't see where it actually compiles the module in a different context.

From looking at module_wrap.cc, it uses the creation context of the ModuleWrap, which is the main context. It should enter the new context first before calling ScriptCompiler::CompileModule().

@devsnek
Copy link
Member Author

devsnek commented Dec 9, 2017

@bnoordhuis thats what i'm working on rn

@Trott Trott added the wip Issues and PRs that are still a work in progress. label Dec 9, 2017
@devsnek
Copy link
Member Author

devsnek commented Dec 9, 2017

@bnoordhuis maybe you can take a look at how i pass the context to ModuleWrap, it seems to sigsegv about 50% of the time but i can't trace the issue.

Copy link
Member

@bnoordhuis bnoordhuis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you undo the whitespace churn in node_contextify.cc? Flattening it is acceptable but not squashed along with functional changes.

@@ -50,6 +50,7 @@ class ModuleWrap : public BaseObject {
v8::Persistent<v8::String> url_;
bool linked_ = false;
std::unordered_map<std::string, v8::Persistent<v8::Promise>> resolve_cache_;
v8::Local<v8::Context> context_;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Must be a Persistent, Locals are only valid for the duration of their enclosing HandleScope.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thank you!! i completely overlooked this 😄

@devsnek
Copy link
Member Author

devsnek commented Dec 9, 2017

@bnoordhuis i'm pretty sure i had to do that in order to use it from module_wrap, if i'm wrong please let me know.

@bnoordhuis
Copy link
Member

Right, but then do it in a lead-up commit and save the functional changes for the follow-up commit.

@devsnek devsnek force-pushed the feature/vm-modules branch 2 times, most recently from 45d91e7 to 53616a7 Compare December 9, 2017 16:42
@devsnek devsnek changed the title [WIP] vm: add module support vm: add module support Dec 9, 2017
@devsnek
Copy link
Member Author

devsnek commented Dec 10, 2017

@Trott i think this is not a WIP anymore

@Trott Trott removed the wip Issues and PRs that are still a work in progress. label Dec 10, 2017
doc/api/vm.md Outdated
@@ -429,6 +429,30 @@ local scope, so the value `localVar` is changed. In this way
`vm.runInThisContext()` is much like an [indirect `eval()` call][], e.g.
`(0,eval)('code')`.

## vm.createModule(code[, options])
<!-- YAML
added:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we generally use REPLACEME here because that will be picked up by the release tooling

lib/vm.js Outdated
}

function createModule(src, {
url = 'vm:module',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we make these generated URL values unique for debugging?

Copy link
Member Author

@devsnek devsnek Dec 12, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't sure how to do that based on the input, maybe a counter variable (vm:module:1), or it could just stay vm:module. scripts get evalmachine.<anonymous> in the stack if they don't have a filename

@devsnek devsnek force-pushed the feature/vm-modules branch 5 times, most recently from 85cd447 to 2b3bb71 Compare December 12, 2017 21:43
@BridgeAR
Copy link
Member

@devsnek Without looking at the code right now - it seems like the commits are meant to land individually. Would you be so kind and name them properly and add a description about what that commit does and why it is implemented the way it is? 😃

@devsnek devsnek force-pushed the feature/vm-modules branch 4 times, most recently from 3f2458a to 261abf7 Compare December 12, 2017 23:22
@devsnek
Copy link
Member Author

devsnek commented Dec 13, 2017

any thoughts on exposing namespace sorta like run() -> { result, get namespace }

@TimothyGu
Copy link
Member

TimothyGu commented Dec 13, 2017

For this feature to be maximally useful, I would rather a lower-level API get exposed. The API should roughly correspond to the spec, thus allowing me to:

  1. Change the importing algorithm. For example, my vm.Module-using application may not want to use the default Node.js file-based algorithm, but instead return the script from an HTTP response. This includes setting the importing algorithm for dynamic imports.
  2. Execute the three steps separately:
    1. Parse the Module source text
    2. Instantiate the module (which will call the the import algorithm I provide it).
    3. Evaluate the instantiated module.
  3. Have access to future ECMAScript features, like being able to set import.meta properties.

There are also other things to consider. For example, if I have two different applications using such an API, they should not conflict with each other. I should also not be allowed to use a Module from one application to fulfill the importing needs of another application.

Some extras that may be useful include the function you mentioned - the function for getting the namespace (corresponding to GetModuleNamespace in spec-speak). But this should come after the core API described above gets established.

It should also be noted that even though the parsing step of the Module doesn't require a Context, at least not in the V8 API, it would probably be a good idea from our API to force a Module to be bound to a specific context from the start to reduce confusion. A special "UnboundModule" could come later.


Those are the requirements. Now here's my take on creating such an API. Note, the following was written without looking into how ModuleWrap is currently implemented, so it may be non-trivial to implement such a set of APIs. But there's no rush.

class ModuleEnvironment {
  // Takes a contextified sandbox object. Check if sandbox is indeed contextified,
  // and throw a TypeError if it is not.
  // Takes an optional `resolveImportedModule` callback, which when called with
  // 1. the parent Module and
  // 2. the specifier string
  // should return a Promise<Module> corresponding to the requested imported
  // module. If it is not specified, the Node.js default resolution algorithm should be
  // used.
  // This could be extended in the future to allow dynamic imports.
  constructor(sandbox, {
    resolveImportedModule = defaultResolver
  } = {}) {
    // ...
  }

  // Optional: create a ModuleEnvironment that bound to the top-level context.
  static createTopEnvironment({
    resolveImportedModule = defaultResolver
  }) {
    // ...
  }

  // Returns the contextified sandbox object.
  // Do not allow changing the context after the ModuleEnvironment has been
  // created.
  // Returns undefined when the context is the top-level context.
  get sandbox() {}
}

class Module {
  // Takes a ModuleEnvironment, a string of source text, and a URL denoting
  // the name of the file. Parses the string as a module (throwing errors if an
  // error occurred).
  // The options object should allow future extension, such as a callback to set
  // properties to `import.meta`.
  constructor(environment, text, { url = 'vm:module' } = {}) {
    // ...
  }

  // Return the bound ModuleEnvironment of this Module. Do NOT allow
  // changing the ModuleEnvironment.
  get environment() {
    // ...
  }

  // Returns one of the possible values for [[Status]] internal slot as listed in
  // https://tc39.github.io/ecma262/#table-38: 'uninstantiated', 'instantiating',
  // 'instantiated', 'evaluating', and 'evaluated'.
  // This should map pretty well to v8::Module::GetStatus().
  get status() {
    // ...
  }

  // Instantiate the Module. Check status is 'uninstantiated'. This will call the
  // resolveImportedModule callback registered with the ModuleEnvironment if
  // this Module has any imports. Check if the Module returned from
  // resolveImportedModule belongs to the same ModuleEnvironment or not.
  // Returns a Promise<void> determining if this operation finished successfully.
  // If it failed, then status should still remain 'uninstantiated' but the Module
  // should be invalidated against future attempts to re-instantiate it, at least until
  // we get evidence that reinstantiation is allowed by the spec and useful in
  // practice.
  instantiate() {
    // ...
  }

  // Evaluate the Module. Check status is 'instantiated'. Return the result of
  // evaluation. If an error occurred during evaluation, invalidate the Module so
  // that it cannot be used for any other purpose. Either way, change status to
  // 'evaluated' at the end.
  evaluate() {
    // ...
  }

  // Return the namespace object of this Module. Check status is 'evaluated'
  // and no error occurred during evaluation.
  getNamespaceObject() {
    // ...
  }
}

This is still a rough draft; whether it is sufficient for the most advanced of use cases or even if it is implementable is still unclear. I'll be asking a few people to look over it, but IMO this is how we should go forward -- with a planning stage where opinions are gathered rather than jumping straight into implementation.

devsnek pushed a commit to devsnek/node that referenced this pull request Feb 21, 2018
PR-URL: nodejs#17560
Reviewed-By: Michaël Zasso <targos@protonmail.com>
Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com>
devsnek added a commit to devsnek/node that referenced this pull request Feb 21, 2018
Adds vm.Module, which wraps around ModuleWrap to provide an interface
for developers to work with modules in a more reflective manner.

Co-authored-by: Timothy Gu <timothygu99@gmail.com>
PR-URL: nodejs#17560
Reviewed-By: Michaël Zasso <targos@protonmail.com>
Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com>
@devsnek devsnek mentioned this pull request Feb 21, 2018
2 tasks
MylesBorins pushed a commit that referenced this pull request Feb 21, 2018
Flattens ContextifyContext allows the context interface to be used in
other parts of the code base.

PR-URL: #17560
Reviewed-By: Michaël Zasso <targos@protonmail.com>
Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com>
MylesBorins pushed a commit that referenced this pull request Feb 21, 2018
Adds vm.Module, which wraps around ModuleWrap to provide an interface
for developers to work with modules in a more reflective manner.

Co-authored-by: Timothy Gu <timothygu99@gmail.com>
PR-URL: #17560
Reviewed-By: Michaël Zasso <targos@protonmail.com>
Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com>
MylesBorins pushed a commit that referenced this pull request Feb 21, 2018
PR-URL: #17560
Reviewed-By: Michaël Zasso <targos@protonmail.com>
Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com>
MylesBorins pushed a commit that referenced this pull request Feb 21, 2018
Flattens ContextifyContext allows the context interface to be used in
other parts of the code base.

PR-URL: #17560
Reviewed-By: Michaël Zasso <targos@protonmail.com>
Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com>
MylesBorins pushed a commit that referenced this pull request Feb 21, 2018
Adds vm.Module, which wraps around ModuleWrap to provide an interface
for developers to work with modules in a more reflective manner.

Co-authored-by: Timothy Gu <timothygu99@gmail.com>
PR-URL: #17560
Reviewed-By: Michaël Zasso <targos@protonmail.com>
Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com>
MylesBorins pushed a commit that referenced this pull request Feb 21, 2018
PR-URL: #17560
Reviewed-By: Michaël Zasso <targos@protonmail.com>
Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com>
@MylesBorins MylesBorins mentioned this pull request Feb 21, 2018
MylesBorins pushed a commit that referenced this pull request Feb 21, 2018
Flattens ContextifyContext allows the context interface to be used in
other parts of the code base.

PR-URL: #17560
Reviewed-By: Michaël Zasso <targos@protonmail.com>
Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com>
MylesBorins pushed a commit that referenced this pull request Feb 21, 2018
Adds vm.Module, which wraps around ModuleWrap to provide an interface
for developers to work with modules in a more reflective manner.

Co-authored-by: Timothy Gu <timothygu99@gmail.com>
PR-URL: #17560
Reviewed-By: Michaël Zasso <targos@protonmail.com>
Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com>
MylesBorins pushed a commit that referenced this pull request Feb 21, 2018
PR-URL: #17560
Reviewed-By: Michaël Zasso <targos@protonmail.com>
Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com>
MylesBorins added a commit that referenced this pull request Feb 21, 2018
Notable changes:

* async_hooks:
  - deprecate unsafe emit{Before,After} (Ali Ijaz Sheikh)
    #18513
  - rename PromiseWrap.parentId to PromiseWrap.isChainedPromise
    (Ali Ijaz Sheikh) #18633
* deps:
  - update node-inspect to 1.11.3 (Jan Krems)
    #18354
  - ICU 60.2 bump (Steven R. Loomis)
    #17687
  - Introduce ScriptOrModule and HostDefinedOptions to V8 (Jan Krems)
    #16889
* http:
  - add options to http.createServer() for `IncomingMessage` and
    `ServerReponse` (Peter Marton)
    #15752
* http2:
  - add http fallback options to .createServer (Peter Marton)
    #15752
* https:
  - Adds the remaining options from tls.createSecureContext() to the
    string generated by Agent#getName(). This allows https.request()
    to accept the options and generate unique sockets appropriately.
    (Jeff Principe)
    #16402
* inspector:
  - --inspect-brk for es modules (Guy Bedford)
    #18194
* lib:
  - allow process kill by signal number (Sam Roberts)
    #16944
* module:
  - enable dynamic import (Myles Borins)
    #18387
  - dynamic import is now supported (Jan Krems)
    #15713
* napi:
  - add methods to open/close callback scope (Michael Dawson)
    #18089
* src:
  - allow --perf-(basic-)?prof in NODE_OPTIONS (Leko)
    #17600
* vm:
  - add support for es modules (Gus Caplan)
    #17560

PR-URL: #18902
MylesBorins added a commit that referenced this pull request Feb 21, 2018
Notable changes:

* async_hooks:
  - deprecate unsafe emit{Before,After} (Ali Ijaz Sheikh)
    #18513
  - rename PromiseWrap.parentId to PromiseWrap.isChainedPromise
    (Ali Ijaz Sheikh) #18633
* deps:
  - update node-inspect to 1.11.3 (Jan Krems)
    #18354
  - ICU 60.2 bump (Steven R. Loomis)
    #17687
  - Introduce ScriptOrModule and HostDefinedOptions to V8 (Jan Krems)
    #16889
* http:
  - add options to http.createServer() for `IncomingMessage` and
    `ServerReponse` (Peter Marton)
    #15752
* http2:
  - add http fallback options to .createServer (Peter Marton)
    #15752
* https:
  - Adds the remaining options from tls.createSecureContext() to the
    string generated by Agent#getName(). This allows https.request()
    to accept the options and generate unique sockets appropriately.
    (Jeff Principe)
    #16402
* inspector:
  - --inspect-brk for es modules (Guy Bedford)
    #18194
* lib:
  - allow process kill by signal number (Sam Roberts)
    #16944
* module:
  - enable dynamic import (Myles Borins)
    #18387
  - dynamic import is now supported (Jan Krems)
    #15713
* napi:
  - add methods to open/close callback scope (Michael Dawson)
    #18089
* src:
  - allow --perf-(basic-)?prof in NODE_OPTIONS (Leko)
    #17600
* vm:
  - add support for es modules (Gus Caplan)
    #17560

PR-URL: #18902
MylesBorins added a commit that referenced this pull request Feb 22, 2018
Notable changes:

* async_hooks:
  - deprecate unsafe emit{Before,After} (Ali Ijaz Sheikh)
    #18513
  - rename PromiseWrap.parentId to PromiseWrap.isChainedPromise
    (Ali Ijaz Sheikh) #18633
* deps:
  - update node-inspect to 1.11.3 (Jan Krems)
    #18354
  - ICU 60.2 bump (Steven R. Loomis)
    #17687
  - Introduce ScriptOrModule and HostDefinedOptions to V8 (Jan Krems)
    #16889
* http:
  - add options to http.createServer() for `IncomingMessage` and
    `ServerReponse` (Peter Marton)
    #15752
* http2:
  - add http fallback options to .createServer (Peter Marton)
    #15752
* https:
  - Adds the remaining options from tls.createSecureContext() to the
    string generated by Agent#getName(). This allows https.request()
    to accept the options and generate unique sockets appropriately.
    (Jeff Principe)
    #16402
* inspector:
  - --inspect-brk for es modules (Guy Bedford)
    #18194
* lib:
  - allow process kill by signal number (Sam Roberts)
    #16944
* module:
  - enable dynamic import (Myles Borins)
    #18387
  - dynamic import is now supported (Jan Krems)
    #15713
* napi:
  - add methods to open/close callback scope (Michael Dawson)
    #18089
* src:
  - allow --perf-(basic-)?prof in NODE_OPTIONS (Leko)
    #17600
* vm:
  - add support for es modules (Gus Caplan)
    #17560

PR-URL: #18902
@devsnek devsnek removed the author ready PRs that have at least one approval, no pending requests for changes, and a CI started. label Mar 18, 2018
MayaLekova pushed a commit to MayaLekova/node that referenced this pull request May 8, 2018
Flattens ContextifyContext allows the context interface to be used in
other parts of the code base.

PR-URL: nodejs#17560
Reviewed-By: Michaël Zasso <targos@protonmail.com>
Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com>
MayaLekova pushed a commit to MayaLekova/node that referenced this pull request May 8, 2018
PR-URL: nodejs#17560
Reviewed-By: Michaël Zasso <targos@protonmail.com>
Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com>
MayaLekova pushed a commit to MayaLekova/node that referenced this pull request May 8, 2018
Adds vm.Module, which wraps around ModuleWrap to provide an interface
for developers to work with modules in a more reflective manner.

Co-authored-by: Timothy Gu <timothygu99@gmail.com>
PR-URL: nodejs#17560
Reviewed-By: Michaël Zasso <targos@protonmail.com>
Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com>
MayaLekova pushed a commit to MayaLekova/node that referenced this pull request May 8, 2018
Notable changes:

* async_hooks:
  - deprecate unsafe emit{Before,After} (Ali Ijaz Sheikh)
    nodejs#18513
  - rename PromiseWrap.parentId to PromiseWrap.isChainedPromise
    (Ali Ijaz Sheikh) nodejs#18633
* deps:
  - update node-inspect to 1.11.3 (Jan Krems)
    nodejs#18354
  - ICU 60.2 bump (Steven R. Loomis)
    nodejs#17687
  - Introduce ScriptOrModule and HostDefinedOptions to V8 (Jan Krems)
    nodejs#16889
* http:
  - add options to http.createServer() for `IncomingMessage` and
    `ServerReponse` (Peter Marton)
    nodejs#15752
* http2:
  - add http fallback options to .createServer (Peter Marton)
    nodejs#15752
* https:
  - Adds the remaining options from tls.createSecureContext() to the
    string generated by Agent#getName(). This allows https.request()
    to accept the options and generate unique sockets appropriately.
    (Jeff Principe)
    nodejs#16402
* inspector:
  - --inspect-brk for es modules (Guy Bedford)
    nodejs#18194
* lib:
  - allow process kill by signal number (Sam Roberts)
    nodejs#16944
* module:
  - enable dynamic import (Myles Borins)
    nodejs#18387
  - dynamic import is now supported (Jan Krems)
    nodejs#15713
* napi:
  - add methods to open/close callback scope (Michael Dawson)
    nodejs#18089
* src:
  - allow --perf-(basic-)?prof in NODE_OPTIONS (Leko)
    nodejs#17600
* vm:
  - add support for es modules (Gus Caplan)
    nodejs#17560

PR-URL: nodejs#18902
@codebytere
Copy link
Member

codebytere commented Aug 2, 2018

@devsnek 3bf34f27a1, 2033a9f436, and 0993fbe5b2 don't apply cleanly v8.x, can you backport to v8.x or add the dont-land label?

@TimothyGu
Copy link
Member

TimothyGu commented Aug 2, 2018

It's an experimental feature, so I'd say let's not land on an LTS branch.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
esm Issues and PRs related to the ECMAScript Modules implementation. vm Issues and PRs related to the vm subsystem.
Projects
None yet
Development

Successfully merging this pull request may close these issues.