diff --git a/src/api/environment.cc b/src/api/environment.cc index 548b685dfe424a..81c95f79cc967f 100644 --- a/src/api/environment.cc +++ b/src/api/environment.cc @@ -244,12 +244,13 @@ Environment* CreateEnvironment(IsolateData* isolate_data, Environment* env = new Environment( isolate_data, context, + args, + exec_args, static_cast(Environment::kIsMainThread | Environment::kOwnsProcessState | Environment::kOwnsInspector)); env->InitializeLibuv(per_process::v8_is_profiling); - env->ProcessCliArgs(args, exec_args); - if (RunBootstrapping(env).IsEmpty()) { + if (env->RunBootstrapping().IsEmpty()) { return nullptr; } diff --git a/src/env.cc b/src/env.cc index 5f408f18f146a4..12f78db81fbee6 100644 --- a/src/env.cc +++ b/src/env.cc @@ -234,8 +234,36 @@ uint64_t Environment::AllocateThreadId() { return next_thread_id++; } +void Environment::CreateProperties() { + HandleScope handle_scope(isolate_); + Local ctx = context(); + Local templ = FunctionTemplate::New(isolate()); + templ->InstanceTemplate()->SetInternalFieldCount(1); + Local obj = templ->GetFunction(ctx) + .ToLocalChecked() + ->NewInstance(ctx) + .ToLocalChecked(); + obj->SetAlignedPointerInInternalField(0, this); + set_as_callback_data(obj); + set_as_callback_data_template(templ); + + // Store primordials setup by the per-context script in the environment. + Local per_context_bindings = + GetPerContextExports(ctx).ToLocalChecked(); + Local primordials = + per_context_bindings->Get(ctx, primordials_string()).ToLocalChecked(); + CHECK(primordials->IsObject()); + set_primordials(primordials.As()); + + Local process_object = + node::CreateProcessObject(this).FromMaybe(Local()); + set_process_object(process_object); +} + Environment::Environment(IsolateData* isolate_data, Local context, + const std::vector& args, + const std::vector& exec_args, Flags flags, uint64_t thread_id) : isolate_(context->GetIsolate()), @@ -243,6 +271,8 @@ Environment::Environment(IsolateData* isolate_data, immediate_info_(context->GetIsolate()), tick_info_(context->GetIsolate()), timer_base_(uv_now(isolate_data->event_loop())), + exec_argv_(exec_args), + argv_(args), should_abort_on_uncaught_toggle_(isolate_, 1), stream_base_state_(isolate_, StreamBase::kNumStreamBaseStateFields), flags_(flags), @@ -253,16 +283,6 @@ Environment::Environment(IsolateData* isolate_data, // We'll be creating new objects so make sure we've entered the context. HandleScope handle_scope(isolate()); Context::Scope context_scope(context); - { - Local templ = FunctionTemplate::New(isolate()); - templ->InstanceTemplate()->SetInternalFieldCount(1); - Local obj = - templ->GetFunction(context).ToLocalChecked()->NewInstance( - context).ToLocalChecked(); - obj->SetAlignedPointerInInternalField(0, this); - set_as_callback_data(obj); - set_as_callback_data_template(templ); - } set_env_vars(per_process::system_environment); @@ -305,6 +325,22 @@ Environment::Environment(IsolateData* isolate_data, performance::NODE_PERFORMANCE_MILESTONE_V8_START, performance::performance_v8_start); + if (*TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED( + TRACING_CATEGORY_NODE1(environment)) != 0) { + auto traced_value = tracing::TracedValue::Create(); + traced_value->BeginArray("args"); + for (const std::string& arg : args) traced_value->AppendString(arg); + traced_value->EndArray(); + traced_value->BeginArray("exec_args"); + for (const std::string& arg : exec_args) traced_value->AppendString(arg); + traced_value->EndArray(); + TRACE_EVENT_NESTABLE_ASYNC_BEGIN1(TRACING_CATEGORY_NODE1(environment), + "Environment", + this, + "args", + std::move(traced_value)); + } + // By default, always abort when --abort-on-uncaught-exception was passed. should_abort_on_uncaught_toggle_[0] = 1; @@ -312,11 +348,13 @@ Environment::Environment(IsolateData* isolate_data, credentials::SafeGetenv("NODE_DEBUG_NATIVE", &debug_cats, this); set_debug_categories(debug_cats, true); - isolate()->GetHeapProfiler()->AddBuildEmbedderGraphCallback( - BuildEmbedderGraph, this); if (options_->no_force_async_hooks_checks) { async_hooks_.no_force_checks(); } + + // TODO(joyeecheung): deserialize when the snapshot covers the environment + // properties. + CreateProperties(); } CompileFnEntry::CompileFnEntry(Environment* env, uint32_t id) @@ -433,35 +471,6 @@ void Environment::ExitEnv() { isolate_->TerminateExecution(); } -MaybeLocal Environment::ProcessCliArgs( - const std::vector& args, - const std::vector& exec_args) { - argv_ = args; - exec_argv_ = exec_args; - - if (*TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED( - TRACING_CATEGORY_NODE1(environment)) != 0) { - auto traced_value = tracing::TracedValue::Create(); - traced_value->BeginArray("args"); - for (const std::string& arg : args) traced_value->AppendString(arg); - traced_value->EndArray(); - traced_value->BeginArray("exec_args"); - for (const std::string& arg : exec_args) traced_value->AppendString(arg); - traced_value->EndArray(); - TRACE_EVENT_NESTABLE_ASYNC_BEGIN1(TRACING_CATEGORY_NODE1(environment), - "Environment", - this, - "args", - std::move(traced_value)); - } - - Local process_object = - node::CreateProcessObject(this, args, exec_args) - .FromMaybe(Local()); - set_process_object(process_object); - return process_object; -} - void Environment::RegisterHandleCleanups() { HandleCleanupCb close_and_finish = [](Environment* env, uv_handle_t* handle, void* arg) { diff --git a/src/env.h b/src/env.h index 7b7e9f61320a36..fb25ee9845d20a 100644 --- a/src/env.h +++ b/src/env.h @@ -74,6 +74,10 @@ namespace profiler { class V8CoverageConnection; class V8CpuProfilerConnection; } // namespace profiler + +namespace inspector { +class ParentInspectorHandle; +} #endif // HAVE_INSPECTOR namespace worker { @@ -794,6 +798,19 @@ class Environment : public MemoryRetainer { bool IsRootNode() const override { return true; } void MemoryInfo(MemoryTracker* tracker) const override; + void CreateProperties(); + // Should be called before InitializeInspector() + void InitializeDiagnostics(); +#if HAVE_INSPECTOR && NODE_USE_V8_PLATFORM + // If the environment is created for a worker, pass parent_handle and + // the ownership if transferred into the Environment. + int InitializeInspector(inspector::ParentInspectorHandle* parent_handle); +#endif + + v8::MaybeLocal BootstrapInternalLoaders(); + v8::MaybeLocal BootstrapNode(); + v8::MaybeLocal RunBootstrapping(); + inline size_t async_callback_scope_depth() const; inline void PushAsyncCallbackScope(); inline void PopAsyncCallbackScope(); @@ -821,14 +838,13 @@ class Environment : public MemoryRetainer { Environment(IsolateData* isolate_data, v8::Local context, + const std::vector& args, + const std::vector& exec_args, Flags flags = Flags(), uint64_t thread_id = kNoThreadId); ~Environment(); void InitializeLibuv(bool start_profiler_idle_notifier); - v8::MaybeLocal ProcessCliArgs( - const std::vector& args, - const std::vector& exec_args); inline const std::vector& exec_argv(); inline const std::vector& argv(); diff --git a/src/inspector/worker_inspector.h b/src/inspector/worker_inspector.h index 9f5604e79aba2d..cf483ae49ebbe1 100644 --- a/src/inspector/worker_inspector.h +++ b/src/inspector/worker_inspector.h @@ -60,6 +60,7 @@ class ParentInspectorHandle { bool WaitForConnect() { return wait_; } + const std::string& url() const { return url_; } private: int id_; diff --git a/src/node.cc b/src/node.cc index 7dccd7ce576828..711e9c305553b7 100644 --- a/src/node.cc +++ b/src/node.cc @@ -46,6 +46,7 @@ #endif #if HAVE_INSPECTOR +#include "inspector_agent.h" #include "inspector_io.h" #endif @@ -58,6 +59,10 @@ #endif // NODE_USE_V8_PLATFORM #include "v8-profiler.h" +#if HAVE_INSPECTOR +#include "inspector/worker_inspector.h" // ParentInspectorHandle +#endif + #ifdef NODE_ENABLE_VTUNE_PROFILING #include "../deps/v8/src/third_party/vtune/v8-vtune.h" #endif @@ -124,7 +129,6 @@ using options_parser::kAllowedInEnvironment; using options_parser::kDisallowedInEnvironment; using v8::Boolean; -using v8::Context; using v8::EscapableHandleScope; using v8::Exception; using v8::Function; @@ -135,7 +139,6 @@ using v8::Local; using v8::Maybe; using v8::MaybeLocal; using v8::Object; -using v8::Script; using v8::String; using v8::Undefined; using v8::V8; @@ -234,124 +237,156 @@ MaybeLocal ExecuteBootstrapper(Environment* env, return scope.EscapeMaybe(result); } -MaybeLocal RunBootstrapping(Environment* env) { - CHECK(!env->has_run_bootstrapping_code()); - - EscapableHandleScope scope(env->isolate()); - Isolate* isolate = env->isolate(); - Local context = env->context(); +#if HAVE_INSPECTOR && NODE_USE_V8_PLATFORM +int Environment::InitializeInspector( + inspector::ParentInspectorHandle* parent_handle) { + std::string inspector_path; + if (parent_handle != nullptr) { + DCHECK(!is_main_thread()); + inspector_path = parent_handle->url(); + inspector_agent_->SetParentHandle( + std::unique_ptr(parent_handle)); + } else { + inspector_path = argv_.size() > 1 ? argv_[1].c_str() : ""; + } -#if HAVE_INSPECTOR - profiler::StartProfilers(env); -#endif // HAVE_INSPECTOR + CHECK(!inspector_agent_->IsListening()); + // Inspector agent can't fail to start, but if it was configured to listen + // right away on the websocket port and fails to bind/etc, this will return + // false. + inspector_agent_->Start(inspector_path, + options_->debug_options(), + inspector_host_port(), + is_main_thread()); + if (options_->debug_options().inspector_enabled && + !inspector_agent_->IsListening()) { + return 12; // Signal internal error + } - // Add a reference to the global object - Local global = context->Global(); + profiler::StartProfilers(this); -#if defined HAVE_DTRACE || defined HAVE_ETW - InitDTrace(env); -#endif + if (options_->debug_options().break_node_first_line) { + inspector_agent_->PauseOnNextJavascriptStatement("Break at bootstrap"); + } - Local process = env->process_object(); + return 0; +} +#endif // HAVE_INSPECTOR && NODE_USE_V8_PLATFORM - // Setting global properties for the bootstrappers to use: - // - global - // Expose the global object as a property on itself - // (Allows you to set stuff on `global` from anywhere in JavaScript.) - global->Set(context, FIXED_ONE_BYTE_STRING(env->isolate(), "global"), global) - .Check(); +void Environment::InitializeDiagnostics() { + isolate_->GetHeapProfiler()->AddBuildEmbedderGraphCallback( + Environment::BuildEmbedderGraph, this); - // Store primordials setup by the per-context script in the environment. - Local per_context_bindings; - Local primordials; - if (!GetPerContextExports(context).ToLocal(&per_context_bindings) || - !per_context_bindings->Get(context, env->primordials_string()) - .ToLocal(&primordials) || - !primordials->IsObject()) { - return MaybeLocal(); - } - env->set_primordials(primordials.As()); +#if defined HAVE_DTRACE || defined HAVE_ETW + InitDTrace(this); +#endif +} -#if HAVE_INSPECTOR - if (env->options()->debug_options().break_node_first_line) { - env->inspector_agent()->PauseOnNextJavascriptStatement( - "Break at bootstrap"); - } -#endif // HAVE_INSPECTOR +MaybeLocal Environment::BootstrapInternalLoaders() { + EscapableHandleScope scope(isolate_); // Create binding loaders std::vector> loaders_params = { - env->process_string(), - FIXED_ONE_BYTE_STRING(isolate, "getLinkedBinding"), - FIXED_ONE_BYTE_STRING(isolate, "getInternalBinding"), - env->primordials_string()}; + process_string(), + FIXED_ONE_BYTE_STRING(isolate_, "getLinkedBinding"), + FIXED_ONE_BYTE_STRING(isolate_, "getInternalBinding"), + primordials_string()}; std::vector> loaders_args = { - process, - env->NewFunctionTemplate(binding::GetLinkedBinding) - ->GetFunction(context) + process_object(), + NewFunctionTemplate(binding::GetLinkedBinding) + ->GetFunction(context()) .ToLocalChecked(), - env->NewFunctionTemplate(binding::GetInternalBinding) - ->GetFunction(context) + NewFunctionTemplate(binding::GetInternalBinding) + ->GetFunction(context()) .ToLocalChecked(), - env->primordials()}; + primordials()}; // Bootstrap internal loaders - MaybeLocal loader_exports = ExecuteBootstrapper( - env, "internal/bootstrap/loaders", &loaders_params, &loaders_args); - if (loader_exports.IsEmpty()) { + Local loader_exports; + if (!ExecuteBootstrapper( + this, "internal/bootstrap/loaders", &loaders_params, &loaders_args) + .ToLocal(&loader_exports)) { return MaybeLocal(); } - - Local loader_exports_obj = - loader_exports.ToLocalChecked().As(); + CHECK(loader_exports->IsObject()); + Local loader_exports_obj = loader_exports.As(); Local internal_binding_loader = - loader_exports_obj->Get(context, env->internal_binding_string()) + loader_exports_obj->Get(context(), internal_binding_string()) .ToLocalChecked(); - env->set_internal_binding_loader(internal_binding_loader.As()); - + CHECK(internal_binding_loader->IsFunction()); + set_internal_binding_loader(internal_binding_loader.As()); Local require = - loader_exports_obj->Get(context, env->require_string()).ToLocalChecked(); - env->set_native_module_require(require.As()); + loader_exports_obj->Get(context(), require_string()).ToLocalChecked(); + CHECK(require->IsFunction()); + set_native_module_require(require.As()); + + return scope.Escape(loader_exports); +} + +MaybeLocal Environment::BootstrapNode() { + EscapableHandleScope scope(isolate_); + + Local global = context()->Global(); + // TODO(joyeecheung): this can be done in JS land now. + global->Set(context(), FIXED_ONE_BYTE_STRING(isolate_, "global"), global) + .Check(); // process, require, internalBinding, isMainThread, // ownsProcessState, primordials std::vector> node_params = { - env->process_string(), - env->require_string(), - env->internal_binding_string(), - FIXED_ONE_BYTE_STRING(isolate, "isMainThread"), - FIXED_ONE_BYTE_STRING(isolate, "ownsProcessState"), - env->primordials_string()}; + process_string(), + require_string(), + internal_binding_string(), + FIXED_ONE_BYTE_STRING(isolate_, "isMainThread"), + FIXED_ONE_BYTE_STRING(isolate_, "ownsProcessState"), + primordials_string()}; std::vector> node_args = { - process, - require, - internal_binding_loader, - Boolean::New(isolate, env->is_main_thread()), - Boolean::New(isolate, env->owns_process_state()), - env->primordials()}; + process_object(), + native_module_require(), + internal_binding_loader(), + Boolean::New(isolate_, is_main_thread()), + Boolean::New(isolate_, owns_process_state()), + primordials()}; MaybeLocal result = ExecuteBootstrapper( - env, "internal/bootstrap/node", &node_params, &node_args); + this, "internal/bootstrap/node", &node_params, &node_args); Local env_var_proxy; - if (!CreateEnvVarProxy(context, isolate, env->as_callback_data()) + if (!CreateEnvVarProxy(context(), isolate_, as_callback_data()) .ToLocal(&env_var_proxy) || - process - ->Set(env->context(), - FIXED_ONE_BYTE_STRING(env->isolate(), "env"), - env_var_proxy) - .IsNothing()) + process_object() + ->Set( + context(), FIXED_ONE_BYTE_STRING(isolate_, "env"), env_var_proxy) + .IsNothing()) { + return MaybeLocal(); + } + + return scope.EscapeMaybe(result); +} + +MaybeLocal Environment::RunBootstrapping() { + EscapableHandleScope scope(isolate_); + + CHECK(!has_run_bootstrapping_code()); + + if (BootstrapInternalLoaders().IsEmpty()) { + return MaybeLocal(); + } + + Local result; + if (!BootstrapNode().ToLocal(&result)) { return MaybeLocal(); + } // Make sure that no request or handle is created during bootstrap - // if necessary those should be done in pre-execution. // TODO(joyeecheung): print handles/requests before aborting - CHECK(env->req_wrap_queue()->IsEmpty()); - CHECK(env->handle_wrap_queue()->IsEmpty()); + CHECK(req_wrap_queue()->IsEmpty()); + CHECK(handle_wrap_queue()->IsEmpty()); - env->set_has_run_bootstrapping_code(true); + set_has_run_bootstrapping_code(true); - return scope.EscapeMaybe(result); + return scope.Escape(result); } void MarkBootstrapComplete(const FunctionCallbackInfo& args) { diff --git a/src/node_internals.h b/src/node_internals.h index cb039f297b190b..2e5acfdebb3e98 100644 --- a/src/node_internals.h +++ b/src/node_internals.h @@ -296,7 +296,6 @@ void DefineZlibConstants(v8::Local target); v8::Isolate* NewIsolate(v8::Isolate::CreateParams* params, uv_loop_t* event_loop, MultiIsolatePlatform* platform); -v8::MaybeLocal RunBootstrapping(Environment* env); v8::MaybeLocal StartExecution(Environment* env, const char* main_script_id); v8::MaybeLocal GetPerContextExports(v8::Local context); diff --git a/src/node_main_instance.cc b/src/node_main_instance.cc index f4f20f43222cf7..5c84dd64e6fa64 100644 --- a/src/node_main_instance.cc +++ b/src/node_main_instance.cc @@ -188,34 +188,24 @@ std::unique_ptr NodeMainInstance::CreateMainEnvironment( std::unique_ptr env = std::make_unique( isolate_data_.get(), context, + args_, + exec_args_, static_cast(Environment::kIsMainThread | Environment::kOwnsProcessState | Environment::kOwnsInspector)); env->InitializeLibuv(per_process::v8_is_profiling); - env->ProcessCliArgs(args_, exec_args_); + env->InitializeDiagnostics(); + // TODO(joyeecheung): when we snapshot the bootstrapped context, + // the inspector and diagnostics setup should after after deserialization. #if HAVE_INSPECTOR && NODE_USE_V8_PLATFORM - CHECK(!env->inspector_agent()->IsListening()); - // Inspector agent can't fail to start, but if it was configured to listen - // right away on the websocket port and fails to bind/etc, this will return - // false. - env->inspector_agent()->Start(args_.size() > 1 ? args_[1].c_str() : "", - env->options()->debug_options(), - env->inspector_host_port(), - true); - if (env->options()->debug_options().inspector_enabled && - !env->inspector_agent()->IsListening()) { - *exit_code = 12; // Signal internal error. + *exit_code = env->InitializeInspector(nullptr); +#endif + if (*exit_code != 0) { return env; } -#else - // inspector_enabled can't be true if !HAVE_INSPECTOR or - // !NODE_USE_V8_PLATFORM - // - the option parser should not allow that. - CHECK(!env->options()->debug_options().inspector_enabled); -#endif // HAVE_INSPECTOR && NODE_USE_V8_PLATFORM - - if (RunBootstrapping(env.get()).IsEmpty()) { + + if (env->RunBootstrapping().IsEmpty()) { *exit_code = 1; } diff --git a/src/node_process.h b/src/node_process.h index f01445b3c515d0..48d5aa704f71e9 100644 --- a/src/node_process.h +++ b/src/node_process.h @@ -31,10 +31,7 @@ v8::Maybe ProcessEmitDeprecationWarning(Environment* env, const char* warning, const char* deprecation_code); -v8::MaybeLocal CreateProcessObject( - Environment* env, - const std::vector& args, - const std::vector& exec_args); +v8::MaybeLocal CreateProcessObject(Environment* env); void PatchProcessObject(const v8::FunctionCallbackInfo& args); namespace task_queue { diff --git a/src/node_process_object.cc b/src/node_process_object.cc index e533245703e8cc..45470254063418 100644 --- a/src/node_process_object.cc +++ b/src/node_process_object.cc @@ -68,10 +68,7 @@ static void GetParentProcessId(Local property, info.GetReturnValue().Set(uv_os_getppid()); } -MaybeLocal CreateProcessObject( - Environment* env, - const std::vector& args, - const std::vector& exec_args) { +MaybeLocal CreateProcessObject(Environment* env) { Isolate* isolate = env->isolate(); EscapableHandleScope scope(isolate); Local context = env->context(); diff --git a/src/node_worker.cc b/src/node_worker.cc index 20b883664da207..2b07619076cbc3 100644 --- a/src/node_worker.cc +++ b/src/node_worker.cc @@ -41,17 +41,6 @@ namespace worker { namespace { #if NODE_USE_V8_PLATFORM && HAVE_INSPECTOR -void StartWorkerInspector( - Environment* child, - std::unique_ptr parent_handle, - const std::string& url) { - child->inspector_agent()->SetParentHandle(std::move(parent_handle)); - child->inspector_agent()->Start(url, - child->options()->debug_options(), - child->inspector_host_port(), - false); -} - void WaitForWorkerInspectorToStop(Environment* child) { child->inspector_agent()->WaitForDisconnect(); child->inspector_agent()->Stop(); @@ -66,11 +55,10 @@ Worker::Worker(Environment* env, std::shared_ptr per_isolate_opts, std::vector&& exec_argv) : AsyncWrap(env, wrap, AsyncWrap::PROVIDER_WORKER), - url_(url), per_isolate_opts_(per_isolate_opts), exec_argv_(exec_argv), platform_(env->isolate_data()->platform()), - profiler_idle_notifier_started_(env->profiler_idle_notifier_started()), + start_profiler_idle_notifier_(env->profiler_idle_notifier_started()), thread_id_(Environment::AllocateThreadId()), env_vars_(env->env_vars()) { Debug(this, "Creating new worker instance with thread id %llu", thread_id_); @@ -255,6 +243,8 @@ void Worker::Run() { // public API. env_.reset(new Environment(data.isolate_data_.get(), context, + std::move(argv_), + std::move(exec_argv_), Environment::kNoFlags, thread_id_)); CHECK_NOT_NULL(env_); @@ -262,8 +252,7 @@ void Worker::Run() { env_->set_abort_on_uncaught_exception(false); env_->set_worker_context(this); - env_->InitializeLibuv(profiler_idle_notifier_started_); - env_->ProcessCliArgs(std::move(argv_), std::move(exec_argv_)); + env_->InitializeLibuv(start_profiler_idle_notifier_); } { Mutex::ScopedLock lock(mutex_); @@ -273,17 +262,15 @@ void Worker::Run() { Debug(this, "Created Environment for worker with id %llu", thread_id_); if (is_stopped()) return; { + env_->InitializeDiagnostics(); #if NODE_USE_V8_PLATFORM && HAVE_INSPECTOR - StartWorkerInspector(env_.get(), - std::move(inspector_parent_handle_), - url_); -#endif + env_->InitializeInspector(inspector_parent_handle_.release()); inspector_started = true; - +#endif HandleScope handle_scope(isolate_); AsyncCallbackScope callback_scope(env_.get()); env_->async_hooks()->push_async_ids(1, 0); - if (!RunBootstrapping(env_.get()).IsEmpty()) { + if (!env_->RunBootstrapping().IsEmpty()) { CreateEnvMessagePort(env_.get()); if (is_stopped()) return; Debug(this, "Created message port for worker %llu", thread_id_); diff --git a/src/node_worker.h b/src/node_worker.h index 8239826a0e8b04..0e659a9f522cda 100644 --- a/src/node_worker.h +++ b/src/node_worker.h @@ -54,7 +54,6 @@ class Worker : public AsyncWrap { private: void OnThreadStopped(); void CreateEnvMessagePort(Environment* env); - const std::string url_; std::shared_ptr per_isolate_opts_; std::vector exec_argv_; @@ -62,7 +61,7 @@ class Worker : public AsyncWrap { MultiIsolatePlatform* platform_; v8::Isolate* isolate_ = nullptr; - bool profiler_idle_notifier_started_; + bool start_profiler_idle_notifier_; uv_thread_t tid_; #if NODE_USE_V8_PLATFORM && HAVE_INSPECTOR