|
|
|
// Copyright Joyent, Inc. and other Node contributors.
|
|
|
|
//
|
|
|
|
// Permission is hereby granted, free of charge, to any person obtaining a
|
|
|
|
// copy of this software and associated documentation files (the
|
|
|
|
// "Software"), to deal in the Software without restriction, including
|
|
|
|
// without limitation the rights to use, copy, modify, merge, publish,
|
|
|
|
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
|
|
|
// persons to whom the Software is furnished to do so, subject to the
|
|
|
|
// following conditions:
|
|
|
|
//
|
|
|
|
// The above copyright notice and this permission notice shall be included
|
|
|
|
// in all copies or substantial portions of the Software.
|
|
|
|
//
|
|
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
|
|
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
|
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
|
|
|
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
|
|
|
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
|
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
|
|
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
|
|
|
|
#include "node.h"
|
|
|
|
#include "node_buffer.h"
|
|
|
|
#include "node_constants.h"
|
|
|
|
#include "node_javascript.h"
|
|
|
|
#include "node_version.h"
|
|
|
|
#include "node_internals.h"
|
|
|
|
#include "node_revert.h"
|
|
|
|
#include "node_debug_options.h"
|
|
|
|
|
|
|
|
#if defined HAVE_PERFCTR
|
|
|
|
#include "node_counters.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if HAVE_OPENSSL
|
|
|
|
#include "node_crypto.h"
|
|
|
|
#endif
|
|
|
|
|
build, i18n: improve Intl build, add "--with-intl"
The two main goals of this change are:
- To make it easier to build the Intl option using ICU (particularly,
using a newer ICU than v8/Chromium's version)
- To enable a much smaller ICU build with only English support The goal
here is to get node.js binaries built this way by default so that the
Intl API can be used. Additional data can be added at execution time
(see Readme and wiki)
More details are at https://github.com/joyent/node/pull/7719
In particular, this change adds the "--with-intl=" configure option to
provide more ways of building "Intl":
- "full-icu" picks up an ICU from deps/icu
- "small-icu" is similar, but builds only English
- "system-icu" uses pkg-config to find an installed ICU
- "none" does nothing (no Intl)
For Windows builds, the "full-icu" or "small-icu" options are added to
vcbuild.bat.
Note that the existing "--with-icu-path" option is not removed from
configure, but may not be used alongside the new option.
Wiki changes have already been made on
https://github.com/joyent/node/wiki/Installation
and a new page created at
https://github.com/joyent/node/wiki/Intl
(marked as provisional until this change lands.)
Summary of changes:
* README.md : doc updates
* .gitignore : added "deps/icu" as this is the location where ICU is
unpacked to.
* Makefile : added the tools/icu/* files to cpplint, but excluded a
problematic file.
* configure : added the "--with-intl" option mentioned above.
Calculate at config time the list of ICU source files to use and data
packaging options.
* node.gyp : add the new files src/node_i18n.cc/.h as well as ICU
linkage.
* src/node.cc : add call into
node::i18n::InitializeICUDirectory(icu_data_dir) as well as new
--icu-data-dir option and NODE_ICU_DATA env variable to configure ICU
data loading. This loading is only relevant in the "small"
configuration.
* src/node_i18n.cc : new source file for the above Initialize..
function, to setup ICU as needed.
* tools/icu : new directory with some tools needed for this build.
* tools/icu/icu-generic.gyp : new .gyp file that builds ICU in some new
ways, both on unix/mac and windows.
* tools/icu/icu-system.gyp : new .gyp file to build node against a
pkg-config detected ICU.
* tools/icu/icu_small.json : new config file for the "English-only" small
build.
* tools/icu/icutrim.py : new tool for trimming down ICU data. Reads the
above .json file.
* tools/icu/iculslocs.cc : new tool for repairing ICU data manifests
after trim operation.
* tools/icu/no-op.cc : dummy file to force .gyp into using a C++ linker.
* vcbuild.bat : added small-icu and full-icu options, to call into
configure.
* Fixed toolset dependencies, see
https://github.com/joyent/node/pull/7719#issuecomment-54641687
Note that because of a bug in gyp {CC,CXX}_host must also be set.
Otherwise gcc/g++ will be used by default for part of the build.
Reviewed-by: Trevor Norris <trev.norris@gmail.com>
Reviewed-by: Fedor Indutny <fedor@indutny.com>
10 years ago
|
|
|
#if defined(NODE_HAVE_I18N_SUPPORT)
|
|
|
|
#include "node_i18n.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if HAVE_INSPECTOR
|
|
|
|
#include "inspector_io.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined HAVE_DTRACE || defined HAVE_ETW
|
|
|
|
#include "node_dtrace.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined HAVE_LTTNG
|
|
|
|
#include "node_lttng.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "ares.h"
|
|
|
|
#include "async-wrap.h"
|
|
|
|
#include "async-wrap-inl.h"
|
|
|
|
#include "env.h"
|
|
|
|
#include "env-inl.h"
|
|
|
|
#include "handle_wrap.h"
|
|
|
|
#include "http_parser.h"
|
|
|
|
#include "req-wrap.h"
|
|
|
|
#include "req-wrap-inl.h"
|
|
|
|
#include "string_bytes.h"
|
|
|
|
#include "tracing/agent.h"
|
|
|
|
#include "util.h"
|
|
|
|
#include "uv.h"
|
|
|
|
#if NODE_USE_V8_PLATFORM
|
|
|
|
#include "libplatform/libplatform.h"
|
|
|
|
#endif // NODE_USE_V8_PLATFORM
|
|
|
|
#include "v8-debug.h"
|
|
|
|
#include "v8-profiler.h"
|
|
|
|
#include "zlib.h"
|
|
|
|
|
|
|
|
#ifdef NODE_ENABLE_VTUNE_PROFILING
|
|
|
|
#include "../deps/v8/src/third_party/vtune/v8-vtune.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
#include <fcntl.h> // _O_RDWR
|
|
|
|
#include <limits.h> // PATH_MAX
|
|
|
|
#include <locale.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
|
|
|
#include <string>
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
#if defined(NODE_HAVE_I18N_SUPPORT)
|
|
|
|
#include <unicode/uvernum.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined(LEAK_SANITIZER)
|
|
|
|
#include <sanitizer/lsan_interface.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined(_MSC_VER)
|
|
|
|
#include <direct.h>
|
|
|
|
#include <io.h>
|
|
|
|
#define getpid GetCurrentProcessId
|
|
|
|
#define umask _umask
|
|
|
|
typedef int mode_t;
|
|
|
|
#else
|
|
|
|
#include <pthread.h>
|
|
|
|
#include <sys/resource.h> // getrlimit, setrlimit
|
|
|
|
#include <unistd.h> // setuid, getuid
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined(__POSIX__) && !defined(__ANDROID__)
|
|
|
|
#include <pwd.h> // getpwnam()
|
|
|
|
#include <grp.h> // getgrnam()
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef __APPLE__
|
|
|
|
#include <crt_externs.h>
|
|
|
|
#define environ (*_NSGetEnviron())
|
|
|
|
#elif !defined(_MSC_VER)
|
|
|
|
extern char **environ;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
namespace node {
|
|
|
|
|
|
|
|
using v8::Array;
|
|
|
|
using v8::ArrayBuffer;
|
|
|
|
using v8::Boolean;
|
|
|
|
using v8::Context;
|
|
|
|
using v8::EscapableHandleScope;
|
|
|
|
using v8::Exception;
|
|
|
|
using v8::Float64Array;
|
|
|
|
using v8::Function;
|
|
|
|
using v8::FunctionCallbackInfo;
|
|
|
|
using v8::HandleScope;
|
|
|
|
using v8::HeapStatistics;
|
|
|
|
using v8::Integer;
|
|
|
|
using v8::Isolate;
|
|
|
|
using v8::Local;
|
|
|
|
using v8::Locker;
|
|
|
|
using v8::MaybeLocal;
|
|
|
|
using v8::Message;
|
|
|
|
using v8::Name;
|
|
|
|
using v8::NamedPropertyHandlerConfiguration;
|
|
|
|
using v8::Null;
|
|
|
|
using v8::Number;
|
|
|
|
using v8::Object;
|
|
|
|
using v8::ObjectTemplate;
|
|
|
|
using v8::Promise;
|
|
|
|
using v8::PromiseHookType;
|
|
|
|
using v8::PromiseRejectMessage;
|
|
|
|
using v8::PropertyCallbackInfo;
|
|
|
|
using v8::ScriptOrigin;
|
|
|
|
using v8::SealHandleScope;
|
|
|
|
using v8::String;
|
|
|
|
using v8::TryCatch;
|
|
|
|
using v8::Uint32Array;
|
|
|
|
using v8::V8;
|
|
|
|
using v8::Value;
|
|
|
|
|
|
|
|
using AsyncHooks = node::Environment::AsyncHooks;
|
|
|
|
|
|
|
|
static bool print_eval = false;
|
|
|
|
static bool force_repl = false;
|
|
|
|
static bool syntax_check_only = false;
|
|
|
|
static bool trace_deprecation = false;
|
|
|
|
static bool throw_deprecation = false;
|
|
|
|
static bool trace_sync_io = false;
|
|
|
|
static bool track_heap_objects = false;
|
|
|
|
static const char* eval_string = nullptr;
|
|
|
|
static std::vector<std::string> preload_modules;
|
|
|
|
static const int v8_default_thread_pool_size = 4;
|
|
|
|
static int v8_thread_pool_size = v8_default_thread_pool_size;
|
|
|
|
static bool prof_process = false;
|
|
|
|
static bool v8_is_profiling = false;
|
|
|
|
static bool node_is_initialized = false;
|
|
|
|
static node_module* modpending;
|
|
|
|
static node_module* modlist_builtin;
|
|
|
|
static node_module* modlist_linked;
|
|
|
|
static node_module* modlist_addon;
|
|
|
|
static bool trace_enabled = false;
|
|
|
|
static std::string trace_enabled_categories; // NOLINT(runtime/string)
|
|
|
|
static bool abort_on_uncaught_exception = false;
|
|
|
|
|
|
|
|
#if defined(NODE_HAVE_I18N_SUPPORT)
|
|
|
|
// Path to ICU data (for i18n / Intl)
|
|
|
|
std::string icu_data_dir; // NOLINT(runtime/string)
|
|
|
|
#endif
|
build, i18n: improve Intl build, add "--with-intl"
The two main goals of this change are:
- To make it easier to build the Intl option using ICU (particularly,
using a newer ICU than v8/Chromium's version)
- To enable a much smaller ICU build with only English support The goal
here is to get node.js binaries built this way by default so that the
Intl API can be used. Additional data can be added at execution time
(see Readme and wiki)
More details are at https://github.com/joyent/node/pull/7719
In particular, this change adds the "--with-intl=" configure option to
provide more ways of building "Intl":
- "full-icu" picks up an ICU from deps/icu
- "small-icu" is similar, but builds only English
- "system-icu" uses pkg-config to find an installed ICU
- "none" does nothing (no Intl)
For Windows builds, the "full-icu" or "small-icu" options are added to
vcbuild.bat.
Note that the existing "--with-icu-path" option is not removed from
configure, but may not be used alongside the new option.
Wiki changes have already been made on
https://github.com/joyent/node/wiki/Installation
and a new page created at
https://github.com/joyent/node/wiki/Intl
(marked as provisional until this change lands.)
Summary of changes:
* README.md : doc updates
* .gitignore : added "deps/icu" as this is the location where ICU is
unpacked to.
* Makefile : added the tools/icu/* files to cpplint, but excluded a
problematic file.
* configure : added the "--with-intl" option mentioned above.
Calculate at config time the list of ICU source files to use and data
packaging options.
* node.gyp : add the new files src/node_i18n.cc/.h as well as ICU
linkage.
* src/node.cc : add call into
node::i18n::InitializeICUDirectory(icu_data_dir) as well as new
--icu-data-dir option and NODE_ICU_DATA env variable to configure ICU
data loading. This loading is only relevant in the "small"
configuration.
* src/node_i18n.cc : new source file for the above Initialize..
function, to setup ICU as needed.
* tools/icu : new directory with some tools needed for this build.
* tools/icu/icu-generic.gyp : new .gyp file that builds ICU in some new
ways, both on unix/mac and windows.
* tools/icu/icu-system.gyp : new .gyp file to build node against a
pkg-config detected ICU.
* tools/icu/icu_small.json : new config file for the "English-only" small
build.
* tools/icu/icutrim.py : new tool for trimming down ICU data. Reads the
above .json file.
* tools/icu/iculslocs.cc : new tool for repairing ICU data manifests
after trim operation.
* tools/icu/no-op.cc : dummy file to force .gyp into using a C++ linker.
* vcbuild.bat : added small-icu and full-icu options, to call into
configure.
* Fixed toolset dependencies, see
https://github.com/joyent/node/pull/7719#issuecomment-54641687
Note that because of a bug in gyp {CC,CXX}_host must also be set.
Otherwise gcc/g++ will be used by default for part of the build.
Reviewed-by: Trevor Norris <trev.norris@gmail.com>
Reviewed-by: Fedor Indutny <fedor@indutny.com>
10 years ago
|
|
|
|
|
|
|
// N-API is in experimental state, disabled by default.
|
|
|
|
bool load_napi_modules = false;
|
|
|
|
|
|
|
|
// used by C++ modules as well
|
|
|
|
bool no_deprecation = false;
|
|
|
|
|
|
|
|
#if HAVE_OPENSSL
|
|
|
|
// use OpenSSL's cert store instead of bundled certs
|
|
|
|
bool ssl_openssl_cert_store =
|
|
|
|
#if defined(NODE_OPENSSL_CERT_STORE)
|
|
|
|
true;
|
|
|
|
#else
|
|
|
|
false;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
# if NODE_FIPS_MODE
|
|
|
|
// used by crypto module
|
|
|
|
bool enable_fips_crypto = false;
|
|
|
|
bool force_fips_crypto = false;
|
|
|
|
# endif // NODE_FIPS_MODE
|
|
|
|
std::string openssl_config; // NOLINT(runtime/string)
|
|
|
|
#endif // HAVE_OPENSSL
|
|
|
|
|
|
|
|
// true if process warnings should be suppressed
|
|
|
|
bool no_process_warnings = false;
|
|
|
|
bool trace_warnings = false;
|
|
|
|
|
|
|
|
// Set in node.cc by ParseArgs when --preserve-symlinks is used.
|
|
|
|
// Used in node_config.cc to set a constant on process.binding('config')
|
|
|
|
// that is used by lib/module.js
|
|
|
|
bool config_preserve_symlinks = false;
|
|
|
|
|
|
|
|
// Set by ParseArgs when --pending-deprecation or NODE_PENDING_DEPRECATION
|
|
|
|
// is used.
|
|
|
|
bool config_pending_deprecation = false;
|
|
|
|
|
|
|
|
// Set in node.cc by ParseArgs when --redirect-warnings= is used.
|
|
|
|
std::string config_warning_file; // NOLINT(runtime/string)
|
|
|
|
|
|
|
|
// Set in node.cc by ParseArgs when --expose-internals or --expose_internals is
|
|
|
|
// used.
|
|
|
|
// Used in node_config.cc to set a constant on process.binding('config')
|
|
|
|
// that is used by lib/internal/bootstrap_node.js
|
|
|
|
bool config_expose_internals = false;
|
|
|
|
|
|
|
|
bool v8_initialized = false;
|
|
|
|
|
|
|
|
bool linux_at_secure = false;
|
|
|
|
|
|
|
|
// process-relative uptime base, initialized at start-up
|
|
|
|
static double prog_start_time;
|
|
|
|
|
|
|
|
static Mutex node_isolate_mutex;
|
|
|
|
static v8::Isolate* node_isolate;
|
|
|
|
|
|
|
|
static node::DebugOptions debug_options;
|
|
|
|
|
|
|
|
static struct {
|
|
|
|
#if NODE_USE_V8_PLATFORM
|
|
|
|
void Initialize(int thread_pool_size) {
|
|
|
|
platform_ = v8::platform::CreateDefaultPlatform(thread_pool_size);
|
|
|
|
V8::InitializePlatform(platform_);
|
|
|
|
tracing::TraceEventHelper::SetCurrentPlatform(platform_);
|
|
|
|
}
|
|
|
|
|
|
|
|
void PumpMessageLoop(Isolate* isolate) {
|
|
|
|
v8::platform::PumpMessageLoop(platform_, isolate);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Dispose() {
|
|
|
|
delete platform_;
|
|
|
|
platform_ = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if HAVE_INSPECTOR
|
|
|
|
bool StartInspector(Environment *env, const char* script_path,
|
|
|
|
const node::DebugOptions& options) {
|
|
|
|
// 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.
|
|
|
|
return env->inspector_agent()->Start(platform_, script_path, options);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool InspectorStarted(Environment *env) {
|
|
|
|
return env->inspector_agent()->IsStarted();
|
|
|
|
}
|
|
|
|
#endif // HAVE_INSPECTOR
|
|
|
|
|
|
|
|
void StartTracingAgent() {
|
|
|
|
CHECK(tracing_agent_ == nullptr);
|
|
|
|
tracing_agent_ = new tracing::Agent();
|
|
|
|
tracing_agent_->Start(platform_, trace_enabled_categories);
|
|
|
|
}
|
|
|
|
|
|
|
|
void StopTracingAgent() {
|
|
|
|
tracing_agent_->Stop();
|
|
|
|
}
|
|
|
|
|
|
|
|
v8::Platform* platform_;
|
|
|
|
tracing::Agent* tracing_agent_;
|
|
|
|
#else // !NODE_USE_V8_PLATFORM
|
|
|
|
void Initialize(int thread_pool_size) {}
|
|
|
|
void PumpMessageLoop(Isolate* isolate) {}
|
|
|
|
void Dispose() {}
|
|
|
|
bool StartInspector(Environment *env, const char* script_path,
|
|
|
|
const node::DebugOptions& options) {
|
|
|
|
env->ThrowError("Node compiled with NODE_USE_V8_PLATFORM=0");
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void StartTracingAgent() {
|
|
|
|
fprintf(stderr, "Node compiled with NODE_USE_V8_PLATFORM=0, "
|
|
|
|
"so event tracing is not available.\n");
|
|
|
|
}
|
|
|
|
void StopTracingAgent() {}
|
|
|
|
#endif // !NODE_USE_V8_PLATFORM
|
|
|
|
|
|
|
|
#if !NODE_USE_V8_PLATFORM || !HAVE_INSPECTOR
|
|
|
|
bool InspectorStarted(Environment *env) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
#endif // !NODE_USE_V8_PLATFORM || !HAVE_INSPECTOR
|
|
|
|
} v8_platform;
|
|
|
|
|
|
|
|
#ifdef __POSIX__
|
|
|
|
static const unsigned kMaxSignal = 32;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static void PrintErrorString(const char* format, ...) {
|
|
|
|
va_list ap;
|
|
|
|
va_start(ap, format);
|
|
|
|
#ifdef _WIN32
|
|
|
|
HANDLE stderr_handle = GetStdHandle(STD_ERROR_HANDLE);
|
|
|
|
|
|
|
|
// Check if stderr is something other than a tty/console
|
|
|
|
if (stderr_handle == INVALID_HANDLE_VALUE ||
|
|
|
|
stderr_handle == nullptr ||
|
|
|
|
uv_guess_handle(_fileno(stderr)) != UV_TTY) {
|
|
|
|
vfprintf(stderr, format, ap);
|
|
|
|
va_end(ap);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fill in any placeholders
|
|
|
|
int n = _vscprintf(format, ap);
|
|
|
|
std::vector<char> out(n + 1);
|
|
|
|
vsprintf(out.data(), format, ap);
|
|
|
|
|
|
|
|
// Get required wide buffer size
|
|
|
|
n = MultiByteToWideChar(CP_UTF8, 0, out.data(), -1, nullptr, 0);
|
|
|
|
|
|
|
|
std::vector<wchar_t> wbuf(n);
|
|
|
|
MultiByteToWideChar(CP_UTF8, 0, out.data(), -1, wbuf.data(), n);
|
|
|
|
|
|
|
|
// Don't include the null character in the output
|
|
|
|
CHECK_GT(n, 0);
|
|
|
|
WriteConsoleW(stderr_handle, wbuf.data(), n - 1, nullptr, nullptr);
|
|
|
|
#else
|
|
|
|
vfprintf(stderr, format, ap);
|
|
|
|
#endif
|
|
|
|
va_end(ap);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void CheckImmediate(uv_check_t* handle) {
|
|
|
|
Environment* env = Environment::from_immediate_check_handle(handle);
|
|
|
|
HandleScope scope(env->isolate());
|
|
|
|
Context::Scope context_scope(env->context());
|
|
|
|
MakeCallback(env->isolate(),
|
|
|
|
env->process_object(),
|
|
|
|
env->immediate_callback_string(),
|
|
|
|
0,
|
|
|
|
nullptr,
|
|
|
|
0, 0).ToLocalChecked();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void IdleImmediateDummy(uv_idle_t* handle) {
|
|
|
|
// Do nothing. Only for maintaining event loop.
|
|
|
|
// TODO(bnoordhuis) Maybe make libuv accept nullptr idle callbacks.
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static inline const char *errno_string(int errorno) {
|
|
|
|
#define ERRNO_CASE(e) case e: return #e;
|
|
|
|
switch (errorno) {
|
|
|
|
#ifdef EACCES
|
|
|
|
ERRNO_CASE(EACCES);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef EADDRINUSE
|
|
|
|
ERRNO_CASE(EADDRINUSE);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef EADDRNOTAVAIL
|
|
|
|
ERRNO_CASE(EADDRNOTAVAIL);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef EAFNOSUPPORT
|
|
|
|
ERRNO_CASE(EAFNOSUPPORT);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef EAGAIN
|
|
|
|
ERRNO_CASE(EAGAIN);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef EWOULDBLOCK
|
|
|
|
# if EAGAIN != EWOULDBLOCK
|
|
|
|
ERRNO_CASE(EWOULDBLOCK);
|
|
|
|
# endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef EALREADY
|
|
|
|
ERRNO_CASE(EALREADY);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef EBADF
|
|
|
|
ERRNO_CASE(EBADF);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef EBADMSG
|
|
|
|
ERRNO_CASE(EBADMSG);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef EBUSY
|
|
|
|
ERRNO_CASE(EBUSY);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef ECANCELED
|
|
|
|
ERRNO_CASE(ECANCELED);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef ECHILD
|
|
|
|
ERRNO_CASE(ECHILD);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef ECONNABORTED
|
|
|
|
ERRNO_CASE(ECONNABORTED);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef ECONNREFUSED
|
|
|
|
ERRNO_CASE(ECONNREFUSED);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef ECONNRESET
|
|
|
|
ERRNO_CASE(ECONNRESET);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef EDEADLK
|
|
|
|
ERRNO_CASE(EDEADLK);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef EDESTADDRREQ
|
|
|
|
ERRNO_CASE(EDESTADDRREQ);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef EDOM
|
|
|
|
ERRNO_CASE(EDOM);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef EDQUOT
|
|
|
|
ERRNO_CASE(EDQUOT);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef EEXIST
|
|
|
|
ERRNO_CASE(EEXIST);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef EFAULT
|
|
|
|
ERRNO_CASE(EFAULT);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef EFBIG
|
|
|
|
ERRNO_CASE(EFBIG);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef EHOSTUNREACH
|
|
|
|
ERRNO_CASE(EHOSTUNREACH);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef EIDRM
|
|
|
|
ERRNO_CASE(EIDRM);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef EILSEQ
|
|
|
|
ERRNO_CASE(EILSEQ);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef EINPROGRESS
|
|
|
|
ERRNO_CASE(EINPROGRESS);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef EINTR
|
|
|
|
ERRNO_CASE(EINTR);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef EINVAL
|
|
|
|
ERRNO_CASE(EINVAL);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef EIO
|
|
|
|
ERRNO_CASE(EIO);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef EISCONN
|
|
|
|
ERRNO_CASE(EISCONN);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef EISDIR
|
|
|
|
ERRNO_CASE(EISDIR);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef ELOOP
|
|
|
|
ERRNO_CASE(ELOOP);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef EMFILE
|
|
|
|
ERRNO_CASE(EMFILE);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef EMLINK
|
|
|
|
ERRNO_CASE(EMLINK);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef EMSGSIZE
|
|
|
|
ERRNO_CASE(EMSGSIZE);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef EMULTIHOP
|
|
|
|
ERRNO_CASE(EMULTIHOP);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef ENAMETOOLONG
|
|
|
|
ERRNO_CASE(ENAMETOOLONG);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef ENETDOWN
|
|
|
|
ERRNO_CASE(ENETDOWN);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef ENETRESET
|
|
|
|
ERRNO_CASE(ENETRESET);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef ENETUNREACH
|
|
|
|
ERRNO_CASE(ENETUNREACH);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef ENFILE
|
|
|
|
ERRNO_CASE(ENFILE);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef ENOBUFS
|
|
|
|
ERRNO_CASE(ENOBUFS);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef ENODATA
|
|
|
|
ERRNO_CASE(ENODATA);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef ENODEV
|
|
|
|
ERRNO_CASE(ENODEV);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef ENOENT
|
|
|
|
ERRNO_CASE(ENOENT);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef ENOEXEC
|
|
|
|
ERRNO_CASE(ENOEXEC);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef ENOLINK
|
|
|
|
ERRNO_CASE(ENOLINK);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef ENOLCK
|
|
|
|
# if ENOLINK != ENOLCK
|
|
|
|
ERRNO_CASE(ENOLCK);
|
|
|
|
# endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef ENOMEM
|
|
|
|
ERRNO_CASE(ENOMEM);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef ENOMSG
|
|
|
|
ERRNO_CASE(ENOMSG);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef ENOPROTOOPT
|
|
|
|
ERRNO_CASE(ENOPROTOOPT);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef ENOSPC
|
|
|
|
ERRNO_CASE(ENOSPC);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef ENOSR
|
|
|
|
ERRNO_CASE(ENOSR);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef ENOSTR
|
|
|
|
ERRNO_CASE(ENOSTR);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef ENOSYS
|
|
|
|
ERRNO_CASE(ENOSYS);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef ENOTCONN
|
|
|
|
ERRNO_CASE(ENOTCONN);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef ENOTDIR
|
|
|
|
ERRNO_CASE(ENOTDIR);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef ENOTEMPTY
|
|
|
|
# if ENOTEMPTY != EEXIST
|
|
|
|
ERRNO_CASE(ENOTEMPTY);
|
|
|
|
# endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef ENOTSOCK
|
|
|
|
ERRNO_CASE(ENOTSOCK);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef ENOTSUP
|
|
|
|
ERRNO_CASE(ENOTSUP);
|
|
|
|
#else
|
|
|
|
# ifdef EOPNOTSUPP
|
|
|
|
ERRNO_CASE(EOPNOTSUPP);
|
|
|
|
# endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef ENOTTY
|
|
|
|
ERRNO_CASE(ENOTTY);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef ENXIO
|
|
|
|
ERRNO_CASE(ENXIO);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef EOVERFLOW
|
|
|
|
ERRNO_CASE(EOVERFLOW);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef EPERM
|
|
|
|
ERRNO_CASE(EPERM);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef EPIPE
|
|
|
|
ERRNO_CASE(EPIPE);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef EPROTO
|
|
|
|
ERRNO_CASE(EPROTO);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef EPROTONOSUPPORT
|
|
|
|
ERRNO_CASE(EPROTONOSUPPORT);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef EPROTOTYPE
|
|
|
|
ERRNO_CASE(EPROTOTYPE);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef ERANGE
|
|
|
|
ERRNO_CASE(ERANGE);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef EROFS
|
|
|
|
ERRNO_CASE(EROFS);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef ESPIPE
|
|
|
|
ERRNO_CASE(ESPIPE);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef ESRCH
|
|
|
|
ERRNO_CASE(ESRCH);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef ESTALE
|
|
|
|
ERRNO_CASE(ESTALE);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef ETIME
|
|
|
|
ERRNO_CASE(ETIME);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef ETIMEDOUT
|
|
|
|
ERRNO_CASE(ETIMEDOUT);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef ETXTBSY
|
|
|
|
ERRNO_CASE(ETXTBSY);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef EXDEV
|
|
|
|
ERRNO_CASE(EXDEV);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
default: return "";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *signo_string(int signo) {
|
|
|
|
#define SIGNO_CASE(e) case e: return #e;
|
|
|
|
switch (signo) {
|
|
|
|
#ifdef SIGHUP
|
|
|
|
SIGNO_CASE(SIGHUP);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef SIGINT
|
|
|
|
SIGNO_CASE(SIGINT);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef SIGQUIT
|
|
|
|
SIGNO_CASE(SIGQUIT);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef SIGILL
|
|
|
|
SIGNO_CASE(SIGILL);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef SIGTRAP
|
|
|
|
SIGNO_CASE(SIGTRAP);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef SIGABRT
|
|
|
|
SIGNO_CASE(SIGABRT);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef SIGIOT
|
|
|
|
# if SIGABRT != SIGIOT
|
|
|
|
SIGNO_CASE(SIGIOT);
|
|
|
|
# endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef SIGBUS
|
|
|
|
SIGNO_CASE(SIGBUS);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef SIGFPE
|
|
|
|
SIGNO_CASE(SIGFPE);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef SIGKILL
|
|
|
|
SIGNO_CASE(SIGKILL);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef SIGUSR1
|
|
|
|
SIGNO_CASE(SIGUSR1);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef SIGSEGV
|
|
|
|
SIGNO_CASE(SIGSEGV);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef SIGUSR2
|
|
|
|
SIGNO_CASE(SIGUSR2);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef SIGPIPE
|
|
|
|
SIGNO_CASE(SIGPIPE);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef SIGALRM
|
|
|
|
SIGNO_CASE(SIGALRM);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
SIGNO_CASE(SIGTERM);
|
|
|
|
|
|
|
|
#ifdef SIGCHLD
|
|
|
|
SIGNO_CASE(SIGCHLD);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef SIGSTKFLT
|
|
|
|
SIGNO_CASE(SIGSTKFLT);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef SIGCONT
|
|
|
|
SIGNO_CASE(SIGCONT);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef SIGSTOP
|
|
|
|
SIGNO_CASE(SIGSTOP);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef SIGTSTP
|
|
|
|
SIGNO_CASE(SIGTSTP);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef SIGBREAK
|
|
|
|
SIGNO_CASE(SIGBREAK);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef SIGTTIN
|
|
|
|
SIGNO_CASE(SIGTTIN);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef SIGTTOU
|
|
|
|
SIGNO_CASE(SIGTTOU);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef SIGURG
|
|
|
|
SIGNO_CASE(SIGURG);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef SIGXCPU
|
|
|
|
SIGNO_CASE(SIGXCPU);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef SIGXFSZ
|
|
|
|
SIGNO_CASE(SIGXFSZ);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef SIGVTALRM
|
|
|
|
SIGNO_CASE(SIGVTALRM);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef SIGPROF
|
|
|
|
SIGNO_CASE(SIGPROF);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef SIGWINCH
|
|
|
|
SIGNO_CASE(SIGWINCH);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef SIGIO
|
|
|
|
SIGNO_CASE(SIGIO);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef SIGPOLL
|
|
|
|
# if SIGPOLL != SIGIO
|
|
|
|
SIGNO_CASE(SIGPOLL);
|
|
|
|
# endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef SIGLOST
|
|
|
|
# if SIGLOST != SIGABRT
|
|
|
|
SIGNO_CASE(SIGLOST);
|
|
|
|
# endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef SIGPWR
|
|
|
|
# if SIGPWR != SIGLOST
|
|
|
|
SIGNO_CASE(SIGPWR);
|
|
|
|
# endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef SIGINFO
|
|
|
|
# if !defined(SIGPWR) || SIGINFO != SIGPWR
|
|
|
|
SIGNO_CASE(SIGINFO);
|
|
|
|
# endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef SIGSYS
|
|
|
|
SIGNO_CASE(SIGSYS);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
default: return "";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Local<Value> ErrnoException(Isolate* isolate,
|
|
|
|
int errorno,
|
|
|
|
const char *syscall,
|
|
|
|
const char *msg,
|
|
|
|
const char *path) {
|
|
|
|
Environment* env = Environment::GetCurrent(isolate);
|
|
|
|
|
|
|
|
Local<Value> e;
|
|
|
|
Local<String> estring = OneByteString(env->isolate(), errno_string(errorno));
|
|
|
|
if (msg == nullptr || msg[0] == '\0') {
|
|
|
|
msg = strerror(errorno);
|
|
|
|
}
|
|
|
|
Local<String> message = OneByteString(env->isolate(), msg);
|
|
|
|
|
|
|
|
Local<String> cons =
|
|
|
|
String::Concat(estring, FIXED_ONE_BYTE_STRING(env->isolate(), ", "));
|
|
|
|
cons = String::Concat(cons, message);
|
|
|
|
|
|
|
|
Local<String> path_string;
|
|
|
|
if (path != nullptr) {
|
|
|
|
// FIXME(bnoordhuis) It's questionable to interpret the file path as UTF-8.
|
|
|
|
path_string = String::NewFromUtf8(env->isolate(), path);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (path_string.IsEmpty() == false) {
|
|
|
|
cons = String::Concat(cons, FIXED_ONE_BYTE_STRING(env->isolate(), " '"));
|
|
|
|
cons = String::Concat(cons, path_string);
|
|
|
|
cons = String::Concat(cons, FIXED_ONE_BYTE_STRING(env->isolate(), "'"));
|
|
|
|
}
|
|
|
|
e = Exception::Error(cons);
|
|
|
|
|
|
|
|
Local<Object> obj = e->ToObject(env->isolate());
|
|
|
|
obj->Set(env->errno_string(), Integer::New(env->isolate(), errorno));
|
|
|
|
obj->Set(env->code_string(), estring);
|
|
|
|
|
|
|
|
if (path_string.IsEmpty() == false) {
|
|
|
|
obj->Set(env->path_string(), path_string);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (syscall != nullptr) {
|
|
|
|
obj->Set(env->syscall_string(), OneByteString(env->isolate(), syscall));
|
|
|
|
}
|
|
|
|
|
|
|
|
return e;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static Local<String> StringFromPath(Isolate* isolate, const char* path) {
|
|
|
|
#ifdef _WIN32
|
|
|
|
if (strncmp(path, "\\\\?\\UNC\\", 8) == 0) {
|
|
|
|
return String::Concat(FIXED_ONE_BYTE_STRING(isolate, "\\\\"),
|
|
|
|
String::NewFromUtf8(isolate, path + 8));
|
|
|
|
} else if (strncmp(path, "\\\\?\\", 4) == 0) {
|
|
|
|
return String::NewFromUtf8(isolate, path + 4);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return String::NewFromUtf8(isolate, path);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Local<Value> UVException(Isolate* isolate,
|
|
|
|
int errorno,
|
|
|
|
const char* syscall,
|
|
|
|
const char* msg,
|
|
|
|
const char* path) {
|
|
|
|
return UVException(isolate, errorno, syscall, msg, path, nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Local<Value> UVException(Isolate* isolate,
|
|
|
|
int errorno,
|
|
|
|
const char* syscall,
|
|
|
|
const char* msg,
|
|
|
|
const char* path,
|
|
|
|
const char* dest) {
|
|
|
|
Environment* env = Environment::GetCurrent(isolate);
|
|
|
|
|
|
|
|
if (!msg || !msg[0])
|
|
|
|
msg = uv_strerror(errorno);
|
|
|
|
|
|
|
|
Local<String> js_code = OneByteString(isolate, uv_err_name(errorno));
|
|
|
|
Local<String> js_syscall = OneByteString(isolate, syscall);
|
|
|
|
Local<String> js_path;
|
|
|
|
Local<String> js_dest;
|
|
|
|
|
|
|
|
Local<String> js_msg = js_code;
|
|
|
|
js_msg = String::Concat(js_msg, FIXED_ONE_BYTE_STRING(isolate, ": "));
|
|
|
|
js_msg = String::Concat(js_msg, OneByteString(isolate, msg));
|
|
|
|
js_msg = String::Concat(js_msg, FIXED_ONE_BYTE_STRING(isolate, ", "));
|
|
|
|
js_msg = String::Concat(js_msg, js_syscall);
|
|
|
|
|
|
|
|
if (path != nullptr) {
|
|
|
|
js_path = StringFromPath(isolate, path);
|
|
|
|
|
|
|
|
js_msg = String::Concat(js_msg, FIXED_ONE_BYTE_STRING(isolate, " '"));
|
|
|
|
js_msg = String::Concat(js_msg, js_path);
|
|
|
|
js_msg = String::Concat(js_msg, FIXED_ONE_BYTE_STRING(isolate, "'"));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dest != nullptr) {
|
|
|
|
js_dest = StringFromPath(isolate, dest);
|
|
|
|
|
|
|
|
js_msg = String::Concat(js_msg, FIXED_ONE_BYTE_STRING(isolate, " -> '"));
|
|
|
|
js_msg = String::Concat(js_msg, js_dest);
|
|
|
|
js_msg = String::Concat(js_msg, FIXED_ONE_BYTE_STRING(isolate, "'"));
|
|
|
|
}
|
|
|
|
|
|
|
|
Local<Object> e = Exception::Error(js_msg)->ToObject(isolate);
|
|
|
|
|
|
|
|
e->Set(env->errno_string(), Integer::New(isolate, errorno));
|
|
|
|
e->Set(env->code_string(), js_code);
|
|
|
|
e->Set(env->syscall_string(), js_syscall);
|
|
|
|
if (!js_path.IsEmpty())
|
|
|
|
e->Set(env->path_string(), js_path);
|
|
|
|
if (!js_dest.IsEmpty())
|
|
|
|
e->Set(env->dest_string(), js_dest);
|
|
|
|
|
|
|
|
return e;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Look up environment variable unless running as setuid root.
|
|
|
|
bool SafeGetenv(const char* key, std::string* text) {
|
|
|
|
#ifndef _WIN32
|
|
|
|
if (linux_at_secure || getuid() != geteuid() || getgid() != getegid())
|
|
|
|
goto fail;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (const char* value = getenv(key)) {
|
|
|
|
*text = value;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
fail:
|
|
|
|
text->clear();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
// Does about the same as strerror(),
|
|
|
|
// but supports all windows error messages
|
|
|
|
static const char *winapi_strerror(const int errorno, bool* must_free) {
|
|
|
|
char *errmsg = nullptr;
|
|
|
|
|
|
|
|
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
|
|
|
|
FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, errorno,
|
|
|
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&errmsg, 0, nullptr);
|
|
|
|
|
|
|
|
if (errmsg) {
|
|
|
|
*must_free = true;
|
|
|
|
|
|
|
|
// Remove trailing newlines
|
|
|
|
for (int i = strlen(errmsg) - 1;
|
|
|
|
i >= 0 && (errmsg[i] == '\n' || errmsg[i] == '\r'); i--) {
|
|
|
|
errmsg[i] = '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
return errmsg;
|
|
|
|
} else {
|
|
|
|
// FormatMessage failed
|
|
|
|
*must_free = false;
|
|
|
|
return "Unknown error";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Local<Value> WinapiErrnoException(Isolate* isolate,
|
|
|
|
int errorno,
|
|
|
|
const char* syscall,
|
|
|
|
const char* msg,
|
|
|
|
const char* path) {
|
|
|
|
Environment* env = Environment::GetCurrent(isolate);
|
|
|
|
Local<Value> e;
|
|
|
|
bool must_free = false;
|
|
|
|
if (!msg || !msg[0]) {
|
|
|
|
msg = winapi_strerror(errorno, &must_free);
|
|
|
|
}
|
|
|
|
Local<String> message = OneByteString(env->isolate(), msg);
|
|
|
|
|
|
|
|
if (path) {
|
|
|
|
Local<String> cons1 =
|
|
|
|
String::Concat(message, FIXED_ONE_BYTE_STRING(isolate, " '"));
|
|
|
|
Local<String> cons2 =
|
|
|
|
String::Concat(cons1, String::NewFromUtf8(isolate, path));
|
|
|
|
Local<String> cons3 =
|
|
|
|
String::Concat(cons2, FIXED_ONE_BYTE_STRING(isolate, "'"));
|
|
|
|
e = Exception::Error(cons3);
|
|
|
|
} else {
|
|
|
|
e = Exception::Error(message);
|
|
|
|
}
|
|
|
|
|
|
|
|
Local<Object> obj = e->ToObject(env->isolate());
|
|
|
|
obj->Set(env->errno_string(), Integer::New(isolate, errorno));
|
|
|
|
|
|
|
|
if (path != nullptr) {
|
|
|
|
obj->Set(env->path_string(), String::NewFromUtf8(isolate, path));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (syscall != nullptr) {
|
|
|
|
obj->Set(env->syscall_string(), OneByteString(isolate, syscall));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (must_free)
|
|
|
|
LocalFree((HLOCAL)msg);
|
|
|
|
|
|
|
|
return e;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
void* ArrayBufferAllocator::Allocate(size_t size) {
|
|
|
|
if (zero_fill_field_ || zero_fill_all_buffers)
|
|
|
|
return node::UncheckedCalloc(size);
|
|
|
|
else
|
|
|
|
return node::UncheckedMalloc(size);
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
bool DomainHasErrorHandler(const Environment* env,
|
|
|
|
const Local<Object>& domain) {
|
|
|
|
HandleScope scope(env->isolate());
|
|
|
|
|
|
|
|
Local<Value> domain_event_listeners_v = domain->Get(env->events_string());
|
|
|
|
if (!domain_event_listeners_v->IsObject())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
Local<Object> domain_event_listeners_o =
|
|
|
|
domain_event_listeners_v.As<Object>();
|
|
|
|
|
|
|
|
Local<Value> domain_error_listeners_v =
|
|
|
|
domain_event_listeners_o->Get(env->error_string());
|
|
|
|
|
|
|
|
if (domain_error_listeners_v->IsFunction() ||
|
|
|
|
(domain_error_listeners_v->IsArray() &&
|
|
|
|
domain_error_listeners_v.As<Array>()->Length() > 0))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DomainsStackHasErrorHandler(const Environment* env) {
|
|
|
|
HandleScope scope(env->isolate());
|
|
|
|
|
|
|
|
if (!env->using_domains())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
Local<Array> domains_stack_array = env->domains_stack_array().As<Array>();
|
|
|
|
if (domains_stack_array->Length() == 0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
uint32_t domains_stack_length = domains_stack_array->Length();
|
|
|
|
for (uint32_t i = domains_stack_length; i > 0; --i) {
|
|
|
|
Local<Value> domain_v = domains_stack_array->Get(i - 1);
|
|
|
|
if (!domain_v->IsObject())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
Local<Object> domain = domain_v.As<Object>();
|
|
|
|
if (DomainHasErrorHandler(env, domain))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool ShouldAbortOnUncaughtException(Isolate* isolate) {
|
|
|
|
HandleScope scope(isolate);
|
|
|
|
|
|
|
|
Environment* env = Environment::GetCurrent(isolate);
|
|
|
|
Local<Object> process_object = env->process_object();
|
|
|
|
Local<String> emitting_top_level_domain_error_key =
|
|
|
|
env->emitting_top_level_domain_error_string();
|
|
|
|
bool isEmittingTopLevelDomainError =
|
|
|
|
process_object->Get(emitting_top_level_domain_error_key)->BooleanValue();
|
|
|
|
|
|
|
|
return isEmittingTopLevelDomainError || !DomainsStackHasErrorHandler(env);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DomainPromiseHook(PromiseHookType type,
|
|
|
|
Local<Promise> promise,
|
|
|
|
Local<Value> parent,
|
|
|
|
void* arg) {
|
|
|
|
Environment* env = static_cast<Environment*>(arg);
|
|
|
|
Local<Context> context = env->context();
|
|
|
|
|
|
|
|
if (type == PromiseHookType::kInit && env->in_domain()) {
|
|
|
|
promise->Set(context,
|
|
|
|
env->domain_string(),
|
|
|
|
env->domain_array()->Get(context,
|
|
|
|
0).ToLocalChecked()).FromJust();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type == PromiseHookType::kBefore) {
|
|
|
|
DomainEnter(env, promise);
|
|
|
|
} else if (type == PromiseHookType::kAfter) {
|
|
|
|
DomainExit(env, promise);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void SetupDomainUse(const FunctionCallbackInfo<Value>& args) {
|
|
|
|
Environment* env = Environment::GetCurrent(args);
|
|
|
|
|
|
|
|
if (env->using_domains())
|
|
|
|
return;
|
|
|
|
env->set_using_domains(true);
|
|
|
|
|
|
|
|
HandleScope scope(env->isolate());
|
|
|
|
Local<Object> process_object = env->process_object();
|
|
|
|
|
|
|
|
Local<String> tick_callback_function_key = env->tick_domain_cb_string();
|
|
|
|
Local<Function> tick_callback_function =
|
|
|
|
process_object->Get(tick_callback_function_key).As<Function>();
|
|
|
|
|
|
|
|
if (!tick_callback_function->IsFunction()) {
|
|
|
|
fprintf(stderr, "process._tickDomainCallback assigned to non-function\n");
|
|
|
|
ABORT();
|
|
|
|
}
|
|
|
|
|
|
|
|
process_object->Set(env->tick_callback_string(), tick_callback_function);
|
|
|
|
env->set_tick_callback_function(tick_callback_function);
|
|
|
|
|
|
|
|
CHECK(args[0]->IsArray());
|
|
|
|
env->set_domain_array(args[0].As<Array>());
|
|
|
|
|
|
|
|
CHECK(args[1]->IsArray());
|
|
|
|
env->set_domains_stack_array(args[1].As<Array>());
|
|
|
|
|
|
|
|
// Do a little housekeeping.
|
|
|
|
env->process_object()->Delete(
|
|
|
|
env->context(),
|
|
|
|
FIXED_ONE_BYTE_STRING(args.GetIsolate(), "_setupDomainUse")).FromJust();
|
|
|
|
|
|
|
|
uint32_t* const fields = env->domain_flag()->fields();
|
|
|
|
uint32_t const fields_count = env->domain_flag()->fields_count();
|
|
|
|
|
|
|
|
Local<ArrayBuffer> array_buffer =
|
|
|
|
ArrayBuffer::New(env->isolate(), fields, sizeof(*fields) * fields_count);
|
|
|
|
|
|
|
|
env->AddPromiseHook(DomainPromiseHook, static_cast<void*>(env));
|
|
|
|
|
|
|
|
args.GetReturnValue().Set(Uint32Array::New(array_buffer, 0, fields_count));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void RunMicrotasks(const FunctionCallbackInfo<Value>& args) {
|
|
|
|
args.GetIsolate()->RunMicrotasks();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void SetupProcessObject(const FunctionCallbackInfo<Value>& args) {
|
|
|
|
Environment* env = Environment::GetCurrent(args);
|
|
|
|
|
|
|
|
CHECK(args[0]->IsFunction());
|
|
|
|
|
|
|
|
env->set_push_values_to_array_function(args[0].As<Function>());
|
|
|
|
env->process_object()->Delete(
|
|
|
|
env->context(),
|
|
|
|
FIXED_ONE_BYTE_STRING(env->isolate(), "_setupProcessObject")).FromJust();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void SetupNextTick(const FunctionCallbackInfo<Value>& args) {
|
|
|
|
Environment* env = Environment::GetCurrent(args);
|
|
|
|
|
|
|
|
CHECK(args[0]->IsFunction());
|
|
|
|
CHECK(args[1]->IsObject());
|
|
|
|
|
|
|
|
env->set_tick_callback_function(args[0].As<Function>());
|
|
|
|
|
|
|
|
env->SetMethod(args[1].As<Object>(), "runMicrotasks", RunMicrotasks);
|
|
|
|
|
|
|
|
// Do a little housekeeping.
|
|
|
|
env->process_object()->Delete(
|
|
|
|
env->context(),
|
|
|
|
FIXED_ONE_BYTE_STRING(args.GetIsolate(), "_setupNextTick")).FromJust();
|
|
|
|
|
|
|
|
// Values use to cross communicate with processNextTick.
|
|
|
|
uint32_t* const fields = env->tick_info()->fields();
|
|
|
|
uint32_t const fields_count = env->tick_info()->fields_count();
|
|
|
|
|
|
|
|
Local<ArrayBuffer> array_buffer =
|
|
|
|
ArrayBuffer::New(env->isolate(), fields, sizeof(*fields) * fields_count);
|
|
|
|
|
|
|
|
args.GetReturnValue().Set(Uint32Array::New(array_buffer, 0, fields_count));
|
|
|
|
}
|
|
|
|
|
|
|
|
void PromiseRejectCallback(PromiseRejectMessage message) {
|
|
|
|
Local<Promise> promise = message.GetPromise();
|
|
|
|
Isolate* isolate = promise->GetIsolate();
|
|
|
|
Local<Value> value = message.GetValue();
|
|
|
|
Local<Integer> event = Integer::New(isolate, message.GetEvent());
|
|
|
|
|
|
|
|
Environment* env = Environment::GetCurrent(isolate);
|
|
|
|
Local<Function> callback = env->promise_reject_function();
|
|
|
|
|
|
|
|
if (value.IsEmpty())
|
|
|
|
value = Undefined(isolate);
|
|
|
|
|
|
|
|
Local<Value> args[] = { event, promise, value };
|
|
|
|
Local<Object> process = env->process_object();
|
|
|
|
|
|
|
|
callback->Call(process, arraysize(args), args);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetupPromises(const FunctionCallbackInfo<Value>& args) {
|
|
|
|
Environment* env = Environment::GetCurrent(args);
|
|
|
|
Isolate* isolate = env->isolate();
|
|
|
|
|
|
|
|
CHECK(args[0]->IsFunction());
|
|
|
|
|
|
|
|
isolate->SetPromiseRejectCallback(PromiseRejectCallback);
|
|
|
|
env->set_promise_reject_function(args[0].As<Function>());
|
|
|
|
|
|
|
|
env->process_object()->Delete(
|
|
|
|
env->context(),
|
|
|
|
FIXED_ONE_BYTE_STRING(args.GetIsolate(), "_setupPromises")).FromJust();
|
|
|
|
}
|
|
|
|
|
|
|
|
} // anonymous namespace
|
|
|
|
|
|
|
|
|
|
|
|
void AddPromiseHook(v8::Isolate* isolate, promise_hook_func fn, void* arg) {
|
|
|
|
Environment* env = Environment::GetCurrent(isolate);
|
|
|
|
env->AddPromiseHook(fn, arg);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
MaybeLocal<Value> MakeCallback(Environment* env,
|
|
|
|
Local<Value> recv,
|
|
|
|
const Local<Function> callback,
|
|
|
|
int argc,
|
|
|
|
Local<Value> argv[],
|
|
|
|
double async_id,
|
|
|
|
double trigger_id) {
|
|
|
|
// If you hit this assertion, you forgot to enter the v8::Context first.
|
|
|
|
CHECK_EQ(env->context(), env->isolate()->GetCurrentContext());
|
|
|
|
|
|
|
|
Local<Object> object;
|
|
|
|
|
|
|
|
Environment::AsyncCallbackScope callback_scope(env);
|
|
|
|
bool disposed_domain = false;
|
|
|
|
|
|
|
|
if (recv->IsObject()) {
|
|
|
|
object = recv.As<Object>();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (env->using_domains()) {
|
|
|
|
CHECK(recv->IsObject());
|
|
|
|
disposed_domain = DomainEnter(env, object);
|
|
|
|
if (disposed_domain) return Undefined(env->isolate());
|
|
|
|
}
|
|
|
|
|
|
|
|
MaybeLocal<Value> ret;
|
|
|
|
|
|
|
|
{
|
|
|
|
AsyncHooks::ExecScope exec_scope(env, async_id, trigger_id);
|
|
|
|
|
|
|
|
if (async_id != 0) {
|
|
|
|
if (!AsyncWrap::EmitBefore(env, async_id)) return Local<Value>();
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = callback->Call(env->context(), recv, argc, argv);
|
|
|
|
|
|
|
|
if (ret.IsEmpty()) {
|
|
|
|
// NOTE: For backwards compatibility with public API we return Undefined()
|
|
|
|
// if the top level call threw.
|
|
|
|
return callback_scope.in_makecallback() ?
|
|
|
|
ret : Undefined(env->isolate());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (async_id != 0) {
|
|
|
|
if (!AsyncWrap::EmitAfter(env, async_id)) return Local<Value>();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (env->using_domains()) {
|
|
|
|
disposed_domain = DomainExit(env, object);
|
|
|
|
if (disposed_domain) return Undefined(env->isolate());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (callback_scope.in_makecallback()) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
Environment::TickInfo* tick_info = env->tick_info();
|
|
|
|
|
|
|
|
if (tick_info->length() == 0) {
|
|
|
|
env->isolate()->RunMicrotasks();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure the stack unwound properly. If there are nested MakeCallback's
|
|
|
|
// then it should return early and not reach this code.
|
|
|
|
CHECK_EQ(env->current_async_id(), async_id);
|
|
|
|
CHECK_EQ(env->trigger_id(), trigger_id);
|
|
|
|
|
|
|
|
Local<Object> process = env->process_object();
|
|
|
|
|
|
|
|
if (tick_info->length() == 0) {
|
|
|
|
tick_info->set_index(0);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (env->tick_callback_function()->Call(process, 0, nullptr).IsEmpty()) {
|
|
|
|
return Undefined(env->isolate());
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Public MakeCallback()s
|
|
|
|
|
|
|
|
|
|
|
|
MaybeLocal<Value> MakeCallback(Isolate* isolate,
|
|
|
|
Local<Object> recv,
|
|
|
|
const char* method,
|
|
|
|
int argc,
|
|
|
|
Local<Value> argv[],
|
|
|
|
async_uid async_id,
|
|
|
|
async_uid trigger_id) {
|
|
|
|
Local<String> method_string =
|
|
|
|
String::NewFromUtf8(isolate, method, v8::NewStringType::kNormal)
|
|
|
|
.ToLocalChecked();
|
|
|
|
return MakeCallback(isolate, recv, method_string, argc, argv,
|
|
|
|
async_id, trigger_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
MaybeLocal<Value> MakeCallback(Isolate* isolate,
|
|
|
|
Local<Object> recv,
|
|
|
|
Local<String> symbol,
|
|
|
|
int argc,
|
|
|
|
Local<Value> argv[],
|
|
|
|
async_uid async_id,
|
|
|
|
async_uid trigger_id) {
|
|
|
|
Local<Value> callback_v = recv->Get(symbol);
|
|
|
|
if (callback_v.IsEmpty()) return Local<Value>();
|
|
|
|
if (!callback_v->IsFunction()) return Local<Value>();
|
|
|
|
Local<Function> callback = callback_v.As<Function>();
|
|
|
|
return MakeCallback(isolate, recv, callback, argc, argv,
|
|
|
|
async_id, trigger_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
MaybeLocal<Value> MakeCallback(Isolate* isolate,
|
|
|
|
Local<Object> recv,
|
|
|
|
Local<Function> callback,
|
|
|
|
int argc,
|
|
|
|
Local<Value> argv[],
|
|
|
|
async_uid async_id,
|
|
|
|
async_uid trigger_id) {
|
|
|
|
// Observe the following two subtleties:
|
|
|
|
//
|
|
|
|
// 1. The environment is retrieved from the callback function's context.
|
|
|
|
// 2. The context to enter is retrieved from the environment.
|
|
|
|
//
|
|
|
|
// Because of the AssignToContext() call in src/node_contextify.cc,
|
|
|
|
// the two contexts need not be the same.
|
|
|
|
Environment* env = Environment::GetCurrent(callback->CreationContext());
|
|
|
|
Context::Scope context_scope(env->context());
|
|
|
|
return MakeCallback(env, recv.As<Value>(), callback, argc, argv,
|
|
|
|
async_id, trigger_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Legacy MakeCallback()s
|
|
|
|
|
|
|
|
Local<Value> MakeCallback(Isolate* isolate,
|
|
|
|
Local<Object> recv,
|
|
|
|
const char* method,
|
|
|
|
int argc,
|
|
|
|
Local<Value>* argv) {
|
|
|
|
EscapableHandleScope handle_scope(isolate);
|
|
|
|
return handle_scope.Escape(
|
|
|
|
MakeCallback(isolate, recv, method, argc, argv, 0, 0)
|
|
|
|
.FromMaybe(Local<Value>()));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Local<Value> MakeCallback(Isolate* isolate,
|
|
|
|
Local<Object> recv,
|
|
|
|
Local<String> symbol,
|
|
|
|
int argc,
|
|
|
|
Local<Value>* argv) {
|
|
|
|
EscapableHandleScope handle_scope(isolate);
|
|
|
|
return handle_scope.Escape(
|
|
|
|
MakeCallback(isolate, recv, symbol, argc, argv, 0, 0)
|
|
|
|
.FromMaybe(Local<Value>()));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Local<Value> MakeCallback(Isolate* isolate,
|
|
|
|
Local<Object> recv,
|
|
|
|
Local<Function> callback,
|
|
|
|
int argc,
|
|
|
|
Local<Value>* argv) {
|
|
|
|
EscapableHandleScope handle_scope(isolate);
|
|
|
|
return handle_scope.Escape(
|
|
|
|
MakeCallback(isolate, recv, callback, argc, argv, 0, 0)
|
|
|
|
.FromMaybe(Local<Value>()));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
enum encoding ParseEncoding(const char* encoding,
|
|
|
|
enum encoding default_encoding) {
|
|
|
|
switch (encoding[0]) {
|
|
|
|
case 'u':
|
|
|
|
// utf8, utf16le
|
|
|
|
if (encoding[1] == 't' && encoding[2] == 'f') {
|
|
|
|
// Skip `-`
|
|
|
|
encoding += encoding[3] == '-' ? 4 : 3;
|
|
|
|
if (encoding[0] == '8' && encoding[1] == '\0')
|
|
|
|
return UTF8;
|
|
|
|
if (strncmp(encoding, "16le", 4) == 0)
|
|
|
|
return UCS2;
|
|
|
|
|
|
|
|
// ucs2
|
|
|
|
} else if (encoding[1] == 'c' && encoding[2] == 's') {
|
|
|
|
encoding += encoding[3] == '-' ? 4 : 3;
|
|
|
|
if (encoding[0] == '2' && encoding[1] == '\0')
|
|
|
|
return UCS2;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'l':
|
|
|
|
// latin1
|
|
|
|
if (encoding[1] == 'a') {
|
|
|
|
if (strncmp(encoding + 2, "tin1", 4) == 0)
|
|
|
|
return LATIN1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'b':
|
|
|
|
// binary
|
|
|
|
if (encoding[1] == 'i') {
|
|
|
|
if (strncmp(encoding + 2, "nary", 4) == 0)
|
|
|
|
return LATIN1;
|
|
|
|
|
|
|
|
// buffer
|
|
|
|
} else if (encoding[1] == 'u') {
|
|
|
|
if (strncmp(encoding + 2, "ffer", 4) == 0)
|
|
|
|
return BUFFER;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case '\0':
|
|
|
|
return default_encoding;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (StringEqualNoCase(encoding, "utf8")) {
|
|
|
|
return UTF8;
|
|
|
|
} else if (StringEqualNoCase(encoding, "utf-8")) {
|
|
|
|
return UTF8;
|
|
|
|
} else if (StringEqualNoCase(encoding, "ascii")) {
|
|
|
|
return ASCII;
|
|
|
|
} else if (StringEqualNoCase(encoding, "base64")) {
|
|
|
|
return BASE64;
|
|
|
|
} else if (StringEqualNoCase(encoding, "ucs2")) {
|
|
|
|
return UCS2;
|
|
|
|
} else if (StringEqualNoCase(encoding, "ucs-2")) {
|
|
|
|
return UCS2;
|
|
|
|
} else if (StringEqualNoCase(encoding, "utf16le")) {
|
|
|
|
return UCS2;
|
|
|
|
} else if (StringEqualNoCase(encoding, "utf-16le")) {
|
|
|
|
return UCS2;
|
|
|
|
} else if (StringEqualNoCase(encoding, "latin1")) {
|
|
|
|
return LATIN1;
|
|
|
|
} else if (StringEqualNoCase(encoding, "binary")) {
|
|
|
|
return LATIN1; // BINARY is a deprecated alias of LATIN1.
|
|
|
|
} else if (StringEqualNoCase(encoding, "buffer")) {
|
|
|
|
return BUFFER;
|
|
|
|
} else if (StringEqualNoCase(encoding, "hex")) {
|
|
|
|
return HEX;
|
|
|
|
} else {
|
|
|
|
return default_encoding;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
enum encoding ParseEncoding(Isolate* isolate,
|
|
|
|
Local<Value> encoding_v,
|
|
|
|
enum encoding default_encoding) {
|
|
|
|
CHECK(!encoding_v.IsEmpty());
|
|
|
|
|
|
|
|
if (!encoding_v->IsString())
|
|
|
|
return default_encoding;
|
|
|
|
|
|
|
|
node::Utf8Value encoding(isolate, encoding_v);
|
|
|
|
|
|
|
|
return ParseEncoding(*encoding, default_encoding);
|
|
|
|
}
|
|
|
|
|
|
|
|
Local<Value> Encode(Isolate* isolate,
|
|
|
|
const char* buf,
|
|
|
|
size_t len,
|
|
|
|
enum encoding encoding) {
|
|
|
|
CHECK_NE(encoding, UCS2);
|
|
|
|
Local<Value> error;
|
|
|
|
return StringBytes::Encode(isolate, buf, len, encoding, &error)
|
|
|
|
.ToLocalChecked();
|
|
|
|
}
|
|
|
|
|
|
|
|
Local<Value> Encode(Isolate* isolate, const uint16_t* buf, size_t len) {
|
|
|
|
Local<Value> error;
|
|
|
|
return StringBytes::Encode(isolate, buf, len, &error)
|
|
|
|
.ToLocalChecked();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns -1 if the handle was not valid for decoding
|
|
|
|
ssize_t DecodeBytes(Isolate* isolate,
|
|
|
|
Local<Value> val,
|
|
|
|
enum encoding encoding) {
|
|
|
|
HandleScope scope(isolate);
|
|
|
|
|
|
|
|
return StringBytes::Size(isolate, val, encoding);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns number of bytes written.
|
|
|
|
ssize_t DecodeWrite(Isolate* isolate,
|
|
|
|
char* buf,
|
|
|
|
size_t buflen,
|
|
|
|
Local<Value> val,
|
|
|
|
enum encoding encoding) {
|
|
|
|
return StringBytes::Write(isolate, buf, buflen, val, encoding, nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsExceptionDecorated(Environment* env, Local<Value> er) {
|
|
|
|
if (!er.IsEmpty() && er->IsObject()) {
|
|
|
|
Local<Object> err_obj = er.As<Object>();
|
|
|
|
auto maybe_value =
|
|
|
|
err_obj->GetPrivate(env->context(), env->decorated_private_symbol());
|
|
|
|
Local<Value> decorated;
|
|
|
|
return maybe_value.ToLocal(&decorated) && decorated->IsTrue();
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void AppendExceptionLine(Environment* env,
|
|
|
|
Local<Value> er,
|
|
|
|
Local<Message> message,
|
|
|
|
enum ErrorHandlingMode mode) {
|
|
|
|
if (message.IsEmpty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
HandleScope scope(env->isolate());
|
|
|
|
Local<Object> err_obj;
|
|
|
|
if (!er.IsEmpty() && er->IsObject()) {
|
|
|
|
err_obj = er.As<Object>();
|
|
|
|
|
|
|
|
auto context = env->context();
|
|
|
|
auto processed_private_symbol = env->processed_private_symbol();
|
|
|
|
// Do it only once per message
|
|
|
|
if (err_obj->HasPrivate(context, processed_private_symbol).FromJust())
|
|
|
|
return;
|
|
|
|
err_obj->SetPrivate(
|
|
|
|
context,
|
|
|
|
processed_private_symbol,
|
|
|
|
True(env->isolate()));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Print (filename):(line number): (message).
|
|
|
|
node::Utf8Value filename(env->isolate(), message->GetScriptResourceName());
|
|
|
|
const char* filename_string = *filename;
|
|
|
|
int linenum = message->GetLineNumber();
|
|
|
|
// Print line of source code.
|
|
|
|
node::Utf8Value sourceline(env->isolate(), message->GetSourceLine());
|
|
|
|
const char* sourceline_string = *sourceline;
|
|
|
|
|
|
|
|
// Because of how node modules work, all scripts are wrapped with a
|
|
|
|
// "function (module, exports, __filename, ...) {"
|
|
|
|
// to provide script local variables.
|
|
|
|
//
|
|
|
|
// When reporting errors on the first line of a script, this wrapper
|
|
|
|
// function is leaked to the user. There used to be a hack here to
|
|
|
|
// truncate off the first 62 characters, but it caused numerous other
|
|
|
|
// problems when vm.runIn*Context() methods were used for non-module
|
|
|
|
// code.
|
|
|
|
//
|
|
|
|
// If we ever decide to re-instate such a hack, the following steps
|
|
|
|
// must be taken:
|
|
|
|
//
|
|
|
|
// 1. Pass a flag around to say "this code was wrapped"
|
|
|
|
// 2. Update the stack frame output so that it is also correct.
|
|
|
|
//
|
|
|
|
// It would probably be simpler to add a line rather than add some
|
|
|
|
// number of characters to the first line, since V8 truncates the
|
|
|
|
// sourceline to 78 characters, and we end up not providing very much
|
|
|
|
// useful debugging info to the user if we remove 62 characters.
|
|
|
|
|
|
|
|
int start = message->GetStartColumn(env->context()).FromMaybe(0);
|
|
|
|
int end = message->GetEndColumn(env->context()).FromMaybe(0);
|
|
|
|
|
|
|
|
char arrow[1024];
|
|
|
|
int max_off = sizeof(arrow) - 2;
|
|
|
|
|
|
|
|
int off = snprintf(arrow,
|
|
|
|
sizeof(arrow),
|
|
|
|
"%s:%i\n%s\n",
|
|
|
|
filename_string,
|
|
|
|
linenum,
|
|
|
|
sourceline_string);
|
|
|
|
CHECK_GE(off, 0);
|
|
|
|
if (off > max_off) {
|
|
|
|
off = max_off;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Print wavy underline (GetUnderline is deprecated).
|
|
|
|
for (int i = 0; i < start; i++) {
|
|
|
|
if (sourceline_string[i] == '\0' || off >= max_off) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
CHECK_LT(off, max_off);
|
|
|
|
arrow[off++] = (sourceline_string[i] == '\t') ? '\t' : ' ';
|
|
|
|
}
|
|
|
|
for (int i = start; i < end; i++) {
|
|
|
|
if (sourceline_string[i] == '\0' || off >= max_off) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
CHECK_LT(off, max_off);
|
|
|
|
arrow[off++] = '^';
|
|
|
|
}
|
|
|
|
CHECK_LE(off, max_off);
|
|
|
|
arrow[off] = '\n';
|
|
|
|
arrow[off + 1] = '\0';
|
|
|
|
|
|
|
|
Local<String> arrow_str = String::NewFromUtf8(env->isolate(), arrow);
|
|
|
|
|
|
|
|
const bool can_set_arrow = !arrow_str.IsEmpty() && !err_obj.IsEmpty();
|
|
|
|
// If allocating arrow_str failed, print it out. There's not much else to do.
|
|
|
|
// If it's not an error, but something needs to be printed out because
|
|
|
|
// it's a fatal exception, also print it out from here.
|
|
|
|
// Otherwise, the arrow property will be attached to the object and handled
|
|
|
|
// by the caller.
|
|
|
|
if (!can_set_arrow || (mode == FATAL_ERROR && !err_obj->IsNativeError())) {
|
|
|
|
if (env->printed_error())
|
|
|
|
return;
|
|
|
|
env->set_printed_error(true);
|
|
|
|
|
|
|
|
uv_tty_reset_mode();
|
|
|
|
PrintErrorString("\n%s", arrow);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
CHECK(err_obj->SetPrivate(
|
|
|
|
env->context(),
|
|
|
|
env->arrow_message_private_symbol(),
|
|
|
|
arrow_str).FromMaybe(false));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void ReportException(Environment* env,
|
|
|
|
Local<Value> er,
|
|
|
|
Local<Message> message) {
|
|
|
|
HandleScope scope(env->isolate());
|
|
|
|
|
|
|
|
AppendExceptionLine(env, er, message, FATAL_ERROR);
|
|
|
|
|
|
|
|
Local<Value> trace_value;
|
|
|
|
Local<Value> arrow;
|
|
|
|
const bool decorated = IsExceptionDecorated(env, er);
|
|
|
|
|
|
|
|
if (er->IsUndefined() || er->IsNull()) {
|
|
|
|
trace_value = Undefined(env->isolate());
|
|
|
|
} else {
|
|
|
|
Local<Object> err_obj = er->ToObject(env->isolate());
|
|
|
|
|
|
|
|
trace_value = err_obj->Get(env->stack_string());
|
|
|
|
arrow =
|
|
|
|
err_obj->GetPrivate(
|
|
|
|
env->context(),
|
|
|
|
env->arrow_message_private_symbol()).ToLocalChecked();
|
|
|
|
}
|
|
|
|
|
|
|
|
node::Utf8Value trace(env->isolate(), trace_value);
|
|
|
|
|
|
|
|
// range errors have a trace member set to undefined
|
|
|
|
if (trace.length() > 0 && !trace_value->IsUndefined()) {
|
|
|
|
if (arrow.IsEmpty() || !arrow->IsString() || decorated) {
|
|
|
|
PrintErrorString("%s\n", *trace);
|
|
|
|
} else {
|
|
|
|
node::Utf8Value arrow_string(env->isolate(), arrow);
|
|
|
|
PrintErrorString("%s\n%s\n", *arrow_string, *trace);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// this really only happens for RangeErrors, since they're the only
|
|
|
|
// kind that won't have all this info in the trace, or when non-Error
|
|
|
|
// objects are thrown manually.
|
|
|
|
Local<Value> message;
|
|
|
|
Local<Value> name;
|
|
|
|
|
|
|
|
if (er->IsObject()) {
|
|
|
|
Local<Object> err_obj = er.As<Object>();
|
|
|
|
message = err_obj->Get(env->message_string());
|
|
|
|
name = err_obj->Get(FIXED_ONE_BYTE_STRING(env->isolate(), "name"));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (message.IsEmpty() ||
|
|
|
|
message->IsUndefined() ||
|
|
|
|
name.IsEmpty() ||
|
|
|
|
name->IsUndefined()) {
|
|
|
|
// Not an error object. Just print as-is.
|
|
|
|
String::Utf8Value message(er);
|
|
|
|
|
|
|
|
PrintErrorString("%s\n", *message ? *message :
|
|
|
|
"<toString() threw exception>");
|
|
|
|
} else {
|
|
|
|
node::Utf8Value name_string(env->isolate(), name);
|
|
|
|
node::Utf8Value message_string(env->isolate(), message);
|
|
|
|
|
|
|
|
if (arrow.IsEmpty() || !arrow->IsString() || decorated) {
|
|
|
|
PrintErrorString("%s: %s\n", *name_string, *message_string);
|
|
|
|
} else {
|
|
|
|
node::Utf8Value arrow_string(env->isolate(), arrow);
|
|
|
|
PrintErrorString("%s\n%s: %s\n",
|
|
|
|
*arrow_string,
|
|
|
|
*name_string,
|
|
|
|
*message_string);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fflush(stderr);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void ReportException(Environment* env, const TryCatch& try_catch) {
|
|
|
|
ReportException(env, try_catch.Exception(), try_catch.Message());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Executes a str within the current v8 context.
|
|
|
|
static Local<Value> ExecuteString(Environment* env,
|
|
|
|
Local<String> source,
|
|
|
|
Local<String> filename) {
|
|
|
|
EscapableHandleScope scope(env->isolate());
|
|
|
|
TryCatch try_catch(env->isolate());
|
|
|
|
|
|
|
|
// try_catch must be nonverbose to disable FatalException() handler,
|
|
|
|
// we will handle exceptions ourself.
|
|
|
|
try_catch.SetVerbose(false);
|
|
|
|
|
|
|
|
ScriptOrigin origin(filename);
|
|
|
|
MaybeLocal<v8::Script> script =
|
|
|
|
v8::Script::Compile(env->context(), source, &origin);
|
|
|
|
if (script.IsEmpty()) {
|
|
|
|
ReportException(env, try_catch);
|
|
|
|
exit(3);
|
|
|
|
}
|
|
|
|
|
|
|
|
Local<Value> result = script.ToLocalChecked()->Run();
|
|
|
|
if (result.IsEmpty()) {
|
|
|
|
ReportException(env, try_catch);
|
|
|
|
exit(4);
|
|
|
|
}
|
|
|
|
|
|
|
|
return scope.Escape(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void GetActiveRequests(const FunctionCallbackInfo<Value>& args) {
|
|
|
|
Environment* env = Environment::GetCurrent(args);
|
|
|
|
|
|
|
|
Local<Array> ary = Array::New(args.GetIsolate());
|
|
|
|
Local<Context> ctx = env->context();
|
|
|
|
Local<Function> fn = env->push_values_to_array_function();
|
|
|
|
Local<Value> argv[NODE_PUSH_VAL_TO_ARRAY_MAX];
|
|
|
|
size_t idx = 0;
|
|
|
|
|
|
|
|
for (auto w : *env->req_wrap_queue()) {
|
|
|
|
if (w->persistent().IsEmpty())
|
|
|
|
continue;
|
|
|
|
argv[idx] = w->object();
|
|
|
|
if (++idx >= arraysize(argv)) {
|
|
|
|
fn->Call(ctx, ary, idx, argv).ToLocalChecked();
|
|
|
|
idx = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (idx > 0) {
|
|
|
|
fn->Call(ctx, ary, idx, argv).ToLocalChecked();
|
|
|
|
}
|
|
|
|
|
|
|
|
args.GetReturnValue().Set(ary);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Non-static, friend of HandleWrap. Could have been a HandleWrap method but
|
|
|
|
// implemented here for consistency with GetActiveRequests().
|
|
|
|
void GetActiveHandles(const FunctionCallbackInfo<Value>& args) {
|
|
|
|
Environment* env = Environment::GetCurrent(args);
|
|
|
|
|
|
|
|
Local<Array> ary = Array::New(env->isolate());
|
|
|
|
Local<Context> ctx = env->context();
|
|
|
|
Local<Function> fn = env->push_values_to_array_function();
|
|
|
|
Local<Value> argv[NODE_PUSH_VAL_TO_ARRAY_MAX];
|
|
|
|
size_t idx = 0;
|
|
|
|
|
|
|
|
Local<String> owner_sym = env->owner_string();
|
|
|
|
|
|
|
|
for (auto w : *env->handle_wrap_queue()) {
|
|
|
|
if (w->persistent().IsEmpty() || !HandleWrap::HasRef(w))
|
|
|
|
continue;
|
|
|
|
Local<Object> object = w->object();
|
|
|
|
Local<Value> owner = object->Get(owner_sym);
|
|
|
|
if (owner->IsUndefined())
|
|
|
|
owner = object;
|
|
|
|
argv[idx] = owner;
|
|
|
|
if (++idx >= arraysize(argv)) {
|
|
|
|
fn->Call(ctx, ary, idx, argv).ToLocalChecked();
|
|
|
|
idx = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (idx > 0) {
|
|
|
|
fn->Call(ctx, ary, idx, argv).ToLocalChecked();
|
|
|
|
}
|
|
|
|
|
|
|
|
args.GetReturnValue().Set(ary);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NO_RETURN void Abort() {
|
|
|
|
DumpBacktrace(stderr);
|
|
|
|
fflush(stderr);
|
|
|
|
ABORT_NO_BACKTRACE();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NO_RETURN void Assert(const char* const (*args)[4]) {
|
|
|
|
auto filename = (*args)[0];
|
|
|
|
auto linenum = (*args)[1];
|
|
|
|
auto message = (*args)[2];
|
|
|
|
auto function = (*args)[3];
|
|
|
|
|
|
|
|
char exepath[256];
|
|
|
|
size_t exepath_size = sizeof(exepath);
|
|
|
|
if (uv_exepath(exepath, &exepath_size))
|
|
|
|
snprintf(exepath, sizeof(exepath), "node");
|
|
|
|
|
|
|
|
char pid[12] = {0};
|
|
|
|
#ifndef _WIN32
|
|
|
|
snprintf(pid, sizeof(pid), "[%u]", getpid());
|
|
|
|
#endif
|
|
|
|
|
|
|
|
fprintf(stderr, "%s%s: %s:%s:%s%s Assertion `%s' failed.\n",
|
|
|
|
exepath, pid, filename, linenum,
|
|
|
|
function, *function ? ":" : "", message);
|
|
|
|
fflush(stderr);
|
|
|
|
|
|
|
|
Abort();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void Abort(const FunctionCallbackInfo<Value>& args) {
|
|
|
|
Abort();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void Chdir(const FunctionCallbackInfo<Value>& args) {
|
|
|
|
Environment* env = Environment::GetCurrent(args);
|
|
|
|
|
|
|
|
if (args.Length() != 1 || !args[0]->IsString()) {
|
|
|
|
return env->ThrowTypeError("Bad argument.");
|
|
|
|
}
|
|
|
|
|
|
|
|
node::Utf8Value path(args.GetIsolate(), args[0]);
|
|
|
|
int err = uv_chdir(*path);
|
|
|
|
if (err) {
|
|
|
|
return env->ThrowUVException(err, "uv_chdir");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void Cwd(const FunctionCallbackInfo<Value>& args) {
|
|
|
|
Environment* env = Environment::GetCurrent(args);
|
|
|
|
#ifdef _WIN32
|
|
|
|
/* MAX_PATH is in characters, not bytes. Make sure we have enough headroom. */
|
|
|
|
char buf[MAX_PATH * 4];
|
|
|
|
#else
|
|
|
|
char buf[PATH_MAX];
|
|
|
|
#endif
|
|
|
|
|
|
|
|
size_t cwd_len = sizeof(buf);
|
|
|
|
int err = uv_cwd(buf, &cwd_len);
|
|
|
|
if (err) {
|
|
|
|
return env->ThrowUVException(err, "uv_cwd");
|
|
|
|
}
|
|
|
|
|
|
|
|
Local<String> cwd = String::NewFromUtf8(env->isolate(),
|
|
|
|
buf,
|
|
|
|
String::kNormalString,
|
|
|
|
cwd_len);
|
|
|
|
args.GetReturnValue().Set(cwd);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void Umask(const FunctionCallbackInfo<Value>& args) {
|
|
|
|
Environment* env = Environment::GetCurrent(args);
|
|
|
|
uint32_t old;
|
|
|
|
|
|
|
|
if (args.Length() < 1 || args[0]->IsUndefined()) {
|
|
|
|
old = umask(0);
|
|
|
|
umask(static_cast<mode_t>(old));
|
|
|
|
} else if (!args[0]->IsInt32() && !args[0]->IsString()) {
|
|
|
|
return env->ThrowTypeError("argument must be an integer or octal string.");
|
|
|
|
} else {
|
|
|
|
int oct;
|
|
|
|
if (args[0]->IsInt32()) {
|
|
|
|
oct = args[0]->Uint32Value();
|
|
|
|
} else {
|
|
|
|
oct = 0;
|
|
|
|
node::Utf8Value str(env->isolate(), args[0]);
|
|
|
|
|
|
|
|
// Parse the octal string.
|
|
|
|
for (size_t i = 0; i < str.length(); i++) {
|
|
|
|
char c = (*str)[i];
|
|
|
|
if (c > '7' || c < '0') {
|
|
|
|
return env->ThrowTypeError("invalid octal string");
|
|
|
|
}
|
|
|
|
oct *= 8;
|
|
|
|
oct += c - '0';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
old = umask(static_cast<mode_t>(oct));
|
|
|
|
}
|
|
|
|
|
|
|
|
args.GetReturnValue().Set(old);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#if defined(__POSIX__) && !defined(__ANDROID__)
|
|
|
|
|
|
|
|
static const uid_t uid_not_found = static_cast<uid_t>(-1);
|
|
|
|
static const gid_t gid_not_found = static_cast<gid_t>(-1);
|
|
|
|
|
|
|
|
|
|
|
|
static uid_t uid_by_name(const char* name) {
|
|
|
|
struct passwd pwd;
|
|
|
|
struct passwd* pp;
|
|
|
|
char buf[8192];
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
pp = nullptr;
|
|
|
|
|
|
|
|
if (getpwnam_r(name, &pwd, buf, sizeof(buf), &pp) == 0 && pp != nullptr) {
|
|
|
|
return pp->pw_uid;
|
|
|
|
}
|
|
|
|
|
|
|
|
return uid_not_found;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static char* name_by_uid(uid_t uid) {
|
|
|
|
struct passwd pwd;
|
|
|
|
struct passwd* pp;
|
|
|
|
char buf[8192];
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
pp = nullptr;
|
|
|
|
|
|
|
|
if ((rc = getpwuid_r(uid, &pwd, buf, sizeof(buf), &pp)) == 0 &&
|
|
|
|
pp != nullptr) {
|
|
|
|
return strdup(pp->pw_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rc == 0) {
|
|
|
|
errno = ENOENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static gid_t gid_by_name(const char* name) {
|
|
|
|
struct group pwd;
|
|
|
|
struct group* pp;
|
|
|
|
char buf[8192];
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
pp = nullptr;
|
|
|
|
|
|
|
|
if (getgrnam_r(name, &pwd, buf, sizeof(buf), &pp) == 0 && pp != nullptr) {
|
|
|
|
return pp->gr_gid;
|
|
|
|
}
|
|
|
|
|
|
|
|
return gid_not_found;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#if 0 // For future use.
|
|
|
|
static const char* name_by_gid(gid_t gid) {
|
|
|
|
struct group pwd;
|
|
|
|
struct group* pp;
|
|
|
|
char buf[8192];
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
pp = nullptr;
|
|
|
|
|
|
|
|
if ((rc = getgrgid_r(gid, &pwd, buf, sizeof(buf), &pp)) == 0 &&
|
|
|
|
pp != nullptr) {
|
|
|
|
return strdup(pp->gr_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rc == 0) {
|
|
|
|
errno = ENOENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
static uid_t uid_by_name(Isolate* isolate, Local<Value> value) {
|
|
|
|
if (value->IsUint32()) {
|
|
|
|
return static_cast<uid_t>(value->Uint32Value());
|
|
|
|
} else {
|
|
|
|
node::Utf8Value name(isolate, value);
|
|
|
|
return uid_by_name(*name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static gid_t gid_by_name(Isolate* isolate, Local<Value> value) {
|
|
|
|
if (value->IsUint32()) {
|
|
|
|
return static_cast<gid_t>(value->Uint32Value());
|
|
|
|
} else {
|
|
|
|
node::Utf8Value name(isolate, value);
|
|
|
|
return gid_by_name(*name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void GetUid(const FunctionCallbackInfo<Value>& args) {
|
|
|
|
// uid_t is an uint32_t on all supported platforms.
|
|
|
|
args.GetReturnValue().Set(static_cast<uint32_t>(getuid()));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void GetGid(const FunctionCallbackInfo<Value>& args) {
|
|
|
|
// gid_t is an uint32_t on all supported platforms.
|
|
|
|
args.GetReturnValue().Set(static_cast<uint32_t>(getgid()));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void GetEUid(const FunctionCallbackInfo<Value>& args) {
|
|
|
|
// uid_t is an uint32_t on all supported platforms.
|
|
|
|
args.GetReturnValue().Set(static_cast<uint32_t>(geteuid()));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void GetEGid(const FunctionCallbackInfo<Value>& args) {
|
|
|
|
// gid_t is an uint32_t on all supported platforms.
|
|
|
|
args.GetReturnValue().Set(static_cast<uint32_t>(getegid()));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void SetGid(const FunctionCallbackInfo<Value>& args) {
|
|
|
|
Environment* env = Environment::GetCurrent(args);
|
|
|
|
|
|
|
|
if (!args[0]->IsUint32() && !args[0]->IsString()) {
|
|
|
|
return env->ThrowTypeError("setgid argument must be a number or a string");
|
|
|
|
}
|
|
|
|
|
|
|
|
gid_t gid = gid_by_name(env->isolate(), args[0]);
|
|
|
|
|
|
|
|
if (gid == gid_not_found) {
|
|
|
|
return env->ThrowError("setgid group id does not exist");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (setgid(gid)) {
|
|
|
|
return env->ThrowErrnoException(errno, "setgid");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void SetEGid(const FunctionCallbackInfo<Value>& args) {
|
|
|
|
Environment* env = Environment::GetCurrent(args);
|
|
|
|
|
|
|
|
if (!args[0]->IsUint32() && !args[0]->IsString()) {
|
|
|
|
return env->ThrowTypeError("setegid argument must be a number or string");
|
|
|
|
}
|
|
|
|
|
|
|
|
gid_t gid = gid_by_name(env->isolate(), args[0]);
|
|
|
|
|
|
|
|
if (gid == gid_not_found) {
|
|
|
|
return env->ThrowError("setegid group id does not exist");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (setegid(gid)) {
|
|
|
|
return env->ThrowErrnoException(errno, "setegid");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void SetUid(const FunctionCallbackInfo<Value>& args) {
|
|
|
|
Environment* env = Environment::GetCurrent(args);
|
|
|
|
|
|
|
|
if (!args[0]->IsUint32() && !args[0]->IsString()) {
|
|
|
|
return env->ThrowTypeError("setuid argument must be a number or a string");
|
|
|
|
}
|
|
|
|
|
|
|
|
uid_t uid = uid_by_name(env->isolate(), args[0]);
|
|
|
|
|
|
|
|
if (uid == uid_not_found) {
|
|
|
|
return env->ThrowError("setuid user id does not exist");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (setuid(uid)) {
|
|
|
|
return env->ThrowErrnoException(errno, "setuid");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void SetEUid(const FunctionCallbackInfo<Value>& args) {
|
|
|
|
Environment* env = Environment::GetCurrent(args);
|
|
|
|
|
|
|
|
if (!args[0]->IsUint32() && !args[0]->IsString()) {
|
|
|
|
return env->ThrowTypeError("seteuid argument must be a number or string");
|
|
|
|
}
|
|
|
|
|
|
|
|
uid_t uid = uid_by_name(env->isolate(), args[0]);
|
|
|
|
|
|
|
|
if (uid == uid_not_found) {
|
|
|
|
return env->ThrowError("seteuid user id does not exist");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (seteuid(uid)) {
|
|
|
|
return env->ThrowErrnoException(errno, "seteuid");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void GetGroups(const FunctionCallbackInfo<Value>& args) {
|
|
|
|
Environment* env = Environment::GetCurrent(args);
|
|
|
|
|
|
|
|
int ngroups = getgroups(0, nullptr);
|
|
|
|
|
|
|
|
if (ngroups == -1) {
|
|
|
|
return env->ThrowErrnoException(errno, "getgroups");
|
|
|
|
}
|
|
|
|
|
|
|
|
gid_t* groups = new gid_t[ngroups];
|
|
|
|
|
|
|
|
ngroups = getgroups(ngroups, groups);
|
|
|
|
|
|
|
|
if (ngroups == -1) {
|
|
|
|
delete[] groups;
|
|
|
|
return env->ThrowErrnoException(errno, "getgroups");
|
|
|
|
}
|
|
|
|
|
|
|
|
Local<Array> groups_list = Array::New(env->isolate(), ngroups);
|
|
|
|
bool seen_egid = false;
|
|
|
|
gid_t egid = getegid();
|
|
|
|
|
|
|
|
for (int i = 0; i < ngroups; i++) {
|
|
|
|
groups_list->Set(i, Integer::New(env->isolate(), groups[i]));
|
|
|
|
if (groups[i] == egid)
|
|
|
|
seen_egid = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
delete[] groups;
|
|
|
|
|
|
|
|
if (seen_egid == false) {
|
|
|
|
groups_list->Set(ngroups, Integer::New(env->isolate(), egid));
|
|
|
|
}
|
|
|
|
|
|
|
|
args.GetReturnValue().Set(groups_list);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void SetGroups(const FunctionCallbackInfo<Value>& args) {
|
|
|
|
Environment* env = Environment::GetCurrent(args);
|
|
|
|
|
|
|
|
if (!args[0]->IsArray()) {
|
|
|
|
return env->ThrowTypeError("argument 1 must be an array");
|
|
|
|
}
|
|
|
|
|
|
|
|
Local<Array> groups_list = args[0].As<Array>();
|
|
|
|
size_t size = groups_list->Length();
|
|
|
|
gid_t* groups = new gid_t[size];
|
|
|
|
|
|
|
|
for (size_t i = 0; i < size; i++) {
|
|
|
|
gid_t gid = gid_by_name(env->isolate(), groups_list->Get(i));
|
|
|
|
|
|
|
|
if (gid == gid_not_found) {
|
|
|
|
delete[] groups;
|
|
|
|
return env->ThrowError("group name not found");
|
|
|
|
}
|
|
|
|
|
|
|
|
groups[i] = gid;
|
|
|
|
}
|
|
|
|
|
|
|
|
int rc = setgroups(size, groups);
|
|
|
|
delete[] groups;
|
|
|
|
|
|
|
|
if (rc == -1) {
|
|
|
|
return env->ThrowErrnoException(errno, "setgroups");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void InitGroups(const FunctionCallbackInfo<Value>& args) {
|
|
|
|
Environment* env = Environment::GetCurrent(args);
|
|
|
|
|
|
|
|
if (!args[0]->IsUint32() && !args[0]->IsString()) {
|
|
|
|
return env->ThrowTypeError("argument 1 must be a number or a string");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!args[1]->IsUint32() && !args[1]->IsString()) {
|
|
|
|
return env->ThrowTypeError("argument 2 must be a number or a string");
|
|
|
|
}
|
|
|
|
|
|
|
|
node::Utf8Value arg0(env->isolate(), args[0]);
|
|
|
|
gid_t extra_group;
|
|
|
|
bool must_free;
|
|
|
|
char* user;
|
|
|
|
|
|
|
|
if (args[0]->IsUint32()) {
|
|
|
|
user = name_by_uid(args[0]->Uint32Value());
|
|
|
|
must_free = true;
|
|
|
|
} else {
|
|
|
|
user = *arg0;
|
|
|
|
must_free = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (user == nullptr) {
|
|
|
|
return env->ThrowError("initgroups user not found");
|
|
|
|
}
|
|
|
|
|
|
|
|
extra_group = gid_by_name(env->isolate(), args[1]);
|
|
|
|
|
|
|
|
if (extra_group == gid_not_found) {
|
|
|
|
if (must_free)
|
|
|
|
free(user);
|
|
|
|
return env->ThrowError("initgroups extra group not found");
|
|
|
|
}
|
|
|
|
|
|
|
|
int rc = initgroups(user, extra_group);
|
|
|
|
|
|
|
|
if (must_free) {
|
|
|
|
free(user);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rc) {
|
|
|
|
return env->ThrowErrnoException(errno, "initgroups");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif // __POSIX__ && !defined(__ANDROID__)
|
|
|
|
|
|
|
|
|
|
|
|
static void WaitForInspectorDisconnect(Environment* env) {
|
|
|
|
#if HAVE_INSPECTOR
|
|
|
|
if (env->inspector_agent()->IsConnected()) {
|
|
|
|
// Restore signal dispositions, the app is done and is no longer
|
|
|
|
// capable of handling signals.
|
|
|
|
#if defined(__POSIX__) && !defined(NODE_SHARED_MODE)
|
|
|
|
struct sigaction act;
|
|
|
|
memset(&act, 0, sizeof(act));
|
|
|
|
for (unsigned nr = 1; nr < kMaxSignal; nr += 1) {
|
|
|
|
if (nr == SIGKILL || nr == SIGSTOP || nr == SIGPROF)
|
|
|
|
continue;
|
|
|
|
act.sa_handler = (nr == SIGPIPE) ? SIG_IGN : SIG_DFL;
|
|
|
|
CHECK_EQ(0, sigaction(nr, &act, nullptr));
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
env->inspector_agent()->WaitForDisconnect();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void Exit(const FunctionCallbackInfo<Value>& args) {
|
|
|
|
WaitForInspectorDisconnect(Environment::GetCurrent(args));
|
|
|
|
exit(args[0]->Int32Value());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void Uptime(const FunctionCallbackInfo<Value>& args) {
|
|
|
|
Environment* env = Environment::GetCurrent(args);
|
|
|
|
double uptime;
|
|
|
|
|
|
|
|
uv_update_time(env->event_loop());
|
|
|
|
uptime = uv_now(env->event_loop()) - prog_start_time;
|
|
|
|
|
|
|
|
args.GetReturnValue().Set(Number::New(env->isolate(), uptime / 1000));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void MemoryUsage(const FunctionCallbackInfo<Value>& args) {
|
|
|
|
Environment* env = Environment::GetCurrent(args);
|
|
|
|
|
|
|
|
size_t rss;
|
|
|
|
int err = uv_resident_set_memory(&rss);
|
|
|
|
if (err) {
|
|
|
|
return env->ThrowUVException(err, "uv_resident_set_memory");
|
|
|
|
}
|
|
|
|
|
|
|
|
Isolate* isolate = env->isolate();
|
|
|
|
// V8 memory usage
|
|
|
|
HeapStatistics v8_heap_stats;
|
|
|
|
isolate->GetHeapStatistics(&v8_heap_stats);
|
|
|
|
|
|
|
|
// Get the double array pointer from the Float64Array argument.
|
|
|
|
CHECK(args[0]->IsFloat64Array());
|
|
|
|
Local<Float64Array> array = args[0].As<Float64Array>();
|
|
|
|
CHECK_EQ(array->Length(), 4);
|
|
|
|
Local<ArrayBuffer> ab = array->Buffer();
|
|
|
|
double* fields = static_cast<double*>(ab->GetContents().Data());
|
|
|
|
|
|
|
|
fields[0] = rss;
|
|
|
|
fields[1] = v8_heap_stats.total_heap_size();
|
|
|
|
fields[2] = v8_heap_stats.used_heap_size();
|
|
|
|
fields[3] = isolate->AdjustAmountOfExternalAllocatedMemory(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void Kill(const FunctionCallbackInfo<Value>& args) {
|
|
|
|
Environment* env = Environment::GetCurrent(args);
|
|
|
|
|
|
|
|
if (args.Length() != 2) {
|
|
|
|
return env->ThrowError("Bad argument.");
|
|
|
|
}
|
|
|
|
|
|
|
|
int pid = args[0]->Int32Value();
|
|
|
|
int sig = args[1]->Int32Value();
|
|
|
|
int err = uv_kill(pid, sig);
|
|
|
|
args.GetReturnValue().Set(err);
|
|
|
|
}
|
|
|
|
|
|
|
|
// used in Hrtime() below
|
|
|
|
#define NANOS_PER_SEC 1000000000
|
|
|
|
|
|
|
|
// Hrtime exposes libuv's uv_hrtime() high-resolution timer.
|
|
|
|
// The value returned by uv_hrtime() is a 64-bit int representing nanoseconds,
|
|
|
|
// so this function instead fills in an Uint32Array with 3 entries,
|
|
|
|
// to avoid any integer overflow possibility.
|
|
|
|
// The first two entries contain the second part of the value
|
|
|
|
// broken into the upper/lower 32 bits to be converted back in JS,
|
|
|
|
// because there is no Uint64Array in JS.
|
|
|
|
// The third entry contains the remaining nanosecond part of the value.
|
|
|
|
static void Hrtime(const FunctionCallbackInfo<Value>& args) {
|
|
|
|
uint64_t t = uv_hrtime();
|
|
|
|
|
|
|
|
Local<ArrayBuffer> ab = args[0].As<Uint32Array>()->Buffer();
|
|
|
|
uint32_t* fields = static_cast<uint32_t*>(ab->GetContents().Data());
|
|
|
|
|
|
|
|
fields[0] = (t / NANOS_PER_SEC) >> 32;
|
|
|
|
fields[1] = (t / NANOS_PER_SEC) & 0xffffffff;
|
|
|
|
fields[2] = t % NANOS_PER_SEC;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Microseconds in a second, as a float, used in CPUUsage() below
|
|
|
|
#define MICROS_PER_SEC 1e6
|
|
|
|
|
|
|
|
// CPUUsage use libuv's uv_getrusage() this-process resource usage accessor,
|
|
|
|
// to access ru_utime (user CPU time used) and ru_stime (system CPU time used),
|
|
|
|
// which are uv_timeval_t structs (long tv_sec, long tv_usec).
|
|
|
|
// Returns those values as Float64 microseconds in the elements of the array
|
|
|
|
// passed to the function.
|
|
|
|
static void CPUUsage(const FunctionCallbackInfo<Value>& args) {
|
|
|
|
uv_rusage_t rusage;
|
|
|
|
|
|
|
|
// Call libuv to get the values we'll return.
|
|
|
|
int err = uv_getrusage(&rusage);
|
|
|
|
if (err) {
|
|
|
|
// On error, return the strerror version of the error code.
|
|
|
|
Local<String> errmsg = OneByteString(args.GetIsolate(), uv_strerror(err));
|
|
|
|
args.GetReturnValue().Set(errmsg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the double array pointer from the Float64Array argument.
|
|
|
|
CHECK(args[0]->IsFloat64Array());
|
|
|
|
Local<Float64Array> array = args[0].As<Float64Array>();
|
|
|
|
CHECK_EQ(array->Length(), 2);
|
|
|
|
Local<ArrayBuffer> ab = array->Buffer();
|
|
|
|
double* fields = static_cast<double*>(ab->GetContents().Data());
|
|
|
|
|
|
|
|
// Set the Float64Array elements to be user / system values in microseconds.
|
|
|
|
fields[0] = MICROS_PER_SEC * rusage.ru_utime.tv_sec + rusage.ru_utime.tv_usec;
|
|
|
|
fields[1] = MICROS_PER_SEC * rusage.ru_stime.tv_sec + rusage.ru_stime.tv_usec;
|
|
|
|
}
|
|
|
|
|
|
|
|
extern "C" void node_module_register(void* m) {
|
|
|
|
struct node_module* mp = reinterpret_cast<struct node_module*>(m);
|
|
|
|
|
|
|
|
if (mp->nm_flags & NM_F_BUILTIN) {
|
|
|
|
mp->nm_link = modlist_builtin;
|
|
|
|
modlist_builtin = mp;
|
|
|
|
} else if (!node_is_initialized) {
|
|
|
|
// "Linked" modules are included as part of the node project.
|
|
|
|
// Like builtins they are registered *before* node::Init runs.
|
|
|
|
mp->nm_flags = NM_F_LINKED;
|
|
|
|
mp->nm_link = modlist_linked;
|
|
|
|
modlist_linked = mp;
|
|
|
|
} else {
|
|
|
|
modpending = mp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct node_module* get_builtin_module(const char* name) {
|
|
|
|
struct node_module* mp;
|
|
|
|
|
|
|
|
for (mp = modlist_builtin; mp != nullptr; mp = mp->nm_link) {
|
|
|
|
if (strcmp(mp->nm_modname, name) == 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
CHECK(mp == nullptr || (mp->nm_flags & NM_F_BUILTIN) != 0);
|
|
|
|
return (mp);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct node_module* get_linked_module(const char* name) {
|
|
|
|
struct node_module* mp;
|
|
|
|
|
|
|
|
for (mp = modlist_linked; mp != nullptr; mp = mp->nm_link) {
|
|
|
|
if (strcmp(mp->nm_modname, name) == 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
CHECK(mp == nullptr || (mp->nm_flags & NM_F_LINKED) != 0);
|
|
|
|
return mp;
|
|
|
|
}
|
|
|
|
|
|
|
|
// DLOpen is process.dlopen(module, filename).
|
|
|
|
// Used to load 'module.node' dynamically shared objects.
|
|
|
|
//
|
|
|
|
// FIXME(bnoordhuis) Not multi-context ready. TBD how to resolve the conflict
|
|
|
|
// when two contexts try to load the same shared object. Maybe have a shadow
|
|
|
|
// cache that's a plain C list or hash table that's shared across contexts?
|
|
|
|
static void DLOpen(const FunctionCallbackInfo<Value>& args) {
|
|
|
|
Environment* env = Environment::GetCurrent(args);
|
|
|
|
uv_lib_t lib;
|
|
|
|
|
|
|
|
CHECK_EQ(modpending, nullptr);
|
|
|
|
|
|
|
|
if (args.Length() != 2) {
|
|
|
|
env->ThrowError("process.dlopen takes exactly 2 arguments.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Local<Object> module = args[0]->ToObject(env->isolate()); // Cast
|
|
|
|
node::Utf8Value filename(env->isolate(), args[1]); // Cast
|
|
|
|
const bool is_dlopen_error = uv_dlopen(*filename, &lib);
|
|
|
|
|
|
|
|
// Objects containing v14 or later modules will have registered themselves
|
|
|
|
// on the pending list. Activate all of them now. At present, only one
|
|
|
|
// module per object is supported.
|
|
|
|
node_module* const mp = modpending;
|
|
|
|
modpending = nullptr;
|
|
|
|
|
|
|
|
if (is_dlopen_error) {
|
|
|
|
Local<String> errmsg = OneByteString(env->isolate(), uv_dlerror(&lib));
|
|
|
|
uv_dlclose(&lib);
|
|
|
|
#ifdef _WIN32
|
|
|
|
// Windows needs to add the filename into the error message
|
|
|
|
errmsg = String::Concat(errmsg, args[1]->ToString(env->isolate()));
|
|
|
|
#endif // _WIN32
|
|
|
|
env->isolate()->ThrowException(Exception::Error(errmsg));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mp == nullptr) {
|
|
|
|
uv_dlclose(&lib);
|
|
|
|
env->ThrowError("Module did not self-register.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (mp->nm_version != NODE_MODULE_VERSION) {
|
|
|
|
char errmsg[1024];
|
|
|
|
if (mp->nm_version == -1) {
|
|
|
|
snprintf(errmsg,
|
|
|
|
sizeof(errmsg),
|
|
|
|
"The module '%s'"
|
|
|
|
"\nwas compiled against the ABI-stable Node.js API (N-API)."
|
|
|
|
"\nThis feature is experimental and must be enabled on the "
|
|
|
|
"\ncommand-line by adding --napi-modules.",
|
|
|
|
*filename);
|
|
|
|
} else {
|
|
|
|
snprintf(errmsg,
|
|
|
|
sizeof(errmsg),
|
|
|
|
"The module '%s'"
|
|
|
|
"\nwas compiled against a different Node.js version using"
|
|
|
|
"\nNODE_MODULE_VERSION %d. This version of Node.js requires"
|
|
|
|
"\nNODE_MODULE_VERSION %d. Please try re-compiling or "
|
|
|
|
"re-installing\nthe module (for instance, using `npm rebuild` "
|
|
|
|
"or `npm install`).",
|
|
|
|
*filename, mp->nm_version, NODE_MODULE_VERSION);
|
|
|
|
}
|
|
|
|
|
|
|
|
// NOTE: `mp` is allocated inside of the shared library's memory, calling
|
|
|
|
// `uv_dlclose` will deallocate it
|
|
|
|
uv_dlclose(&lib);
|
|
|
|
env->ThrowError(errmsg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (mp->nm_flags & NM_F_BUILTIN) {
|
|
|
|
uv_dlclose(&lib);
|
|
|
|
env->ThrowError("Built-in module self-registered.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
mp->nm_dso_handle = lib.handle;
|
|
|
|
mp->nm_link = modlist_addon;
|
|
|
|
modlist_addon = mp;
|
|
|
|
|
|
|
|
Local<String> exports_string = env->exports_string();
|
|
|
|
Local<Object> exports = module->Get(exports_string)->ToObject(env->isolate());
|
|
|
|
|
|
|
|
if (mp->nm_context_register_func != nullptr) {
|
|
|
|
mp->nm_context_register_func(exports, module, env->context(), mp->nm_priv);
|
|
|
|
} else if (mp->nm_register_func != nullptr) {
|
|
|
|
mp->nm_register_func(exports, module, mp->nm_priv);
|
|
|
|
} else {
|
|
|
|
uv_dlclose(&lib);
|
|
|
|
env->ThrowError("Module has no declared entry point.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Tell coverity that 'handle' should not be freed when we return.
|
|
|
|
// coverity[leaked_storage]
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void OnFatalError(const char* location, const char* message) {
|
|
|
|
if (location) {
|
|
|
|
PrintErrorString("FATAL ERROR: %s %s\n", location, message);
|
|
|
|
} else {
|
|
|
|
PrintErrorString("FATAL ERROR: %s\n", message);
|
|
|
|
}
|
|
|
|
fflush(stderr);
|
|
|
|
ABORT();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NO_RETURN void FatalError(const char* location, const char* message) {
|
|
|
|
OnFatalError(location, message);
|
|
|
|
// to suppress compiler warning
|
|
|
|
ABORT();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void FatalException(Isolate* isolate,
|
|
|
|
Local<Value> error,
|
|
|
|
Local<Message> message) {
|
|
|
|
HandleScope scope(isolate);
|
|
|
|
|
|
|
|
Environment* env = Environment::GetCurrent(isolate);
|
|
|
|
Local<Object> process_object = env->process_object();
|
|
|
|
Local<String> fatal_exception_string = env->fatal_exception_string();
|
|
|
|
Local<Function> fatal_exception_function =
|
|
|
|
process_object->Get(fatal_exception_string).As<Function>();
|
|
|
|
|
|
|
|
int exit_code = 0;
|
|
|
|
if (!fatal_exception_function->IsFunction()) {
|
|
|
|
// failed before the process._fatalException function was added!
|
|
|
|
// this is probably pretty bad. Nothing to do but report and exit.
|
|
|
|
ReportException(env, error, message);
|
|
|
|
exit_code = 6;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (exit_code == 0) {
|
|
|
|
TryCatch fatal_try_catch(isolate);
|
|
|
|
|
|
|
|
// Do not call FatalException when _fatalException handler throws
|
|
|
|
fatal_try_catch.SetVerbose(false);
|
|
|
|
|
|
|
|
// this will return true if the JS layer handled it, false otherwise
|
|
|
|
Local<Value> caught =
|
|
|
|
fatal_exception_function->Call(process_object, 1, &error);
|
|
|
|
|
|
|
|
if (fatal_try_catch.HasCaught()) {
|
|
|
|
// the fatal exception function threw, so we must exit
|
|
|
|
ReportException(env, fatal_try_catch);
|
|
|
|
exit_code = 7;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (exit_code == 0 && false == caught->BooleanValue()) {
|
|
|
|
ReportException(env, error, message);
|
|
|
|
exit_code = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (exit_code) {
|
|
|
|
#if HAVE_INSPECTOR
|
|
|
|
env->inspector_agent()->FatalException(error, message);
|
|
|
|
#endif
|
|
|
|
exit(exit_code);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void FatalException(Isolate* isolate, const TryCatch& try_catch) {
|
|
|
|
HandleScope scope(isolate);
|
|
|
|
if (!try_catch.IsVerbose()) {
|
|
|
|
FatalException(isolate, try_catch.Exception(), try_catch.Message());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void OnMessage(Local<Message> message, Local<Value> error) {
|
|
|
|
// The current version of V8 sends messages for errors only
|
|
|
|
// (thus `error` is always set).
|
|
|
|
FatalException(Isolate::GetCurrent(), error, message);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ClearFatalExceptionHandlers(Environment* env) {
|
|
|
|
Local<Object> process = env->process_object();
|
|
|
|
Local<Value> events =
|
|
|
|
process->Get(env->context(), env->events_string()).ToLocalChecked();
|
|
|
|
|
|
|
|
if (events->IsObject()) {
|
|
|
|
events.As<Object>()->Set(
|
|
|
|
env->context(),
|
|
|
|
OneByteString(env->isolate(), "uncaughtException"),
|
|
|
|
Undefined(env->isolate())).FromJust();
|
|
|
|
}
|
|
|
|
|
|
|
|
process->Set(
|
|
|
|
env->context(),
|
|
|
|
env->domain_string(),
|
|
|
|
Undefined(env->isolate())).FromJust();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Call process.emitWarning(str), fmt is a snprintf() format string
|
|
|
|
void ProcessEmitWarning(Environment* env, const char* fmt, ...) {
|
|
|
|
char warning[1024];
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
va_start(ap, fmt);
|
|
|
|
vsnprintf(warning, sizeof(warning), fmt, ap);
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
HandleScope handle_scope(env->isolate());
|
|
|
|
Context::Scope context_scope(env->context());
|
|
|
|
|
|
|
|
Local<Object> process = env->process_object();
|
|
|
|
MaybeLocal<Value> emit_warning = process->Get(env->context(),
|
|
|
|
FIXED_ONE_BYTE_STRING(env->isolate(), "emitWarning"));
|
|
|
|
Local<Value> arg = node::OneByteString(env->isolate(), warning);
|
|
|
|
|
|
|
|
Local<Value> f;
|
|
|
|
|
|
|
|
if (!emit_warning.ToLocal(&f)) return;
|
|
|
|
if (!f->IsFunction()) return;
|
|
|
|
|
|
|
|
// MakeCallback() unneeded, because emitWarning is internal code, it calls
|
|
|
|
// process.emit('warning', ..), but does so on the nextTick.
|
|
|
|
f.As<v8::Function>()->Call(process, 1, &arg);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void Binding(const FunctionCallbackInfo<Value>& args) {
|
|
|
|
Environment* env = Environment::GetCurrent(args);
|
|
|
|
|
|
|
|
Local<String> module = args[0]->ToString(env->isolate());
|
|
|
|
node::Utf8Value module_v(env->isolate(), module);
|
|
|
|
|
|
|
|
Local<Object> cache = env->binding_cache_object();
|
|
|
|
Local<Object> exports;
|
|
|
|
|
|
|
|
if (cache->Has(env->context(), module).FromJust()) {
|
|
|
|
exports = cache->Get(module)->ToObject(env->isolate());
|
|
|
|
args.GetReturnValue().Set(exports);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Append a string to process.moduleLoadList
|
|
|
|
char buf[1024];
|
|
|
|
snprintf(buf, sizeof(buf), "Binding %s", *module_v);
|
|
|
|
|
|
|
|
Local<Array> modules = env->module_load_list_array();
|
|
|
|
uint32_t l = modules->Length();
|
|
|
|
modules->Set(l, OneByteString(env->isolate(), buf));
|
|
|
|
|
|
|
|
node_module* mod = get_builtin_module(*module_v);
|
|
|
|
if (mod != nullptr) {
|
|
|
|
exports = Object::New(env->isolate());
|
|
|
|
// Internal bindings don't have a "module" object, only exports.
|
|
|
|
CHECK_EQ(mod->nm_register_func, nullptr);
|
|
|
|
CHECK_NE(mod->nm_context_register_func, nullptr);
|
|
|
|
Local<Value> unused = Undefined(env->isolate());
|
|
|
|
mod->nm_context_register_func(exports, unused,
|
|
|
|
env->context(), mod->nm_priv);
|
|
|
|
cache->Set(module, exports);
|
|
|
|
} else if (!strcmp(*module_v, "constants")) {
|
|
|
|
exports = Object::New(env->isolate());
|
|
|
|
CHECK(exports->SetPrototype(env->context(),
|
|
|
|
Null(env->isolate())).FromJust());
|
|
|
|
DefineConstants(env->isolate(), exports);
|
|
|
|
cache->Set(module, exports);
|
|
|
|
} else if (!strcmp(*module_v, "natives")) {
|
|
|
|
exports = Object::New(env->isolate());
|
|
|
|
DefineJavaScript(env, exports);
|
|
|
|
cache->Set(module, exports);
|
|
|
|
} else {
|
|
|
|
char errmsg[1024];
|
|
|
|
snprintf(errmsg,
|
|
|
|
sizeof(errmsg),
|
|
|
|
"No such module: %s",
|
|
|
|
*module_v);
|
|
|
|
return env->ThrowError(errmsg);
|
|
|
|
}
|
|
|
|
|
|
|
|
args.GetReturnValue().Set(exports);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void LinkedBinding(const FunctionCallbackInfo<Value>& args) {
|
|
|
|
Environment* env = Environment::GetCurrent(args.GetIsolate());
|
|
|
|
|
|
|
|
Local<String> module_name = args[0]->ToString(env->isolate());
|
|
|
|
|
|
|
|
Local<Object> cache = env->binding_cache_object();
|
|
|
|
Local<Value> exports_v = cache->Get(module_name);
|
|
|
|
|
|
|
|
if (exports_v->IsObject())
|
|
|
|
return args.GetReturnValue().Set(exports_v.As<Object>());
|
|
|
|
|
|
|
|
node::Utf8Value module_name_v(env->isolate(), module_name);
|
|
|
|
node_module* mod = get_linked_module(*module_name_v);
|
|
|
|
|
|
|
|
if (mod == nullptr) {
|
|
|
|
char errmsg[1024];
|
|
|
|
snprintf(errmsg,
|
|
|
|
sizeof(errmsg),
|
|
|
|
"No such module was linked: %s",
|
|
|
|
*module_name_v);
|
|
|
|
return env->ThrowError(errmsg);
|
|
|
|
}
|
|
|
|
|
|
|
|
Local<Object> module = Object::New(env->isolate());
|
|
|
|
Local<Object> exports = Object::New(env->isolate());
|
|
|
|
Local<String> exports_prop = String::NewFromUtf8(env->isolate(), "exports");
|
|
|
|
module->Set(exports_prop, exports);
|
|
|
|
|
|
|
|
if (mod->nm_context_register_func != nullptr) {
|
|
|
|
mod->nm_context_register_func(exports,
|
|
|
|
module,
|
|
|
|
env->context(),
|
|
|
|
mod->nm_priv);
|
|
|
|
} else if (mod->nm_register_func != nullptr) {
|
|
|
|
mod->nm_register_func(exports, module, mod->nm_priv);
|
|
|
|
} else {
|
|
|
|
return env->ThrowError("Linked module has no declared entry point.");
|
|
|
|
}
|
|
|
|
|
|
|
|
auto effective_exports = module->Get(exports_prop);
|
|
|
|
cache->Set(module_name, effective_exports);
|
|
|
|
|
|
|
|
args.GetReturnValue().Set(effective_exports);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ProcessTitleGetter(Local<Name> property,
|
|
|
|
const PropertyCallbackInfo<Value>& info) {
|
|
|
|
char buffer[512];
|
|
|
|
uv_get_process_title(buffer, sizeof(buffer));
|
|
|
|
info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), buffer));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void ProcessTitleSetter(Local<Name> property,
|
|
|
|
Local<Value> value,
|
|
|
|
const PropertyCallbackInfo<void>& info) {
|
|
|
|
node::Utf8Value title(info.GetIsolate(), value);
|
|
|
|
// TODO(piscisaureus): protect with a lock
|
|
|
|
uv_set_process_title(*title);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void EnvGetter(Local<Name> property,
|
|
|
|
const PropertyCallbackInfo<Value>& info) {
|
|
|
|
Isolate* isolate = info.GetIsolate();
|
|
|
|
if (property->IsSymbol()) {
|
|
|
|
return info.GetReturnValue().SetUndefined();
|
|
|
|
}
|
|
|
|
#ifdef __POSIX__
|
|
|
|
node::Utf8Value key(isolate, property);
|
|
|
|
const char* val = getenv(*key);
|
|
|
|
if (val) {
|
|
|
|
return info.GetReturnValue().Set(String::NewFromUtf8(isolate, val));
|
|
|
|
}
|
|
|
|
#else // _WIN32
|
|
|
|
node::TwoByteValue key(isolate, property);
|
|
|
|
WCHAR buffer[32767]; // The maximum size allowed for environment variables.
|
|
|
|
DWORD result = GetEnvironmentVariableW(reinterpret_cast<WCHAR*>(*key),
|
|
|
|
buffer,
|
|
|
|
arraysize(buffer));
|
|
|
|
// If result >= sizeof buffer the buffer was too small. That should never
|
|
|
|
// happen. If result == 0 and result != ERROR_SUCCESS the variable was not
|
|
|
|
// not found.
|
|
|
|
if ((result > 0 || GetLastError() == ERROR_SUCCESS) &&
|
|
|
|
result < arraysize(buffer)) {
|
|
|
|
const uint16_t* two_byte_buffer = reinterpret_cast<const uint16_t*>(buffer);
|
|
|
|
Local<String> rc = String::NewFromTwoByte(isolate, two_byte_buffer);
|
|
|
|
return info.GetReturnValue().Set(rc);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void EnvSetter(Local<Name> property,
|
|
|
|
Local<Value> value,
|
|
|
|
const PropertyCallbackInfo<Value>& info) {
|
|
|
|
#ifdef __POSIX__
|
|
|
|
node::Utf8Value key(info.GetIsolate(), property);
|
|
|
|
node::Utf8Value val(info.GetIsolate(), value);
|
|
|
|
setenv(*key, *val, 1);
|
|
|
|
#else // _WIN32
|
|
|
|
node::TwoByteValue key(info.GetIsolate(), property);
|
|
|
|
node::TwoByteValue val(info.GetIsolate(), value);
|
|
|
|
WCHAR* key_ptr = reinterpret_cast<WCHAR*>(*key);
|
|
|
|
// Environment variables that start with '=' are read-only.
|
|
|
|
if (key_ptr[0] != L'=') {
|
|
|
|
SetEnvironmentVariableW(key_ptr, reinterpret_cast<WCHAR*>(*val));
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
// Whether it worked or not, always return value.
|
|
|
|
info.GetReturnValue().Set(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void EnvQuery(Local<Name> property,
|
|
|
|
const PropertyCallbackInfo<Integer>& info) {
|
|
|
|
int32_t rc = -1; // Not found unless proven otherwise.
|
|
|
|
if (property->IsString()) {
|
|
|
|
#ifdef __POSIX__
|
|
|
|
node::Utf8Value key(info.GetIsolate(), property);
|
|
|
|
if (getenv(*key))
|
|
|
|
rc = 0;
|
|
|
|
#else // _WIN32
|
|
|
|
node::TwoByteValue key(info.GetIsolate(), property);
|
|
|
|
WCHAR* key_ptr = reinterpret_cast<WCHAR*>(*key);
|
|
|
|
if (GetEnvironmentVariableW(key_ptr, nullptr, 0) > 0 ||
|
|
|
|
GetLastError() == ERROR_SUCCESS) {
|
|
|
|
rc = 0;
|
|
|
|
if (key_ptr[0] == L'=') {
|
|
|
|
// Environment variables that start with '=' are hidden and read-only.
|
|
|
|
rc = static_cast<int32_t>(v8::ReadOnly) |
|
|
|
|
static_cast<int32_t>(v8::DontDelete) |
|
|
|
|
static_cast<int32_t>(v8::DontEnum);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
if (rc != -1)
|
|
|
|
info.GetReturnValue().Set(rc);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void EnvDeleter(Local<Name> property,
|
|
|
|
const PropertyCallbackInfo<Boolean>& info) {
|
|
|
|
if (property->IsString()) {
|
|
|
|
#ifdef __POSIX__
|
|
|
|
node::Utf8Value key(info.GetIsolate(), property);
|
|
|
|
unsetenv(*key);
|
|
|
|
#else
|
|
|
|
node::TwoByteValue key(info.GetIsolate(), property);
|
|
|
|
WCHAR* key_ptr = reinterpret_cast<WCHAR*>(*key);
|
|
|
|
SetEnvironmentVariableW(key_ptr, nullptr);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
// process.env never has non-configurable properties, so always
|
|
|
|
// return true like the tc39 delete operator.
|
|
|
|
info.GetReturnValue().Set(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void EnvEnumerator(const PropertyCallbackInfo<Array>& info) {
|
|
|
|
Environment* env = Environment::GetCurrent(info);
|
|
|
|
Isolate* isolate = env->isolate();
|
|
|
|
Local<Context> ctx = env->context();
|
|
|
|
Local<Function> fn = env->push_values_to_array_function();
|
|
|
|
Local<Value> argv[NODE_PUSH_VAL_TO_ARRAY_MAX];
|
|
|
|
size_t idx = 0;
|
|
|
|
|
|
|
|
#ifdef __POSIX__
|
|
|
|
int size = 0;
|
|
|
|
while (environ[size])
|
|
|
|
size++;
|
|
|
|
|
|
|
|
Local<Array> envarr = Array::New(isolate);
|
|
|
|
|
|
|
|
for (int i = 0; i < size; ++i) {
|
|
|
|
const char* var = environ[i];
|
|
|
|
const char* s = strchr(var, '=');
|
|
|
|
const int length = s ? s - var : strlen(var);
|
|
|
|
argv[idx] = String::NewFromUtf8(isolate,
|
|
|
|
var,
|
|
|
|
String::kNormalString,
|
|
|
|
length);
|
|
|
|
if (++idx >= arraysize(argv)) {
|
|
|
|
fn->Call(ctx, envarr, idx, argv).ToLocalChecked();
|
|
|
|
idx = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (idx > 0) {
|
|
|
|
fn->Call(ctx, envarr, idx, argv).ToLocalChecked();
|
|
|
|
}
|
|
|
|
#else // _WIN32
|
|
|
|
WCHAR* environment = GetEnvironmentStringsW();
|
|
|
|
if (environment == nullptr)
|
|
|
|
return; // This should not happen.
|
|
|
|
Local<Array> envarr = Array::New(isolate);
|
|
|
|
WCHAR* p = environment;
|
|
|
|
while (*p) {
|
|
|
|
WCHAR *s;
|
|
|
|
if (*p == L'=') {
|
|
|
|
// If the key starts with '=' it is a hidden environment variable.
|
|
|
|
p += wcslen(p) + 1;
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
s = wcschr(p, L'=');
|
|
|
|
}
|
|
|
|
if (!s) {
|
|
|
|
s = p + wcslen(p);
|
|
|
|
}
|
|
|
|
const uint16_t* two_byte_buffer = reinterpret_cast<const uint16_t*>(p);
|
|
|
|
const size_t two_byte_buffer_len = s - p;
|
|
|
|
argv[idx] = String::NewFromTwoByte(isolate,
|
|
|
|
two_byte_buffer,
|
|
|
|
String::kNormalString,
|
|
|
|
two_byte_buffer_len);
|
|
|
|
if (++idx >= arraysize(argv)) {
|
|
|
|
fn->Call(ctx, envarr, idx, argv).ToLocalChecked();
|
|
|
|
idx = 0;
|
|
|
|
}
|
|
|
|
p = s + wcslen(s) + 1;
|
|
|
|
}
|
|
|
|
if (idx > 0) {
|
|
|
|
fn->Call(ctx, envarr, idx, argv).ToLocalChecked();
|
|
|
|
}
|
|
|
|
FreeEnvironmentStringsW(environment);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
info.GetReturnValue().Set(envarr);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static Local<Object> GetFeatures(Environment* env) {
|
|
|
|
EscapableHandleScope scope(env->isolate());
|
|
|
|
|
|
|
|
Local<Object> obj = Object::New(env->isolate());
|
|
|
|
#if defined(DEBUG) && DEBUG
|
|
|
|
Local<Value> debug = True(env->isolate());
|
|
|
|
#else
|
|
|
|
Local<Value> debug = False(env->isolate());
|
|
|
|
#endif // defined(DEBUG) && DEBUG
|
|
|
|
|
|
|
|
obj->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "debug"), debug);
|
|
|
|
obj->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "uv"), True(env->isolate()));
|
|
|
|
// TODO(bnoordhuis) ping libuv
|
|
|
|
obj->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "ipv6"), True(env->isolate()));
|
|
|
|
|
|
|
|
#ifndef OPENSSL_NO_NEXTPROTONEG
|
|
|
|
Local<Boolean> tls_npn = True(env->isolate());
|
|
|
|
#else
|
|
|
|
Local<Boolean> tls_npn = False(env->isolate());
|
|
|
|
#endif
|
|
|
|
obj->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "tls_npn"), tls_npn);
|
|
|
|
|
|
|
|
#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
|
|
|
|
Local<Boolean> tls_alpn = True(env->isolate());
|
|
|
|
#else
|
|
|
|
Local<Boolean> tls_alpn = False(env->isolate());
|
|
|
|
#endif
|
|
|
|
obj->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "tls_alpn"), tls_alpn);
|
|
|
|
|
|
|
|
#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
|
|
|
|
Local<Boolean> tls_sni = True(env->isolate());
|
|
|
|
#else
|
|
|
|
Local<Boolean> tls_sni = False(env->isolate());
|
|
|
|
#endif
|
|
|
|
obj->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "tls_sni"), tls_sni);
|
|
|
|
|
|
|
|
#if !defined(OPENSSL_NO_TLSEXT) && defined(SSL_CTX_set_tlsext_status_cb)
|
|
|
|
Local<Boolean> tls_ocsp = True(env->isolate());
|
|
|
|
#else
|
|
|
|
Local<Boolean> tls_ocsp = False(env->isolate());
|
|
|
|
#endif // !defined(OPENSSL_NO_TLSEXT) && defined(SSL_CTX_set_tlsext_status_cb)
|
|
|
|
obj->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "tls_ocsp"), tls_ocsp);
|
|
|
|
|
|
|
|
obj->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "tls"),
|
|
|
|
Boolean::New(env->isolate(),
|
|
|
|
get_builtin_module("crypto") != nullptr));
|
|
|
|
|
|
|
|
return scope.Escape(obj);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void DebugPortGetter(Local<Name> property,
|
|
|
|
const PropertyCallbackInfo<Value>& info) {
|
|
|
|
int port = debug_options.port();
|
|
|
|
#if HAVE_INSPECTOR
|
|
|
|
if (port == 0) {
|
|
|
|
Environment* env = Environment::GetCurrent(info);
|
|
|
|
if (auto io = env->inspector_agent()->io())
|
|
|
|
port = io->port();
|
|
|
|
}
|
|
|
|
#endif // HAVE_INSPECTOR
|
|
|
|
info.GetReturnValue().Set(port);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void DebugPortSetter(Local<Name> property,
|
|
|
|
Local<Value> value,
|
|
|
|
const PropertyCallbackInfo<void>& info) {
|
|
|
|
debug_options.set_port(value->Int32Value());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void DebugProcess(const FunctionCallbackInfo<Value>& args);
|
|
|
|
static void DebugPause(const FunctionCallbackInfo<Value>& args);
|
|
|
|
static void DebugEnd(const FunctionCallbackInfo<Value>& args);
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
void NeedImmediateCallbackGetter(Local<Name> property,
|
|
|
|
const PropertyCallbackInfo<Value>& info) {
|
|
|
|
Environment* env = Environment::GetCurrent(info);
|
|
|
|
const uv_check_t* immediate_check_handle = env->immediate_check_handle();
|
|
|
|
bool active = uv_is_active(
|
|
|
|
reinterpret_cast<const uv_handle_t*>(immediate_check_handle));
|
|
|
|
info.GetReturnValue().Set(active);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void NeedImmediateCallbackSetter(
|
|
|
|
Local<Name> property,
|
|
|
|
Local<Value> value,
|
|
|
|
const PropertyCallbackInfo<void>& info) {
|
|
|
|
Environment* env = Environment::GetCurrent(info);
|
|
|
|
|
|
|
|
uv_check_t* immediate_check_handle = env->immediate_check_handle();
|
|
|
|
bool active = uv_is_active(
|
|
|
|
reinterpret_cast<const uv_handle_t*>(immediate_check_handle));
|
|
|
|
|
|
|
|
if (active == value->BooleanValue())
|
|
|
|
return;
|
|
|
|
|
|
|
|
uv_idle_t* immediate_idle_handle = env->immediate_idle_handle();
|
|
|
|
|
|
|
|
if (active) {
|
|
|
|
uv_check_stop(immediate_check_handle);
|
|
|
|
uv_idle_stop(immediate_idle_handle);
|
|
|
|
} else {
|
|
|
|
uv_check_start(immediate_check_handle, CheckImmediate);
|
|
|
|
// Idle handle is needed only to stop the event loop from blocking in poll.
|
|
|
|
uv_idle_start(immediate_idle_handle, IdleImmediateDummy);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void StartProfilerIdleNotifier(const FunctionCallbackInfo<Value>& args) {
|
|
|
|
Environment* env = Environment::GetCurrent(args);
|
|
|
|
env->StartProfilerIdleNotifier();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void StopProfilerIdleNotifier(const FunctionCallbackInfo<Value>& args) {
|
|
|
|
Environment* env = Environment::GetCurrent(args);
|
|
|
|
env->StopProfilerIdleNotifier();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#define READONLY_PROPERTY(obj, str, var) \
|
|
|
|
do { \
|
|
|
|
obj->DefineOwnProperty(env->context(), \
|
|
|
|
OneByteString(env->isolate(), str), \
|
|
|
|
var, \
|
|
|
|
v8::ReadOnly).FromJust(); \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
#define READONLY_DONT_ENUM_PROPERTY(obj, str, var) \
|
|
|
|
do { \
|
|
|
|
obj->DefineOwnProperty(env->context(), \
|
|
|
|
OneByteString(env->isolate(), str), \
|
|
|
|
var, \
|
|
|
|
static_cast<v8::PropertyAttribute>(v8::ReadOnly | \
|
|
|
|
v8::DontEnum)) \
|
|
|
|
.FromJust(); \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
} // anonymous namespace
|
|
|
|
|
|
|
|
void SetupProcessObject(Environment* env,
|
|
|
|
int argc,
|
|
|
|
const char* const* argv,
|
|
|
|
int exec_argc,
|
|
|
|
const char* const* exec_argv) {
|
|
|
|
HandleScope scope(env->isolate());
|
|
|
|
|
|
|
|
Local<Object> process = env->process_object();
|
|
|
|
|
|
|
|
auto title_string = FIXED_ONE_BYTE_STRING(env->isolate(), "title");
|
|
|
|
CHECK(process->SetAccessor(env->context(),
|
|
|
|
title_string,
|
|
|
|
ProcessTitleGetter,
|
|
|
|
ProcessTitleSetter,
|
|
|
|
env->as_external()).FromJust());
|
|
|
|
|
|
|
|
// process.version
|
|
|
|
READONLY_PROPERTY(process,
|
|
|
|
"version",
|
|
|
|
FIXED_ONE_BYTE_STRING(env->isolate(), NODE_VERSION));
|
|
|
|
|
|
|
|
// process.moduleLoadList
|
|
|
|
READONLY_PROPERTY(process,
|
|
|
|
"moduleLoadList",
|
|
|
|
env->module_load_list_array());
|
|
|
|
|
|
|
|
// process.versions
|
|
|
|
Local<Object> versions = Object::New(env->isolate());
|
|
|
|
READONLY_PROPERTY(process, "versions", versions);
|
|
|
|
|
|
|
|
const char http_parser_version[] = NODE_STRINGIFY(HTTP_PARSER_VERSION_MAJOR)
|
|
|
|
"."
|
|
|
|
NODE_STRINGIFY(HTTP_PARSER_VERSION_MINOR)
|
|
|
|
"."
|
|
|
|
NODE_STRINGIFY(HTTP_PARSER_VERSION_PATCH);
|
|
|
|
READONLY_PROPERTY(versions,
|
|
|
|
"http_parser",
|
|
|
|
FIXED_ONE_BYTE_STRING(env->isolate(), http_parser_version));
|
|
|
|
|
|
|
|
// +1 to get rid of the leading 'v'
|
|
|
|
READONLY_PROPERTY(versions,
|
|
|
|
"node",
|
|
|
|
OneByteString(env->isolate(), NODE_VERSION + 1));
|
|
|
|
READONLY_PROPERTY(versions,
|
|
|
|
"v8",
|
|
|
|
OneByteString(env->isolate(), V8::GetVersion()));
|
|
|
|
READONLY_PROPERTY(versions,
|
|
|
|
"uv",
|
|
|
|
OneByteString(env->isolate(), uv_version_string()));
|
|
|
|
READONLY_PROPERTY(versions,
|
|
|
|
"zlib",
|
|
|
|
FIXED_ONE_BYTE_STRING(env->isolate(), ZLIB_VERSION));
|
|
|
|
READONLY_PROPERTY(versions,
|
|
|
|
"ares",
|
|
|
|
FIXED_ONE_BYTE_STRING(env->isolate(), ARES_VERSION_STR));
|
|
|
|
|
|
|
|
const char node_modules_version[] = NODE_STRINGIFY(NODE_MODULE_VERSION);
|
|
|
|
READONLY_PROPERTY(
|
|
|
|
versions,
|
|
|
|
"modules",
|
|
|
|
FIXED_ONE_BYTE_STRING(env->isolate(), node_modules_version));
|
|
|
|
|
|
|
|
// process._promiseRejectEvent
|
|
|
|
Local<Object> promiseRejectEvent = Object::New(env->isolate());
|
|
|
|
READONLY_DONT_ENUM_PROPERTY(process,
|
|
|
|
"_promiseRejectEvent",
|
|
|
|
promiseRejectEvent);
|
|
|
|
READONLY_PROPERTY(promiseRejectEvent,
|
|
|
|
"unhandled",
|
|
|
|
Integer::New(env->isolate(),
|
|
|
|
v8::kPromiseRejectWithNoHandler));
|
|
|
|
READONLY_PROPERTY(promiseRejectEvent,
|
|
|
|
"handled",
|
|
|
|
Integer::New(env->isolate(),
|
|
|
|
v8::kPromiseHandlerAddedAfterReject));
|
|
|
|
|
|
|
|
#if HAVE_OPENSSL
|
|
|
|
// Stupid code to slice out the version string.
|
|
|
|
{ // NOLINT(whitespace/braces)
|
|
|
|
size_t i, j, k;
|
|
|
|
int c;
|
|
|
|
for (i = j = 0, k = sizeof(OPENSSL_VERSION_TEXT) - 1; i < k; ++i) {
|
|
|
|
c = OPENSSL_VERSION_TEXT[i];
|
|
|
|
if ('0' <= c && c <= '9') {
|
|
|
|
for (j = i + 1; j < k; ++j) {
|
|
|
|
c = OPENSSL_VERSION_TEXT[j];
|
|
|
|
if (c == ' ')
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
READONLY_PROPERTY(
|
|
|
|
versions,
|
|
|
|
"openssl",
|
|
|
|
OneByteString(env->isolate(), &OPENSSL_VERSION_TEXT[i], j - i));
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// process.arch
|
|
|
|
READONLY_PROPERTY(process, "arch", OneByteString(env->isolate(), NODE_ARCH));
|
|
|
|
|
|
|
|
// process.platform
|
|
|
|
READONLY_PROPERTY(process,
|
|
|
|
"platform",
|
|
|
|
OneByteString(env->isolate(), NODE_PLATFORM));
|
|
|
|
|
|
|
|
// process.release
|
|
|
|
Local<Object> release = Object::New(env->isolate());
|
|
|
|
READONLY_PROPERTY(process, "release", release);
|
|
|
|
READONLY_PROPERTY(release, "name", OneByteString(env->isolate(), "node"));
|
|
|
|
|
|
|
|
// if this is a release build and no explicit base has been set
|
|
|
|
// substitute the standard release download URL
|
|
|
|
#ifndef NODE_RELEASE_URLBASE
|
|
|
|
# if NODE_VERSION_IS_RELEASE
|
|
|
|
# define NODE_RELEASE_URLBASE "https://nodejs.org/download/release/"
|
|
|
|
# endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined(NODE_RELEASE_URLBASE)
|
|
|
|
# define NODE_RELEASE_URLPFX NODE_RELEASE_URLBASE "v" NODE_VERSION_STRING "/"
|
|
|
|
# define NODE_RELEASE_URLFPFX NODE_RELEASE_URLPFX "node-v" NODE_VERSION_STRING
|
|
|
|
|
|
|
|
READONLY_PROPERTY(release,
|
|
|
|
"sourceUrl",
|
|
|
|
OneByteString(env->isolate(),
|
|
|
|
NODE_RELEASE_URLFPFX ".tar.gz"));
|
|
|
|
READONLY_PROPERTY(release,
|
|
|
|
"headersUrl",
|
|
|
|
OneByteString(env->isolate(),
|
|
|
|
NODE_RELEASE_URLFPFX "-headers.tar.gz"));
|
|
|
|
# ifdef _WIN32
|
|
|
|
READONLY_PROPERTY(release,
|
|
|
|
"libUrl",
|
|
|
|
OneByteString(env->isolate(),
|
|
|
|
strcmp(NODE_ARCH, "ia32") ? NODE_RELEASE_URLPFX "win-"
|
|
|
|
NODE_ARCH "/node.lib"
|
|
|
|
: NODE_RELEASE_URLPFX
|
|
|
|
"win-x86/node.lib"));
|
|
|
|
# endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// process.argv
|
|
|
|
Local<Array> arguments = Array::New(env->isolate(), argc);
|
|
|
|
for (int i = 0; i < argc; ++i) {
|
|
|
|
arguments->Set(i, String::NewFromUtf8(env->isolate(), argv[i]));
|
|
|
|
}
|
|
|
|
process->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "argv"), arguments);
|
|
|
|
|
|
|
|
// process.execArgv
|
|
|
|
Local<Array> exec_arguments = Array::New(env->isolate(), exec_argc);
|
|
|
|
for (int i = 0; i < exec_argc; ++i) {
|
|
|
|
exec_arguments->Set(i, String::NewFromUtf8(env->isolate(), exec_argv[i]));
|
|
|
|
}
|
|
|
|
process->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "execArgv"),
|
|
|
|
exec_arguments);
|
|
|
|
|
|
|
|
// create process.env
|
|
|
|
Local<ObjectTemplate> process_env_template =
|
|
|
|
ObjectTemplate::New(env->isolate());
|
|
|
|
process_env_template->SetHandler(NamedPropertyHandlerConfiguration(
|
|
|
|
EnvGetter,
|
|
|
|
EnvSetter,
|
|
|
|
EnvQuery,
|
|
|
|
EnvDeleter,
|
|
|
|
EnvEnumerator,
|
|
|
|
env->as_external()));
|
|
|
|
|
|
|
|
Local<Object> process_env =
|
|
|
|
process_env_template->NewInstance(env->context()).ToLocalChecked();
|
|
|
|
process->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "env"), process_env);
|
|
|
|
|
|
|
|
READONLY_PROPERTY(process, "pid", Integer::New(env->isolate(), getpid()));
|
|
|
|
READONLY_PROPERTY(process, "features", GetFeatures(env));
|
|
|
|
|
|
|
|
auto need_immediate_callback_string =
|
|
|
|
FIXED_ONE_BYTE_STRING(env->isolate(), "_needImmediateCallback");
|
|
|
|
CHECK(process->SetAccessor(env->context(), need_immediate_callback_string,
|
|
|
|
NeedImmediateCallbackGetter,
|
|
|
|
NeedImmediateCallbackSetter,
|
|
|
|
env->as_external()).FromJust());
|
|
|
|
|
|
|
|
// -e, --eval
|
|
|
|
if (eval_string) {
|
|
|
|
READONLY_PROPERTY(process,
|
|
|
|
"_eval",
|
|
|
|
String::NewFromUtf8(env->isolate(), eval_string));
|
|
|
|
}
|
|
|
|
|
|
|
|
// -p, --print
|
|
|
|
if (print_eval) {
|
|
|
|
READONLY_PROPERTY(process, "_print_eval", True(env->isolate()));
|
|
|
|
}
|
|
|
|
|
|
|
|
// -c, --check
|
|
|
|
if (syntax_check_only) {
|
|
|
|
READONLY_PROPERTY(process, "_syntax_check_only", True(env->isolate()));
|
|
|
|
}
|
|
|
|
|
|
|
|
// -i, --interactive
|
|
|
|
if (force_repl) {
|
|
|
|
READONLY_PROPERTY(process, "_forceRepl", True(env->isolate()));
|
|
|
|
}
|
|
|
|
|
|
|
|
// -r, --require
|
|
|
|
if (!preload_modules.empty()) {
|
|
|
|
Local<Array> array = Array::New(env->isolate());
|
|
|
|
for (unsigned int i = 0; i < preload_modules.size(); ++i) {
|
|
|
|
Local<String> module = String::NewFromUtf8(env->isolate(),
|
|
|
|
preload_modules[i].c_str());
|
|
|
|
array->Set(i, module);
|
|
|
|
}
|
|
|
|
READONLY_PROPERTY(process,
|
|
|
|
"_preload_modules",
|
|
|
|
array);
|
|
|
|
|
|
|
|
preload_modules.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
// --no-deprecation
|
|
|
|
if (no_deprecation) {
|
|
|
|
READONLY_PROPERTY(process, "noDeprecation", True(env->isolate()));
|
|
|
|
}
|
|
|
|
|
|
|
|
// --no-warnings
|
|
|
|
if (no_process_warnings) {
|
|
|
|
READONLY_PROPERTY(process, "noProcessWarnings", True(env->isolate()));
|
|
|
|
}
|
|
|
|
|
|
|
|
// --trace-warnings
|
|
|
|
if (trace_warnings) {
|
|
|
|
READONLY_PROPERTY(process, "traceProcessWarnings", True(env->isolate()));
|
|
|
|
}
|
|
|
|
|
|
|
|
// --throw-deprecation
|
|
|
|
if (throw_deprecation) {
|
|
|
|
READONLY_PROPERTY(process, "throwDeprecation", True(env->isolate()));
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef NODE_NO_BROWSER_GLOBALS
|
|
|
|
// configure --no-browser-globals
|
|
|
|
READONLY_PROPERTY(process, "_noBrowserGlobals", True(env->isolate()));
|
|
|
|
#endif // NODE_NO_BROWSER_GLOBALS
|
|
|
|
|
|
|
|
// --prof-process
|
|
|
|
if (prof_process) {
|
|
|
|
READONLY_PROPERTY(process, "profProcess", True(env->isolate()));
|
|
|
|
}
|
|
|
|
|
|
|
|
// --trace-deprecation
|
|
|
|
if (trace_deprecation) {
|
|
|
|
READONLY_PROPERTY(process, "traceDeprecation", True(env->isolate()));
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO(refack): move the following 3 to `node_config`
|
|
|
|
// --inspect-brk
|
|
|
|
if (debug_options.wait_for_connect()) {
|
|
|
|
READONLY_DONT_ENUM_PROPERTY(process,
|
|
|
|
"_breakFirstLine", True(env->isolate()));
|
|
|
|
}
|
|
|
|
|
|
|
|
// --inspect --debug-brk
|
|
|
|
if (debug_options.deprecated_invocation()) {
|
|
|
|
READONLY_DONT_ENUM_PROPERTY(process,
|
|
|
|
"_deprecatedDebugBrk", True(env->isolate()));
|
|
|
|
}
|
|
|
|
|
|
|
|
// --debug or, --debug-brk without --inspect
|
|
|
|
if (debug_options.invalid_invocation()) {
|
|
|
|
READONLY_DONT_ENUM_PROPERTY(process,
|
|
|
|
"_invalidDebug", True(env->isolate()));
|
|
|
|
}
|
|
|
|
|
|
|
|
// --security-revert flags
|
|
|
|
#define V(code, _, __) \
|
|
|
|
do { \
|
|
|
|
if (IsReverted(REVERT_ ## code)) { \
|
|
|
|
READONLY_PROPERTY(process, "REVERT_" #code, True(env->isolate())); \
|
|
|
|
} \
|
|
|
|
} while (0);
|
|
|
|
REVERSIONS(V)
|
|
|
|
#undef V
|
|
|
|
|
|
|
|
size_t exec_path_len = 2 * PATH_MAX;
|
|
|
|
char* exec_path = new char[exec_path_len];
|
|
|
|
Local<String> exec_path_value;
|
|
|
|
if (uv_exepath(exec_path, &exec_path_len) == 0) {
|
|
|
|
exec_path_value = String::NewFromUtf8(env->isolate(),
|
|
|
|
exec_path,
|
|
|
|
String::kNormalString,
|
|
|
|
exec_path_len);
|
|
|
|
} else {
|
|
|
|
exec_path_value = String::NewFromUtf8(env->isolate(), argv[0]);
|
|
|
|
}
|
|
|
|
process->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "execPath"),
|
|
|
|
exec_path_value);
|
|
|
|
delete[] exec_path;
|
|
|
|
|
|
|
|
auto debug_port_string = FIXED_ONE_BYTE_STRING(env->isolate(), "debugPort");
|
|
|
|
CHECK(process->SetAccessor(env->context(),
|
|
|
|
debug_port_string,
|
|
|
|
DebugPortGetter,
|
|
|
|
DebugPortSetter,
|
|
|
|
env->as_external()).FromJust());
|
|
|
|
|
|
|
|
// define various internal methods
|
|
|
|
env->SetMethod(process,
|
|
|
|
"_startProfilerIdleNotifier",
|
|
|
|
StartProfilerIdleNotifier);
|
|
|
|
env->SetMethod(process,
|
|
|
|
"_stopProfilerIdleNotifier",
|
|
|
|
StopProfilerIdleNotifier);
|
|
|
|
env->SetMethod(process, "_getActiveRequests", GetActiveRequests);
|
|
|
|
env->SetMethod(process, "_getActiveHandles", GetActiveHandles);
|
|
|
|
env->SetMethod(process, "reallyExit", Exit);
|
|
|
|
env->SetMethod(process, "abort", Abort);
|
|
|
|
env->SetMethod(process, "chdir", Chdir);
|
|
|
|
env->SetMethod(process, "cwd", Cwd);
|
|
|
|
|
|
|
|
env->SetMethod(process, "umask", Umask);
|
|
|
|
|
|
|
|
#if defined(__POSIX__) && !defined(__ANDROID__)
|
|
|
|
env->SetMethod(process, "getuid", GetUid);
|
|
|
|
env->SetMethod(process, "geteuid", GetEUid);
|
|
|
|
env->SetMethod(process, "setuid", SetUid);
|
|
|
|
env->SetMethod(process, "seteuid", SetEUid);
|
|
|
|
|
|
|
|
env->SetMethod(process, "setgid", SetGid);
|
|
|
|
env->SetMethod(process, "setegid", SetEGid);
|
|
|
|
env->SetMethod(process, "getgid", GetGid);
|
|
|
|
env->SetMethod(process, "getegid", GetEGid);
|
|
|
|
|
|
|
|
env->SetMethod(process, "getgroups", GetGroups);
|
|
|
|
env->SetMethod(process, "setgroups", SetGroups);
|
|
|
|
env->SetMethod(process, "initgroups", InitGroups);
|
|
|
|
#endif // __POSIX__ && !defined(__ANDROID__)
|
|
|
|
|
|
|
|
env->SetMethod(process, "_kill", Kill);
|
|
|
|
|
|
|
|
env->SetMethod(process, "_debugProcess", DebugProcess);
|
|
|
|
env->SetMethod(process, "_debugPause", DebugPause);
|
|
|
|
env->SetMethod(process, "_debugEnd", DebugEnd);
|
|
|
|
|
|
|
|
env->SetMethod(process, "hrtime", Hrtime);
|
|
|
|
|
|
|
|
env->SetMethod(process, "cpuUsage", CPUUsage);
|
|
|
|
|
|
|
|
env->SetMethod(process, "dlopen", DLOpen);
|
|
|
|
|
|
|
|
env->SetMethod(process, "uptime", Uptime);
|
|
|
|
env->SetMethod(process, "memoryUsage", MemoryUsage);
|
|
|
|
|
|
|
|
env->SetMethod(process, "binding", Binding);
|
|
|
|
env->SetMethod(process, "_linkedBinding", LinkedBinding);
|
|
|
|
|
|
|
|
env->SetMethod(process, "_setupProcessObject", SetupProcessObject);
|
|
|
|
env->SetMethod(process, "_setupNextTick", SetupNextTick);
|
|
|
|
env->SetMethod(process, "_setupPromises", SetupPromises);
|
|
|
|
env->SetMethod(process, "_setupDomainUse", SetupDomainUse);
|
|
|
|
|
|
|
|
// pre-set _events object for faster emit checks
|
|
|
|
Local<Object> events_obj = Object::New(env->isolate());
|
|
|
|
CHECK(events_obj->SetPrototype(env->context(),
|
|
|
|
Null(env->isolate())).FromJust());
|
|
|
|
process->Set(env->events_string(), events_obj);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#undef READONLY_PROPERTY
|
|
|
|
|
|
|
|
|
|
|
|
void SignalExit(int signo) {
|
|
|
|
uv_tty_reset_mode();
|
|
|
|
if (trace_enabled) {
|
|
|
|
v8_platform.StopTracingAgent();
|
|
|
|
}
|
|
|
|
#ifdef __FreeBSD__
|
|
|
|
// FreeBSD has a nasty bug, see RegisterSignalHandler for details
|
|
|
|
struct sigaction sa;
|
|
|
|
memset(&sa, 0, sizeof(sa));
|
|
|
|
sa.sa_handler = SIG_DFL;
|
|
|
|
CHECK_EQ(sigaction(signo, &sa, nullptr), 0);
|
|
|
|
#endif
|
|
|
|
raise(signo);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Most of the time, it's best to use `console.error` to write
|
|
|
|
// to the process.stderr stream. However, in some cases, such as
|
|
|
|
// when debugging the stream.Writable class or the process.nextTick
|
|
|
|
// function, it is useful to bypass JavaScript entirely.
|
|
|
|
static void RawDebug(const FunctionCallbackInfo<Value>& args) {
|
|
|
|
CHECK(args.Length() == 1 && args[0]->IsString() &&
|
|
|
|
"must be called with a single string");
|
|
|
|
node::Utf8Value message(args.GetIsolate(), args[0]);
|
|
|
|
PrintErrorString("%s\n", *message);
|
|
|
|
fflush(stderr);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void LoadEnvironment(Environment* env) {
|
|
|
|
HandleScope handle_scope(env->isolate());
|
|
|
|
|
|
|
|
TryCatch try_catch(env->isolate());
|
|
|
|
|
|
|
|
// Disable verbose mode to stop FatalException() handler from trying
|
|
|
|
// to handle the exception. Errors this early in the start-up phase
|
|
|
|
// are not safe to ignore.
|
|
|
|
try_catch.SetVerbose(false);
|
|
|
|
|
|
|
|
// Execute the lib/internal/bootstrap_node.js file which was included as a
|
|
|
|
// static C string in node_natives.h by node_js2c.
|
|
|
|
// 'internal_bootstrap_node_native' is the string containing that source code.
|
|
|
|
Local<String> script_name = FIXED_ONE_BYTE_STRING(env->isolate(),
|
|
|
|
"bootstrap_node.js");
|
|
|
|
Local<Value> f_value = ExecuteString(env, MainSource(env), script_name);
|
|
|
|
if (try_catch.HasCaught()) {
|
|
|
|
ReportException(env, try_catch);
|
|
|
|
exit(10);
|
|
|
|
}
|
|
|
|
// The bootstrap_node.js file returns a function 'f'
|
|
|
|
CHECK(f_value->IsFunction());
|
|
|
|
Local<Function> f = Local<Function>::Cast(f_value);
|
|
|
|
|
|
|
|
// Add a reference to the global object
|
|
|
|
Local<Object> global = env->context()->Global();
|
|
|
|
|
|
|
|
#if defined HAVE_DTRACE || defined HAVE_ETW
|
|
|
|
InitDTrace(env, global);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined HAVE_LTTNG
|
|
|
|
InitLTTNG(env, global);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined HAVE_PERFCTR
|
|
|
|
InitPerfCounters(env, global);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Enable handling of uncaught exceptions
|
|
|
|
// (FatalException(), break on uncaught exception in debugger)
|
|
|
|
//
|
|
|
|
// This is not strictly necessary since it's almost impossible
|
|
|
|
// to attach the debugger fast enough to break on exception
|
|
|
|
// thrown during process startup.
|
|
|
|
try_catch.SetVerbose(true);
|
|
|
|
|
|
|
|
env->SetMethod(env->process_object(), "_rawDebug", RawDebug);
|
|
|
|
|
|
|
|
// Expose the global object as a property on itself
|
|
|
|
// (Allows you to set stuff on `global` from anywhere in JavaScript.)
|
|
|
|
global->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "global"), global);
|
|
|
|
|
|
|
|
// Now we call 'f' with the 'process' variable that we've built up with
|
|
|
|
// all our bindings. Inside bootstrap_node.js and internal/process we'll
|
|
|
|
// take care of assigning things to their places.
|
|
|
|
|
|
|
|
// We start the process this way in order to be more modular. Developers
|
|
|
|
// who do not like how bootstrap_node.js sets up the module system but do
|
|
|
|
// like Node's I/O bindings may want to replace 'f' with their own function.
|
|
|
|
Local<Value> arg = env->process_object();
|
|
|
|
f->Call(Null(env->isolate()), 1, &arg);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void PrintHelp() {
|
|
|
|
// XXX: If you add an option here, please also add it to doc/node.1 and
|
|
|
|
// doc/api/cli.md
|
|
|
|
printf("Usage: node [options] [ -e script | script.js | - ] [arguments]\n"
|
|
|
|
" node inspect script.js [arguments]\n"
|
|
|
|
"\n"
|
|
|
|
"Options:\n"
|
|
|
|
" -v, --version print Node.js version\n"
|
|
|
|
" -e, --eval script evaluate script\n"
|
|
|
|
" -p, --print evaluate script and print result\n"
|
|
|
|
" -c, --check syntax check script without executing\n"
|
|
|
|
" -i, --interactive always enter the REPL even if stdin\n"
|
|
|
|
" does not appear to be a terminal\n"
|
|
|
|
" -r, --require module to preload (option can be "
|
|
|
|
"repeated)\n"
|
|
|
|
" - script read from stdin (default; "
|
|
|
|
"interactive mode if a tty)\n"
|
|
|
|
#if HAVE_INSPECTOR
|
|
|
|
" --inspect[=[host:]port] activate inspector on host:port\n"
|
|
|
|
" (default: 127.0.0.1:9229)\n"
|
|
|
|
" --inspect-brk[=[host:]port]\n"
|
|
|
|
" activate inspector on host:port\n"
|
|
|
|
" and break at start of user script\n"
|
|
|
|
" --inspect-port=[host:]port\n"
|
|
|
|
" set host:port for inspector\n"
|
|
|
|
#endif
|
|
|
|
" --no-deprecation silence deprecation warnings\n"
|
|
|
|
" --trace-deprecation show stack traces on deprecations\n"
|
|
|
|
" --throw-deprecation throw an exception on deprecations\n"
|
|
|
|
" --pending-deprecation emit pending deprecation warnings\n"
|
|
|
|
" --no-warnings silence all process warnings\n"
|
|
|
|
" --napi-modules load N-API modules\n"
|
|
|
|
" --trace-warnings show stack traces on process warnings\n"
|
|
|
|
" --redirect-warnings=file\n"
|
|
|
|
" write warnings to file instead of\n"
|
|
|
|
" stderr\n"
|
|
|
|
" --trace-sync-io show stack trace when use of sync IO\n"
|
|
|
|
" is detected after the first tick\n"
|
|
|
|
" --trace-events-enabled track trace events\n"
|
|
|
|
" --trace-event-categories comma separated list of trace event\n"
|
|
|
|
" categories to record\n"
|
|
|
|
" --track-heap-objects track heap object allocations for heap "
|
|
|
|
"snapshots\n"
|
|
|
|
" --prof-process process v8 profiler output generated\n"
|
|
|
|
" using --prof\n"
|
|
|
|
" --zero-fill-buffers automatically zero-fill all newly "
|
|
|
|
"allocated\n"
|
|
|
|
" Buffer and SlowBuffer instances\n"
|
|
|
|
" --v8-options print v8 command line options\n"
|
|
|
|
" --v8-pool-size=num set v8's thread pool size\n"
|
|
|
|
#if HAVE_OPENSSL
|
|
|
|
" --tls-cipher-list=val use an alternative default TLS cipher "
|
|
|
|
"list\n"
|
|
|
|
" --use-bundled-ca use bundled CA store"
|
|
|
|
#if !defined(NODE_OPENSSL_CERT_STORE)
|
|
|
|
" (default)"
|
|
|
|
#endif
|
|
|
|
"\n"
|
|
|
|
" --use-openssl-ca use OpenSSL's default CA store"
|
|
|
|
#if defined(NODE_OPENSSL_CERT_STORE)
|
|
|
|
" (default)"
|
|
|
|
#endif
|
|
|
|
"\n"
|
|
|
|
#if NODE_FIPS_MODE
|
|
|
|
" --enable-fips enable FIPS crypto at startup\n"
|
|
|
|
" --force-fips force FIPS crypto (cannot be disabled)\n"
|
|
|
|
#endif /* NODE_FIPS_MODE */
|
|
|
|
" --openssl-config=file load OpenSSL configuration from the\n"
|
|
|
|
" specified file (overrides\n"
|
|
|
|
" OPENSSL_CONF)\n"
|
|
|
|
#endif /* HAVE_OPENSSL */
|
|
|
|
#if defined(NODE_HAVE_I18N_SUPPORT)
|
|
|
|
" --icu-data-dir=dir set ICU data load path to dir\n"
|
|
|
|
" (overrides NODE_ICU_DATA)\n"
|
|
|
|
#if !defined(NODE_HAVE_SMALL_ICU)
|
|
|
|
" note: linked-in ICU data is present\n"
|
|
|
|
#endif
|
|
|
|
" --preserve-symlinks preserve symbolic links when resolving\n"
|
|
|
|
" and caching modules\n"
|
|
|
|
#endif
|
|
|
|
"\n"
|
|
|
|
"Environment variables:\n"
|
|
|
|
"NODE_DEBUG ','-separated list of core modules\n"
|
|
|
|
" that should print debug information\n"
|
|
|
|
"NODE_DISABLE_COLORS set to 1 to disable colors in the REPL\n"
|
|
|
|
"NODE_EXTRA_CA_CERTS path to additional CA certificates\n"
|
|
|
|
" file\n"
|
|
|
|
#if defined(NODE_HAVE_I18N_SUPPORT)
|
|
|
|
"NODE_ICU_DATA data path for ICU (Intl object) data\n"
|
|
|
|
#if !defined(NODE_HAVE_SMALL_ICU)
|
|
|
|
" (will extend linked-in data)\n"
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
"NODE_NO_WARNINGS set to 1 to silence process warnings\n"
|
|
|
|
#if !defined(NODE_WITHOUT_NODE_OPTIONS)
|
|
|
|
"NODE_OPTIONS set CLI options in the environment\n"
|
|
|
|
#endif
|
|
|
|
#ifdef _WIN32
|
|
|
|
"NODE_PATH ';'-separated list of directories\n"
|
|
|
|
#else
|
|
|
|
"NODE_PATH ':'-separated list of directories\n"
|
|
|
|
#endif
|
|
|
|
" prefixed to the module search path\n"
|
|
|
|
"NODE_REPL_HISTORY path to the persistent REPL history\n"
|
|
|
|
" file\n"
|
|
|
|
"NODE_REDIRECT_WARNINGS write warnings to path instead of\n"
|
|
|
|
" stderr\n"
|
|
|
|
"OPENSSL_CONF load OpenSSL configuration from file\n"
|
|
|
|
"\n"
|
|
|
|
"Documentation can be found at https://nodejs.org/\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void CheckIfAllowedInEnv(const char* exe, bool is_env,
|
|
|
|
const char* arg) {
|
|
|
|
if (!is_env)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Find the arg prefix when its --some_arg=val
|
|
|
|
const char* eq = strchr(arg, '=');
|
|
|
|
size_t arglen = eq ? eq - arg : strlen(arg);
|
|
|
|
|
|
|
|
static const char* whitelist[] = {
|
|
|
|
// Node options, sorted in `node --help` order for ease of comparison.
|
|
|
|
"--require", "-r",
|
|
|
|
"--inspect",
|
|
|
|
"--inspect-brk",
|
|
|
|
"--inspect-port",
|
|
|
|
"--no-deprecation",
|
|
|
|
"--trace-deprecation",
|
|
|
|
"--throw-deprecation",
|
|
|
|
"--no-warnings",
|
|
|
|
"--napi-modules",
|
|
|
|
"--trace-warnings",
|
|
|
|
"--redirect-warnings",
|
|
|
|
"--trace-sync-io",
|
|
|
|
"--trace-events-enabled",
|
|
|
|
"--trace-events-categories",
|
|
|
|
"--track-heap-objects",
|
|
|
|
"--zero-fill-buffers",
|
|
|
|
"--v8-pool-size",
|
|
|
|
"--tls-cipher-list",
|
|
|
|
"--use-bundled-ca",
|
|
|
|
"--use-openssl-ca",
|
|
|
|
"--enable-fips",
|
|
|
|
"--force-fips",
|
|
|
|
"--openssl-config",
|
|
|
|
"--icu-data-dir",
|
|
|
|
|
|
|
|
// V8 options
|
|
|
|
"--max_old_space_size",
|
|
|
|
};
|
|
|
|
|
|
|
|
for (unsigned i = 0; i < arraysize(whitelist); i++) {
|
|
|
|
const char* allowed = whitelist[i];
|
|
|
|
if (strlen(allowed) == arglen && strncmp(allowed, arg, arglen) == 0)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
fprintf(stderr, "%s: %s is not allowed in NODE_OPTIONS\n", exe, arg);
|
|
|
|
exit(9);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Parse command line arguments.
|
|
|
|
//
|
|
|
|
// argv is modified in place. exec_argv and v8_argv are out arguments that
|
|
|
|
// ParseArgs() allocates memory for and stores a pointer to the output
|
|
|
|
// vector in. The caller should free them with delete[].
|
|
|
|
//
|
|
|
|
// On exit:
|
|
|
|
//
|
|
|
|
// * argv contains the arguments with node and V8 options filtered out.
|
|
|
|
// * exec_argv contains both node and V8 options and nothing else.
|
|
|
|
// * v8_argv contains argv[0] plus any V8 options
|
|
|
|
static void ParseArgs(int* argc,
|
|
|
|
const char** argv,
|
|
|
|
int* exec_argc,
|
|
|
|
const char*** exec_argv,
|
|
|
|
int* v8_argc,
|
|
|
|
const char*** v8_argv,
|
|
|
|
bool is_env) {
|
|
|
|
const unsigned int nargs = static_cast<unsigned int>(*argc);
|
|
|
|
const char** new_exec_argv = new const char*[nargs];
|
|
|
|
const char** new_v8_argv = new const char*[nargs];
|
|
|
|
const char** new_argv = new const char*[nargs];
|
|
|
|
#if HAVE_OPENSSL
|
|
|
|
bool use_bundled_ca = false;
|
|
|
|
bool use_openssl_ca = false;
|
|
|
|
#endif // HAVE_OPENSSL
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i < nargs; ++i) {
|
|
|
|
new_exec_argv[i] = nullptr;
|
|
|
|
new_v8_argv[i] = nullptr;
|
|
|
|
new_argv[i] = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// exec_argv starts with the first option, the other two start with argv[0].
|
|
|
|
unsigned int new_exec_argc = 0;
|
|
|
|
unsigned int new_v8_argc = 1;
|
|
|
|
unsigned int new_argc = 1;
|
|
|
|
new_v8_argv[0] = argv[0];
|
|
|
|
new_argv[0] = argv[0];
|
|
|
|
|
|
|
|
unsigned int index = 1;
|
|
|
|
bool short_circuit = false;
|
|
|
|
while (index < nargs && argv[index][0] == '-' && !short_circuit) {
|
|
|
|
const char* const arg = argv[index];
|
|
|
|
unsigned int args_consumed = 1;
|
|
|
|
|
|
|
|
CheckIfAllowedInEnv(argv[0], is_env, arg);
|
|
|
|
|
|
|
|
if (debug_options.ParseOption(argv[0], arg)) {
|
|
|
|
// Done, consumed by DebugOptions::ParseOption().
|
|
|
|
} else if (strcmp(arg, "--version") == 0 || strcmp(arg, "-v") == 0) {
|
|
|
|
printf("%s\n", NODE_VERSION);
|
|
|
|
exit(0);
|
|
|
|
} else if (strcmp(arg, "--help") == 0 || strcmp(arg, "-h") == 0) {
|
|
|
|
PrintHelp();
|
|
|
|
exit(0);
|
|
|
|
} else if (strcmp(arg, "--eval") == 0 ||
|
|
|
|
strcmp(arg, "-e") == 0 ||
|
|
|
|
strcmp(arg, "--print") == 0 ||
|
|
|
|
strcmp(arg, "-pe") == 0 ||
|
|
|
|
strcmp(arg, "-p") == 0) {
|
|
|
|
bool is_eval = strchr(arg, 'e') != nullptr;
|
|
|
|
bool is_print = strchr(arg, 'p') != nullptr;
|
|
|
|
print_eval = print_eval || is_print;
|
|
|
|
// --eval, -e and -pe always require an argument.
|
|
|
|
if (is_eval == true) {
|
|
|
|
args_consumed += 1;
|
|
|
|
eval_string = argv[index + 1];
|
|
|
|
if (eval_string == nullptr) {
|
|
|
|
fprintf(stderr, "%s: %s requires an argument\n", argv[0], arg);
|
|
|
|
exit(9);
|
|
|
|
}
|
|
|
|
} else if ((index + 1 < nargs) &&
|
|
|
|
argv[index + 1] != nullptr &&
|
|
|
|
argv[index + 1][0] != '-') {
|
|
|
|
args_consumed += 1;
|
|
|
|
eval_string = argv[index + 1];
|
|
|
|
if (strncmp(eval_string, "\\-", 2) == 0) {
|
|
|
|
// Starts with "\\-": escaped expression, drop the backslash.
|
|
|
|
eval_string += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (strcmp(arg, "--require") == 0 ||
|
|
|
|
strcmp(arg, "-r") == 0) {
|
|
|
|
const char* module = argv[index + 1];
|
|
|
|
if (module == nullptr) {
|
|
|
|
fprintf(stderr, "%s: %s requires an argument\n", argv[0], arg);
|
|
|
|
exit(9);
|
|
|
|
}
|
|
|
|
args_consumed += 1;
|
|
|
|
preload_modules.push_back(module);
|
|
|
|
} else if (strcmp(arg, "--check") == 0 || strcmp(arg, "-c") == 0) {
|
|
|
|
syntax_check_only = true;
|
|
|
|
} else if (strcmp(arg, "--interactive") == 0 || strcmp(arg, "-i") == 0) {
|
|
|
|
force_repl = true;
|
|
|
|
} else if (strcmp(arg, "--no-deprecation") == 0) {
|
|
|
|
no_deprecation = true;
|
|
|
|
} else if (strcmp(arg, "--napi-modules") == 0) {
|
|
|
|
load_napi_modules = true;
|
|
|
|
} else if (strcmp(arg, "--no-warnings") == 0) {
|
|
|
|
no_process_warnings = true;
|
|
|
|
} else if (strcmp(arg, "--trace-warnings") == 0) {
|
|
|
|
trace_warnings = true;
|
|
|
|
} else if (strncmp(arg, "--redirect-warnings=", 20) == 0) {
|
|
|
|
config_warning_file = arg + 20;
|
|
|
|
} else if (strcmp(arg, "--trace-deprecation") == 0) {
|
|
|
|
trace_deprecation = true;
|
|
|
|
} else if (strcmp(arg, "--trace-sync-io") == 0) {
|
|
|
|
trace_sync_io = true;
|
|
|
|
} else if (strcmp(arg, "--trace-events-enabled") == 0) {
|
|
|
|
trace_enabled = true;
|
|
|
|
} else if (strcmp(arg, "--trace-event-categories") == 0) {
|
|
|
|
const char* categories = argv[index + 1];
|
|
|
|
if (categories == nullptr) {
|
|
|
|
fprintf(stderr, "%s: %s requires an argument\n", argv[0], arg);
|
|
|
|
exit(9);
|
|
|
|
}
|
|
|
|
args_consumed += 1;
|
|
|
|
trace_enabled_categories = categories;
|
|
|
|
} else if (strcmp(arg, "--track-heap-objects") == 0) {
|
|
|
|
track_heap_objects = true;
|
|
|
|
} else if (strcmp(arg, "--throw-deprecation") == 0) {
|
|
|
|
throw_deprecation = true;
|
|
|
|
} else if (strncmp(arg, "--security-revert=", 18) == 0) {
|
|
|
|
const char* cve = arg + 18;
|
|
|
|
Revert(cve);
|
|
|
|
} else if (strcmp(arg, "--preserve-symlinks") == 0) {
|
|
|
|
config_preserve_symlinks = true;
|
|
|
|
} else if (strcmp(arg, "--prof-process") == 0) {
|
|
|
|
prof_process = true;
|
|
|
|
short_circuit = true;
|
|
|
|
} else if (strcmp(arg, "--zero-fill-buffers") == 0) {
|
|
|
|
zero_fill_all_buffers = true;
|
|
|
|
} else if (strcmp(arg, "--pending-deprecation") == 0) {
|
|
|
|
config_pending_deprecation = true;
|
|
|
|
} else if (strcmp(arg, "--v8-options") == 0) {
|
|
|
|
new_v8_argv[new_v8_argc] = "--help";
|
|
|
|
new_v8_argc += 1;
|
|
|
|
} else if (strncmp(arg, "--v8-pool-size=", 15) == 0) {
|
|
|
|
v8_thread_pool_size = atoi(arg + 15);
|
|
|
|
#if HAVE_OPENSSL
|
|
|
|
} else if (strncmp(arg, "--tls-cipher-list=", 18) == 0) {
|
|
|
|
default_cipher_list = arg + 18;
|
|
|
|
} else if (strncmp(arg, "--use-openssl-ca", 16) == 0) {
|
|
|
|
ssl_openssl_cert_store = true;
|
|
|
|
use_openssl_ca = true;
|
|
|
|
} else if (strncmp(arg, "--use-bundled-ca", 16) == 0) {
|
|
|
|
use_bundled_ca = true;
|
|
|
|
ssl_openssl_cert_store = false;
|
|
|
|
#if NODE_FIPS_MODE
|
|
|
|
} else if (strcmp(arg, "--enable-fips") == 0) {
|
|
|
|
enable_fips_crypto = true;
|
|
|
|
} else if (strcmp(arg, "--force-fips") == 0) {
|
|
|
|
force_fips_crypto = true;
|
|
|
|
#endif /* NODE_FIPS_MODE */
|
|
|
|
} else if (strncmp(arg, "--openssl-config=", 17) == 0) {
|
|
|
|
openssl_config.assign(arg + 17);
|
|
|
|
#endif /* HAVE_OPENSSL */
|
|
|
|
#if defined(NODE_HAVE_I18N_SUPPORT)
|
|
|
|
} else if (strncmp(arg, "--icu-data-dir=", 15) == 0) {
|
|
|
|
icu_data_dir.assign(arg + 15);
|
|
|
|
#endif
|
|
|
|
} else if (strcmp(arg, "--expose-internals") == 0 ||
|
|
|
|
strcmp(arg, "--expose_internals") == 0) {
|
|
|
|
config_expose_internals = true;
|
|
|
|
} else if (strcmp(arg, "-") == 0) {
|
|
|
|
break;
|
|
|
|
} else if (strcmp(arg, "--") == 0) {
|
|
|
|
index += 1;
|
|
|
|
break;
|
|
|
|
} else if (strcmp(arg, "--abort-on-uncaught-exception") == 0 ||
|
|
|
|
strcmp(arg, "--abort_on_uncaught_exception") == 0) {
|
|
|
|
abort_on_uncaught_exception = true;
|
|
|
|
// Also a V8 option. Pass through as-is.
|
|
|
|
new_v8_argv[new_v8_argc] = arg;
|
|
|
|
new_v8_argc += 1;
|
|
|
|
} else {
|
|
|
|
// V8 option. Pass through as-is.
|
|
|
|
new_v8_argv[new_v8_argc] = arg;
|
|
|
|
new_v8_argc += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(new_exec_argv + new_exec_argc,
|
|
|
|
argv + index,
|
|
|
|
args_consumed * sizeof(*argv));
|
|
|
|
|
|
|
|
new_exec_argc += args_consumed;
|
|
|
|
index += args_consumed;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if HAVE_OPENSSL
|
|
|
|
if (use_openssl_ca && use_bundled_ca) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"%s: either --use-openssl-ca or --use-bundled-ca can be used, "
|
|
|
|
"not both\n",
|
|
|
|
argv[0]);
|
|
|
|
exit(9);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (eval_string != nullptr && syntax_check_only) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"%s: either --check or --eval can be used, not both\n", argv[0]);
|
|
|
|
exit(9);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Copy remaining arguments.
|
|
|
|
const unsigned int args_left = nargs - index;
|
|
|
|
|
|
|
|
if (is_env && args_left) {
|
|
|
|
fprintf(stderr, "%s: %s is not supported in NODE_OPTIONS\n",
|
|
|
|
argv[0], argv[index]);
|
|
|
|
exit(9);
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(new_argv + new_argc, argv + index, args_left * sizeof(*argv));
|
|
|
|
new_argc += args_left;
|
|
|
|
|
|
|
|
*exec_argc = new_exec_argc;
|
|
|
|
*exec_argv = new_exec_argv;
|
|
|
|
*v8_argc = new_v8_argc;
|
|
|
|
*v8_argv = new_v8_argv;
|
|
|
|
|
|
|
|
// Copy new_argv over argv and update argc.
|
|
|
|
memcpy(argv, new_argv, new_argc * sizeof(*argv));
|
|
|
|
delete[] new_argv;
|
|
|
|
*argc = static_cast<int>(new_argc);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void StartInspector(Environment* env, const char* path,
|
|
|
|
DebugOptions debug_options) {
|
|
|
|
#if HAVE_INSPECTOR
|
|
|
|
CHECK(!env->inspector_agent()->IsStarted());
|
|
|
|
v8_platform.StartInspector(env, path, debug_options);
|
|
|
|
#endif // HAVE_INSPECTOR
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef __POSIX__
|
|
|
|
void RegisterSignalHandler(int signal,
|
|
|
|
void (*handler)(int signal),
|
|
|
|
bool reset_handler) {
|
|
|
|
struct sigaction sa;
|
|
|
|
memset(&sa, 0, sizeof(sa));
|
|
|
|
sa.sa_handler = handler;
|
|
|
|
#ifndef __FreeBSD__
|
|
|
|
// FreeBSD has a nasty bug with SA_RESETHAND reseting the SA_SIGINFO, that is
|
|
|
|
// in turn set for a libthr wrapper. This leads to a crash.
|
|
|
|
// Work around the issue by manually setting SIG_DFL in the signal handler
|
|
|
|
sa.sa_flags = reset_handler ? SA_RESETHAND : 0;
|
|
|
|
#endif
|
|
|
|
sigfillset(&sa.sa_mask);
|
|
|
|
CHECK_EQ(sigaction(signal, &sa, nullptr), 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DebugProcess(const FunctionCallbackInfo<Value>& args) {
|
|
|
|
Environment* env = Environment::GetCurrent(args);
|
|
|
|
|
|
|
|
if (args.Length() != 1) {
|
|
|
|
return env->ThrowError("Invalid number of arguments.");
|
|
|
|
}
|
|
|
|
|
|
|
|
pid_t pid;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
pid = args[0]->IntegerValue();
|
|
|
|
r = kill(pid, SIGUSR1);
|
|
|
|
if (r != 0) {
|
|
|
|
return env->ThrowErrnoException(errno, "kill");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif // __POSIX__
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
static int GetDebugSignalHandlerMappingName(DWORD pid, wchar_t* buf,
|
|
|
|
size_t buf_len) {
|
|
|
|
return _snwprintf(buf, buf_len, L"node-debug-handler-%u", pid);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void DebugProcess(const FunctionCallbackInfo<Value>& args) {
|
|
|
|
Environment* env = Environment::GetCurrent(args);
|
|
|
|
Isolate* isolate = args.GetIsolate();
|
|
|
|
DWORD pid;
|
|
|
|
HANDLE process = nullptr;
|
|
|
|
HANDLE thread = nullptr;
|
|
|
|
HANDLE mapping = nullptr;
|
|
|
|
wchar_t mapping_name[32];
|
|
|
|
LPTHREAD_START_ROUTINE* handler = nullptr;
|
|
|
|
|
|
|
|
if (args.Length() != 1) {
|
|
|
|
env->ThrowError("Invalid number of arguments.");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
pid = (DWORD) args[0]->IntegerValue();
|
|
|
|
|
|
|
|
process = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION |
|
|
|
|
PROCESS_VM_OPERATION | PROCESS_VM_WRITE |
|
|
|
|
PROCESS_VM_READ,
|
|
|
|
FALSE,
|
|
|
|
pid);
|
|
|
|
if (process == nullptr) {
|
|
|
|
isolate->ThrowException(
|
|
|
|
WinapiErrnoException(isolate, GetLastError(), "OpenProcess"));
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (GetDebugSignalHandlerMappingName(pid,
|
|
|
|
mapping_name,
|
|
|
|
arraysize(mapping_name)) < 0) {
|
|
|
|
env->ThrowErrnoException(errno, "sprintf");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
mapping = OpenFileMappingW(FILE_MAP_READ, FALSE, mapping_name);
|
|
|
|
if (mapping == nullptr) {
|
|
|
|
isolate->ThrowException(WinapiErrnoException(isolate,
|
|
|
|
GetLastError(),
|
|
|
|
"OpenFileMappingW"));
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
handler = reinterpret_cast<LPTHREAD_START_ROUTINE*>(
|
|
|
|
MapViewOfFile(mapping,
|
|
|
|
FILE_MAP_READ,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
sizeof *handler));
|
|
|
|
if (handler == nullptr || *handler == nullptr) {
|
|
|
|
isolate->ThrowException(
|
|
|
|
WinapiErrnoException(isolate, GetLastError(), "MapViewOfFile"));
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
thread = CreateRemoteThread(process,
|
|
|
|
nullptr,
|
|
|
|
0,
|
|
|
|
*handler,
|
|
|
|
nullptr,
|
|
|
|
0,
|
|
|
|
nullptr);
|
|
|
|
if (thread == nullptr) {
|
|
|
|
isolate->ThrowException(WinapiErrnoException(isolate,
|
|
|
|
GetLastError(),
|
|
|
|
"CreateRemoteThread"));
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Wait for the thread to terminate
|
|
|
|
if (WaitForSingleObject(thread, INFINITE) != WAIT_OBJECT_0) {
|
|
|
|
isolate->ThrowException(WinapiErrnoException(isolate,
|
|
|
|
GetLastError(),
|
|
|
|
"WaitForSingleObject"));
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
if (process != nullptr)
|
|
|
|
CloseHandle(process);
|
|
|
|
if (thread != nullptr)
|
|
|
|
CloseHandle(thread);
|
|
|
|
if (handler != nullptr)
|
|
|
|
UnmapViewOfFile(handler);
|
|
|
|
if (mapping != nullptr)
|
|
|
|
CloseHandle(mapping);
|
|
|
|
}
|
|
|
|
#endif // _WIN32
|
|
|
|
|
|
|
|
|
|
|
|
static void DebugPause(const FunctionCallbackInfo<Value>& args) {
|
|
|
|
v8::Debug::DebugBreak(args.GetIsolate());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void DebugEnd(const FunctionCallbackInfo<Value>& args) {
|
|
|
|
#if HAVE_INSPECTOR
|
|
|
|
Environment* env = Environment::GetCurrent(args);
|
|
|
|
if (env->inspector_agent()->IsStarted()) {
|
|
|
|
env->inspector_agent()->Stop();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
inline void PlatformInit() {
|
|
|
|
#ifdef __POSIX__
|
|
|
|
#if HAVE_INSPECTOR
|
|
|
|
sigset_t sigmask;
|
|
|
|
sigemptyset(&sigmask);
|
|
|
|
sigaddset(&sigmask, SIGUSR1);
|
|
|
|
const int err = pthread_sigmask(SIG_SETMASK, &sigmask, nullptr);
|
|
|
|
#endif // HAVE_INSPECTOR
|
|
|
|
|
|
|
|
// Make sure file descriptors 0-2 are valid before we start logging anything.
|
|
|
|
for (int fd = STDIN_FILENO; fd <= STDERR_FILENO; fd += 1) {
|
|
|
|
struct stat ignored;
|
|
|
|
if (fstat(fd, &ignored) == 0)
|
|
|
|
continue;
|
|
|
|
// Anything but EBADF means something is seriously wrong. We don't
|
|
|
|
// have to special-case EINTR, fstat() is not interruptible.
|
|
|
|
if (errno != EBADF)
|
|
|
|
ABORT();
|
|
|
|
if (fd != open("/dev/null", O_RDWR))
|
|
|
|
ABORT();
|
|
|
|
}
|
|
|
|
|
|
|
|
#if HAVE_INSPECTOR
|
|
|
|
CHECK_EQ(err, 0);
|
|
|
|
#endif // HAVE_INSPECTOR
|
|
|
|
|
|
|
|
#ifndef NODE_SHARED_MODE
|
|
|
|
// Restore signal dispositions, the parent process may have changed them.
|
|
|
|
struct sigaction act;
|
|
|
|
memset(&act, 0, sizeof(act));
|
|
|
|
|
|
|
|
// The hard-coded upper limit is because NSIG is not very reliable; on Linux,
|
|
|
|
// it evaluates to 32, 34 or 64, depending on whether RT signals are enabled.
|
|
|
|
// Counting up to SIGRTMIN doesn't work for the same reason.
|
|
|
|
for (unsigned nr = 1; nr < kMaxSignal; nr += 1) {
|
|
|
|
if (nr == SIGKILL || nr == SIGSTOP)
|
|
|
|
continue;
|
|
|
|
act.sa_handler = (nr == SIGPIPE) ? SIG_IGN : SIG_DFL;
|
|
|
|
CHECK_EQ(0, sigaction(nr, &act, nullptr));
|
|
|
|
}
|
|
|
|
#endif // !NODE_SHARED_MODE
|
|
|
|
|
|
|
|
RegisterSignalHandler(SIGINT, SignalExit, true);
|
|
|
|
RegisterSignalHandler(SIGTERM, SignalExit, true);
|
|
|
|
|
|
|
|
// Raise the open file descriptor limit.
|
|
|
|
struct rlimit lim;
|
|
|
|
if (getrlimit(RLIMIT_NOFILE, &lim) == 0 && lim.rlim_cur != lim.rlim_max) {
|
|
|
|
// Do a binary search for the limit.
|
|
|
|
rlim_t min = lim.rlim_cur;
|
|
|
|
rlim_t max = 1 << 20;
|
|
|
|
// But if there's a defined upper bound, don't search, just set it.
|
|
|
|
if (lim.rlim_max != RLIM_INFINITY) {
|
|
|
|
min = lim.rlim_max;
|
|
|
|
max = lim.rlim_max;
|
|
|
|
}
|
|
|
|
do {
|
|
|
|
lim.rlim_cur = min + (max - min) / 2;
|
|
|
|
if (setrlimit(RLIMIT_NOFILE, &lim)) {
|
|
|
|
max = lim.rlim_cur;
|
|
|
|
} else {
|
|
|
|
min = lim.rlim_cur;
|
|
|
|
}
|
|
|
|
} while (min + 1 < max);
|
|
|
|
}
|
|
|
|
#endif // __POSIX__
|
|
|
|
#ifdef _WIN32
|
|
|
|
for (int fd = 0; fd <= 2; ++fd) {
|
|
|
|
auto handle = reinterpret_cast<HANDLE>(_get_osfhandle(fd));
|
|
|
|
if (handle == INVALID_HANDLE_VALUE ||
|
|
|
|
GetFileType(handle) == FILE_TYPE_UNKNOWN) {
|
|
|
|
// Ignore _close result. If it fails or not depends on used Windows
|
|
|
|
// version. We will just check _open result.
|
|
|
|
_close(fd);
|
|
|
|
if (fd != _open("nul", _O_RDWR))
|
|
|
|
ABORT();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif // _WIN32
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ProcessArgv(int* argc,
|
|
|
|
const char** argv,
|
|
|
|
int* exec_argc,
|
|
|
|
const char*** exec_argv,
|
|
|
|
bool is_env = false) {
|
|
|
|
// Parse a few arguments which are specific to Node.
|
|
|
|
int v8_argc;
|
|
|
|
const char** v8_argv;
|
|
|
|
ParseArgs(argc, argv, exec_argc, exec_argv, &v8_argc, &v8_argv, is_env);
|
|
|
|
|
|
|
|
// TODO(bnoordhuis) Intercept --prof arguments and start the CPU profiler
|
|
|
|
// manually? That would give us a little more control over its runtime
|
|
|
|
// behavior but it could also interfere with the user's intentions in ways
|
|
|
|
// we fail to anticipate. Dillema.
|
|
|
|
for (int i = 1; i < v8_argc; ++i) {
|
|
|
|
if (strncmp(v8_argv[i], "--prof", sizeof("--prof") - 1) == 0) {
|
|
|
|
v8_is_profiling = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef __POSIX__
|
|
|
|
// Block SIGPROF signals when sleeping in epoll_wait/kevent/etc. Avoids the
|
|
|
|
// performance penalty of frequent EINTR wakeups when the profiler is running.
|
|
|
|
// Only do this for v8.log profiling, as it breaks v8::CpuProfiler users.
|
|
|
|
if (v8_is_profiling) {
|
|
|
|
uv_loop_configure(uv_default_loop(), UV_LOOP_BLOCK_SIGNAL, SIGPROF);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// The const_cast doesn't violate conceptual const-ness. V8 doesn't modify
|
|
|
|
// the argv array or the elements it points to.
|
|
|
|
if (v8_argc > 1)
|
|
|
|
V8::SetFlagsFromCommandLine(&v8_argc, const_cast<char**>(v8_argv), true);
|
|
|
|
|
|
|
|
// Anything that's still in v8_argv is not a V8 or a node option.
|
|
|
|
for (int i = 1; i < v8_argc; i++) {
|
|
|
|
fprintf(stderr, "%s: bad option: %s\n", argv[0], v8_argv[i]);
|
|
|
|
}
|
|
|
|
delete[] v8_argv;
|
|
|
|
v8_argv = nullptr;
|
|
|
|
|
|
|
|
if (v8_argc > 1) {
|
|
|
|
exit(9);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Init(int* argc,
|
|
|
|
const char** argv,
|
|
|
|
int* exec_argc,
|
|
|
|
const char*** exec_argv) {
|
|
|
|
// Initialize prog_start_time to get relative uptime.
|
|
|
|
prog_start_time = static_cast<double>(uv_now(uv_default_loop()));
|
|
|
|
|
|
|
|
// Make inherited handles noninheritable.
|
|
|
|
uv_disable_stdio_inheritance();
|
|
|
|
|
|
|
|
#if defined(NODE_HAVE_I18N_SUPPORT)
|
|
|
|
// Set the ICU casing flag early
|
|
|
|
// so the user can disable a flag --foo at run-time by passing
|
|
|
|
// --no_foo from the command line.
|
|
|
|
const char icu_case_mapping[] = "--icu_case_mapping";
|
|
|
|
V8::SetFlagsFromString(icu_case_mapping, sizeof(icu_case_mapping) - 1);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined(NODE_V8_OPTIONS)
|
|
|
|
// Should come before the call to V8::SetFlagsFromCommandLine()
|
|
|
|
// so the user can disable a flag --foo at run-time by passing
|
|
|
|
// --no_foo from the command line.
|
|
|
|
V8::SetFlagsFromString(NODE_V8_OPTIONS, sizeof(NODE_V8_OPTIONS) - 1);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
{
|
|
|
|
std::string text;
|
|
|
|
config_pending_deprecation =
|
|
|
|
SafeGetenv("NODE_PENDING_DEPRECATION", &text) && text[0] == '1';
|
|
|
|
}
|
|
|
|
|
|
|
|
// Allow for environment set preserving symlinks.
|
|
|
|
{
|
|
|
|
std::string text;
|
|
|
|
config_preserve_symlinks =
|
|
|
|
SafeGetenv("NODE_PRESERVE_SYMLINKS", &text) && text[0] == '1';
|
|
|
|
}
|
|
|
|
|
|
|
|
if (config_warning_file.empty())
|
|
|
|
SafeGetenv("NODE_REDIRECT_WARNINGS", &config_warning_file);
|
|
|
|
|
|
|
|
#if HAVE_OPENSSL
|
|
|
|
if (openssl_config.empty())
|
|
|
|
SafeGetenv("OPENSSL_CONF", &openssl_config);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if !defined(NODE_WITHOUT_NODE_OPTIONS)
|
|
|
|
std::string node_options;
|
|
|
|
if (SafeGetenv("NODE_OPTIONS", &node_options)) {
|
|
|
|
// Smallest tokens are 2-chars (a not space and a space), plus 2 extra
|
|
|
|
// pointers, for the prepended executable name, and appended NULL pointer.
|
|
|
|
size_t max_len = 2 + (node_options.length() + 1) / 2;
|
|
|
|
const char** argv_from_env = new const char*[max_len];
|
|
|
|
int argc_from_env = 0;
|
|
|
|
// [0] is expected to be the program name, fill it in from the real argv.
|
|
|
|
argv_from_env[argc_from_env++] = argv[0];
|
|
|
|
|
|
|
|
char* cstr = strdup(node_options.c_str());
|
|
|
|
char* initptr = cstr;
|
|
|
|
char* token;
|
|
|
|
while ((token = strtok(initptr, " "))) { // NOLINT(runtime/threadsafe_fn)
|
|
|
|
initptr = nullptr;
|
|
|
|
argv_from_env[argc_from_env++] = token;
|
|
|
|
}
|
|
|
|
argv_from_env[argc_from_env] = nullptr;
|
|
|
|
int exec_argc_;
|
|
|
|
const char** exec_argv_ = nullptr;
|
|
|
|
ProcessArgv(&argc_from_env, argv_from_env, &exec_argc_, &exec_argv_, true);
|
|
|
|
delete[] exec_argv_;
|
|
|
|
delete[] argv_from_env;
|
|
|
|
free(cstr);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
ProcessArgv(argc, argv, exec_argc, exec_argv);
|
|
|
|
|
build, i18n: improve Intl build, add "--with-intl"
The two main goals of this change are:
- To make it easier to build the Intl option using ICU (particularly,
using a newer ICU than v8/Chromium's version)
- To enable a much smaller ICU build with only English support The goal
here is to get node.js binaries built this way by default so that the
Intl API can be used. Additional data can be added at execution time
(see Readme and wiki)
More details are at https://github.com/joyent/node/pull/7719
In particular, this change adds the "--with-intl=" configure option to
provide more ways of building "Intl":
- "full-icu" picks up an ICU from deps/icu
- "small-icu" is similar, but builds only English
- "system-icu" uses pkg-config to find an installed ICU
- "none" does nothing (no Intl)
For Windows builds, the "full-icu" or "small-icu" options are added to
vcbuild.bat.
Note that the existing "--with-icu-path" option is not removed from
configure, but may not be used alongside the new option.
Wiki changes have already been made on
https://github.com/joyent/node/wiki/Installation
and a new page created at
https://github.com/joyent/node/wiki/Intl
(marked as provisional until this change lands.)
Summary of changes:
* README.md : doc updates
* .gitignore : added "deps/icu" as this is the location where ICU is
unpacked to.
* Makefile : added the tools/icu/* files to cpplint, but excluded a
problematic file.
* configure : added the "--with-intl" option mentioned above.
Calculate at config time the list of ICU source files to use and data
packaging options.
* node.gyp : add the new files src/node_i18n.cc/.h as well as ICU
linkage.
* src/node.cc : add call into
node::i18n::InitializeICUDirectory(icu_data_dir) as well as new
--icu-data-dir option and NODE_ICU_DATA env variable to configure ICU
data loading. This loading is only relevant in the "small"
configuration.
* src/node_i18n.cc : new source file for the above Initialize..
function, to setup ICU as needed.
* tools/icu : new directory with some tools needed for this build.
* tools/icu/icu-generic.gyp : new .gyp file that builds ICU in some new
ways, both on unix/mac and windows.
* tools/icu/icu-system.gyp : new .gyp file to build node against a
pkg-config detected ICU.
* tools/icu/icu_small.json : new config file for the "English-only" small
build.
* tools/icu/icutrim.py : new tool for trimming down ICU data. Reads the
above .json file.
* tools/icu/iculslocs.cc : new tool for repairing ICU data manifests
after trim operation.
* tools/icu/no-op.cc : dummy file to force .gyp into using a C++ linker.
* vcbuild.bat : added small-icu and full-icu options, to call into
configure.
* Fixed toolset dependencies, see
https://github.com/joyent/node/pull/7719#issuecomment-54641687
Note that because of a bug in gyp {CC,CXX}_host must also be set.
Otherwise gcc/g++ will be used by default for part of the build.
Reviewed-by: Trevor Norris <trev.norris@gmail.com>
Reviewed-by: Fedor Indutny <fedor@indutny.com>
10 years ago
|
|
|
#if defined(NODE_HAVE_I18N_SUPPORT)
|
|
|
|
// If the parameter isn't given, use the env variable.
|
|
|
|
if (icu_data_dir.empty())
|
|
|
|
SafeGetenv("NODE_ICU_DATA", &icu_data_dir);
|
build, i18n: improve Intl build, add "--with-intl"
The two main goals of this change are:
- To make it easier to build the Intl option using ICU (particularly,
using a newer ICU than v8/Chromium's version)
- To enable a much smaller ICU build with only English support The goal
here is to get node.js binaries built this way by default so that the
Intl API can be used. Additional data can be added at execution time
(see Readme and wiki)
More details are at https://github.com/joyent/node/pull/7719
In particular, this change adds the "--with-intl=" configure option to
provide more ways of building "Intl":
- "full-icu" picks up an ICU from deps/icu
- "small-icu" is similar, but builds only English
- "system-icu" uses pkg-config to find an installed ICU
- "none" does nothing (no Intl)
For Windows builds, the "full-icu" or "small-icu" options are added to
vcbuild.bat.
Note that the existing "--with-icu-path" option is not removed from
configure, but may not be used alongside the new option.
Wiki changes have already been made on
https://github.com/joyent/node/wiki/Installation
and a new page created at
https://github.com/joyent/node/wiki/Intl
(marked as provisional until this change lands.)
Summary of changes:
* README.md : doc updates
* .gitignore : added "deps/icu" as this is the location where ICU is
unpacked to.
* Makefile : added the tools/icu/* files to cpplint, but excluded a
problematic file.
* configure : added the "--with-intl" option mentioned above.
Calculate at config time the list of ICU source files to use and data
packaging options.
* node.gyp : add the new files src/node_i18n.cc/.h as well as ICU
linkage.
* src/node.cc : add call into
node::i18n::InitializeICUDirectory(icu_data_dir) as well as new
--icu-data-dir option and NODE_ICU_DATA env variable to configure ICU
data loading. This loading is only relevant in the "small"
configuration.
* src/node_i18n.cc : new source file for the above Initialize..
function, to setup ICU as needed.
* tools/icu : new directory with some tools needed for this build.
* tools/icu/icu-generic.gyp : new .gyp file that builds ICU in some new
ways, both on unix/mac and windows.
* tools/icu/icu-system.gyp : new .gyp file to build node against a
pkg-config detected ICU.
* tools/icu/icu_small.json : new config file for the "English-only" small
build.
* tools/icu/icutrim.py : new tool for trimming down ICU data. Reads the
above .json file.
* tools/icu/iculslocs.cc : new tool for repairing ICU data manifests
after trim operation.
* tools/icu/no-op.cc : dummy file to force .gyp into using a C++ linker.
* vcbuild.bat : added small-icu and full-icu options, to call into
configure.
* Fixed toolset dependencies, see
https://github.com/joyent/node/pull/7719#issuecomment-54641687
Note that because of a bug in gyp {CC,CXX}_host must also be set.
Otherwise gcc/g++ will be used by default for part of the build.
Reviewed-by: Trevor Norris <trev.norris@gmail.com>
Reviewed-by: Fedor Indutny <fedor@indutny.com>
10 years ago
|
|
|
// Initialize ICU.
|
|
|
|
// If icu_data_dir is empty here, it will load the 'minimal' data.
|
build, i18n: improve Intl build, add "--with-intl"
The two main goals of this change are:
- To make it easier to build the Intl option using ICU (particularly,
using a newer ICU than v8/Chromium's version)
- To enable a much smaller ICU build with only English support The goal
here is to get node.js binaries built this way by default so that the
Intl API can be used. Additional data can be added at execution time
(see Readme and wiki)
More details are at https://github.com/joyent/node/pull/7719
In particular, this change adds the "--with-intl=" configure option to
provide more ways of building "Intl":
- "full-icu" picks up an ICU from deps/icu
- "small-icu" is similar, but builds only English
- "system-icu" uses pkg-config to find an installed ICU
- "none" does nothing (no Intl)
For Windows builds, the "full-icu" or "small-icu" options are added to
vcbuild.bat.
Note that the existing "--with-icu-path" option is not removed from
configure, but may not be used alongside the new option.
Wiki changes have already been made on
https://github.com/joyent/node/wiki/Installation
and a new page created at
https://github.com/joyent/node/wiki/Intl
(marked as provisional until this change lands.)
Summary of changes:
* README.md : doc updates
* .gitignore : added "deps/icu" as this is the location where ICU is
unpacked to.
* Makefile : added the tools/icu/* files to cpplint, but excluded a
problematic file.
* configure : added the "--with-intl" option mentioned above.
Calculate at config time the list of ICU source files to use and data
packaging options.
* node.gyp : add the new files src/node_i18n.cc/.h as well as ICU
linkage.
* src/node.cc : add call into
node::i18n::InitializeICUDirectory(icu_data_dir) as well as new
--icu-data-dir option and NODE_ICU_DATA env variable to configure ICU
data loading. This loading is only relevant in the "small"
configuration.
* src/node_i18n.cc : new source file for the above Initialize..
function, to setup ICU as needed.
* tools/icu : new directory with some tools needed for this build.
* tools/icu/icu-generic.gyp : new .gyp file that builds ICU in some new
ways, both on unix/mac and windows.
* tools/icu/icu-system.gyp : new .gyp file to build node against a
pkg-config detected ICU.
* tools/icu/icu_small.json : new config file for the "English-only" small
build.
* tools/icu/icutrim.py : new tool for trimming down ICU data. Reads the
above .json file.
* tools/icu/iculslocs.cc : new tool for repairing ICU data manifests
after trim operation.
* tools/icu/no-op.cc : dummy file to force .gyp into using a C++ linker.
* vcbuild.bat : added small-icu and full-icu options, to call into
configure.
* Fixed toolset dependencies, see
https://github.com/joyent/node/pull/7719#issuecomment-54641687
Note that because of a bug in gyp {CC,CXX}_host must also be set.
Otherwise gcc/g++ will be used by default for part of the build.
Reviewed-by: Trevor Norris <trev.norris@gmail.com>
Reviewed-by: Fedor Indutny <fedor@indutny.com>
10 years ago
|
|
|
if (!i18n::InitializeICUDirectory(icu_data_dir)) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"%s: could not initialize ICU "
|
|
|
|
"(check NODE_ICU_DATA or --icu-data-dir parameters)",
|
|
|
|
argv[0]);
|
|
|
|
exit(9);
|
build, i18n: improve Intl build, add "--with-intl"
The two main goals of this change are:
- To make it easier to build the Intl option using ICU (particularly,
using a newer ICU than v8/Chromium's version)
- To enable a much smaller ICU build with only English support The goal
here is to get node.js binaries built this way by default so that the
Intl API can be used. Additional data can be added at execution time
(see Readme and wiki)
More details are at https://github.com/joyent/node/pull/7719
In particular, this change adds the "--with-intl=" configure option to
provide more ways of building "Intl":
- "full-icu" picks up an ICU from deps/icu
- "small-icu" is similar, but builds only English
- "system-icu" uses pkg-config to find an installed ICU
- "none" does nothing (no Intl)
For Windows builds, the "full-icu" or "small-icu" options are added to
vcbuild.bat.
Note that the existing "--with-icu-path" option is not removed from
configure, but may not be used alongside the new option.
Wiki changes have already been made on
https://github.com/joyent/node/wiki/Installation
and a new page created at
https://github.com/joyent/node/wiki/Intl
(marked as provisional until this change lands.)
Summary of changes:
* README.md : doc updates
* .gitignore : added "deps/icu" as this is the location where ICU is
unpacked to.
* Makefile : added the tools/icu/* files to cpplint, but excluded a
problematic file.
* configure : added the "--with-intl" option mentioned above.
Calculate at config time the list of ICU source files to use and data
packaging options.
* node.gyp : add the new files src/node_i18n.cc/.h as well as ICU
linkage.
* src/node.cc : add call into
node::i18n::InitializeICUDirectory(icu_data_dir) as well as new
--icu-data-dir option and NODE_ICU_DATA env variable to configure ICU
data loading. This loading is only relevant in the "small"
configuration.
* src/node_i18n.cc : new source file for the above Initialize..
function, to setup ICU as needed.
* tools/icu : new directory with some tools needed for this build.
* tools/icu/icu-generic.gyp : new .gyp file that builds ICU in some new
ways, both on unix/mac and windows.
* tools/icu/icu-system.gyp : new .gyp file to build node against a
pkg-config detected ICU.
* tools/icu/icu_small.json : new config file for the "English-only" small
build.
* tools/icu/icutrim.py : new tool for trimming down ICU data. Reads the
above .json file.
* tools/icu/iculslocs.cc : new tool for repairing ICU data manifests
after trim operation.
* tools/icu/no-op.cc : dummy file to force .gyp into using a C++ linker.
* vcbuild.bat : added small-icu and full-icu options, to call into
configure.
* Fixed toolset dependencies, see
https://github.com/joyent/node/pull/7719#issuecomment-54641687
Note that because of a bug in gyp {CC,CXX}_host must also be set.
Otherwise gcc/g++ will be used by default for part of the build.
Reviewed-by: Trevor Norris <trev.norris@gmail.com>
Reviewed-by: Fedor Indutny <fedor@indutny.com>
10 years ago
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Unconditionally force typed arrays to allocate outside the v8 heap. This
|
|
|
|
// is to prevent memory pointers from being moved around that are returned by
|
|
|
|
// Buffer::Data().
|
|
|
|
const char no_typed_array_heap[] = "--typed_array_max_size_in_heap=0";
|
|
|
|
V8::SetFlagsFromString(no_typed_array_heap, sizeof(no_typed_array_heap) - 1);
|
|
|
|
|
|
|
|
// We should set node_is_initialized here instead of in node::Start,
|
|
|
|
// otherwise embedders using node::Init to initialize everything will not be
|
|
|
|
// able to set it and native modules will not load for them.
|
|
|
|
node_is_initialized = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void RunAtExit(Environment* env) {
|
|
|
|
env->RunAtExitCallbacks();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static uv_key_t thread_local_env;
|
|
|
|
|
|
|
|
|
|
|
|
void AtExit(void (*cb)(void* arg), void* arg) {
|
|
|
|
auto env = static_cast<Environment*>(uv_key_get(&thread_local_env));
|
|
|
|
AtExit(env, cb, arg);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void AtExit(Environment* env, void (*cb)(void* arg), void* arg) {
|
|
|
|
CHECK_NE(env, nullptr);
|
|
|
|
env->AtExit(cb, arg);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void EmitBeforeExit(Environment* env) {
|
|
|
|
HandleScope handle_scope(env->isolate());
|
|
|
|
Context::Scope context_scope(env->context());
|
|
|
|
Local<Object> process_object = env->process_object();
|
|
|
|
Local<String> exit_code = FIXED_ONE_BYTE_STRING(env->isolate(), "exitCode");
|
|
|
|
Local<Value> args[] = {
|
|
|
|
FIXED_ONE_BYTE_STRING(env->isolate(), "beforeExit"),
|
|
|
|
process_object->Get(exit_code)->ToInteger(env->isolate())
|
|
|
|
};
|
|
|
|
MakeCallback(env->isolate(),
|
|
|
|
process_object, "emit", arraysize(args), args,
|
|
|
|
0, 0).ToLocalChecked();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int EmitExit(Environment* env) {
|
|
|
|
// process.emit('exit')
|
|
|
|
HandleScope handle_scope(env->isolate());
|
|
|
|
Context::Scope context_scope(env->context());
|
|
|
|
Local<Object> process_object = env->process_object();
|
|
|
|
process_object->Set(env->exiting_string(), True(env->isolate()));
|
|
|
|
|
|
|
|
Local<String> exitCode = env->exit_code_string();
|
|
|
|
int code = process_object->Get(exitCode)->Int32Value();
|
|
|
|
|
|
|
|
Local<Value> args[] = {
|
|
|
|
env->exit_string(),
|
|
|
|
Integer::New(env->isolate(), code)
|
|
|
|
};
|
|
|
|
|
|
|
|
MakeCallback(env->isolate(),
|
|
|
|
process_object, "emit", arraysize(args), args,
|
|
|
|
0, 0).ToLocalChecked();
|
|
|
|
|
|
|
|
// Reload exit code, it may be changed by `emit('exit')`
|
|
|
|
return process_object->Get(exitCode)->Int32Value();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
IsolateData* CreateIsolateData(Isolate* isolate, uv_loop_t* loop) {
|
|
|
|
return new IsolateData(isolate, loop);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void FreeIsolateData(IsolateData* isolate_data) {
|
|
|
|
delete isolate_data;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Environment* CreateEnvironment(IsolateData* isolate_data,
|
|
|
|
Local<Context> context,
|
|
|
|
int argc,
|
|
|
|
const char* const* argv,
|
|
|
|
int exec_argc,
|
|
|
|
const char* const* exec_argv) {
|
|
|
|
Isolate* isolate = context->GetIsolate();
|
|
|
|
HandleScope handle_scope(isolate);
|
|
|
|
Context::Scope context_scope(context);
|
|
|
|
auto env = new Environment(isolate_data, context);
|
|
|
|
env->Start(argc, argv, exec_argc, exec_argv, v8_is_profiling);
|
|
|
|
return env;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void FreeEnvironment(Environment* env) {
|
|
|
|
delete env;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
inline int Start(Isolate* isolate, IsolateData* isolate_data,
|
|
|
|
int argc, const char* const* argv,
|
|
|
|
int exec_argc, const char* const* exec_argv) {
|
|
|
|
HandleScope handle_scope(isolate);
|
|
|
|
Local<Context> context = Context::New(isolate);
|
|
|
|
Context::Scope context_scope(context);
|
|
|
|
Environment env(isolate_data, context);
|
|
|
|
CHECK_EQ(0, uv_key_create(&thread_local_env));
|
|
|
|
uv_key_set(&thread_local_env, &env);
|
|
|
|
env.Start(argc, argv, exec_argc, exec_argv, v8_is_profiling);
|
|
|
|
|
|
|
|
const char* path = argc > 1 ? argv[1] : nullptr;
|
|
|
|
StartInspector(&env, path, debug_options);
|
|
|
|
|
|
|
|
if (debug_options.inspector_enabled() && !v8_platform.InspectorStarted(&env))
|
|
|
|
return 12; // Signal internal error.
|
|
|
|
|
|
|
|
env.set_abort_on_uncaught_exception(abort_on_uncaught_exception);
|
|
|
|
|
|
|
|
{
|
|
|
|
Environment::AsyncCallbackScope callback_scope(&env);
|
|
|
|
Environment::AsyncHooks::ExecScope exec_scope(&env, 1, 0);
|
|
|
|
LoadEnvironment(&env);
|
|
|
|
}
|
|
|
|
|
|
|
|
env.set_trace_sync_io(trace_sync_io);
|
|
|
|
|
|
|
|
if (load_napi_modules) {
|
|
|
|
ProcessEmitWarning(&env, "N-API is an experimental feature "
|
|
|
|
"and could change at any time.");
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
SealHandleScope seal(isolate);
|
|
|
|
bool more;
|
|
|
|
do {
|
|
|
|
v8_platform.PumpMessageLoop(isolate);
|
|
|
|
more = uv_run(env.event_loop(), UV_RUN_ONCE);
|
|
|
|
|
|
|
|
if (more == false) {
|
|
|
|
v8_platform.PumpMessageLoop(isolate);
|
|
|
|
EmitBeforeExit(&env);
|
|
|
|
|
|
|
|
// Emit `beforeExit` if the loop became alive either after emitting
|
|
|
|
// event, or after running some callbacks.
|
|
|
|
more = uv_loop_alive(env.event_loop());
|
|
|
|
if (uv_run(env.event_loop(), UV_RUN_NOWAIT) != 0)
|
|
|
|
more = true;
|
|
|
|
}
|
|
|
|
} while (more == true);
|
|
|
|
}
|
|
|
|
|
|
|
|
env.set_trace_sync_io(false);
|
|
|
|
|
|
|
|
const int exit_code = EmitExit(&env);
|
|
|
|
RunAtExit(&env);
|
|
|
|
uv_key_delete(&thread_local_env);
|
|
|
|
|
|
|
|
WaitForInspectorDisconnect(&env);
|
|
|
|
#if defined(LEAK_SANITIZER)
|
|
|
|
__lsan_do_leak_check();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return exit_code;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline int Start(uv_loop_t* event_loop,
|
|
|
|
int argc, const char* const* argv,
|
|
|
|
int exec_argc, const char* const* exec_argv) {
|
|
|
|
Isolate::CreateParams params;
|
|
|
|
ArrayBufferAllocator allocator;
|
|
|
|
params.array_buffer_allocator = &allocator;
|
|
|
|
#ifdef NODE_ENABLE_VTUNE_PROFILING
|
|
|
|
params.code_event_handler = vTune::GetVtuneCodeEventHandler();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
Isolate* const isolate = Isolate::New(params);
|
|
|
|
if (isolate == nullptr)
|
|
|
|
return 12; // Signal internal error.
|
|
|
|
|
|
|
|
isolate->AddMessageListener(OnMessage);
|
|
|
|
isolate->SetAbortOnUncaughtExceptionCallback(ShouldAbortOnUncaughtException);
|
|
|
|
isolate->SetAutorunMicrotasks(false);
|
|
|
|
isolate->SetFatalErrorHandler(OnFatalError);
|
|
|
|
|
|
|
|
if (track_heap_objects) {
|
|
|
|
isolate->GetHeapProfiler()->StartTrackingHeapObjects(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
Mutex::ScopedLock scoped_lock(node_isolate_mutex);
|
|
|
|
CHECK_EQ(node_isolate, nullptr);
|
|
|
|
node_isolate = isolate;
|
|
|
|
}
|
|
|
|
|
|
|
|
int exit_code;
|
|
|
|
{
|
|
|
|
Locker locker(isolate);
|
|
|
|
Isolate::Scope isolate_scope(isolate);
|
|
|
|
HandleScope handle_scope(isolate);
|
|
|
|
IsolateData isolate_data(isolate, event_loop, allocator.zero_fill_field());
|
|
|
|
exit_code = Start(isolate, &isolate_data, argc, argv, exec_argc, exec_argv);
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
Mutex::ScopedLock scoped_lock(node_isolate_mutex);
|
|
|
|
CHECK_EQ(node_isolate, isolate);
|
|
|
|
node_isolate = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
isolate->Dispose();
|
|
|
|
|
|
|
|
return exit_code;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Start(int argc, char** argv) {
|
|
|
|
atexit([] () { uv_tty_reset_mode(); });
|
|
|
|
PlatformInit();
|
|
|
|
|
|
|
|
CHECK_GT(argc, 0);
|
|
|
|
|
|
|
|
// Hack around with the argv pointer. Used for process.title = "blah".
|
|
|
|
argv = uv_setup_args(argc, argv);
|
|
|
|
|
|
|
|
// This needs to run *before* V8::Initialize(). The const_cast is not
|
|
|
|
// optional, in case you're wondering.
|
|
|
|
int exec_argc;
|
|
|
|
const char** exec_argv;
|
|
|
|
Init(&argc, const_cast<const char**>(argv), &exec_argc, &exec_argv);
|
|
|
|
|
|
|
|
#if HAVE_OPENSSL
|
|
|
|
{
|
|
|
|
std::string extra_ca_certs;
|
|
|
|
if (SafeGetenv("NODE_EXTRA_CA_CERTS", &extra_ca_certs))
|
|
|
|
crypto::UseExtraCaCerts(extra_ca_certs);
|
|
|
|
}
|
|
|
|
#ifdef NODE_FIPS_MODE
|
|
|
|
// In the case of FIPS builds we should make sure
|
|
|
|
// the random source is properly initialized first.
|
|
|
|
OPENSSL_init();
|
|
|
|
#endif // NODE_FIPS_MODE
|
|
|
|
// V8 on Windows doesn't have a good source of entropy. Seed it from
|
|
|
|
// OpenSSL's pool.
|
|
|
|
V8::SetEntropySource(crypto::EntropySource);
|
|
|
|
#endif // HAVE_OPENSSL
|
|
|
|
|
|
|
|
v8_platform.Initialize(v8_thread_pool_size);
|
|
|
|
// Enable tracing when argv has --trace-events-enabled.
|
|
|
|
if (trace_enabled) {
|
|
|
|
fprintf(stderr, "Warning: Trace event is an experimental feature "
|
|
|
|
"and could change at any time.\n");
|
|
|
|
v8_platform.StartTracingAgent();
|
|
|
|
}
|
|
|
|
V8::Initialize();
|
|
|
|
v8_initialized = true;
|
|
|
|
const int exit_code =
|
|
|
|
Start(uv_default_loop(), argc, argv, exec_argc, exec_argv);
|
|
|
|
if (trace_enabled) {
|
|
|
|
v8_platform.StopTracingAgent();
|
|
|
|
}
|
|
|
|
v8_initialized = false;
|
|
|
|
V8::Dispose();
|
|
|
|
|
|
|
|
v8_platform.Dispose();
|
|
|
|
|
|
|
|
delete[] exec_argv;
|
|
|
|
exec_argv = nullptr;
|
|
|
|
|
|
|
|
return exit_code;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
} // namespace node
|
|
|
|
|
|
|
|
#if !HAVE_INSPECTOR
|
|
|
|
static void InitEmptyBindings() {}
|
|
|
|
|
|
|
|
NODE_MODULE_CONTEXT_AWARE_BUILTIN(inspector, InitEmptyBindings)
|
|
|
|
#endif // !HAVE_INSPECTOR
|