diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/env.cc | 13 | ||||
-rw-r--r-- | src/env.h | 26 | ||||
-rw-r--r-- | src/node.cc | 216 | ||||
-rw-r--r-- | src/node_internals.h | 5 | ||||
-rw-r--r-- | src/node_native_module.cc | 20 | ||||
-rw-r--r-- | src/node_native_module.h | 24 | ||||
-rw-r--r-- | src/node_worker.cc | 12 |
7 files changed, 192 insertions, 124 deletions
diff --git a/src/env.cc b/src/env.cc index 78ed42f89a..8ecab92df7 100644 --- a/src/env.cc +++ b/src/env.cc @@ -332,9 +332,20 @@ void Environment::Start(bool start_profiler_idle_notifier) { uv_key_set(&thread_local_env, this); } -MaybeLocal<Object> Environment::CreateProcessObject( +MaybeLocal<Object> Environment::ProcessCliArgs( const std::vector<std::string>& args, const std::vector<std::string>& exec_args) { + if (args.size() > 1) { + std::string first_arg = args[1]; + if (first_arg == "inspect") { + execution_mode_ = ExecutionMode::kInspect; + } else if (first_arg == "debug") { + execution_mode_ = ExecutionMode::kDebug; + } else if (first_arg != "-") { + execution_mode_ = ExecutionMode::kRunMainModule; + } + } + if (*TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED( TRACING_CATEGORY_NODE1(environment)) != 0) { auto traced_value = tracing::TracedValue::Create(); @@ -315,7 +315,7 @@ constexpr size_t kFsStatsBufferLength = kFsStatsFieldsNumber * 2; V(write_host_object_string, "_writeHostObject") \ V(write_queue_size_string, "writeQueueSize") \ V(x_forwarded_string, "x-forwarded-for") \ - V(zero_return_string, "ZERO_RETURN") \ + V(zero_return_string, "ZERO_RETURN") #define ENVIRONMENT_STRONG_PERSISTENT_PROPERTIES(V) \ V(as_external, v8::External) \ @@ -355,11 +355,13 @@ constexpr size_t kFsStatsBufferLength = kFsStatsFieldsNumber * 2; V(http2session_on_stream_trailers_function, v8::Function) \ V(http2settings_constructor_template, v8::ObjectTemplate) \ V(http2stream_constructor_template, v8::ObjectTemplate) \ + V(internal_binding_loader, v8::Function) \ V(immediate_callback_function, v8::Function) \ V(inspector_console_extension_installer, v8::Function) \ V(libuv_stream_wrap_ctor_template, v8::FunctionTemplate) \ V(message_port, v8::Object) \ V(message_port_constructor_template, v8::FunctionTemplate) \ + V(native_module_require, v8::Function) \ V(performance_entry_callback, v8::Function) \ V(performance_entry_template, v8::Function) \ V(pipe_constructor_template, v8::FunctionTemplate) \ @@ -371,7 +373,6 @@ constexpr size_t kFsStatsBufferLength = kFsStatsFieldsNumber * 2; V(script_data_constructor_function, v8::Function) \ V(secure_context_constructor_template, v8::FunctionTemplate) \ V(shutdown_wrap_template, v8::ObjectTemplate) \ - V(start_execution_function, v8::Function) \ V(tcp_constructor_template, v8::FunctionTemplate) \ V(tick_callback_function, v8::Function) \ V(timers_callback_function, v8::Function) \ @@ -611,7 +612,7 @@ class Environment { ~Environment(); void Start(bool start_profiler_idle_notifier); - v8::MaybeLocal<v8::Object> CreateProcessObject( + v8::MaybeLocal<v8::Object> ProcessCliArgs( const std::vector<std::string>& args, const std::vector<std::string>& exec_args); @@ -928,6 +929,24 @@ class Environment { inline std::shared_ptr<EnvironmentOptions> options(); inline std::shared_ptr<HostPort> inspector_host_port(); + enum class ExecutionMode { + kDefault, + kInspect, // node inspect + kDebug, // node debug + kPrintHelp, // node --help + kPrintBashCompletion, // node --completion-bash + kProfProcess, // node --prof-process + kEvalString, // node --eval without --interactive + kCheckSyntax, // node --check (incompatible with --eval) + kRepl, + kEvalStdin, + kRunMainModule + }; + + inline ExecutionMode execution_mode() { return execution_mode_; } + + inline void set_execution_mode(ExecutionMode mode) { execution_mode_ = mode; } + private: inline void CreateImmediate(native_immediate_callback cb, void* data, @@ -937,6 +956,7 @@ class Environment { inline void ThrowError(v8::Local<v8::Value> (*fun)(v8::Local<v8::String>), const char* errmsg); + ExecutionMode execution_mode_ = ExecutionMode::kDefault; std::list<binding::DLib> loaded_addons_; v8::Isolate* const isolate_; IsolateData* const isolate_data_; diff --git a/src/node.cc b/src/node.cc index 881ace6e42..e67b16af83 100644 --- a/src/node.cc +++ b/src/node.cc @@ -100,6 +100,7 @@ #if defined(_MSC_VER) #include <direct.h> #include <io.h> +#define STDIN_FILENO 0 #else #include <pthread.h> #include <sys/resource.h> // getrlimit, setrlimit @@ -114,6 +115,7 @@ using v8::Array; using v8::Boolean; using v8::Context; using v8::DEFAULT; +using v8::EscapableHandleScope; using v8::Exception; using v8::Function; using v8::FunctionCallbackInfo; @@ -605,8 +607,20 @@ static MaybeLocal<Value> ExecuteBootstrapper( const char* id, std::vector<Local<String>>* parameters, std::vector<Local<Value>>* arguments) { - MaybeLocal<Value> ret = per_process::native_module_loader.CompileAndCall( - env->context(), id, parameters, arguments, env); + EscapableHandleScope scope(env->isolate()); + MaybeLocal<Function> maybe_fn = + per_process::native_module_loader.LookupAndCompile( + env->context(), id, parameters, env); + + if (maybe_fn.IsEmpty()) { + return MaybeLocal<Value>(); + } + + Local<Function> fn = maybe_fn.ToLocalChecked(); + MaybeLocal<Value> result = fn->Call(env->context(), + Undefined(env->isolate()), + arguments->size(), + arguments->data()); // If there was an error during bootstrap then it was either handled by the // FatalException handler or it's unrecoverable (e.g. max call stack @@ -615,44 +629,17 @@ static MaybeLocal<Value> ExecuteBootstrapper( // There are only two ways to have a stack size > 1: 1) the user manually // called MakeCallback or 2) user awaited during bootstrap, which triggered // _tickCallback(). - if (ret.IsEmpty()) { + if (result.IsEmpty()) { env->async_hooks()->clear_async_id_stack(); } - return ret; -} - -void LoadEnvironment(Environment* env) { - RunBootstrapping(env); - - // To allow people to extend Node in different ways, this hook allows - // one to drop a file lib/_third_party_main.js into the build - // directory which will be executed instead of Node's normal loading. - if (per_process::native_module_loader.Exists("_third_party_main")) { - StartExecution(env, "_third_party_main"); - } else { - // TODO(joyeecheung): create different scripts for different - // execution modes: - // - `main_thread_main.js` when env->is_main_thread() - // - `worker_thread_main.js` when !env->is_main_thread() - // - `run_third_party_main.js` for `_third_party_main` - // - `inspect_main.js` for `node inspect` - // - `mkcodecache_main.js` for the code cache generator - // - `print_help_main.js` for --help - // - `bash_completion_main.js` for --completion-bash - // - `internal/v8_prof_processor` for --prof-process - // And leave bootstrap/node.js dedicated to the setup of the environment. - // We may want to move this switch out of LoadEnvironment, especially for - // the per-process options. - StartExecution(env, nullptr); - } + return scope.EscapeMaybe(result); } -void RunBootstrapping(Environment* env) { +MaybeLocal<Value> RunBootstrapping(Environment* env) { CHECK(!env->has_run_bootstrapping_code()); - env->set_has_run_bootstrapping_code(true); - HandleScope handle_scope(env->isolate()); + EscapableHandleScope scope(env->isolate()); Isolate* isolate = env->isolate(); Local<Context> context = env->context(); @@ -702,14 +689,24 @@ void RunBootstrapping(Environment* env) { Boolean::New(isolate, env->options()->expose_internals)}; - MaybeLocal<Value> loader_exports; // Bootstrap internal loaders - loader_exports = ExecuteBootstrapper( + MaybeLocal<Value> loader_exports = ExecuteBootstrapper( env, "internal/bootstrap/loaders", &loaders_params, &loaders_args); if (loader_exports.IsEmpty()) { - return; + return MaybeLocal<Value>(); } + Local<Object> loader_exports_obj = + loader_exports.ToLocalChecked().As<Object>(); + Local<Value> internal_binding_loader = + loader_exports_obj->Get(context, env->internal_binding_string()) + .ToLocalChecked(); + env->set_internal_binding_loader(internal_binding_loader.As<Function>()); + + Local<Value> require = + loader_exports_obj->Get(context, env->require_string()).ToLocalChecked(); + env->set_native_module_require(require.As<Function>()); + // process, loaderExports, isMainThread std::vector<Local<String>> node_params = { env->process_string(), @@ -717,43 +714,107 @@ void RunBootstrapping(Environment* env) { FIXED_ONE_BYTE_STRING(isolate, "isMainThread")}; std::vector<Local<Value>> node_args = { process, - loader_exports.ToLocalChecked(), + loader_exports_obj, Boolean::New(isolate, env->is_main_thread())}; - Local<Value> start_execution; - if (!ExecuteBootstrapper( - env, "internal/bootstrap/node", &node_params, &node_args) - .ToLocal(&start_execution)) { - return; - } + MaybeLocal<Value> result = ExecuteBootstrapper( + env, "internal/bootstrap/node", &node_params, &node_args); - if (start_execution->IsFunction()) - env->set_start_execution_function(start_execution.As<Function>()); + env->set_has_run_bootstrapping_code(true); + + return scope.EscapeMaybe(result); } -void StartExecution(Environment* env, const char* main_script_id) { - HandleScope handle_scope(env->isolate()); - // We have to use Local<>::New because of the optimized way in which we access - // the object in the env->...() getters, which does not play well with - // resetting the handle while we're accessing the object through the Local<>. - Local<Function> start_execution = - Local<Function>::New(env->isolate(), env->start_execution_function()); - env->set_start_execution_function(Local<Function>()); - - if (start_execution.IsEmpty()) return; - - Local<Value> main_script_v; - if (main_script_id == nullptr) { - // TODO(joyeecheung): make this mandatory - we may also create an overload - // for main_script that is a Local<Function>. - main_script_v = Undefined(env->isolate()); - } else { - main_script_v = OneByteString(env->isolate(), main_script_id); +void MarkBootstrapComplete(const FunctionCallbackInfo<Value>& args) { + Environment* env = Environment::GetCurrent(args); + env->performance_state()->Mark( + performance::NODE_PERFORMANCE_MILESTONE_BOOTSTRAP_COMPLETE); +} + +MaybeLocal<Value> StartExecution(Environment* env, const char* main_script_id) { + EscapableHandleScope scope(env->isolate()); + CHECK_NE(main_script_id, nullptr); + + std::vector<Local<String>> parameters = { + env->process_string(), + env->require_string(), + env->internal_binding_string(), + FIXED_ONE_BYTE_STRING(env->isolate(), "markBootstrapComplete")}; + + std::vector<Local<Value>> arguments = { + env->process_object(), + env->native_module_require(), + env->internal_binding_loader(), + env->NewFunctionTemplate(MarkBootstrapComplete) + ->GetFunction(env->context()) + .ToLocalChecked()}; + + MaybeLocal<Value> result = + ExecuteBootstrapper(env, main_script_id, ¶meters, &arguments); + return scope.EscapeMaybe(result); +} + +MaybeLocal<Value> StartMainThreadExecution(Environment* env) { + // To allow people to extend Node in different ways, this hook allows + // one to drop a file lib/_third_party_main.js into the build + // directory which will be executed instead of Node's normal loading. + if (per_process::native_module_loader.Exists("_third_party_main")) { + return StartExecution(env, "internal/main/run_third_party_main"); + } + + if (env->execution_mode() == Environment::ExecutionMode::kInspect || + env->execution_mode() == Environment::ExecutionMode::kDebug) { + return StartExecution(env, "internal/main/inspect"); + } + + if (per_process::cli_options->print_help) { + env->set_execution_mode(Environment::ExecutionMode::kPrintHelp); + return StartExecution(env, "internal/main/print_help"); + } + + if (per_process::cli_options->print_bash_completion) { + env->set_execution_mode(Environment::ExecutionMode::kPrintBashCompletion); + return StartExecution(env, "internal/main/print_bash_completion"); + } + + if (env->options()->prof_process) { + env->set_execution_mode(Environment::ExecutionMode::kPrintBashCompletion); + return StartExecution(env, "internal/main/prof_process"); + } + + // -e/--eval without -i/--interactive + if (env->options()->has_eval_string && !env->options()->force_repl) { + env->set_execution_mode(Environment::ExecutionMode::kEvalString); + return StartExecution(env, "internal/main/eval_string"); + } + + if (env->options()->syntax_check_only) { + env->set_execution_mode(Environment::ExecutionMode::kCheckSyntax); + return StartExecution(env, "internal/main/check_syntax"); + } + + if (env->execution_mode() == Environment::ExecutionMode::kRunMainModule) { + return StartExecution(env, "internal/main/run_main_module"); + } + + if (env->options()->force_repl || uv_guess_handle(STDIN_FILENO) == UV_TTY) { + env->set_execution_mode(Environment::ExecutionMode::kRepl); + return StartExecution(env, "internal/main/repl"); } - Local<Value> argv[] = {main_script_v}; - USE(start_execution->Call( - env->context(), Undefined(env->isolate()), arraysize(argv), argv)); + env->set_execution_mode(Environment::ExecutionMode::kEvalStdin); + return StartExecution(env, "internal/main/eval_stdin"); +} + +void LoadEnvironment(Environment* env) { + CHECK(env->is_main_thread()); + // TODO(joyeecheung): Not all of the execution modes in + // StartMainThreadExecution() make sense for embedders. Pick the + // useful ones out, and allow embedders to customize the entry + // point more directly without using _third_party_main.js + if (!RunBootstrapping(env).IsEmpty()) { + USE(StartMainThreadExecution(env)); + } } @@ -1180,7 +1241,7 @@ Environment* CreateEnvironment(IsolateData* isolate_data, std::vector<std::string> exec_args(exec_argv, exec_argv + exec_argc); Environment* env = new Environment(isolate_data, context); env->Start(per_process::v8_is_profiling); - env->CreateProcessObject(args, exec_args); + env->ProcessCliArgs(args, exec_args); return env; } @@ -1220,7 +1281,7 @@ void FreePlatform(MultiIsolatePlatform* platform) { Local<Context> NewContext(Isolate* isolate, Local<ObjectTemplate> object_template) { - auto context = Context::New(isolate, nullptr, object_template); + Local<Context> context = Context::New(isolate, nullptr, object_template); if (context.IsEmpty()) return context; HandleScope handle_scope(isolate); @@ -1233,12 +1294,19 @@ Local<Context> NewContext(Isolate* isolate, std::vector<Local<String>> parameters = { FIXED_ONE_BYTE_STRING(isolate, "global")}; - std::vector<Local<Value>> arguments = {context->Global()}; - MaybeLocal<Value> result = per_process::native_module_loader.CompileAndCall( - context, "internal/per_context", ¶meters, &arguments, nullptr); + Local<Value> arguments[] = {context->Global()}; + MaybeLocal<Function> maybe_fn = + per_process::native_module_loader.LookupAndCompile( + context, "internal/per_context", ¶meters, nullptr); + if (maybe_fn.IsEmpty()) { + return Local<Context>(); + } + Local<Function> fn = maybe_fn.ToLocalChecked(); + MaybeLocal<Value> result = + fn->Call(context, Undefined(isolate), arraysize(arguments), arguments); + // Execution failed during context creation. + // TODO(joyeecheung): deprecate this signature and return a MaybeLocal. if (result.IsEmpty()) { - // Execution failed during context creation. - // TODO(joyeecheung): deprecate this signature and return a MaybeLocal. return Local<Context>(); } } @@ -1255,7 +1323,7 @@ inline int Start(Isolate* isolate, IsolateData* isolate_data, Context::Scope context_scope(context); Environment env(isolate_data, context); env.Start(per_process::v8_is_profiling); - env.CreateProcessObject(args, exec_args); + env.ProcessCliArgs(args, exec_args); #if HAVE_INSPECTOR && NODE_USE_V8_PLATFORM CHECK(!env.inspector_agent()->IsListening()); diff --git a/src/node_internals.h b/src/node_internals.h index 0d4fe74ebf..bf66c77e6f 100644 --- a/src/node_internals.h +++ b/src/node_internals.h @@ -268,8 +268,9 @@ bool SafeGetenv(const char* key, std::string* text); void DefineZlibConstants(v8::Local<v8::Object> target); -void RunBootstrapping(Environment* env); -void StartExecution(Environment* env, const char* main_script_id); +v8::MaybeLocal<v8::Value> RunBootstrapping(Environment* env); +v8::MaybeLocal<v8::Value> StartExecution(Environment* env, + const char* main_script_id); } // namespace node diff --git a/src/node_native_module.cc b/src/node_native_module.cc index 27456dd546..675495e34b 100644 --- a/src/node_native_module.cc +++ b/src/node_native_module.cc @@ -175,26 +175,6 @@ void NativeModuleLoader::CompileFunction( } } -// TODO(joyeecheung): it should be possible to generate the argument names -// from some special comments for the bootstrapper case. -MaybeLocal<Value> NativeModuleLoader::CompileAndCall( - Local<Context> context, - const char* id, - std::vector<Local<String>>* parameters, - std::vector<Local<Value>>* arguments, - Environment* optional_env) { - Isolate* isolate = context->GetIsolate(); - MaybeLocal<Function> compiled = - per_process::native_module_loader.LookupAndCompile( - context, id, parameters, nullptr); - if (compiled.IsEmpty()) { - return MaybeLocal<Value>(); - } - Local<Function> fn = compiled.ToLocalChecked().As<Function>(); - return fn->Call( - context, v8::Null(isolate), arguments->size(), arguments->data()); -} - MaybeLocal<Function> NativeModuleLoader::CompileAsModule(Environment* env, const char* id) { std::vector<Local<String>> parameters = {env->exports_string(), diff --git a/src/node_native_module.h b/src/node_native_module.h index 62c417a0b6..be1fc92a76 100644 --- a/src/node_native_module.h +++ b/src/node_native_module.h @@ -42,20 +42,17 @@ class NativeModuleLoader { // Returns config.gypi as a JSON string v8::Local<v8::String> GetConfigString(v8::Isolate* isolate) const; - // Run a script with JS source bundled inside the binary as if it's wrapped - // in a function called with a null receiver and arguments specified in C++. - // The returned value is empty if an exception is encountered. - // JS code run with this method can assume that their top-level - // declarations won't affect the global scope. - v8::MaybeLocal<v8::Value> CompileAndCall( + bool Exists(const char* id); + + // For bootstrappers optional_env may be a nullptr. + // If an exception is encountered (e.g. source code contains + // syntax error), the returned value is empty. + v8::MaybeLocal<v8::Function> LookupAndCompile( v8::Local<v8::Context> context, const char* id, std::vector<v8::Local<v8::String>>* parameters, - std::vector<v8::Local<v8::Value>>* arguments, Environment* optional_env); - bool Exists(const char* id); - private: static void GetCacheUsage(const v8::FunctionCallbackInfo<v8::Value>& args); // Passing ids of builtin module source code into JS land as @@ -87,15 +84,6 @@ class NativeModuleLoader { static v8::MaybeLocal<v8::Function> CompileAsModule(Environment* env, const char* id); - // For bootstrappers optional_env may be a nullptr. - // If an exception is encountered (e.g. source code contains - // syntax error), the returned value is empty. - v8::MaybeLocal<v8::Function> LookupAndCompile( - v8::Local<v8::Context> context, - const char* id, - std::vector<v8::Local<v8::String>>* parameters, - Environment* optional_env); - NativeModuleRecordMap source_; NativeModuleCacheMap code_cache_; UnionBytes config_; diff --git a/src/node_worker.cc b/src/node_worker.cc index e5ba438bc1..4b78d65392 100644 --- a/src/node_worker.cc +++ b/src/node_worker.cc @@ -133,8 +133,8 @@ Worker::Worker(Environment* env, env_->set_thread_id(thread_id_); env_->Start(env->profiler_idle_notifier_started()); - env_->CreateProcessObject(std::vector<std::string>{}, - std::vector<std::string>{}); + env_->ProcessCliArgs(std::vector<std::string>{}, + std::vector<std::string>{}); // Done while on the parent thread AddWorkerInspector(env, env_.get(), thread_id_, url_); } @@ -192,10 +192,10 @@ void Worker::Run() { HandleScope handle_scope(isolate_); Environment::AsyncCallbackScope callback_scope(env_.get()); env_->async_hooks()->push_async_ids(1, 0); - RunBootstrapping(env_.get()); - // TODO(joyeecheung): create a main script for worker threads - // that starts listening on the message port. - StartExecution(env_.get(), nullptr); + if (!RunBootstrapping(env_.get()).IsEmpty()) { + USE(StartExecution(env_.get(), "internal/main/worker_thread")); + } + env_->async_hooks()->pop_async_id(1); Debug(this, "Loaded environment for worker %llu", thread_id_); |