// 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_buffer.h" #include "node_constants.h" #include "node_javascript.h" #include "node_code_cache.h" #include "node_platform.h" #include "node_version.h" #include "node_internals.h" #include "node_revert.h" #include "node_perf.h" #include "node_context_data.h" #include "tracing/traced_value.h" #if HAVE_OPENSSL #include "node_crypto.h" #endif #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 #include "ares.h" #include "async_wrap-inl.h" #include "env-inl.h" #include "handle_wrap.h" #include "http_parser.h" #include "nghttp2/nghttp2ver.h" #include "req_wrap-inl.h" #include "string_bytes.h" #include "tracing/agent.h" #include "tracing/node_trace_writer.h" #include "util.h" #include "uv.h" #if NODE_USE_V8_PLATFORM #include "libplatform/libplatform.h" #endif // NODE_USE_V8_PLATFORM #include "v8-profiler.h" #include "zlib.h" #ifdef NODE_ENABLE_VTUNE_PROFILING #include "../deps/v8/src/third_party/vtune/v8-vtune.h" #endif #include #include // _O_RDWR #include // PATH_MAX #include #include #include #include #include #include #include #include #if defined(NODE_HAVE_I18N_SUPPORT) #include #endif #if defined(LEAK_SANITIZER) #include #endif #if defined(_MSC_VER) #include #include #define umask _umask typedef int mode_t; #else #include #include // getrlimit, setrlimit #include // setuid, getuid #endif #if defined(__POSIX__) && !defined(__ANDROID__) && !defined(__CloudABI__) #include // getpwnam() #include // getgrnam() #endif #if defined(__POSIX__) #include #endif // This is used to load built-in modules. Instead of using // __attribute__((constructor)), we call the _register_ // function for each built-in modules explicitly in // node::RegisterBuiltinModules(). This is only forward declaration. // The definitions are in each module's implementation when calling // the NODE_BUILTIN_MODULE_CONTEXT_AWARE. #define V(modname) void _register_##modname(); NODE_BUILTIN_MODULES(V) #undef V namespace node { using options_parser::kAllowedInEnvironment; using options_parser::kDisallowedInEnvironment; using v8::Array; using v8::ArrayBuffer; using v8::Boolean; using v8::Context; using v8::DEFAULT; using v8::DontEnum; using v8::EscapableHandleScope; using v8::Exception; using v8::Function; using v8::FunctionCallbackInfo; using v8::HandleScope; using v8::Int32; using v8::Integer; using v8::Isolate; using v8::Just; using v8::Local; using v8::Locker; using v8::Maybe; using v8::MaybeLocal; using v8::Message; using v8::MicrotasksPolicy; using v8::Name; using v8::NamedPropertyHandlerConfiguration; using v8::NewStringType; using v8::None; using v8::Nothing; using v8::Null; using v8::Number; using v8::Object; using v8::ObjectTemplate; using v8::Promise; using v8::PropertyAttribute; using v8::ReadOnly; using v8::Script; using v8::ScriptCompiler; using v8::ScriptOrigin; using v8::SealHandleScope; using v8::SideEffectType; using v8::String; using v8::TracingController; using v8::TryCatch; using v8::Undefined; using v8::V8; using v8::Value; static bool v8_is_profiling = false; static bool node_is_initialized = false; static uv_once_t init_modpending_once = UV_ONCE_INIT; static uv_key_t thread_local_modpending; static node_module* modlist_builtin; static node_module* modlist_internal; static node_module* modlist_linked; static node_module* modlist_addon; // Bit flag used to track security reverts (see node_revert.h) unsigned int reverted = 0; bool v8_initialized = false; bool linux_at_secure = false; // process-relative uptime base, initialized at start-up double prog_start_time; Mutex per_process_opts_mutex; std::shared_ptr per_process_opts { new PerProcessOptions() }; static Mutex node_isolate_mutex; static Isolate* node_isolate; // Ensures that __metadata trace events are only emitted // when tracing is enabled. class NodeTraceStateObserver : public TracingController::TraceStateObserver { public: void OnTraceEnabled() override { char name_buffer[512]; if (uv_get_process_title(name_buffer, sizeof(name_buffer)) == 0) { // Only emit the metadata event if the title can be retrieved // successfully. Ignore it otherwise. TRACE_EVENT_METADATA1("__metadata", "process_name", "name", TRACE_STR_COPY(name_buffer)); } TRACE_EVENT_METADATA1("__metadata", "version", "node", NODE_VERSION_STRING); TRACE_EVENT_METADATA1("__metadata", "thread_name", "name", "JavaScriptMainThread"); auto trace_process = tracing::TracedValue::Create(); trace_process->BeginDictionary("versions"); const char http_parser_version[] = NODE_STRINGIFY(HTTP_PARSER_VERSION_MAJOR) "." NODE_STRINGIFY(HTTP_PARSER_VERSION_MINOR) "." NODE_STRINGIFY(HTTP_PARSER_VERSION_PATCH); const char node_napi_version[] = NODE_STRINGIFY(NAPI_VERSION); const char node_modules_version[] = NODE_STRINGIFY(NODE_MODULE_VERSION); trace_process->SetString("http_parser", http_parser_version); trace_process->SetString("node", NODE_VERSION_STRING); trace_process->SetString("v8", V8::GetVersion()); trace_process->SetString("uv", uv_version_string()); trace_process->SetString("zlib", ZLIB_VERSION); trace_process->SetString("ares", ARES_VERSION_STR); trace_process->SetString("modules", node_modules_version); trace_process->SetString("nghttp2", NGHTTP2_VERSION); trace_process->SetString("napi", node_napi_version); #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; } } trace_process->SetString("openssl", std::string(&OPENSSL_VERSION_TEXT[i], j - i)); } #endif trace_process->EndDictionary(); trace_process->SetString("arch", NODE_ARCH); trace_process->SetString("platform", NODE_PLATFORM); trace_process->BeginDictionary("release"); trace_process->SetString("name", NODE_RELEASE); #if NODE_VERSION_IS_LTS trace_process->SetString("lts", NODE_VERSION_LTS_CODENAME); #endif trace_process->EndDictionary(); TRACE_EVENT_METADATA1("__metadata", "node", "process", std::move(trace_process)); // This only runs the first time tracing is enabled controller_->RemoveTraceStateObserver(this); delete this; } void OnTraceDisabled() override { // Do nothing here. This should never be called because the // observer removes itself when OnTraceEnabled() is called. UNREACHABLE(); } explicit NodeTraceStateObserver(TracingController* controller) : controller_(controller) {} ~NodeTraceStateObserver() override {} private: TracingController* controller_; }; static struct { #if NODE_USE_V8_PLATFORM void Initialize(int thread_pool_size) { tracing_agent_.reset(new tracing::Agent()); auto controller = tracing_agent_->GetTracingController(); controller->AddTraceStateObserver(new NodeTraceStateObserver(controller)); tracing::TraceEventHelper::SetTracingController(controller); StartTracingAgent(); platform_ = new NodePlatform(thread_pool_size, controller); V8::InitializePlatform(platform_); } void Dispose() { tracing_agent_.reset(nullptr); platform_->Shutdown(); delete platform_; platform_ = nullptr; } void DrainVMTasks(Isolate* isolate) { platform_->DrainTasks(isolate); } void CancelVMTasks(Isolate* isolate) { platform_->CancelPendingDelayedTasks(isolate); } #if HAVE_INSPECTOR bool StartInspector(Environment* env, const char* script_path, std::shared_ptr 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( script_path == nullptr ? "" : script_path, options, true); } bool InspectorStarted(Environment* env) { return env->inspector_agent()->IsListening(); } #endif // HAVE_INSPECTOR void StartTracingAgent() { if (per_process_opts->trace_event_categories.empty()) { tracing_file_writer_ = tracing_agent_->DefaultHandle(); } else { tracing_file_writer_ = tracing_agent_->AddClient( ParseCommaSeparatedSet(per_process_opts->trace_event_categories), std::unique_ptr( new tracing::NodeTraceWriter( per_process_opts->trace_event_file_pattern)), tracing::Agent::kUseDefaultCategories); } } void StopTracingAgent() { tracing_file_writer_.reset(); } tracing::AgentWriterHandle* GetTracingAgentWriter() { return &tracing_file_writer_; } NodePlatform* Platform() { return platform_; } std::unique_ptr tracing_agent_; tracing::AgentWriterHandle tracing_file_writer_; NodePlatform* platform_; #else // !NODE_USE_V8_PLATFORM void Initialize(int thread_pool_size) {} void Dispose() {} void DrainVMTasks(Isolate* isolate) {} void CancelVMTasks(Isolate* isolate) {} bool StartInspector(Environment* env, const char* script_path, const DebugOptions& options) { env->ThrowError("Node compiled with NODE_USE_V8_PLATFORM=0"); return true; } void StartTracingAgent() { if (!trace_enabled_categories.empty()) { fprintf(stderr, "Node compiled with NODE_USE_V8_PLATFORM=0, " "so event tracing is not available.\n"); } } void StopTracingAgent() {} tracing::AgentWriterHandle* GetTracingAgentWriter() { return nullptr; } NodePlatform* Platform() { return nullptr; } #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 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 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 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); } 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 ""; } } // Look up environment variable unless running as setuid root. bool SafeGetenv(const char* key, std::string* text) { #if !defined(__CloudABI__) && !defined(_WIN32) if (linux_at_secure || getuid() != geteuid() || getgid() != getegid()) goto fail; #endif { Mutex::ScopedLock lock(environ_mutex); if (const char* value = getenv(key)) { *text = value; return true; } } fail: text->clear(); return false; } void* ArrayBufferAllocator::Allocate(size_t size) { if (zero_fill_field_ || zero_fill_all_buffers) return UncheckedCalloc(size); else return UncheckedMalloc(size); } namespace { bool ShouldAbortOnUncaughtException(Isolate* isolate) { HandleScope scope(isolate); Environment* env = Environment::GetCurrent(isolate); return env != nullptr && env->should_abort_on_uncaught_toggle()[0] && !env->inside_should_not_abort_on_uncaught_scope(); } } // anonymous namespace void AddPromiseHook(Isolate* isolate, promise_hook_func fn, void* arg) { Environment* env = Environment::GetCurrent(isolate); CHECK_NOT_NULL(env); env->AddPromiseHook(fn, arg); } void AddEnvironmentCleanupHook(Isolate* isolate, void (*fun)(void* arg), void* arg) { Environment* env = Environment::GetCurrent(isolate); CHECK_NOT_NULL(env); env->AddCleanupHook(fun, arg); } void RemoveEnvironmentCleanupHook(Isolate* isolate, void (*fun)(void* arg), void* arg) { Environment* env = Environment::GetCurrent(isolate); CHECK_NOT_NULL(env); env->RemoveCleanupHook(fun, arg); } MaybeLocal InternalMakeCallback(Environment* env, Local recv, const Local callback, int argc, Local argv[], async_context asyncContext) { CHECK(!recv.IsEmpty()); InternalCallbackScope scope(env, recv, asyncContext); if (scope.Failed()) { return MaybeLocal(); } Local domain_cb = env->domain_callback(); MaybeLocal ret; if (asyncContext.async_id != 0 || domain_cb.IsEmpty() || recv.IsEmpty()) { ret = callback->Call(env->context(), recv, argc, argv); } else { std::vector> args(1 + argc); args[0] = callback; std::copy(&argv[0], &argv[argc], args.begin() + 1); ret = domain_cb->Call(env->context(), recv, args.size(), &args[0]); } if (ret.IsEmpty()) { scope.MarkAsFailed(); return MaybeLocal(); } scope.Close(); if (scope.Failed()) { return MaybeLocal(); } return ret; } // Public MakeCallback()s MaybeLocal MakeCallback(Isolate* isolate, Local recv, const char* method, int argc, Local argv[], async_context asyncContext) { Local method_string = String::NewFromUtf8(isolate, method, NewStringType::kNormal) .ToLocalChecked(); return MakeCallback(isolate, recv, method_string, argc, argv, asyncContext); } MaybeLocal MakeCallback(Isolate* isolate, Local recv, Local symbol, int argc, Local argv[], async_context asyncContext) { Local callback_v = recv->Get(symbol); if (callback_v.IsEmpty()) return Local(); if (!callback_v->IsFunction()) return Local(); Local callback = callback_v.As(); return MakeCallback(isolate, recv, callback, argc, argv, asyncContext); } MaybeLocal MakeCallback(Isolate* isolate, Local recv, Local callback, int argc, Local argv[], async_context asyncContext) { // 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()); CHECK_NOT_NULL(env); Context::Scope context_scope(env->context()); MaybeLocal ret = InternalMakeCallback(env, recv, callback, argc, argv, asyncContext); if (ret.IsEmpty() && env->makecallback_depth() == 0) { // This is only for legacy compatiblity and we may want to look into // removing/adjusting it. return Undefined(env->isolate()); } return ret; } // Legacy MakeCallback()s Local MakeCallback(Isolate* isolate, Local recv, const char* method, int argc, Local* argv) { EscapableHandleScope handle_scope(isolate); return handle_scope.Escape( MakeCallback(isolate, recv, method, argc, argv, {0, 0}) .FromMaybe(Local())); } Local MakeCallback(Isolate* isolate, Local recv, Local symbol, int argc, Local* argv) { EscapableHandleScope handle_scope(isolate); return handle_scope.Escape( MakeCallback(isolate, recv, symbol, argc, argv, {0, 0}) .FromMaybe(Local())); } Local MakeCallback(Isolate* isolate, Local recv, Local callback, int argc, Local* argv) { EscapableHandleScope handle_scope(isolate); return handle_scope.Escape( MakeCallback(isolate, recv, callback, argc, argv, {0, 0}) .FromMaybe(Local())); } bool IsExceptionDecorated(Environment* env, Local er) { if (!er.IsEmpty() && er->IsObject()) { Local err_obj = er.As(); auto maybe_value = err_obj->GetPrivate(env->context(), env->decorated_private_symbol()); Local decorated; return maybe_value.ToLocal(&decorated) && decorated->IsTrue(); } return false; } void AppendExceptionLine(Environment* env, Local er, Local message, enum ErrorHandlingMode mode) { if (message.IsEmpty()) return; HandleScope scope(env->isolate()); Local err_obj; if (!er.IsEmpty() && er->IsObject()) { err_obj = er.As(); } // Print (filename):(line number): (message). ScriptOrigin origin = message->GetScriptOrigin(); node::Utf8Value filename(env->isolate(), message->GetScriptResourceName()); const char* filename_string = *filename; int linenum = message->GetLineNumber(env->context()).FromJust(); // Print line of source code. MaybeLocal source_line_maybe = message->GetSourceLine(env->context()); node::Utf8Value sourceline(env->isolate(), source_line_maybe.ToLocalChecked()); const char* sourceline_string = *sourceline; if (strstr(sourceline_string, "node-do-not-add-exception-line") != nullptr) return; // 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 script_start = (linenum - origin.ResourceLineOffset()->Value()) == 1 ? origin.ResourceColumnOffset()->Value() : 0; int start = message->GetStartColumn(env->context()).FromMaybe(0); int end = message->GetEndColumn(env->context()).FromMaybe(0); if (start >= script_start) { CHECK_GE(end, start); start -= script_start; end -= script_start; } 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 arrow_str = String::NewFromUtf8(env->isolate(), arrow, NewStringType::kNormal).ToLocalChecked(); 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; Mutex::ScopedLock lock(process_mutex); 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)); } void ReportException(Environment* env, Local er, Local message) { CHECK(!er.IsEmpty()); HandleScope scope(env->isolate()); if (message.IsEmpty()) message = Exception::CreateMessage(env->isolate(), er); AppendExceptionLine(env, er, message, FATAL_ERROR); Local trace_value; Local arrow; const bool decorated = IsExceptionDecorated(env, er); if (er->IsUndefined() || er->IsNull()) { trace_value = Undefined(env->isolate()); } else { Local err_obj = er->ToObject(env->context()).ToLocalChecked(); 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 message; Local name; if (er->IsObject()) { Local err_obj = er.As(); 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(env->isolate(), er); PrintErrorString("%s\n", *message ? *message : ""); } 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); #if HAVE_INSPECTOR env->inspector_agent()->FatalException(er, message); #endif } 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 MaybeLocal ExecuteString(Environment* env, Local source, Local 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