-
Notifications
You must be signed in to change notification settings - Fork 29.9k
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
[wip] src, inspector: support opted-in VM contexts #14231
Conversation
Based on work by Bradley Farias <[email protected]> and Eugene Ostroukhov <[email protected]>. TODO: V8's console is injected unconditionally, which may be not desirable. Fixes: nodejs#7593 Refs: nodejs#9272
This is pretty close to a solution I started a couple days ago, but mine avoids refactoring Contextify, instead introducing a Anyway, I like your solution better @TimothyGu. |
@TimothyGu I'll push my branch and you can lift the test I wrote for it if you like. Same API. |
@refack Debug context is going to be removed by the end of this year. I'm not sure what you are referring to by "Inspector context". |
@jgoz Thanks, I'll have to look into how the inspector harness works, and your script is a good place to start! |
More specifically my question is does the two new endpoint have any use outside of module.exports = {
open: (port, host, wait) => open(port, host, !!wait),
close: process._debugEnd,
url: url, url: url,
+ Session,
+ attachContext,
+ detachContext
}; Which leads me to the (trivial) observation that this PR would be easier to review if you provided a draft doc and/or some tests... 🤷♂️ |
Unless there is another way to create V8 contexts in Node, I think you are correct that these are only useful for
Something worth testing would be calling |
A dilemma indeed... I have some weird ideas like adding an implementation module arg to runInDebugContext(code, require('inspector')); Or adding a scope method to require('inspector').runInScope(context); |
Not recommended for multiple runs on the same context.
@jgoz That would actually work, but only synchronously. That's still a better default behavior though, so I implemented it and added a big caveat in the documentation. I've added tests and docs, but still need to figure out the As I was talking to @addaleax at JSConf China, when On our side I foresee three options:
All three are implementable right now, but the first two are difficult to implement w/o side effects. I would prefer it if V8 adds a flag to disable this feature. I'm working on writing a CL to V8, but Google's custom tools require a bit of a setup. |
Unfortunately, the To @ak239, @hashseed, and the rest of @nodejs/v8: We are trying to add Inspector support for contexts created through Node.js's VM module. The way we do that can be roughly illustrated as: const inspector = require('inspector');
const vm = require('vm');
const sandbox = {
customProp: 42
};
vm.createContext(sandbox);
inspector.attachContext(sandbox);
vm.runInContext('newProp = customProp + 1;', sandbox);
console.log(sandbox);
// Prints { customProp: 42, newProp: 43 } Now, V8's This is not really a problem for the main (top) context, as we already provide a global In the V8 version we currently have, the Please offer some ideas about how we can and should proceed re. this issue. Thanks! |
@@ -43,6 +48,10 @@ using v8_inspector::V8Inspector; | |||
static uv_sem_t start_io_thread_semaphore; | |||
static uv_async_t start_io_thread_async; | |||
|
|||
// Used in NodeInspectorClient::currentTimeMS() below. | |||
const int NANOS_PER_MSEC = 1000000; |
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've you just moved this line, but maybe do const double NANOS_PER_MSEC = 1E6;
since AFAICT it's only used for double arithmetic.
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.
High level LGTM
I'm still -0.5 on increasing the surface area of the public inspector
module.
The global assignment line line from v8/v8@54271c2: JSObject::AddProperty(global, name, console, DONT_ENUM); AFAICT the property is non enumerable so |
Okay, the enumerable bit notwithstanding, |
Ack. Having the ability to flag V8 not to add it, is best. |
Deleting the console property after creating the context sounds like the simplest solution. You could also get it removed in the snapshot by deleting it before creating the snapshot, but then it will be missing in the main context. That could be solved by actually serializing two separate contexts, but that adds to the snapshot size... V8 includes the console object as builtin precisely to be able to take advantage of the snapshot. |
(if support for inspector is available in the Node.js build). If not, and if | ||
`doNotInformInspector` is not specified, this function attaches it to inspector | ||
using [`inspector.attachContext()`][], and detaches it before returning. | ||
However, *it is still recommended that you handle context attachment and |
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.
Please avoid the use of you
in the docs :-)
(if support for inspector is available in the Node.js build). If not, and if | ||
`doNotInformInspector` is not specified, this function attaches it to inspector | ||
using [`inspector.attachContext()`][], and detaches it before returning. | ||
However, *it is still recommended that you handle context attachment and |
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.
Here too... please avoid you
@hashseed what would be the implications of adding: Handle<JSObject> console = factory->NewJSObject(cons, TENURED);
DCHECK(console->IsJSObject());
+ if (!global.has(name)) {
JSObject::AddProperty(global, name, console, DONT_ENUM);
+ } |
It doesn't work. We only create one context to capture snapshot from, which acts as template for all contexts. The sandboxed object is not even copied into the global object, but hooked into it via interceptor, right? We do have a step where we copy over all properties from the deserialized global object into the global object created from object template. Maybe we can hook that into that place. Iirc the interceptor is disabled during bootstrapping though? |
|
||
|
||
Runs the compiled code contained by the `vm.Script` object within the given | ||
`contextifiedSandbox` and returns the result. Running code does not have access | ||
to local scope. | ||
|
||
By default, this function checks if inspector was already aware of the context | ||
(if support for inspector is available in the Node.js build). If not, and if | ||
`doNotInformInspector` is not specified, this function attaches it to inspector |
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.
Lots of negatives in here, which might get confusing. How about something like this:
This function will attach the context to inspector (in Node.js builds with inspector support) using [
inspector.attachContext()
][] before running the script and detach it with [inspector.detachContext()
][] before returning. The context will only be attached if inspector was not already aware of the context, or ifdoNotInformInspector
is unspecified orfalse
. Note that it is recommended to handle context attachment and detachment manually, since:
|
||
The `vm.runInContext()` method compiles `code`, runs it within the context of | ||
the `contextifiedSandbox`, then returns the result. Running code does not have | ||
access to the local scope. The `contextifiedSandbox` object *must* have been | ||
previously [contextified][] using the [`vm.createContext()`][] method. | ||
|
||
By default, this function checks if inspector was already aware of the context | ||
(if support for inspector is available in the Node.js build). If not, and if | ||
`doNotInformInspector` is not specified, this function attaches it to inspector |
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.
Same recommended rewording as my above comment.
@TimothyGu Given the caveats you outlined in the documentation for automatic attachment/detachment in |
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 strongly feel like this is not a proper fix as this really needs to be fixed in V8... What will happen to this new API if/when the bug is fixed in V8?
@@ -102,6 +137,7 @@ class Agent { | |||
std::unique_ptr<NodeInspectorClient> client_; | |||
std::unique_ptr<InspectorIo> io_; | |||
v8::Platform* platform_; | |||
std::vector<const node::inspector::ContextInfo*> contexts_; |
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.
Please move this to NodeInspectorClient
session->server_->MessageReceived(session->id_, | ||
std::string(buf->base, read)); | ||
std::string str(buf->base, read); | ||
#if DEBUG_TRANSACTION |
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.
Please remove this before committing.
I think we need to fix it on inspector side and allow V8 embedders not to call contextDestroyed and then we can just call contextCreated when context is created in Node.js. |
@eugeneo, if you guys can fix it on the V8 side -- even better! We still need some sort of API to provide context name (since @hashseed Right now it is actually both copied and intercepted. Because the copying is done synchronously, async in VM contexts is already not supported perfectly so I'd be fine with leaving it that way (/cc @jgoz). In fact, this copying step has led to a lot of problems. @AnnaMag is doing some work to rectify that situation in #13265. And yes, the interceptors are supposed to be disabled during bootstrapping. |
@hashseed, @TimothyGu Sandbox object is intercepted, while the copying step happens after and is a filler only for the own properties (in the global context) that were not set on the sandbox via the interceptors. |
I would appreciate if you checked out https://github.com/eugeneo/node/tree/multicontext-v8-patch, as an alternative to that patch. That branch includes a pending V8 change backported to the Node. I am testing right now, looks like it mostly works - e.g. no crashes and some context are even getting garbage collected... Remaining issues are:
I am planning to start a new PR if we manage to resolve issue #2 and if the V8 changes get landed into that tree. |
@eugeneo I'd be happy to defer to your branch if the V8 issues are resolved. |
Closed in favor of #14465. |
The `auxData` field is not exposed to JavaScript, as DevTools uses it for its `isDefault` parameter, which is implemented faithfully, contributing to the nice indentation in the context selection panel. Without the indentation, when `Target` domain gets implemented (along with a single Inspector for cluster) in #16627, subprocesses and VM contexts will be mixed up, causing confusion. PR-URL: #17720 Refs: #14231 (comment) Reviewed-By: Ben Noordhuis <[email protected]> Reviewed-By: Jon Moss <[email protected]> Reviewed-By: James M Snell <[email protected]>
The `auxData` field is not exposed to JavaScript, as DevTools uses it for its `isDefault` parameter, which is implemented faithfully, contributing to the nice indentation in the context selection panel. Without the indentation, when `Target` domain gets implemented (along with a single Inspector for cluster) in #16627, subprocesses and VM contexts will be mixed up, causing confusion. PR-URL: #17720 Refs: #14231 (comment) Reviewed-By: Ben Noordhuis <[email protected]> Reviewed-By: Jon Moss <[email protected]> Reviewed-By: James M Snell <[email protected]>
The `auxData` field is not exposed to JavaScript, as DevTools uses it for its `isDefault` parameter, which is implemented faithfully, contributing to the nice indentation in the context selection panel. Without the indentation, when `Target` domain gets implemented (along with a single Inspector for cluster) in #16627, subprocesses and VM contexts will be mixed up, causing confusion. PR-URL: #17720 Refs: #14231 (comment) Reviewed-By: Ben Noordhuis <[email protected]> Reviewed-By: Jon Moss <[email protected]> Reviewed-By: James M Snell <[email protected]>
The `auxData` field is not exposed to JavaScript, as DevTools uses it for its `isDefault` parameter, which is implemented faithfully, contributing to the nice indentation in the context selection panel. Without the indentation, when `Target` domain gets implemented (along with a single Inspector for cluster) in #16627, subprocesses and VM contexts will be mixed up, causing confusion. PR-URL: #17720 Refs: #14231 (comment) Reviewed-By: Ben Noordhuis <[email protected]> Reviewed-By: Jon Moss <[email protected]> Reviewed-By: James M Snell <[email protected]>
Works, doesn't crash
, but no tests or docs at this moment.V8's console is injected unconditionally upon every
attachContext
call, which may be not desirable.Uses an approach inspired by @jgoz in #7593 (comment).
/cc @eugeneo @bmeck
Fixes: #7593
Refs: #9272
Checklist
make -j4 test
(UNIX), orvcbuild test
(Windows) passesAffected core subsystem(s)
inspector