Skip to content

Commit

Permalink
add environment flags
Browse files Browse the repository at this point in the history
  • Loading branch information
vmoroz committed Sep 4, 2024
1 parent 9b60f91 commit 0bb3b5a
Show file tree
Hide file tree
Showing 5 changed files with 162 additions and 32 deletions.
97 changes: 73 additions & 24 deletions src/node_api_embedding.cc
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ struct EmbeddedEnvironmentOptions {
delete;

bool is_frozen_{false};
node_api_env_flags flags_{node_api_env_default_flags};
std::vector<std::string> args_;
std::vector<std::string> exec_args_;
node::EmbedderSnapshotData::Pointer snapshot_;
Expand Down Expand Up @@ -212,15 +213,6 @@ class EmbeddedEnvironment final : public node_napi_env__ {
std::optional<IsolateLocker> isolate_locker_;
};

std::vector<const char*> ToCStringVector(const std::vector<std::string>& vec) {
std::vector<const char*> 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;
Expand Down Expand Up @@ -265,6 +257,47 @@ node::ProcessInitializationFlags::Flags GetProcessInitializationFlags(
return static_cast<node::ProcessInitializationFlags::Flags>(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<node::EnvironmentFlags::Flags>(result);
}

} // end of anonymous namespace
} // end of namespace v8impl

Expand Down Expand Up @@ -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<v8impl::EmbeddedEnvironmentOptions*>(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<v8impl::EmbeddedEnvironmentOptions*>(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<v8impl::EmbeddedEnvironmentOptions*>(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;
}

Expand Down Expand Up @@ -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::Flags>(
node::EnvironmentFlags::kDefaultFlags |
node::EnvironmentFlags::kNoCreateInspector);
v8impl::GetEnvironmentFlags(env_options->flags_);
if (env_options->snapshot_) {
env_setup = node::CommonEnvironmentSetup::CreateFromSnapshot(
platform,
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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;

Expand Down
87 changes: 81 additions & 6 deletions src/node_api_embedding.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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[]);

Expand Down Expand Up @@ -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<node_api_platform_flags>(static_cast<int32_t>(lhs) |
static_cast<int32_t>(rhs));
}

inline node_api_env_flags operator|(node_api_env_flags lhs,
node_api_env_flags rhs) {
return static_cast<node_api_env_flags>(static_cast<uint64_t>(lhs) |
static_cast<uint64_t>(rhs));
}

inline node_api_snapshot_flags operator|(node_api_snapshot_flags lhs,
node_api_snapshot_flags rhs) {
return static_cast<node_api_snapshot_flags>(static_cast<int32_t>(lhs) |
static_cast<int32_t>(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.
Expand Down
6 changes: 6 additions & 0 deletions test/embedding/embedtest_concurrent_node_api.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down Expand Up @@ -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));
Expand Down
2 changes: 1 addition & 1 deletion test/embedding/embedtest_modules_node_api.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
2 changes: 1 addition & 1 deletion test/embedding/embedtest_node_api.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down

0 comments on commit 0bb3b5a

Please sign in to comment.