diff --git a/src/node_api_embedding.cc b/src/node_api_embedding.cc index 3e131ad4e1b5ba..f21bb57ee2133e 100644 --- a/src/node_api_embedding.cc +++ b/src/node_api_embedding.cc @@ -121,6 +121,7 @@ struct EmbeddedEnvironmentOptions { delete; bool is_frozen_{false}; + node_api_env_flags flags_{node_api_env_default_flags}; std::vector args_; std::vector exec_args_; node::EmbedderSnapshotData::Pointer snapshot_; @@ -212,15 +213,6 @@ class EmbeddedEnvironment final : public node_napi_env__ { std::optional isolate_locker_; }; -std::vector ToCStringVector(const std::vector& vec) { - std::vector result; - result.reserve(vec.size()); - for (const std::string& str : vec) { - result.push_back(str.c_str()); - } - return result; -} - node::ProcessInitializationFlags::Flags GetProcessInitializationFlags( node_api_platform_flags flags) { uint32_t result = node::ProcessInitializationFlags::kNoFlags; @@ -265,6 +257,47 @@ node::ProcessInitializationFlags::Flags GetProcessInitializationFlags( return static_cast(result); } +node::EnvironmentFlags::Flags GetEnvironmentFlags(node_api_env_flags flags) { + uint64_t result = node::EnvironmentFlags::kNoFlags; + if ((flags & node_api_env_default_flags) != 0) { + result |= node::EnvironmentFlags::kDefaultFlags; + } + if ((flags & node_api_env_owns_process_state) != 0) { + result |= node::EnvironmentFlags::kOwnsProcessState; + } + if ((flags & node_api_env_owns_inspector) != 0) { + result |= node::EnvironmentFlags::kOwnsInspector; + } + if ((flags & node_api_env_no_register_esm_loader) != 0) { + result |= node::EnvironmentFlags::kNoRegisterESMLoader; + } + if ((flags & node_api_env_track_unmanaged_fds) != 0) { + result |= node::EnvironmentFlags::kTrackUnmanagedFds; + } + if ((flags & node_api_env_hide_console_windows) != 0) { + result |= node::EnvironmentFlags::kHideConsoleWindows; + } + if ((flags & node_api_env_no_native_addons) != 0) { + result |= node::EnvironmentFlags::kNoNativeAddons; + } + if ((flags & node_api_env_no_global_search_paths) != 0) { + result |= node::EnvironmentFlags::kNoGlobalSearchPaths; + } + if ((flags & node_api_env_no_browser_globals) != 0) { + result |= node::EnvironmentFlags::kNoBrowserGlobals; + } + if ((flags & node_api_env_no_create_inspector) != 0) { + result |= node::EnvironmentFlags::kNoCreateInspector; + } + if ((flags & node_api_env_no_start_debug_signal_handler) != 0) { + result |= node::EnvironmentFlags::kNoStartDebugSignalHandler; + } + if ((flags & node_api_env_no_wait_for_inspector_frontend) != 0) { + result |= node::EnvironmentFlags::kNoWaitForInspectorFrontend; + } + return static_cast(result); +} + } // end of anonymous namespace } // end of namespace v8impl @@ -353,31 +386,43 @@ node_api_env_options_get_args(node_api_env_options options, return napi_ok; } -napi_status NAPI_CDECL node_api_env_options_set_args( - node_api_env_options options, size_t argc, const char* argv[]) { +napi_status NAPI_CDECL +node_api_env_options_get_exec_args(node_api_env_options options, + node_api_get_args_callback get_args_cb, + void* cb_data) { + if (options == nullptr) return napi_invalid_arg; + if (get_args_cb == nullptr) return napi_invalid_arg; + + v8impl::EmbeddedEnvironmentOptions* env_options = + reinterpret_cast(options); + v8impl::CStringArray args(env_options->exec_args_); + get_args_cb(cb_data, args.argc(), args.argv()); + + return napi_ok; +} + +napi_status NAPI_CDECL node_api_env_options_set_flags( + node_api_env_options options, node_api_env_flags flags) { if (options == nullptr) return napi_invalid_arg; - if (argv == nullptr) return napi_invalid_arg; v8impl::EmbeddedEnvironmentOptions* env_options = reinterpret_cast(options); if (env_options->is_frozen_) return napi_generic_failure; - env_options->args_.assign(argv, argv + argc); + env_options->flags_ = flags; return napi_ok; } -napi_status NAPI_CDECL -node_api_env_options_get_exec_args(node_api_env_options options, - node_api_get_args_callback get_args_cb, - void* cb_data) { +napi_status NAPI_CDECL node_api_env_options_set_args( + node_api_env_options options, size_t argc, const char* argv[]) { if (options == nullptr) return napi_invalid_arg; - if (get_args_cb == nullptr) return napi_invalid_arg; + if (argv == nullptr) return napi_invalid_arg; v8impl::EmbeddedEnvironmentOptions* env_options = reinterpret_cast(options); - v8impl::CStringArray args(env_options->exec_args_); - get_args_cb(cb_data, args.argc(), args.argv()); + if (env_options->is_frozen_) return napi_generic_failure; + env_options->args_.assign(argv, argv + argc); return napi_ok; } @@ -458,9 +503,7 @@ node_api_create_env(node_api_env_options options, node::MultiIsolatePlatform* platform = v8impl::EmbeddedPlatform::GetInstance()->get_v8_platform(); node::EnvironmentFlags::Flags flags = - static_cast( - node::EnvironmentFlags::kDefaultFlags | - node::EnvironmentFlags::kNoCreateInspector); + v8impl::GetEnvironmentFlags(env_options->flags_); if (env_options->snapshot_) { env_setup = node::CommonEnvironmentSetup::CreateFromSnapshot( platform, @@ -605,7 +648,8 @@ napi_status NAPI_CDECL node_api_run_env_while(napi_env env, napi_status NAPI_CDECL node_api_await_promise(napi_env env, napi_value promise, - napi_value* result) { + napi_value* result, + bool* has_more_work) { NAPI_PREAMBLE(env); CHECK_ARG(env, result); @@ -641,6 +685,11 @@ napi_status NAPI_CDECL node_api_await_promise(napi_env env, *result = v8impl::JsValueFromV8LocalValue(scope.Escape(promise_object->Result())); + + if (has_more_work != nullptr) { + *has_more_work = uv_loop_alive(embedded_env->node_env()->event_loop()); + } + if (promise_object->State() == v8::Promise::PromiseState::kRejected) return napi_pending_exception; diff --git a/src/node_api_embedding.h b/src/node_api_embedding.h index 571735b3b763af..affb82562ed7a4 100644 --- a/src/node_api_embedding.h +++ b/src/node_api_embedding.h @@ -37,6 +37,57 @@ typedef enum { node_api_platform_generate_predictable_snapshot = 1 << 14, } node_api_platform_flags; +typedef enum : uint64_t { + node_api_env_no_flags = 0, + // Use the default behaviour for Node.js instances. + node_api_env_default_flags = 1 << 0, + // Controls whether this Environment is allowed to affect per-process state + // (e.g. cwd, process title, uid, etc.). + // This is set when using node_api_env_default_flags. + node_api_env_owns_process_state = 1 << 1, + // Set if this Environment instance is associated with the global inspector + // handling code (i.e. listening on SIGUSR1). + // This is set when using node_api_env_default_flags. + node_api_env_owns_inspector = 1 << 2, + // Set if Node.js should not run its own esm loader. This is needed by some + // embedders, because it's possible for the Node.js esm loader to conflict + // with another one in an embedder environment, e.g. Blink's in Chromium. + node_api_env_no_register_esm_loader = 1 << 3, + // Set this flag to make Node.js track "raw" file descriptors, i.e. managed + // by fs.open() and fs.close(), and close them during node_api_delete_env(). + node_api_env_track_unmanaged_fds = 1 << 4, + // Set this flag to force hiding console windows when spawning child + // processes. This is usually used when embedding Node.js in GUI programs on + // Windows. + node_api_env_hide_console_windows = 1 << 5, + // Set this flag to disable loading native addons via `process.dlopen`. + // This environment flag is especially important for worker threads + // so that a worker thread can't load a native addon even if `execArgv` + // is overwritten and `--no-addons` is not specified but was specified + // for this Environment instance. + node_api_env_no_native_addons = 1 << 6, + // Set this flag to disable searching modules from global paths like + // $HOME/.node_modules and $NODE_PATH. This is used by standalone apps that + // do not expect to have their behaviors changed because of globally + // installed modules. + node_api_env_no_global_search_paths = 1 << 7, + // Do not export browser globals like setTimeout, console, etc. + node_api_env_no_browser_globals = 1 << 8, + // Controls whether or not the Environment should call V8Inspector::create(). + // This control is needed by embedders who may not want to initialize the V8 + // inspector in situations where one has already been created, + // e.g. Blink's in Chromium. + node_api_env_no_create_inspector = 1 << 9, + // Controls whether or not the InspectorAgent for this Environment should + // call StartDebugSignalHandler. This control is needed by embedders who may + // not want to allow other processes to start the V8 inspector. + node_api_env_no_start_debug_signal_handler = 1 << 10, + // Controls whether the InspectorAgent created for this Environment waits for + // Inspector frontend events during the Environment creation. It's used to + // call node::Stop(env) on a Worker thread that is waiting for the events. + node_api_env_no_wait_for_inspector_frontend = 1 << 11 +} node_api_env_flags; + typedef enum { node_api_snapshot_no_flags = 0, // Whether code cache should be generated as part of the snapshot. @@ -79,14 +130,17 @@ node_api_env_options_get_args(node_api_env_options options, node_api_get_args_callback get_args_cb, void* cb_data); -NAPI_EXTERN napi_status NAPI_CDECL node_api_env_options_set_args( - node_api_env_options options, size_t argc, const char* argv[]); - NAPI_EXTERN napi_status NAPI_CDECL node_api_env_options_get_exec_args(node_api_env_options options, node_api_get_args_callback get_args_cb, void* cb_data); +NAPI_EXTERN napi_status NAPI_CDECL node_api_env_options_set_flags( + node_api_env_options options, node_api_env_flags flags); + +NAPI_EXTERN napi_status NAPI_CDECL node_api_env_options_set_args( + node_api_env_options options, size_t argc, const char* argv[]); + NAPI_EXTERN napi_status NAPI_CDECL node_api_env_options_set_exec_args( node_api_env_options options, size_t argc, const char* argv[]); @@ -126,21 +180,42 @@ node_api_run_env_while(napi_env env, NAPI_EXTERN napi_status NAPI_CDECL node_api_await_promise(napi_env env, napi_value promise, - napi_value* result); + napi_value* result, + bool* has_more_work); EXTERN_C_END +#ifdef __cplusplus + +inline node_api_platform_flags operator|(node_api_platform_flags lhs, + node_api_platform_flags rhs) { + return static_cast(static_cast(lhs) | + static_cast(rhs)); +} + +inline node_api_env_flags operator|(node_api_env_flags lhs, + node_api_env_flags rhs) { + return static_cast(static_cast(lhs) | + static_cast(rhs)); +} + +inline node_api_snapshot_flags operator|(node_api_snapshot_flags lhs, + node_api_snapshot_flags rhs) { + return static_cast(static_cast(lhs) | + static_cast(rhs)); +} + +#endif + #endif // SRC_NODE_API_EMBEDDING_H_ // TODO: (vmoroz) Remove the main_script parameter. // TODO: (vmoroz) Add startup callback with process and require parameters. // TODO: (vmoroz) Add ABI-safe way to access internal module functionality. -// TODO: (vmoroz) Add EnvironmentFlags to env_options. // TODO: (vmoroz) Allow setting the global inspector for a specific environment. // TODO: (vmoroz) Start workers from C++. // TODO: (vmoroz) Worker to inherit parent inspector. // TODO: (vmoroz) Cancel pending tasks on delete env. -// TODO: (vmoroz) await_promise -> add has_more_work parameter. // TODO: (vmoroz) Can we init plat again if it retuns early? // TODO: (vmoroz) Add simpler threading model - without open/close scope. // TODO: (vmoroz) Simplify API use for simple default cases. diff --git a/test/embedding/embedtest_concurrent_node_api.cc b/test/embedding/embedtest_concurrent_node_api.cc index 73a61bc0b67ed1..c04244d56c0a7f 100644 --- a/test/embedding/embedtest_concurrent_node_api.cc +++ b/test/embedding/embedtest_concurrent_node_api.cc @@ -27,6 +27,9 @@ extern "C" int32_t test_main_concurrent_node_api(int32_t argc, char* argv[]) { int32_t exit_code = [&]() { node_api_env_options options; CHECK(node_api_create_env_options(&options)); + CHECK(node_api_env_options_set_flags( + options, + node_api_env_default_flags | node_api_env_no_create_inspector)); napi_env env; CHECK(node_api_create_env( options, nullptr, nullptr, main_script, NAPI_VERSION, &env)); @@ -80,6 +83,9 @@ extern "C" int32_t test_main_multi_env_node_api(int32_t argc, char* argv[]) { for (size_t i = 0; i < env_count; i++) { node_api_env_options options; CHECK(node_api_create_env_options(&options)); + CHECK(node_api_env_options_set_flags( + options, + node_api_env_default_flags | node_api_env_no_create_inspector)); napi_env env; CHECK(node_api_create_env( options, nullptr, nullptr, main_script, NAPI_VERSION, &env)); diff --git a/test/embedding/embedtest_modules_node_api.cc b/test/embedding/embedtest_modules_node_api.cc index 936fc977418ca3..d1d03361444981 100644 --- a/test/embedding/embedtest_modules_node_api.cc +++ b/test/embedding/embedtest_modules_node_api.cc @@ -37,7 +37,7 @@ extern "C" int32_t test_main_modules_node_api(int32_t argc, char* argv[]) { size_t bufferlen; CHECK(napi_call_function(env, global, import, 1, &es6, &es6_promise)); - CHECK(node_api_await_promise(env, es6_promise, &es6_module)); + CHECK(node_api_await_promise(env, es6_promise, &es6_module, nullptr)); CHECK(napi_get_property(env, es6_module, value, &es6_result)); CHECK(napi_get_value_string_utf8( diff --git a/test/embedding/embedtest_node_api.cc b/test/embedding/embedtest_node_api.cc index dcefaf6a996bbc..42c04359d5c423 100644 --- a/test/embedding/embedtest_node_api.cc +++ b/test/embedding/embedtest_node_api.cc @@ -176,7 +176,7 @@ int32_t waitMeWithCheese(napi_env env) { FAIL("Result is not a Promise\n"); } - napi_status r = node_api_await_promise(env, promise, &result); + napi_status r = node_api_await_promise(env, promise, &result, nullptr); if (r != napi_ok && r != napi_pending_exception) { FAIL("Failed awaiting promise: %d\n", r); }