-
Notifications
You must be signed in to change notification settings - Fork 29.6k
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
embedding: allow creating context within inspectable node::Environment #31424
Conversation
Co-Authored-By: Anna Henningsen <[email protected]>
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.
If the goal is to call Environment::AssignToContext()
then I think the current approach is a little on the inflexible side.
For one, it turns the context into a Node.js context. That's something you as an embedder may not necessarily want because it means, for example, that Atomics.wake()
is stripped out.
A better (but still not perfect) approach is:
- Embedder creates a Context
- Embedder optionally calls InitializeContext() to turn it into a Node.js context
- Embedder calls AssignToContext()
(Open question: how much of ContextInfo to pass to node::AssignToContext()
. I'd rather not make that class public because that sets its API and ABI in stone.)
That said, I expect that what you really want is to be able to call v8_inspector::V8Inspector::contextCreated()
and v8_inspector::V8InspectorcontextDestroyed()
.
Maybe we should think about exposing Node's instance of v8_inspector::V8Inspector
. That probably requires some refactoring on our side because we currently treat it more as a per-context/per-environment property than the per-isolate property that it really is.
@@ -362,6 +362,13 @@ NODE_EXTERN v8::Local<v8::Context> NewContext( | |||
v8::Local<v8::ObjectTemplate> object_template = | |||
v8::Local<v8::ObjectTemplate>()); | |||
|
|||
// Create a new context for an existing environment. | |||
// This add several fields to make inspector work properly. |
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 add several fields to make inspector work properly. | |
// This adds several fields to make the inspector work properly. |
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 would disagree, since there are three major problems in what you have described:
- The API through which
v8_inspector::V8Inspector
is accessed must be aware both in compile-time and in runtime whether an inspector agent actually exists (currentlynode::Environment
does that); v8_inspector::V8Inspector::ContextCreated()
is intended to use from the context-creating functions. It is not a notification and it will happily re-assign thedebug_context_id
leading to undefined behavior for any outstanding requests from the frontend with the olddebug_context_id
.- In the V8 API there are per-isolate properties like
v8::Isolate::SetPrepareStackTraceCallback()
. If the environment does not handle that there should be another class in the hierarchy:node::MultiIsolatePlatform -> (per-isolate object) -> node::Environment
. This would require major untangling of thenode::Environment
and it is not related to the main goal here: creating a new context.
I think that hiding the context creation process steps from the API is way more stable than exposing the inspector agent to the embedder. Currently node::Environment
perform some per-isolate processes like v8::Isolate::SetPrepareStackTraceCallback
. In the V8 engine v8::Context
objects are part of v8::Isolate
, and there are certain API that are per-isolate. Therefore, if node::Environment
does not handle the per-isolate API, there must be another class that handles it (and node::Environment
would probably contains shared pointer towards it). And since the inspector agent would always be hidden within an instance of a node's class (see the first two points above), the best approach for the context creation process would be to have node::NewContext
that accepts the node's per-isolate object and perform node's specific initialization (like reporting the context to its inspector agent).
For the current minor version, node::Environment
is still per-isolate, and the exposed function signature allow proper initialization with provided embedder's v8::ObjectTemplate
while reporting to the inspector agent is not exposed and how it is done can be changed in the future versions.
The only problem is the initialize
argument. To become future-proof compatible, it would be better if the third parameter is some white-box (publicly exposed) structure, that can be expanded with other properties in the future versions.
The last parameter currently apply only to initialization. Using a structire instead ensures that the API will remain the same even when the structure changes.
8ae28ff
to
2935f72
Compare
Looks like this needs a rebase and more discussion to move forward. @Dragiyski, do you intend to keep moving this forward? |
No, as both |
Checklist
make -j4 test
(UNIX), orvcbuild test
(Windows) passesNodeJS allow native addons that can access V8 environment through V8 api or through
node
namespace. Currently there are two options to createv8::Context
:v8::Context::New
andnode::NewContext
. Once created the embedder can create JavaScript code usingv8::ScriptCompiler
. Unfortunately, this JS code cannot be inspected bynode --inspect
option. The reason is that the internal handle of thev8::Context
does not have associateddebug_context_id
and the inspector frontend is not notified for the creation of the new context. For example functions created in the new context throughv8::ScriptCompiler::CompileFunctionInContext
would not have a location and their source would not be visible in the inspector. This might be the module's intended behavior.However, C++ addons that compile JavaScript code operating in different context within the same isolate, might want to allow users to debug their code. This could only happen when a
node::Environment
exists with an existing inspector agent client. Embedders can still write their own inspector agents in case of different types of embedding, but C++ addons should rely on the existing client provided by the--inspect
option.This is a proposal for a solution to this problem. A C++ addon could create thin
v8::Context
(similar tov8::Context::New
) using polymorphic variant ofnode::NewContext
or alternatively it can specify parameter to create fullnode::NewContext
(with initialization). In both cases, since an environment is provided, it can be assigned to new context which will allow:ContextCreated
notification);v8::PrepareStackTraceCallback
does not modify stack traces for contexts that are not assigned to annode::Environment
; Embedder could potentially fix this by assign new callback, but the callback is assigned to an isolate and the current isolate might (and almost always will be) the node's isolate, thus rendering large part of the node's internal unusable (i.e. it is bad solution); With the new feature the embedder does not need to do that.node_contextify
is the way to create new context within nodejs's JavaScript environment. However, it is difficult (not impossible though) for C++ addon torequire('vm')
and the newly created context will have global based on some innerv8::FunctionTemplate
. C++ addons and other embedders might need to be able to createv8::Context
with an existingv8::ObjectTemplate
and still compile inspectable code.A more elaborate feature could be later implemented to handle situations where more than one
node::Environment
with an inspector agent exists for the same isolate, or to handle debugging code within anode::MultiIsolatePlatform
, etc. However, currently for C++ addons, this solution is better, because they will operate within only onenode::Environment
, and the solution does not require any changes to an existing functions, or any additional memory.