#include "env.h" #include "allocated_buffer-inl.h" #include "async_wrap.h" #include "base_object-inl.h" #include "debug_utils-inl.h" #include "diagnosticfilename-inl.h" #include "memory_tracker-inl.h" #include "node_buffer.h" #include "node_context_data.h" #include "node_errors.h" #include "node_internals.h" #include "node_options-inl.h" #include "node_process.h" #include "node_v8_platform-inl.h" #include "node_worker.h" #include "req_wrap-inl.h" #include "stream_base.h" #include "tracing/agent.h" #include "tracing/traced_value.h" #include "util-inl.h" #include "v8-profiler.h" #include #include #include #include #include #include #include namespace node { using errors::TryCatchScope; using v8::Boolean; using v8::Context; using v8::EmbedderGraph; using v8::Function; using v8::FunctionTemplate; using v8::HandleScope; using v8::Integer; using v8::Isolate; using v8::Local; using v8::MaybeLocal; using v8::NewStringType; using v8::Number; using v8::Object; using v8::Private; using v8::Script; using v8::SnapshotCreator; using v8::StackTrace; using v8::String; using v8::Symbol; using v8::TracingController; using v8::TryCatch; using v8::Undefined; using v8::Value; using worker::Worker; int const Environment::kNodeContextTag = 0x6e6f64; void* const Environment::kNodeContextTagPtr = const_cast( static_cast(&Environment::kNodeContextTag)); std::vector IsolateData::Serialize(SnapshotCreator* creator) { Isolate* isolate = creator->GetIsolate(); std::vector indexes; HandleScope handle_scope(isolate); // XXX(joyeecheung): technically speaking, the indexes here should be // consecutive and we could just return a range instead of an array, // but that's not part of the V8 API contract so we use an array // just to be safe. #define VP(PropertyName, StringValue) V(Private, PropertyName) #define VY(PropertyName, StringValue) V(Symbol, PropertyName) #define VS(PropertyName, StringValue) V(String, PropertyName) #define V(TypeName, PropertyName) \ indexes.push_back(creator->AddData(PropertyName##_.Get(isolate))); PER_ISOLATE_PRIVATE_SYMBOL_PROPERTIES(VP) PER_ISOLATE_SYMBOL_PROPERTIES(VY) PER_ISOLATE_STRING_PROPERTIES(VS) #undef V #undef VY #undef VS #undef VP for (size_t i = 0; i < AsyncWrap::PROVIDERS_LENGTH; i++) indexes.push_back(creator->AddData(async_wrap_provider(i))); return indexes; } void IsolateData::DeserializeProperties(const std::vector* indexes) { size_t i = 0; HandleScope handle_scope(isolate_); #define VP(PropertyName, StringValue) V(Private, PropertyName) #define VY(PropertyName, StringValue) V(Symbol, PropertyName) #define VS(PropertyName, StringValue) V(String, PropertyName) #define V(TypeName, PropertyName) \ do { \ MaybeLocal maybe_field = \ isolate_->GetDataFromSnapshotOnce((*indexes)[i++]); \ Local field; \ if (!maybe_field.ToLocal(&field)) { \ fprintf(stderr, "Failed to deserialize " #PropertyName "\n"); \ } \ PropertyName##_.Set(isolate_, field); \ } while (0); PER_ISOLATE_PRIVATE_SYMBOL_PROPERTIES(VP) PER_ISOLATE_SYMBOL_PROPERTIES(VY) PER_ISOLATE_STRING_PROPERTIES(VS) #undef V #undef VY #undef VS #undef VP for (size_t j = 0; j < AsyncWrap::PROVIDERS_LENGTH; j++) { MaybeLocal maybe_field = isolate_->GetDataFromSnapshotOnce((*indexes)[i++]); Local field; if (!maybe_field.ToLocal(&field)) { fprintf(stderr, "Failed to deserialize AsyncWrap provider %zu\n", j); } async_wrap_providers_[j].Set(isolate_, field); } } void IsolateData::CreateProperties() { // Create string and private symbol properties as internalized one byte // strings after the platform is properly initialized. // // Internalized because it makes property lookups a little faster and // because the string is created in the old space straight away. It's going // to end up in the old space sooner or later anyway but now it doesn't go // through v8::Eternal's new space handling first. // // One byte because our strings are ASCII and we can safely skip V8's UTF-8 // decoding step. HandleScope handle_scope(isolate_); #define V(PropertyName, StringValue) \ PropertyName##_.Set( \ isolate_, \ Private::New(isolate_, \ String::NewFromOneByte( \ isolate_, \ reinterpret_cast(StringValue), \ NewStringType::kInternalized, \ sizeof(StringValue) - 1) \ .ToLocalChecked())); PER_ISOLATE_PRIVATE_SYMBOL_PROPERTIES(V) #undef V #define V(PropertyName, StringValue) \ PropertyName##_.Set( \ isolate_, \ Symbol::New(isolate_, \ String::NewFromOneByte( \ isolate_, \ reinterpret_cast(StringValue), \ NewStringType::kInternalized, \ sizeof(StringValue) - 1) \ .ToLocalChecked())); PER_ISOLATE_SYMBOL_PROPERTIES(V) #undef V #define V(PropertyName, StringValue) \ PropertyName##_.Set( \ isolate_, \ String::NewFromOneByte(isolate_, \ reinterpret_cast(StringValue), \ NewStringType::kInternalized, \ sizeof(StringValue) - 1) \ .ToLocalChecked()); PER_ISOLATE_STRING_PROPERTIES(V) #undef V // Create all the provider strings that will be passed to JS. Place them in // an array so the array index matches the PROVIDER id offset. This way the // strings can be retrieved quickly. #define V(Provider) \ async_wrap_providers_[AsyncWrap::PROVIDER_ ## Provider].Set( \ isolate_, \ String::NewFromOneByte( \ isolate_, \ reinterpret_cast(#Provider), \ NewStringType::kInternalized, \ sizeof(#Provider) - 1).ToLocalChecked()); NODE_ASYNC_PROVIDER_TYPES(V) #undef V } IsolateData::IsolateData(Isolate* isolate, uv_loop_t* event_loop, MultiIsolatePlatform* platform, ArrayBufferAllocator* node_allocator, const std::vector* indexes) : isolate_(isolate), event_loop_(event_loop), node_allocator_(node_allocator == nullptr ? nullptr : node_allocator->GetImpl()), platform_(platform) { options_.reset( new PerIsolateOptions(*(per_process::cli_options->per_isolate))); if (indexes == nullptr) { CreateProperties(); } else { DeserializeProperties(indexes); } } void IsolateData::MemoryInfo(MemoryTracker* tracker) const { #define V(PropertyName, StringValue) \ tracker->TrackField(#PropertyName, PropertyName()); PER_ISOLATE_SYMBOL_PROPERTIES(V) PER_ISOLATE_STRING_PROPERTIES(V) #undef V tracker->TrackField("async_wrap_providers", async_wrap_providers_); if (node_allocator_ != nullptr) { tracker->TrackFieldWithSize( "node_allocator", sizeof(*node_allocator_), "NodeArrayBufferAllocator"); } tracker->TrackFieldWithSize( "platform", sizeof(*platform_), "MultiIsolatePlatform"); // TODO(joyeecheung): implement MemoryRetainer in the option classes. } void TrackingTraceStateObserver::UpdateTraceCategoryState() { if (!env_->owns_process_state() || !env_->can_call_into_js()) { // Ideally, we’d have a consistent story that treats all threads/Environment // instances equally here. However, tracing is essentially global, and this // callback is called from whichever thread calls `StartTracing()` or // `StopTracing()`. The only way to do this in a threadsafe fashion // seems to be only tracking this from the main thread, and only allowing // these state modifications from the main thread. return; } bool async_hooks_enabled = (*(TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED( TRACING_CATEGORY_NODE1(async_hooks)))) != 0; Isolate* isolate = env_->isolate(); HandleScope handle_scope(isolate); Local cb = env_->trace_category_state_function(); if (cb.IsEmpty()) return; TryCatchScope try_catch(env_); try_catch.SetVerbose(true); Local args[] = {Boolean::New(isolate, async_hooks_enabled)}; USE(cb->Call(env_->context(), Undefined(isolate), arraysize(args), args)); } void Environment::CreateProperties() { HandleScope handle_scope(isolate_); Local ctx = context(); { Context::Scope context_scope(ctx); Local templ = FunctionTemplate::New(isolate()); templ->InstanceTemplate()->SetInternalFieldCount( BaseObject::kInternalFieldCount); templ->Inherit(BaseObject::GetConstructorTemplate(this)); set_binding_data_ctor_template(templ); } // Store primordials setup by the per-context script in the environment. Local per_context_bindings = GetPerContextExports(ctx).ToLocalChecked(); Local primordials = per_context_bindings->Get(ctx, primordials_string()).ToLocalChecked(); CHECK(primordials->IsObject()); set_primordials(primordials.As()); Local process_object = node::CreateProcessObject(this).FromMaybe(Local()); set_process_object(process_object); } std::string GetExecPath(const std::vector& argv) { char exec_path_buf[2 * PATH_MAX]; size_t exec_path_len = sizeof(exec_path_buf); std::string exec_path; if (uv_exepath(exec_path_buf, &exec_path_len) == 0) { exec_path = std::string(exec_path_buf, exec_path_len); } else { exec_path = argv[0]; } // On OpenBSD process.execPath will be relative unless we // get the full path before process.execPath is used. #if defined(__OpenBSD__) uv_fs_t req; req.ptr = nullptr; if (0 == uv_fs_realpath(nullptr, &req, exec_path.c_str(), nullptr)) { CHECK_NOT_NULL(req.ptr); exec_path = std::string(static_cast(req.ptr)); } uv_fs_req_cleanup(&req); #endif return exec_path; } Environment::Environment(IsolateData* isolate_data, Isolate* isolate, const std::vector& args, const std::vector& exec_args, const EnvSerializeInfo* env_info, EnvironmentFlags::Flags flags, ThreadId thread_id) : isolate_(isolate), isolate_data_(isolate_data), async_hooks_(isolate, MAYBE_FIELD_PTR(env_info, async_hooks)), immediate_info_(isolate, MAYBE_FIELD_PTR(env_info, immediate_info)), tick_info_(isolate, MAYBE_FIELD_PTR(env_info, tick_info)), timer_base_(uv_now(isolate_data->event_loop())), exec_argv_(exec_args), argv_(args), exec_path_(GetExecPath(args)), should_abort_on_uncaught_toggle_( isolate_, 1, MAYBE_FIELD_PTR(env_info, should_abort_on_uncaught_toggle)), stream_base_state_(isolate_, StreamBase::kNumStreamBaseStateFields, MAYBE_FIELD_PTR(env_info, stream_base_state)), environment_start_time_(PERFORMANCE_NOW()), flags_(flags), thread_id_(thread_id.id == static_cast(-1) ? AllocateEnvironmentThreadId().id : thread_id.id) { // We'll be creating new objects so make sure we've entered the context. HandleScope handle_scope(isolate); // Set some flags if only kDefaultFlags was passed. This can make API version // transitions easier for embedders. if (flags_ & EnvironmentFlags::kDefaultFlags) { flags_ = flags_ | EnvironmentFlags::kOwnsProcessState | EnvironmentFlags::kOwnsInspector; } set_env_vars(per_process::system_environment); // TODO(joyeecheung): pass Isolate* and env_vars to it instead of the entire // env enabled_debug_list_.Parse(this); // We create new copies of the per-Environment option sets, so that it is // easier to modify them after Environment creation. The defaults are // part of the per-Isolate option set, for which in turn the defaults are // part of the per-process option set. options_.reset(new EnvironmentOptions(*isolate_data->options()->per_env)); inspector_host_port_.reset( new ExclusiveAccess(options_->debug_options().host_port)); if (!(flags_ & EnvironmentFlags::kOwnsProcessState)) { set_abort_on_uncaught_exception(false); } #if HAVE_INSPECTOR // We can only create the inspector agent after having cloned the options. inspector_agent_ = std::make_unique(this); #endif if (tracing::AgentWriterHandle* writer = GetTracingAgentWriter()) { trace_state_observer_ = std::make_unique(this); if (TracingController* tracing_controller = writer->GetTracingController()) tracing_controller->AddTraceStateObserver(trace_state_observer_.get()); } destroy_async_id_list_.reserve(512); performance_state_ = std::make_unique( isolate, MAYBE_FIELD_PTR(env_info, performance_state)); if (*TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED( TRACING_CATEGORY_NODE1(environment)) != 0) { auto traced_value = tracing::TracedValue::Create(); traced_value->BeginArray("args"); for (const std::string& arg : args) traced_value->AppendString(arg); traced_value->EndArray(); traced_value->BeginArray("exec_args"); for (const std::string& arg : exec_args) traced_value->AppendString(arg); traced_value->EndArray(); TRACE_EVENT_NESTABLE_ASYNC_BEGIN1(TRACING_CATEGORY_NODE1(environment), "Environment", this, "args", std::move(traced_value)); } // This adjusts the return value of base_object_count() so that tests that // check the count do not have to account for internally created BaseObjects. initial_base_object_count_ = base_object_count(); } Environment::Environment(IsolateData* isolate_data, Local context, const std::vector& args, const std::vector& exec_args, const EnvSerializeInfo* env_info, EnvironmentFlags::Flags flags, ThreadId thread_id) : Environment(isolate_data, context->GetIsolate(), args, exec_args, env_info, flags, thread_id) { InitializeMainContext(context, env_info); } void Environment::InitializeMainContext(Local context, const EnvSerializeInfo* env_info) { context_.Reset(context->GetIsolate(), context); AssignToContext(context, ContextInfo("")); if (env_info != nullptr) { DeserializeProperties(env_info); } else { CreateProperties(); } if (options_->no_force_async_hooks_checks) { async_hooks_.no_force_checks(); } // By default, always abort when --abort-on-uncaught-exception was passed. should_abort_on_uncaught_toggle_[0] = 1; performance_state_->Mark(performance::NODE_PERFORMANCE_MILESTONE_ENVIRONMENT, environment_start_time_); performance_state_->Mark(performance::NODE_PERFORMANCE_MILESTONE_NODE_START, per_process::node_start_time); performance_state_->Mark(performance::NODE_PERFORMANCE_MILESTONE_V8_START, performance::performance_v8_start); // This adjusts the return value of base_object_count() so that tests that // check the count do not have to account for internally created BaseObjects. initial_base_object_count_ = base_object_count(); } Environment::~Environment() { if (Environment** interrupt_data = interrupt_data_.load()) { // There are pending RequestInterrupt() callbacks. Tell them not to run, // then force V8 to run interrupts by compiling and running an empty script // so as not to leak memory. *interrupt_data = nullptr; Isolate::AllowJavascriptExecutionScope allow_js_here(isolate()); HandleScope handle_scope(isolate()); TryCatch try_catch(isolate()); Context::Scope context_scope(context()); #ifdef DEBUG bool consistency_check = false; isolate()->RequestInterrupt([](Isolate*, void* data) { *static_cast(data) = true; }, &consistency_check); #endif Local