#include #if HAVE_OPENSSL #include "crypto/crypto_util.h" #endif // HAVE_OPENSSL #include "env.h" #include "env_properties.h" #include "node.h" #include "node_builtins.h" #include "node_context_data.h" #include "node_debug.h" #include "node_errors.h" #include "node_exit_code.h" #include "node_internals.h" #include "node_options-inl.h" #include "node_platform.h" #include "node_realm-inl.h" #include "node_shadow_realm.h" #include "node_snapshot_builder.h" #include "node_v8_platform-inl.h" #include "node_wasm_web_api.h" #include "uv.h" #ifdef NODE_ENABLE_VTUNE_PROFILING #include "../deps/v8/third_party/vtune/v8-vtune.h" #endif #if HAVE_INSPECTOR #include "inspector/worker_inspector.h" // ParentInspectorHandle #endif #include "v8-cppgc.h" namespace node { using errors::TryCatchScope; using v8::Array; using v8::Boolean; using v8::Context; using v8::CppHeap; using v8::CppHeapCreateParams; using v8::EscapableHandleScope; using v8::Function; using v8::FunctionCallbackInfo; using v8::HandleScope; using v8::Isolate; using v8::IsolateGroup; using v8::Just; using v8::JustVoid; using v8::Local; using v8::Maybe; using v8::MaybeLocal; using v8::Nothing; using v8::Null; using v8::Object; using v8::ObjectTemplate; using v8::Private; using v8::PropertyDescriptor; using v8::SealHandleScope; using v8::String; using v8::Value; bool AllowWasmCodeGenerationCallback(Local context, Local) { Local wasm_code_gen = context->GetEmbedderData(ContextEmbedderIndex::kAllowWasmCodeGeneration); return wasm_code_gen->IsUndefined() || wasm_code_gen->IsTrue(); } bool ShouldAbortOnUncaughtException(Isolate* isolate) { DebugSealHandleScope scope(isolate); Environment* env = Environment::GetCurrent(isolate); return env != nullptr && (env->is_main_thread() || !env->is_stopping()) && env->abort_on_uncaught_exception() && env->should_abort_on_uncaught_toggle()[0] && !env->inside_should_not_abort_on_uncaught_scope(); } MaybeLocal PrepareStackTraceCallback(Local context, Local exception, Local trace) { Environment* env = Environment::GetCurrent(context); if (env == nullptr) { return exception->ToString(context).FromMaybe(Local()); } Realm* realm = Realm::GetCurrent(context); Local prepare; if (realm != nullptr) { // If we are in a Realm, call the realm specific prepareStackTrace callback // to avoid passing the JS objects (the exception and trace) across the // realm boundary with the `Error.prepareStackTrace` override. prepare = realm->prepare_stack_trace_callback(); } else { // The context is created with ContextifyContext, call the principal // realm's prepareStackTrace callback. prepare = env->principal_realm()->prepare_stack_trace_callback(); } if (prepare.IsEmpty()) { return exception->ToString(context).FromMaybe(Local()); } Local args[] = { context->Global(), exception, trace, }; // This TryCatch + Rethrow is required by V8 due to details around exception // handling there. For C++ callbacks, V8 expects a scheduled exception (which // is what ReThrow gives us). Just returning the empty MaybeLocal would leave // us with a pending exception. TryCatchScope try_catch(env); MaybeLocal result = prepare->Call(context, Undefined(env->isolate()), arraysize(args), args); if (try_catch.HasCaught() && !try_catch.HasTerminated()) { try_catch.ReThrow(); } return result; } void* NodeArrayBufferAllocator::Allocate(size_t size) { void* ret; COUNT_GENERIC_USAGE("NodeArrayBufferAllocator.Allocate.ZeroFilled"); ret = allocator_->Allocate(size); if (ret != nullptr) [[likely]] { total_mem_usage_.fetch_add(size, std::memory_order_relaxed); } return ret; } void* NodeArrayBufferAllocator::AllocateUninitialized(size_t size) { COUNT_GENERIC_USAGE("NodeArrayBufferAllocator.Allocate.Uninitialized"); void* ret = allocator_->AllocateUninitialized(size); if (ret != nullptr) [[likely]] { total_mem_usage_.fetch_add(size, std::memory_order_relaxed); } return ret; } void NodeArrayBufferAllocator::Free(void* data, size_t size) { total_mem_usage_.fetch_sub(size, std::memory_order_relaxed); allocator_->Free(data, size); } DebuggingArrayBufferAllocator::~DebuggingArrayBufferAllocator() { CHECK(allocations_.empty()); } void* DebuggingArrayBufferAllocator::Allocate(size_t size) { Mutex::ScopedLock lock(mutex_); void* data = NodeArrayBufferAllocator::Allocate(size); RegisterPointerInternal(data, size); return data; } void* DebuggingArrayBufferAllocator::AllocateUninitialized(size_t size) { Mutex::ScopedLock lock(mutex_); void* data = NodeArrayBufferAllocator::AllocateUninitialized(size); RegisterPointerInternal(data, size); return data; } void DebuggingArrayBufferAllocator::Free(void* data, size_t size) { Mutex::ScopedLock lock(mutex_); UnregisterPointerInternal(data, size); NodeArrayBufferAllocator::Free(data, size); } void DebuggingArrayBufferAllocator::RegisterPointer(void* data, size_t size) { Mutex::ScopedLock lock(mutex_); NodeArrayBufferAllocator::RegisterPointer(data, size); RegisterPointerInternal(data, size); } void DebuggingArrayBufferAllocator::UnregisterPointer(void* data, size_t size) { Mutex::ScopedLock lock(mutex_); NodeArrayBufferAllocator::UnregisterPointer(data, size); UnregisterPointerInternal(data, size); } void DebuggingArrayBufferAllocator::UnregisterPointerInternal(void* data, size_t size) { if (data == nullptr) return; auto it = allocations_.find(data); CHECK_NE(it, allocations_.end()); if (size > 0) { // We allow allocations with size 1 for 0-length buffers to avoid having // to deal with nullptr values. CHECK_EQ(it->second, size); } allocations_.erase(it); } void DebuggingArrayBufferAllocator::RegisterPointerInternal(void* data, size_t size) { if (data == nullptr) return; CHECK_EQ(allocations_.count(data), 0); allocations_[data] = size; } std::unique_ptr ArrayBufferAllocator::Create(bool debug) { if (debug || per_process::cli_options->debug_arraybuffer_allocations) return std::make_unique(); else return std::make_unique(); } ArrayBufferAllocator* CreateArrayBufferAllocator() { return ArrayBufferAllocator::Create().release(); } void FreeArrayBufferAllocator(ArrayBufferAllocator* allocator) { delete allocator; } void SetIsolateCreateParamsForNode(Isolate::CreateParams* params) { const uint64_t constrained_memory = uv_get_constrained_memory(); const uint64_t total_memory = constrained_memory > 0 ? std::min(uv_get_total_memory(), constrained_memory) : uv_get_total_memory(); if (total_memory > 0 && params->constraints.max_old_generation_size_in_bytes() == 0) { // V8 defaults to 700MB or 1.4GB on 32 and 64 bit platforms respectively. // This default is based on browser use-cases. Tell V8 to configure the // heap based on the actual physical memory. params->constraints.ConfigureDefaults(total_memory, 0); } #ifdef NODE_ENABLE_VTUNE_PROFILING params->code_event_handler = vTune::GetVtuneCodeEventHandler(); #endif } void SetIsolateErrorHandlers(v8::Isolate* isolate, const IsolateSettings& s) { if (s.flags & MESSAGE_LISTENER_WITH_ERROR_LEVEL) isolate->AddMessageListenerWithErrorLevel( errors::PerIsolateMessageListener, Isolate::MessageErrorLevel::kMessageError | Isolate::MessageErrorLevel::kMessageWarning); auto* abort_callback = s.should_abort_on_uncaught_exception_callback ? s.should_abort_on_uncaught_exception_callback : ShouldAbortOnUncaughtException; isolate->SetAbortOnUncaughtExceptionCallback(abort_callback); auto* fatal_error_cb = s.fatal_error_callback ? s.fatal_error_callback : OnFatalError; isolate->SetFatalErrorHandler(fatal_error_cb); auto* oom_error_cb = s.oom_error_callback ? s.oom_error_callback : OOMErrorHandler; isolate->SetOOMErrorHandler(oom_error_cb); if ((s.flags & SHOULD_NOT_SET_PREPARE_STACK_TRACE_CALLBACK) == 0) { auto* prepare_stack_trace_cb = s.prepare_stack_trace_callback ? s.prepare_stack_trace_callback : PrepareStackTraceCallback; isolate->SetPrepareStackTraceCallback(prepare_stack_trace_cb); } } void SetIsolateMiscHandlers(v8::Isolate* isolate, const IsolateSettings& s) { isolate->SetMicrotasksPolicy(s.policy); auto* allow_wasm_codegen_cb = s.allow_wasm_code_generation_callback ? s.allow_wasm_code_generation_callback : AllowWasmCodeGenerationCallback; isolate->SetAllowWasmCodeGenerationCallback(allow_wasm_codegen_cb); auto* modify_code_generation_from_strings_callback = ModifyCodeGenerationFromStrings; if (s.modify_code_generation_from_strings_callback != nullptr) { modify_code_generation_from_strings_callback = s.modify_code_generation_from_strings_callback; } isolate->SetModifyCodeGenerationFromStringsCallback( modify_code_generation_from_strings_callback); isolate->SetWasmStreamingCallback(wasm_web_api::StartStreamingCompilation); Mutex::ScopedLock lock(node::per_process::cli_options_mutex); if (per_process::cli_options->get_per_isolate_options() ->experimental_shadow_realm) { isolate->SetHostCreateShadowRealmContextCallback( shadow_realm::HostCreateShadowRealmContextCallback); } if ((s.flags & SHOULD_NOT_SET_PROMISE_REJECTION_CALLBACK) == 0) { auto* promise_reject_cb = s.promise_reject_callback ? s.promise_reject_callback : PromiseRejectCallback; isolate->SetPromiseRejectCallback(promise_reject_cb); } if (s.flags & DETAILED_SOURCE_POSITIONS_FOR_PROFILING) v8::CpuProfiler::UseDetailedSourcePositionsForProfiling(isolate); } void SetIsolateUpForNode(v8::Isolate* isolate, const IsolateSettings& settings) { Isolate::Scope isolate_scope(isolate); SetIsolateErrorHandlers(isolate, settings); SetIsolateMiscHandlers(isolate, settings); } void SetIsolateUpForNode(v8::Isolate* isolate) { IsolateSettings settings; SetIsolateUpForNode(isolate, settings); } // IsolateGroup GetOrCreateIsolateGroup() { // When pointer compression is disabled, we cannot create new groups, // in which case we'll always return the default. if (IsolateGroup::CanCreateNewGroups()) { return IsolateGroup::Create(); } return IsolateGroup::GetDefault(); } // TODO(joyeecheung): we may want to expose this, but then we need to be // careful about what we override in the params. Isolate* NewIsolate(Isolate::CreateParams* params, uv_loop_t* event_loop, MultiIsolatePlatform* platform, const SnapshotData* snapshot_data, const IsolateSettings& settings) { IsolateGroup group = GetOrCreateIsolateGroup(); Isolate* isolate = Isolate::Allocate(group); if (isolate == nullptr) return nullptr; if (snapshot_data != nullptr) { SnapshotBuilder::InitializeIsolateParams(snapshot_data, params); } { // Because it uses a shared readonly-heap, V8 requires all snapshots used // for creating Isolates to be identical. This isn't really memory-safe // but also otherwise just doesn't work, and the only real alternative // is disabling shared-readonly-heap mode altogether. static Isolate::CreateParams first_params = *params; params->snapshot_blob = first_params.snapshot_blob; params->external_references = first_params.external_references; } // Register the isolate on the platform before the isolate gets initialized, // so that the isolate can access the platform during initialization. platform->RegisterIsolate(isolate, event_loop); // Ensure that there is always a CppHeap. if (settings.cpp_heap == nullptr) { params->cpp_heap = CppHeap::Create(platform, CppHeapCreateParams{{}}).release(); } else { params->cpp_heap = settings.cpp_heap; } SetIsolateCreateParamsForNode(params); Isolate::Initialize(isolate, *params); Isolate::Scope isolate_scope(isolate); if (snapshot_data == nullptr) { // If in deserialize mode, delay until after the deserialization is // complete. SetIsolateUpForNode(isolate, settings); } else { SetIsolateMiscHandlers(isolate, settings); } return isolate; } Isolate* NewIsolate(ArrayBufferAllocator* allocator, uv_loop_t* event_loop, MultiIsolatePlatform* platform, const EmbedderSnapshotData* snapshot_data, const IsolateSettings& settings) { Isolate::CreateParams params; if (allocator != nullptr) params.array_buffer_allocator = allocator; return NewIsolate(¶ms, event_loop, platform, SnapshotData::FromEmbedderWrapper(snapshot_data), settings); } Isolate* NewIsolate(std::shared_ptr allocator, uv_loop_t* event_loop, MultiIsolatePlatform* platform, const EmbedderSnapshotData* snapshot_data, const IsolateSettings& settings) { Isolate::CreateParams params; if (allocator) params.array_buffer_allocator_shared = allocator; return NewIsolate(¶ms, event_loop, platform, SnapshotData::FromEmbedderWrapper(snapshot_data), settings); } IsolateData* CreateIsolateData( Isolate* isolate, uv_loop_t* loop, MultiIsolatePlatform* platform, ArrayBufferAllocator* allocator, const EmbedderSnapshotData* embedder_snapshot_data) { return IsolateData::CreateIsolateData( isolate, loop, platform, allocator, embedder_snapshot_data); } void FreeIsolateData(IsolateData* isolate_data) { delete isolate_data; } // Hide the internal handle class from the public API. #if HAVE_INSPECTOR struct InspectorParentHandleImpl : public InspectorParentHandle { std::unique_ptr impl; explicit InspectorParentHandleImpl( std::unique_ptr&& impl) : impl(std::move(impl)) {} }; #endif Environment* CreateEnvironment( IsolateData* isolate_data, Local context, const std::vector& args, const std::vector& exec_args, EnvironmentFlags::Flags flags, ThreadId thread_id, std::unique_ptr inspector_parent_handle) { return CreateEnvironment(isolate_data, context, args, exec_args, flags, thread_id, std::move(inspector_parent_handle), {}); } Environment* CreateEnvironment( IsolateData* isolate_data, Local context, const std::vector& args, const std::vector& exec_args, EnvironmentFlags::Flags flags, ThreadId thread_id, std::unique_ptr inspector_parent_handle, std::string_view thread_name) { Isolate* isolate = isolate_data->isolate(); Isolate::Scope isolate_scope(isolate); HandleScope handle_scope(isolate); const bool use_snapshot = context.IsEmpty(); const EnvSerializeInfo* env_snapshot_info = nullptr; if (use_snapshot) { CHECK_NOT_NULL(isolate_data->snapshot_data()); env_snapshot_info = &isolate_data->snapshot_data()->env_info; } // TODO(addaleax): This is a much better place for parsing per-Environment // options than the global parse call. Environment* env = new Environment(isolate_data, isolate, args, exec_args, env_snapshot_info, flags, thread_id, thread_name); CHECK_NOT_NULL(env); if (use_snapshot) { context = Context::FromSnapshot(isolate, SnapshotData::kNodeMainContextIndex, v8::DeserializeInternalFieldsCallback( DeserializeNodeInternalFields, env), nullptr, MaybeLocal(), nullptr, v8::DeserializeContextDataCallback( DeserializeNodeContextData, env)) .ToLocalChecked(); CHECK(!context.IsEmpty()); Context::Scope context_scope(context); if (InitializeContextRuntime(context).IsNothing()) { FreeEnvironment(env); return nullptr; } SetIsolateErrorHandlers(isolate, {}); } Context::Scope context_scope(context); env->InitializeMainContext(context, env_snapshot_info); #if HAVE_INSPECTOR if (env->should_create_inspector()) { if (inspector_parent_handle) { env->InitializeInspector(std::move( static_cast(inspector_parent_handle.get()) ->impl)); } else { env->InitializeInspector({}); } } #endif if (!use_snapshot && env->principal_realm()->RunBootstrapping().IsEmpty()) { FreeEnvironment(env); return nullptr; } return env; } void FreeEnvironment(Environment* env) { Isolate* isolate = env->isolate(); Isolate::DisallowJavascriptExecutionScope disallow_js(isolate, Isolate::DisallowJavascriptExecutionScope::THROW_ON_FAILURE); { HandleScope handle_scope(isolate); // For env->context(). Context::Scope context_scope(env->context()); SealHandleScope seal_handle_scope(isolate); // Set the flag in accordance with the DisallowJavascriptExecutionScope // above. env->set_can_call_into_js(false); env->set_stopping(true); env->stop_sub_worker_contexts(); env->RunCleanup(); RunAtExit(env); } delete env; } NODE_EXTERN std::unique_ptr GetInspectorParentHandle( Environment* env, ThreadId thread_id, const char* url) { return GetInspectorParentHandle(env, thread_id, url, ""); } NODE_EXTERN std::unique_ptr GetInspectorParentHandle( Environment* env, ThreadId thread_id, const char* url, const char* name) { if (url == nullptr) url = ""; if (name == nullptr) name = ""; std::string_view url_view(url); std::string_view name_view(name); return GetInspectorParentHandle(env, thread_id, url_view, name_view); } NODE_EXTERN std::unique_ptr GetInspectorParentHandle( Environment* env, ThreadId thread_id, std::string_view url, std::string_view name) { CHECK_NOT_NULL(env); CHECK_NE(thread_id.id, static_cast(-1)); if (!env->should_create_inspector()) { return nullptr; } #if HAVE_INSPECTOR return std::make_unique( env->inspector_agent()->GetParentHandle(thread_id.id, url, name)); #else return {}; #endif } MaybeLocal LoadEnvironment(Environment* env, StartExecutionCallbackWithModule cb, EmbedderPreloadCallback preload) { env->InitializeLibuv(); env->InitializeDiagnostics(); if (preload) { env->set_embedder_preload(std::move(preload)); } env->InitializeCompileCache(); return StartExecution(env, cb); } struct StartExecutionCallbackInfoWithModule::Impl { Environment* env = nullptr; Local process_object; Local native_require; Local run_module; }; StartExecutionCallbackInfoWithModule::StartExecutionCallbackInfoWithModule() : impl_(std::make_unique()) {} StartExecutionCallbackInfoWithModule::~StartExecutionCallbackInfoWithModule() = default; StartExecutionCallbackInfoWithModule::StartExecutionCallbackInfoWithModule( StartExecutionCallbackInfoWithModule&&) = default; StartExecutionCallbackInfoWithModule& StartExecutionCallbackInfoWithModule::operator=( StartExecutionCallbackInfoWithModule&&) = default; Environment* StartExecutionCallbackInfoWithModule::env() const { return impl_->env; } Local StartExecutionCallbackInfoWithModule::process_object() const { return impl_->process_object; } Local StartExecutionCallbackInfoWithModule::native_require() const { return impl_->native_require; } Local StartExecutionCallbackInfoWithModule::run_module() const { return impl_->run_module; } void StartExecutionCallbackInfoWithModule::set_env(Environment* env) { impl_->env = env; } void StartExecutionCallbackInfoWithModule::set_process_object( Local process_object) { impl_->process_object = process_object; } void StartExecutionCallbackInfoWithModule::set_native_require( Local native_require) { impl_->native_require = native_require; } void StartExecutionCallbackInfoWithModule::set_run_module( Local run_module) { impl_->run_module = run_module; } struct ModuleData::Impl { std::string_view source; ModuleFormat format = ModuleFormat::kCommonJS; std::string_view resource_name; }; ModuleData::ModuleData() : impl_(std::make_unique()) {} ModuleData::~ModuleData() = default; ModuleData::ModuleData(ModuleData&&) = default; ModuleData& ModuleData::operator=(ModuleData&&) = default; void ModuleData::set_source(std::string_view source) { impl_->source = source; } void ModuleData::set_format(ModuleFormat format) { impl_->format = format; } void ModuleData::set_resource_name(std::string_view name) { impl_->resource_name = name; } std::string_view ModuleData::source() const { return impl_->source; } ModuleFormat ModuleData::format() const { return impl_->format; } std::string_view ModuleData::resource_name() const { return impl_->resource_name; } MaybeLocal LoadEnvironment(Environment* env, StartExecutionCallback cb, EmbedderPreloadCallback preload) { if (!cb) { return LoadEnvironment( env, StartExecutionCallbackWithModule{}, std::move(preload)); } return LoadEnvironment( env, [cb = std::move(cb)](const StartExecutionCallbackInfoWithModule& info) -> MaybeLocal { StartExecutionCallbackInfo legacy_info{ info.process_object(), info.native_require(), info.run_module()}; return cb(legacy_info); }, std::move(preload)); } MaybeLocal LoadEnvironment(Environment* env, std::string_view main_script_source_utf8, EmbedderPreloadCallback preload) { ModuleData data; data.set_source(main_script_source_utf8); data.set_format(ModuleFormat::kCommonJS); data.set_resource_name(env->exec_path()); return LoadEnvironment(env, &data, std::move(preload)); } MaybeLocal LoadEnvironment(Environment* env, const ModuleData* data, EmbedderPreloadCallback preload) { // It could be empty when it's used by SEA to load an empty script. CHECK_IMPLIES(data->source().size() > 0, data->source().data()); return LoadEnvironment( env, [data](const StartExecutionCallbackInfoWithModule& info) -> MaybeLocal { Environment* env = info.env(); Local context = env->context(); Isolate* isolate = env->isolate(); Local main_script = ToV8Value(context, data->source()).ToLocalChecked(); Local format = v8::Integer::New(isolate, static_cast(data->format())); Local resource_name = ToV8Value(context, data->resource_name()).ToLocalChecked(); Local args[] = {main_script, format, resource_name}; return info.run_module()->Call( context, Null(isolate), arraysize(args), args); }, std::move(preload)); } Environment* GetCurrentEnvironment(Local context) { return Environment::GetCurrent(context); } IsolateData* GetEnvironmentIsolateData(Environment* env) { return env->isolate_data(); } ArrayBufferAllocator* GetArrayBufferAllocator(IsolateData* isolate_data) { return isolate_data->node_allocator(); } Local GetMainContext(Environment* env) { return env->context(); } MultiIsolatePlatform* GetMultiIsolatePlatform(Environment* env) { return GetMultiIsolatePlatform(env->isolate_data()); } MultiIsolatePlatform* GetMultiIsolatePlatform(IsolateData* env) { return env->platform(); } std::unique_ptr MultiIsolatePlatform::Create( int thread_pool_size, v8::TracingController* tracing_controller, v8::PageAllocator* page_allocator) { return std::make_unique(thread_pool_size, tracing_controller, page_allocator); } MaybeLocal GetPerContextExports(Local context, IsolateData* isolate_data) { Isolate* isolate = Isolate::GetCurrent(); EscapableHandleScope handle_scope(isolate); Local global = context->Global(); Local key = Private::ForApi(isolate, FIXED_ONE_BYTE_STRING(isolate, "node:per_context_binding_exports")); Local existing_value; if (!global->GetPrivate(context, key).ToLocal(&existing_value)) return MaybeLocal(); if (existing_value->IsObject()) return handle_scope.Escape(existing_value.As()); // To initialize the per-context binding exports, a non-nullptr isolate_data // is needed CHECK(isolate_data); Local exports = Object::New(isolate); if (context->Global()->SetPrivate(context, key, exports).IsNothing() || InitializePrimordials(context, isolate_data).IsNothing()) { return MaybeLocal(); } return handle_scope.Escape(exports); } // Any initialization logic should be performed in // InitializeContext, because embedders don't necessarily // call NewContext and so they will experience breakages. Local NewContext(Isolate* isolate, Local object_template) { auto context = Context::New(isolate, nullptr, object_template); if (context.IsEmpty()) return context; if (InitializeContext(context).IsNothing()) { return Local(); } return context; } void ProtoThrower(const FunctionCallbackInfo& info) { THROW_ERR_PROTO_ACCESS(info.GetIsolate()); } // This runs at runtime, regardless of whether the context // is created from a snapshot. Maybe InitializeContextRuntime(Local context) { Isolate* isolate = Isolate::GetCurrent(); HandleScope handle_scope(isolate); // When `IsCodeGenerationFromStringsAllowed` is true, V8 takes the fast path // and ignores the ModifyCodeGenerationFromStrings callback. Set it to false // to delegate the code generation validation to // node::ModifyCodeGenerationFromStrings. // The `IsCodeGenerationFromStringsAllowed` can be refreshed by V8 according // to the runtime flags, propagate the value to the embedder data. bool is_code_generation_from_strings_allowed = context->IsCodeGenerationFromStringsAllowed(); context->AllowCodeGenerationFromStrings(false); context->SetEmbedderData( ContextEmbedderIndex::kAllowCodeGenerationFromStrings, Boolean::New(isolate, is_code_generation_from_strings_allowed)); if (per_process::cli_options->disable_proto == "") { return JustVoid(); } // Remove __proto__ // https://github.com/nodejs/node/issues/31951 Local prototype; { Local object_string = FIXED_ONE_BYTE_STRING(isolate, "Object"); Local prototype_string = FIXED_ONE_BYTE_STRING(isolate, "prototype"); Local object_v; if (!context->Global() ->Get(context, object_string) .ToLocal(&object_v)) { return Nothing(); } Local prototype_v; if (!object_v.As() ->Get(context, prototype_string) .ToLocal(&prototype_v)) { return Nothing(); } prototype = prototype_v.As(); } Local proto_string = FIXED_ONE_BYTE_STRING(isolate, "__proto__"); if (per_process::cli_options->disable_proto == "delete") { if (prototype ->Delete(context, proto_string) .IsNothing()) { return Nothing(); } } else if (per_process::cli_options->disable_proto == "throw") { Local thrower; if (!Function::New(context, ProtoThrower) .ToLocal(&thrower)) { return Nothing(); } PropertyDescriptor descriptor(thrower, thrower); descriptor.set_enumerable(false); descriptor.set_configurable(true); if (prototype ->DefineProperty(context, proto_string, descriptor) .IsNothing()) { return Nothing(); } } else if (per_process::cli_options->disable_proto != "") { // Validated in ProcessGlobalArgs UNREACHABLE("invalid --disable-proto mode"); } return JustVoid(); } Maybe InitializeBaseContextForSnapshot(Local context) { Isolate* isolate = Isolate::GetCurrent(); HandleScope handle_scope(isolate); // Delete `Intl.v8BreakIterator` // https://github.com/nodejs/node/issues/14909 { Context::Scope context_scope(context); Local intl_string = FIXED_ONE_BYTE_STRING(isolate, "Intl"); Local break_iter_string = FIXED_ONE_BYTE_STRING(isolate, "v8BreakIterator"); Local intl_v; if (!context->Global()->Get(context, intl_string).ToLocal(&intl_v)) { return Nothing(); } if (intl_v->IsObject() && intl_v.As()->Delete(context, break_iter_string).IsNothing()) { return Nothing(); } } return JustVoid(); } Maybe InitializeMainContextForSnapshot(Local context) { Isolate* isolate = Isolate::GetCurrent(); HandleScope handle_scope(isolate); // Initialize the default values. context->SetEmbedderData(ContextEmbedderIndex::kAllowWasmCodeGeneration, True(isolate)); context->SetEmbedderData( ContextEmbedderIndex::kAllowCodeGenerationFromStrings, True(isolate)); if (InitializeBaseContextForSnapshot(context).IsNothing()) { return Nothing(); } return JustVoid(); } MaybeLocal InitializePrivateSymbols(Local context, IsolateData* isolate_data) { CHECK(isolate_data); Isolate* isolate = Isolate::GetCurrent(); EscapableHandleScope scope(isolate); Context::Scope context_scope(context); Local private_symbols = ObjectTemplate::New(isolate); #define V(PropertyName, _) \ private_symbols->Set(isolate, #PropertyName, isolate_data->PropertyName()); PER_ISOLATE_PRIVATE_SYMBOL_PROPERTIES(V) #undef V Local private_symbols_object; if (!private_symbols->NewInstance(context).ToLocal(&private_symbols_object) || private_symbols_object->SetPrototypeV2(context, Null(isolate)) .IsNothing()) { return MaybeLocal(); } return scope.Escape(private_symbols_object); } MaybeLocal InitializePerIsolateSymbols(Local context, IsolateData* isolate_data) { CHECK(isolate_data); Isolate* isolate = Isolate::GetCurrent(); EscapableHandleScope scope(isolate); Context::Scope context_scope(context); Local per_isolate_symbols = ObjectTemplate::New(isolate); #define V(PropertyName, _) \ per_isolate_symbols->Set( \ isolate, #PropertyName, isolate_data->PropertyName()); PER_ISOLATE_SYMBOL_PROPERTIES(V) #undef V Local per_isolate_symbols_object; if (!per_isolate_symbols->NewInstance(context).ToLocal( &per_isolate_symbols_object) || per_isolate_symbols_object->SetPrototypeV2(context, Null(isolate)) .IsNothing()) { return MaybeLocal(); } return scope.Escape(per_isolate_symbols_object); } Maybe InitializePrimordials(Local context, IsolateData* isolate_data) { // Run per-context JS files. Isolate* isolate = Isolate::GetCurrent(); Context::Scope context_scope(context); Local exports; if (!GetPerContextExports(context).ToLocal(&exports)) { return Nothing(); } Local primordials_string = isolate_data->primordials_string(); // Ensure that `InitializePrimordials` is called exactly once on a given // context. CHECK(!exports->Has(context, primordials_string).FromJust()); Local primordials = Object::New(isolate, Null(isolate), nullptr, nullptr, 0); // Create primordials and make it available to per-context scripts. if (exports->Set(context, primordials_string, primordials).IsNothing()) { return Nothing(); } Local private_symbols; if (!InitializePrivateSymbols(context, isolate_data) .ToLocal(&private_symbols)) { return Nothing(); } Local per_isolate_symbols; if (!InitializePerIsolateSymbols(context, isolate_data) .ToLocal(&per_isolate_symbols)) { return Nothing(); } static const char* context_files[] = {"internal/per_context/primordials", "internal/per_context/domexception", "internal/per_context/messageport", nullptr}; // We do not have access to a per-Environment BuiltinLoader instance // at this point, because this code runs before an Environment exists // in the first place. However, creating BuiltinLoader instances is // relatively cheap and all the scripts that we may want to run at // startup are always present in it. thread_local builtins::BuiltinLoader builtin_loader; // Primordials can always be just eagerly compiled. builtin_loader.SetEagerCompile(); for (const char** module = context_files; *module != nullptr; module++) { Local arguments[] = { exports, primordials, private_symbols, per_isolate_symbols}; if (builtin_loader .CompileAndCallWith( context, *module, arraysize(arguments), arguments, nullptr) .IsEmpty()) { // Execution failed during context creation. return Nothing(); } } return JustVoid(); } // This initializes the main context (i.e. vm contexts are not included). Maybe InitializeContext(Local context) { if (InitializeMainContextForSnapshot(context).IsNothing()) { return Nothing(); } if (InitializeContextRuntime(context).IsNothing()) { return Nothing(); } return Just(true); } void RegisterContext(Environment* env, v8::Local context, std::string_view name, std::string_view origin) { ContextInfo info{std::string(name), std::string(origin)}; env->AssignToContext(context, nullptr, info); } void UnregisterContext(Environment* env, v8::Local context) { env->UnassignFromContext(context); } uv_loop_t* GetCurrentEventLoop(Isolate* isolate) { HandleScope handle_scope(isolate); Local context = isolate->GetCurrentContext(); if (context.IsEmpty()) return nullptr; Environment* env = Environment::GetCurrent(context); if (env == nullptr) return nullptr; return env->event_loop(); } void AddLinkedBinding(Environment* env, const node_module& mod) { CHECK_NOT_NULL(env); Mutex::ScopedLock lock(env->extra_linked_bindings_mutex()); node_module* prev_tail = env->extra_linked_bindings_tail(); env->extra_linked_bindings()->push_back(mod); if (prev_tail != nullptr) prev_tail->nm_link = &env->extra_linked_bindings()->back(); } void AddLinkedBinding(Environment* env, const napi_module& mod) { node_module node_mod = napi_module_to_node_module(&mod); node_mod.nm_flags = NM_F_LINKED; AddLinkedBinding(env, node_mod); } void AddLinkedBinding(Environment* env, const char* name, addon_context_register_func fn, void* priv) { node_module mod = { NODE_MODULE_VERSION, NM_F_LINKED, nullptr, // nm_dso_handle nullptr, // nm_filename nullptr, // nm_register_func fn, name, priv, nullptr // nm_link }; AddLinkedBinding(env, mod); } void AddLinkedBinding(Environment* env, const char* name, napi_addon_register_func fn, int32_t module_api_version) { node_module mod = { -1, // nm_version for Node-API NM_F_LINKED, // nm_flags nullptr, // nm_dso_handle nullptr, // nm_filename nullptr, // nm_register_func get_node_api_context_register_func(env, name, module_api_version), name, // nm_modname reinterpret_cast(fn), // nm_priv nullptr // nm_link }; AddLinkedBinding(env, mod); } static std::atomic next_thread_id{0}; ThreadId AllocateEnvironmentThreadId() { return ThreadId { next_thread_id++ }; } [[noreturn]] void Exit(ExitCode exit_code) { exit(static_cast(exit_code)); } void DefaultProcessExitHandlerInternal(Environment* env, ExitCode exit_code) { env->set_stopping(true); env->set_can_call_into_js(false); env->stop_sub_worker_contexts(); env->isolate()->DumpAndResetStats(); // The tracing agent could be in the process of writing data using the // threadpool. Stop it before shutting down libuv. The rest of the tracing // agent disposal will be performed in DisposePlatform(). per_process::v8_platform.StopTracingAgent(); // When the process exits, the tasks in the thread pool may also need to // access the data of V8Platform, such as trace agent, or a field // added in the future. So make sure the thread pool exits first. // And make sure V8Platform don not call into Libuv threadpool, see Dispose // in node_v8_platform-inl.h uv_library_shutdown(); DisposePlatform(); #if HAVE_OPENSSL crypto::CleanupCachedRootCertificates(); #endif // HAVE_OPENSSL Exit(exit_code); } void DefaultProcessExitHandler(Environment* env, int exit_code) { DefaultProcessExitHandlerInternal(env, static_cast(exit_code)); } void SetProcessExitHandler( Environment* env, std::function&& handler) { env->set_process_exit_handler(std::move(handler)); } void SetProcessExitHandler(Environment* env, std::function&& handler) { auto movedHandler = std::move(handler); env->set_process_exit_handler([=](Environment* env, ExitCode exit_code) { movedHandler(env, static_cast(exit_code)); }); } } // namespace node