• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include "node.h"
2 #include "node_builtins.h"
3 #include "node_context_data.h"
4 #include "node_errors.h"
5 #include "node_internals.h"
6 #include "node_options-inl.h"
7 #include "node_platform.h"
8 #include "node_realm-inl.h"
9 #include "node_shadow_realm.h"
10 #include "node_v8_platform-inl.h"
11 #include "node_wasm_web_api.h"
12 #include "uv.h"
13 #ifdef NODE_ENABLE_VTUNE_PROFILING
14 #include "../deps/v8/src/third_party/vtune/v8-vtune.h"
15 #endif
16 #if HAVE_INSPECTOR
17 #include "inspector/worker_inspector.h"  // ParentInspectorHandle
18 #endif
19 
20 namespace node {
21 using errors::TryCatchScope;
22 using v8::Array;
23 using v8::Boolean;
24 using v8::Context;
25 using v8::EscapableHandleScope;
26 using v8::Function;
27 using v8::FunctionCallbackInfo;
28 using v8::HandleScope;
29 using v8::Isolate;
30 using v8::Just;
31 using v8::Local;
32 using v8::Maybe;
33 using v8::MaybeLocal;
34 using v8::Nothing;
35 using v8::Null;
36 using v8::Object;
37 using v8::ObjectTemplate;
38 using v8::Private;
39 using v8::PropertyDescriptor;
40 using v8::SealHandleScope;
41 using v8::String;
42 using v8::Value;
43 
AllowWasmCodeGenerationCallback(Local<Context> context,Local<String>)44 bool AllowWasmCodeGenerationCallback(Local<Context> context,
45                                      Local<String>) {
46   Local<Value> wasm_code_gen =
47       context->GetEmbedderData(ContextEmbedderIndex::kAllowWasmCodeGeneration);
48   return wasm_code_gen->IsUndefined() || wasm_code_gen->IsTrue();
49 }
50 
ShouldAbortOnUncaughtException(Isolate * isolate)51 bool ShouldAbortOnUncaughtException(Isolate* isolate) {
52   DebugSealHandleScope scope(isolate);
53   Environment* env = Environment::GetCurrent(isolate);
54   return env != nullptr &&
55          (env->is_main_thread() || !env->is_stopping()) &&
56          env->abort_on_uncaught_exception() &&
57          env->should_abort_on_uncaught_toggle()[0] &&
58          !env->inside_should_not_abort_on_uncaught_scope();
59 }
60 
PrepareStackTraceCallback(Local<Context> context,Local<Value> exception,Local<Array> trace)61 MaybeLocal<Value> PrepareStackTraceCallback(Local<Context> context,
62                                             Local<Value> exception,
63                                             Local<Array> trace) {
64   Environment* env = Environment::GetCurrent(context);
65   if (env == nullptr) {
66     return exception->ToString(context).FromMaybe(Local<Value>());
67   }
68   Local<Function> prepare = env->prepare_stack_trace_callback();
69   if (prepare.IsEmpty()) {
70     return exception->ToString(context).FromMaybe(Local<Value>());
71   }
72   Local<Value> args[] = {
73       context->Global(),
74       exception,
75       trace,
76   };
77   // This TryCatch + Rethrow is required by V8 due to details around exception
78   // handling there. For C++ callbacks, V8 expects a scheduled exception (which
79   // is what ReThrow gives us). Just returning the empty MaybeLocal would leave
80   // us with a pending exception.
81   TryCatchScope try_catch(env);
82   MaybeLocal<Value> result = prepare->Call(
83       context, Undefined(env->isolate()), arraysize(args), args);
84   if (try_catch.HasCaught() && !try_catch.HasTerminated()) {
85     try_catch.ReThrow();
86   }
87   return result;
88 }
89 
Allocate(size_t size)90 void* NodeArrayBufferAllocator::Allocate(size_t size) {
91   void* ret;
92   if (zero_fill_field_ || per_process::cli_options->zero_fill_all_buffers)
93     ret = allocator_->Allocate(size);
94   else
95     ret = allocator_->AllocateUninitialized(size);
96   if (LIKELY(ret != nullptr))
97     total_mem_usage_.fetch_add(size, std::memory_order_relaxed);
98   return ret;
99 }
100 
AllocateUninitialized(size_t size)101 void* NodeArrayBufferAllocator::AllocateUninitialized(size_t size) {
102   void* ret = allocator_->AllocateUninitialized(size);
103   if (LIKELY(ret != nullptr))
104     total_mem_usage_.fetch_add(size, std::memory_order_relaxed);
105   return ret;
106 }
107 
Reallocate(void * data,size_t old_size,size_t size)108 void* NodeArrayBufferAllocator::Reallocate(
109     void* data, size_t old_size, size_t size) {
110   void* ret = allocator_->Reallocate(data, old_size, size);
111   if (LIKELY(ret != nullptr) || UNLIKELY(size == 0))
112     total_mem_usage_.fetch_add(size - old_size, std::memory_order_relaxed);
113   return ret;
114 }
115 
Free(void * data,size_t size)116 void NodeArrayBufferAllocator::Free(void* data, size_t size) {
117   total_mem_usage_.fetch_sub(size, std::memory_order_relaxed);
118   allocator_->Free(data, size);
119 }
120 
~DebuggingArrayBufferAllocator()121 DebuggingArrayBufferAllocator::~DebuggingArrayBufferAllocator() {
122   CHECK(allocations_.empty());
123 }
124 
Allocate(size_t size)125 void* DebuggingArrayBufferAllocator::Allocate(size_t size) {
126   Mutex::ScopedLock lock(mutex_);
127   void* data = NodeArrayBufferAllocator::Allocate(size);
128   RegisterPointerInternal(data, size);
129   return data;
130 }
131 
AllocateUninitialized(size_t size)132 void* DebuggingArrayBufferAllocator::AllocateUninitialized(size_t size) {
133   Mutex::ScopedLock lock(mutex_);
134   void* data = NodeArrayBufferAllocator::AllocateUninitialized(size);
135   RegisterPointerInternal(data, size);
136   return data;
137 }
138 
Free(void * data,size_t size)139 void DebuggingArrayBufferAllocator::Free(void* data, size_t size) {
140   Mutex::ScopedLock lock(mutex_);
141   UnregisterPointerInternal(data, size);
142   NodeArrayBufferAllocator::Free(data, size);
143 }
144 
Reallocate(void * data,size_t old_size,size_t size)145 void* DebuggingArrayBufferAllocator::Reallocate(void* data,
146                                                 size_t old_size,
147                                                 size_t size) {
148   Mutex::ScopedLock lock(mutex_);
149   void* ret = NodeArrayBufferAllocator::Reallocate(data, old_size, size);
150   if (ret == nullptr) {
151     if (size == 0) {  // i.e. equivalent to free().
152       // suppress coverity warning as data is used as key versus as pointer
153       // in UnregisterPointerInternal
154       // coverity[pass_freed_arg]
155       UnregisterPointerInternal(data, old_size);
156     }
157     return nullptr;
158   }
159 
160   if (data != nullptr) {
161     auto it = allocations_.find(data);
162     CHECK_NE(it, allocations_.end());
163     allocations_.erase(it);
164   }
165 
166   RegisterPointerInternal(ret, size);
167   return ret;
168 }
169 
RegisterPointer(void * data,size_t size)170 void DebuggingArrayBufferAllocator::RegisterPointer(void* data, size_t size) {
171   Mutex::ScopedLock lock(mutex_);
172   NodeArrayBufferAllocator::RegisterPointer(data, size);
173   RegisterPointerInternal(data, size);
174 }
175 
UnregisterPointer(void * data,size_t size)176 void DebuggingArrayBufferAllocator::UnregisterPointer(void* data, size_t size) {
177   Mutex::ScopedLock lock(mutex_);
178   NodeArrayBufferAllocator::UnregisterPointer(data, size);
179   UnregisterPointerInternal(data, size);
180 }
181 
UnregisterPointerInternal(void * data,size_t size)182 void DebuggingArrayBufferAllocator::UnregisterPointerInternal(void* data,
183                                                               size_t size) {
184   if (data == nullptr) return;
185   auto it = allocations_.find(data);
186   CHECK_NE(it, allocations_.end());
187   if (size > 0) {
188     // We allow allocations with size 1 for 0-length buffers to avoid having
189     // to deal with nullptr values.
190     CHECK_EQ(it->second, size);
191   }
192   allocations_.erase(it);
193 }
194 
RegisterPointerInternal(void * data,size_t size)195 void DebuggingArrayBufferAllocator::RegisterPointerInternal(void* data,
196                                                             size_t size) {
197   if (data == nullptr) return;
198   CHECK_EQ(allocations_.count(data), 0);
199   allocations_[data] = size;
200 }
201 
Create(bool debug)202 std::unique_ptr<ArrayBufferAllocator> ArrayBufferAllocator::Create(bool debug) {
203   if (debug || per_process::cli_options->debug_arraybuffer_allocations)
204     return std::make_unique<DebuggingArrayBufferAllocator>();
205   else
206     return std::make_unique<NodeArrayBufferAllocator>();
207 }
208 
CreateArrayBufferAllocator()209 ArrayBufferAllocator* CreateArrayBufferAllocator() {
210   return ArrayBufferAllocator::Create().release();
211 }
212 
FreeArrayBufferAllocator(ArrayBufferAllocator * allocator)213 void FreeArrayBufferAllocator(ArrayBufferAllocator* allocator) {
214   delete allocator;
215 }
216 
SetIsolateCreateParamsForNode(Isolate::CreateParams * params)217 void SetIsolateCreateParamsForNode(Isolate::CreateParams* params) {
218   const uint64_t constrained_memory = uv_get_constrained_memory();
219   const uint64_t total_memory = constrained_memory > 0 ?
220       std::min(uv_get_total_memory(), constrained_memory) :
221       uv_get_total_memory();
222   if (total_memory > 0 &&
223       params->constraints.max_old_generation_size_in_bytes() == 0) {
224     // V8 defaults to 700MB or 1.4GB on 32 and 64 bit platforms respectively.
225     // This default is based on browser use-cases. Tell V8 to configure the
226     // heap based on the actual physical memory.
227     params->constraints.ConfigureDefaults(total_memory, 0);
228   }
229   params->embedder_wrapper_object_index = BaseObject::InternalFields::kSlot;
230   params->embedder_wrapper_type_index = std::numeric_limits<int>::max();
231 
232 #ifdef NODE_ENABLE_VTUNE_PROFILING
233   params->code_event_handler = vTune::GetVtuneCodeEventHandler();
234 #endif
235 }
236 
SetIsolateErrorHandlers(v8::Isolate * isolate,const IsolateSettings & s)237 void SetIsolateErrorHandlers(v8::Isolate* isolate, const IsolateSettings& s) {
238   if (s.flags & MESSAGE_LISTENER_WITH_ERROR_LEVEL)
239     isolate->AddMessageListenerWithErrorLevel(
240             errors::PerIsolateMessageListener,
241             Isolate::MessageErrorLevel::kMessageError |
242                 Isolate::MessageErrorLevel::kMessageWarning);
243 
244   auto* abort_callback = s.should_abort_on_uncaught_exception_callback ?
245       s.should_abort_on_uncaught_exception_callback :
246       ShouldAbortOnUncaughtException;
247   isolate->SetAbortOnUncaughtExceptionCallback(abort_callback);
248 
249   auto* fatal_error_cb = s.fatal_error_callback ?
250       s.fatal_error_callback : OnFatalError;
251   isolate->SetFatalErrorHandler(fatal_error_cb);
252   isolate->SetOOMErrorHandler(OOMErrorHandler);
253 
254   if ((s.flags & SHOULD_NOT_SET_PREPARE_STACK_TRACE_CALLBACK) == 0) {
255     auto* prepare_stack_trace_cb = s.prepare_stack_trace_callback ?
256         s.prepare_stack_trace_callback : PrepareStackTraceCallback;
257     isolate->SetPrepareStackTraceCallback(prepare_stack_trace_cb);
258   }
259 }
260 
SetIsolateMiscHandlers(v8::Isolate * isolate,const IsolateSettings & s)261 void SetIsolateMiscHandlers(v8::Isolate* isolate, const IsolateSettings& s) {
262   isolate->SetMicrotasksPolicy(s.policy);
263 
264   auto* allow_wasm_codegen_cb = s.allow_wasm_code_generation_callback ?
265     s.allow_wasm_code_generation_callback : AllowWasmCodeGenerationCallback;
266   isolate->SetAllowWasmCodeGenerationCallback(allow_wasm_codegen_cb);
267 
268   auto* modify_code_generation_from_strings_callback =
269       ModifyCodeGenerationFromStrings;
270   if (s.flags & ALLOW_MODIFY_CODE_GENERATION_FROM_STRINGS_CALLBACK &&
271       s.modify_code_generation_from_strings_callback) {
272     modify_code_generation_from_strings_callback =
273         s.modify_code_generation_from_strings_callback;
274   }
275   isolate->SetModifyCodeGenerationFromStringsCallback(
276       modify_code_generation_from_strings_callback);
277 
278   Mutex::ScopedLock lock(node::per_process::cli_options_mutex);
279   if (per_process::cli_options->get_per_isolate_options()
280           ->get_per_env_options()
281           ->experimental_fetch) {
282     isolate->SetWasmStreamingCallback(wasm_web_api::StartStreamingCompilation);
283   }
284 
285   if (per_process::cli_options->get_per_isolate_options()
286           ->experimental_shadow_realm) {
287     isolate->SetHostCreateShadowRealmContextCallback(
288         shadow_realm::HostCreateShadowRealmContextCallback);
289   }
290 
291   if ((s.flags & SHOULD_NOT_SET_PROMISE_REJECTION_CALLBACK) == 0) {
292     auto* promise_reject_cb = s.promise_reject_callback ?
293       s.promise_reject_callback : PromiseRejectCallback;
294     isolate->SetPromiseRejectCallback(promise_reject_cb);
295   }
296 
297   if (s.flags & DETAILED_SOURCE_POSITIONS_FOR_PROFILING)
298     v8::CpuProfiler::UseDetailedSourcePositionsForProfiling(isolate);
299 }
300 
SetIsolateUpForNode(v8::Isolate * isolate,const IsolateSettings & settings)301 void SetIsolateUpForNode(v8::Isolate* isolate,
302                          const IsolateSettings& settings) {
303   SetIsolateErrorHandlers(isolate, settings);
304   SetIsolateMiscHandlers(isolate, settings);
305 }
306 
SetIsolateUpForNode(v8::Isolate * isolate)307 void SetIsolateUpForNode(v8::Isolate* isolate) {
308   IsolateSettings settings;
309   SetIsolateUpForNode(isolate, settings);
310 }
311 
312 // TODO(joyeecheung): we may want to expose this, but then we need to be
313 // careful about what we override in the params.
NewIsolate(Isolate::CreateParams * params,uv_loop_t * event_loop,MultiIsolatePlatform * platform,bool has_snapshot_data)314 Isolate* NewIsolate(Isolate::CreateParams* params,
315                     uv_loop_t* event_loop,
316                     MultiIsolatePlatform* platform,
317                     bool has_snapshot_data) {
318   Isolate* isolate = Isolate::Allocate();
319   if (isolate == nullptr) return nullptr;
320 #ifdef NODE_V8_SHARED_RO_HEAP
321   {
322     // In shared-readonly-heap mode, V8 requires all snapshots used for
323     // creating Isolates to be identical. This isn't really memory-safe
324     // but also otherwise just doesn't work, and the only real alternative
325     // is disabling shared-readonly-heap mode altogether.
326     static Isolate::CreateParams first_params = *params;
327     params->snapshot_blob = first_params.snapshot_blob;
328     params->external_references = first_params.external_references;
329   }
330 #endif
331 
332   // Register the isolate on the platform before the isolate gets initialized,
333   // so that the isolate can access the platform during initialization.
334   platform->RegisterIsolate(isolate, event_loop);
335 
336   SetIsolateCreateParamsForNode(params);
337   Isolate::Initialize(isolate, *params);
338   if (!has_snapshot_data) {
339     // If in deserialize mode, delay until after the deserialization is
340     // complete.
341     SetIsolateUpForNode(isolate);
342   } else {
343     SetIsolateMiscHandlers(isolate, {});
344   }
345 
346   return isolate;
347 }
348 
NewIsolate(ArrayBufferAllocator * allocator,uv_loop_t * event_loop,MultiIsolatePlatform * platform)349 Isolate* NewIsolate(ArrayBufferAllocator* allocator,
350                     uv_loop_t* event_loop,
351                     MultiIsolatePlatform* platform) {
352   Isolate::CreateParams params;
353   if (allocator != nullptr) params.array_buffer_allocator = allocator;
354   return NewIsolate(&params, event_loop, platform);
355 }
356 
NewIsolate(std::shared_ptr<ArrayBufferAllocator> allocator,uv_loop_t * event_loop,MultiIsolatePlatform * platform)357 Isolate* NewIsolate(std::shared_ptr<ArrayBufferAllocator> allocator,
358                     uv_loop_t* event_loop,
359                     MultiIsolatePlatform* platform) {
360   Isolate::CreateParams params;
361   if (allocator) params.array_buffer_allocator_shared = allocator;
362   return NewIsolate(&params, event_loop, platform);
363 }
364 
CreateIsolateData(Isolate * isolate,uv_loop_t * loop,MultiIsolatePlatform * platform,ArrayBufferAllocator * allocator)365 IsolateData* CreateIsolateData(Isolate* isolate,
366                                uv_loop_t* loop,
367                                MultiIsolatePlatform* platform,
368                                ArrayBufferAllocator* allocator) {
369   return new IsolateData(isolate, loop, platform, allocator);
370 }
371 
FreeIsolateData(IsolateData * isolate_data)372 void FreeIsolateData(IsolateData* isolate_data) {
373   delete isolate_data;
374 }
375 
~InspectorParentHandle()376 InspectorParentHandle::~InspectorParentHandle() {}
377 
378 // Hide the internal handle class from the public API.
379 #if HAVE_INSPECTOR
380 struct InspectorParentHandleImpl : public InspectorParentHandle {
381   std::unique_ptr<inspector::ParentInspectorHandle> impl;
382 
InspectorParentHandleImplnode::InspectorParentHandleImpl383   explicit InspectorParentHandleImpl(
384       std::unique_ptr<inspector::ParentInspectorHandle>&& impl)
385     : impl(std::move(impl)) {}
386 };
387 #endif
388 
CreateEnvironment(IsolateData * isolate_data,Local<Context> context,const std::vector<std::string> & args,const std::vector<std::string> & exec_args,EnvironmentFlags::Flags flags,ThreadId thread_id,std::unique_ptr<InspectorParentHandle> inspector_parent_handle)389 Environment* CreateEnvironment(
390     IsolateData* isolate_data,
391     Local<Context> context,
392     const std::vector<std::string>& args,
393     const std::vector<std::string>& exec_args,
394     EnvironmentFlags::Flags flags,
395     ThreadId thread_id,
396     std::unique_ptr<InspectorParentHandle> inspector_parent_handle) {
397   Isolate* isolate = context->GetIsolate();
398   HandleScope handle_scope(isolate);
399   Context::Scope context_scope(context);
400   // TODO(addaleax): This is a much better place for parsing per-Environment
401   // options than the global parse call.
402   Environment* env = new Environment(
403       isolate_data, context, args, exec_args, nullptr, flags, thread_id);
404 
405 #if HAVE_INSPECTOR
406   if (env->should_create_inspector()) {
407     if (inspector_parent_handle) {
408       env->InitializeInspector(std::move(
409           static_cast<InspectorParentHandleImpl*>(inspector_parent_handle.get())
410               ->impl));
411     } else {
412       env->InitializeInspector({});
413     }
414   }
415 #endif
416 
417   if (env->principal_realm()->RunBootstrapping().IsEmpty()) {
418     FreeEnvironment(env);
419     return nullptr;
420   }
421 
422   return env;
423 }
424 
FreeEnvironment(Environment * env)425 void FreeEnvironment(Environment* env) {
426   Isolate* isolate = env->isolate();
427   Isolate::DisallowJavascriptExecutionScope disallow_js(isolate,
428       Isolate::DisallowJavascriptExecutionScope::THROW_ON_FAILURE);
429   {
430     HandleScope handle_scope(isolate);  // For env->context().
431     Context::Scope context_scope(env->context());
432     SealHandleScope seal_handle_scope(isolate);
433 
434     // Set the flag in accordance with the DisallowJavascriptExecutionScope
435     // above.
436     env->set_can_call_into_js(false);
437     env->set_stopping(true);
438     env->stop_sub_worker_contexts();
439     env->RunCleanup();
440     RunAtExit(env);
441   }
442 
443   // This call needs to be made while the `Environment` is still alive
444   // because we assume that it is available for async tracking in the
445   // NodePlatform implementation.
446   MultiIsolatePlatform* platform = env->isolate_data()->platform();
447   if (platform != nullptr)
448     platform->DrainTasks(isolate);
449 
450   delete env;
451 }
452 
GetInspectorParentHandle(Environment * env,ThreadId thread_id,const char * url)453 NODE_EXTERN std::unique_ptr<InspectorParentHandle> GetInspectorParentHandle(
454     Environment* env,
455     ThreadId thread_id,
456     const char* url) {
457   return GetInspectorParentHandle(env, thread_id, url, "");
458 }
459 
GetInspectorParentHandle(Environment * env,ThreadId thread_id,const char * url,const char * name)460 NODE_EXTERN std::unique_ptr<InspectorParentHandle> GetInspectorParentHandle(
461     Environment* env, ThreadId thread_id, const char* url, const char* name) {
462   CHECK_NOT_NULL(env);
463   if (name == nullptr) name = "";
464   CHECK_NE(thread_id.id, static_cast<uint64_t>(-1));
465   if (!env->should_create_inspector()) {
466     return nullptr;
467   }
468 #if HAVE_INSPECTOR
469   return std::make_unique<InspectorParentHandleImpl>(
470       env->inspector_agent()->GetParentHandle(thread_id.id, url, name));
471 #else
472   return {};
473 #endif
474 }
475 
LoadEnvironment(Environment * env,StartExecutionCallback cb)476 MaybeLocal<Value> LoadEnvironment(
477     Environment* env,
478     StartExecutionCallback cb) {
479   env->InitializeLibuv();
480   env->InitializeDiagnostics();
481 
482   return StartExecution(env, cb);
483 }
484 
LoadEnvironment(Environment * env,const char * main_script_source_utf8)485 MaybeLocal<Value> LoadEnvironment(
486     Environment* env,
487     const char* main_script_source_utf8) {
488   CHECK_NOT_NULL(main_script_source_utf8);
489   return LoadEnvironment(
490       env, [&](const StartExecutionCallbackInfo& info) -> MaybeLocal<Value> {
491         std::string name = "embedder_main_" + std::to_string(env->thread_id());
492         builtins::BuiltinLoader::Add(name.c_str(), main_script_source_utf8);
493         Realm* realm = env->principal_realm();
494 
495         // Arguments must match the parameters specified in
496         // BuiltinLoader::LookupAndCompile().
497         std::vector<Local<Value>> args = {realm->process_object(),
498                                           realm->builtin_module_require()};
499         return realm->ExecuteBootstrapper(name.c_str(), &args);
500       });
501 }
502 
GetCurrentEnvironment(Local<Context> context)503 Environment* GetCurrentEnvironment(Local<Context> context) {
504   return Environment::GetCurrent(context);
505 }
506 
GetEnvironmentIsolateData(Environment * env)507 IsolateData* GetEnvironmentIsolateData(Environment* env) {
508   return env->isolate_data();
509 }
510 
GetArrayBufferAllocator(IsolateData * isolate_data)511 ArrayBufferAllocator* GetArrayBufferAllocator(IsolateData* isolate_data) {
512   return isolate_data->node_allocator();
513 }
514 
GetMultiIsolatePlatform(Environment * env)515 MultiIsolatePlatform* GetMultiIsolatePlatform(Environment* env) {
516   return GetMultiIsolatePlatform(env->isolate_data());
517 }
518 
GetMultiIsolatePlatform(IsolateData * env)519 MultiIsolatePlatform* GetMultiIsolatePlatform(IsolateData* env) {
520   return env->platform();
521 }
522 
CreatePlatform(int thread_pool_size,node::tracing::TracingController * tracing_controller)523 MultiIsolatePlatform* CreatePlatform(
524     int thread_pool_size,
525     node::tracing::TracingController* tracing_controller) {
526   return CreatePlatform(
527       thread_pool_size,
528       static_cast<v8::TracingController*>(tracing_controller));
529 }
530 
CreatePlatform(int thread_pool_size,v8::TracingController * tracing_controller)531 MultiIsolatePlatform* CreatePlatform(
532     int thread_pool_size,
533     v8::TracingController* tracing_controller) {
534   return MultiIsolatePlatform::Create(thread_pool_size,
535                                       tracing_controller)
536       .release();
537 }
538 
FreePlatform(MultiIsolatePlatform * platform)539 void FreePlatform(MultiIsolatePlatform* platform) {
540   delete platform;
541 }
542 
Create(int thread_pool_size,v8::TracingController * tracing_controller,v8::PageAllocator * page_allocator)543 std::unique_ptr<MultiIsolatePlatform> MultiIsolatePlatform::Create(
544     int thread_pool_size,
545     v8::TracingController* tracing_controller,
546     v8::PageAllocator* page_allocator) {
547   return std::make_unique<NodePlatform>(thread_pool_size,
548                                         tracing_controller,
549                                         page_allocator);
550 }
551 
GetPerContextExports(Local<Context> context)552 MaybeLocal<Object> GetPerContextExports(Local<Context> context) {
553   Isolate* isolate = context->GetIsolate();
554   EscapableHandleScope handle_scope(isolate);
555 
556   Local<Object> global = context->Global();
557   Local<Private> key = Private::ForApi(isolate,
558       FIXED_ONE_BYTE_STRING(isolate, "node:per_context_binding_exports"));
559 
560   Local<Value> existing_value;
561   if (!global->GetPrivate(context, key).ToLocal(&existing_value))
562     return MaybeLocal<Object>();
563   if (existing_value->IsObject())
564     return handle_scope.Escape(existing_value.As<Object>());
565 
566   Local<Object> exports = Object::New(isolate);
567   if (context->Global()->SetPrivate(context, key, exports).IsNothing() ||
568       InitializePrimordials(context).IsNothing())
569     return MaybeLocal<Object>();
570   return handle_scope.Escape(exports);
571 }
572 
573 // Any initialization logic should be performed in
574 // InitializeContext, because embedders don't necessarily
575 // call NewContext and so they will experience breakages.
NewContext(Isolate * isolate,Local<ObjectTemplate> object_template)576 Local<Context> NewContext(Isolate* isolate,
577                           Local<ObjectTemplate> object_template) {
578   auto context = Context::New(isolate, nullptr, object_template);
579   if (context.IsEmpty()) return context;
580 
581   if (InitializeContext(context).IsNothing()) {
582     return Local<Context>();
583   }
584 
585   return context;
586 }
587 
ProtoThrower(const FunctionCallbackInfo<Value> & info)588 void ProtoThrower(const FunctionCallbackInfo<Value>& info) {
589   THROW_ERR_PROTO_ACCESS(info.GetIsolate());
590 }
591 
592 // This runs at runtime, regardless of whether the context
593 // is created from a snapshot.
InitializeContextRuntime(Local<Context> context)594 Maybe<bool> InitializeContextRuntime(Local<Context> context) {
595   Isolate* isolate = context->GetIsolate();
596   HandleScope handle_scope(isolate);
597 
598   // When `IsCodeGenerationFromStringsAllowed` is true, V8 takes the fast path
599   // and ignores the ModifyCodeGenerationFromStrings callback. Set it to false
600   // to delegate the code generation validation to
601   // node::ModifyCodeGenerationFromStrings.
602   // The `IsCodeGenerationFromStringsAllowed` can be refreshed by V8 according
603   // to the runtime flags, propagate the value to the embedder data.
604   bool is_code_generation_from_strings_allowed =
605       context->IsCodeGenerationFromStringsAllowed();
606   context->AllowCodeGenerationFromStrings(false);
607   context->SetEmbedderData(
608       ContextEmbedderIndex::kAllowCodeGenerationFromStrings,
609       Boolean::New(isolate, is_code_generation_from_strings_allowed));
610 
611   if (per_process::cli_options->disable_proto == "") {
612     return Just(true);
613   }
614 
615   // Remove __proto__
616   // https://github.com/nodejs/node/issues/31951
617   Local<Object> prototype;
618   {
619     Local<String> object_string =
620       FIXED_ONE_BYTE_STRING(isolate, "Object");
621     Local<String> prototype_string =
622       FIXED_ONE_BYTE_STRING(isolate, "prototype");
623 
624     Local<Value> object_v;
625     if (!context->Global()
626         ->Get(context, object_string)
627         .ToLocal(&object_v)) {
628       return Nothing<bool>();
629     }
630 
631     Local<Value> prototype_v;
632     if (!object_v.As<Object>()
633         ->Get(context, prototype_string)
634         .ToLocal(&prototype_v)) {
635       return Nothing<bool>();
636     }
637 
638     prototype = prototype_v.As<Object>();
639   }
640 
641   Local<String> proto_string =
642     FIXED_ONE_BYTE_STRING(isolate, "__proto__");
643 
644   if (per_process::cli_options->disable_proto == "delete") {
645     if (prototype
646         ->Delete(context, proto_string)
647         .IsNothing()) {
648       return Nothing<bool>();
649     }
650   } else if (per_process::cli_options->disable_proto == "throw") {
651     Local<Value> thrower;
652     if (!Function::New(context, ProtoThrower)
653         .ToLocal(&thrower)) {
654       return Nothing<bool>();
655     }
656 
657     PropertyDescriptor descriptor(thrower, thrower);
658     descriptor.set_enumerable(false);
659     descriptor.set_configurable(true);
660     if (prototype
661         ->DefineProperty(context, proto_string, descriptor)
662         .IsNothing()) {
663       return Nothing<bool>();
664     }
665   } else if (per_process::cli_options->disable_proto != "") {
666     // Validated in ProcessGlobalArgs
667     OnFatalError("InitializeContextRuntime()", "invalid --disable-proto mode");
668   }
669 
670   return Just(true);
671 }
672 
InitializeBaseContextForSnapshot(Local<Context> context)673 Maybe<bool> InitializeBaseContextForSnapshot(Local<Context> context) {
674   Isolate* isolate = context->GetIsolate();
675   HandleScope handle_scope(isolate);
676 
677   // Delete `Intl.v8BreakIterator`
678   // https://github.com/nodejs/node/issues/14909
679   {
680     Context::Scope context_scope(context);
681     Local<String> intl_string = FIXED_ONE_BYTE_STRING(isolate, "Intl");
682     Local<String> break_iter_string =
683         FIXED_ONE_BYTE_STRING(isolate, "v8BreakIterator");
684 
685     Local<Value> intl_v;
686     if (!context->Global()->Get(context, intl_string).ToLocal(&intl_v)) {
687       return Nothing<bool>();
688     }
689 
690     if (intl_v->IsObject() &&
691         intl_v.As<Object>()->Delete(context, break_iter_string).IsNothing()) {
692       return Nothing<bool>();
693     }
694   }
695   return Just(true);
696 }
697 
InitializeMainContextForSnapshot(Local<Context> context)698 Maybe<bool> InitializeMainContextForSnapshot(Local<Context> context) {
699   Isolate* isolate = context->GetIsolate();
700   HandleScope handle_scope(isolate);
701 
702   // Initialize the default values.
703   context->SetEmbedderData(ContextEmbedderIndex::kAllowWasmCodeGeneration,
704                            True(isolate));
705   context->SetEmbedderData(
706       ContextEmbedderIndex::kAllowCodeGenerationFromStrings, True(isolate));
707 
708   if (InitializeBaseContextForSnapshot(context).IsNothing()) {
709     return Nothing<bool>();
710   }
711   return InitializePrimordials(context);
712 }
713 
InitializePrimordials(Local<Context> context)714 Maybe<bool> InitializePrimordials(Local<Context> context) {
715   // Run per-context JS files.
716   Isolate* isolate = context->GetIsolate();
717   Context::Scope context_scope(context);
718   Local<Object> exports;
719 
720   Local<String> primordials_string =
721       FIXED_ONE_BYTE_STRING(isolate, "primordials");
722 
723   // Create primordials first and make it available to per-context scripts.
724   Local<Object> primordials = Object::New(isolate);
725   if (primordials->SetPrototype(context, Null(isolate)).IsNothing() ||
726       !GetPerContextExports(context).ToLocal(&exports) ||
727       exports->Set(context, primordials_string, primordials).IsNothing()) {
728     return Nothing<bool>();
729   }
730 
731   static const char* context_files[] = {"internal/per_context/primordials",
732                                         "internal/per_context/domexception",
733                                         "internal/per_context/messageport",
734                                         nullptr};
735 
736   for (const char** module = context_files; *module != nullptr; module++) {
737     // Arguments must match the parameters specified in
738     // BuiltinLoader::LookupAndCompile().
739     Local<Value> arguments[] = {exports, primordials};
740     MaybeLocal<Function> maybe_fn =
741         builtins::BuiltinLoader::LookupAndCompile(context, *module, nullptr);
742     Local<Function> fn;
743     if (!maybe_fn.ToLocal(&fn)) {
744       return Nothing<bool>();
745     }
746     MaybeLocal<Value> result =
747         fn->Call(context, Undefined(isolate), arraysize(arguments), arguments);
748     // Execution failed during context creation.
749     if (result.IsEmpty()) {
750       return Nothing<bool>();
751     }
752   }
753 
754   return Just(true);
755 }
756 
757 // This initializes the main context (i.e. vm contexts are not included).
InitializeContext(Local<Context> context)758 Maybe<bool> InitializeContext(Local<Context> context) {
759   if (InitializeMainContextForSnapshot(context).IsNothing()) {
760     return Nothing<bool>();
761   }
762 
763   return InitializeContextRuntime(context);
764 }
765 
GetCurrentEventLoop(Isolate * isolate)766 uv_loop_t* GetCurrentEventLoop(Isolate* isolate) {
767   HandleScope handle_scope(isolate);
768   Local<Context> context = isolate->GetCurrentContext();
769   if (context.IsEmpty()) return nullptr;
770   Environment* env = Environment::GetCurrent(context);
771   if (env == nullptr) return nullptr;
772   return env->event_loop();
773 }
774 
AddLinkedBinding(Environment * env,const node_module & mod)775 void AddLinkedBinding(Environment* env, const node_module& mod) {
776   CHECK_NOT_NULL(env);
777   Mutex::ScopedLock lock(env->extra_linked_bindings_mutex());
778 
779   node_module* prev_tail = env->extra_linked_bindings_tail();
780   env->extra_linked_bindings()->push_back(mod);
781   if (prev_tail != nullptr)
782     prev_tail->nm_link = &env->extra_linked_bindings()->back();
783 }
784 
AddLinkedBinding(Environment * env,const napi_module & mod)785 void AddLinkedBinding(Environment* env, const napi_module& mod) {
786   node_module node_mod = napi_module_to_node_module(&mod);
787   node_mod.nm_flags = NM_F_LINKED;
788   AddLinkedBinding(env, node_mod);
789 }
790 
AddLinkedBinding(Environment * env,const char * name,addon_context_register_func fn,void * priv)791 void AddLinkedBinding(Environment* env,
792                       const char* name,
793                       addon_context_register_func fn,
794                       void* priv) {
795   node_module mod = {
796     NODE_MODULE_VERSION,
797     NM_F_LINKED,
798     nullptr,  // nm_dso_handle
799     nullptr,  // nm_filename
800     nullptr,  // nm_register_func
801     fn,
802     name,
803     priv,
804     nullptr   // nm_link
805   };
806   AddLinkedBinding(env, mod);
807 }
808 
AddLinkedBinding(Environment * env,const char * name,napi_addon_register_func fn,int32_t module_api_version)809 void AddLinkedBinding(Environment* env,
810                       const char* name,
811                       napi_addon_register_func fn,
812                       int32_t module_api_version) {
813   node_module mod = {
814       -1,           // nm_version for Node-API
815       NM_F_LINKED,  // nm_flags
816       nullptr,      // nm_dso_handle
817       nullptr,      // nm_filename
818       nullptr,      // nm_register_func
819       get_node_api_context_register_func(env, name, module_api_version),
820       name,                         // nm_modname
821       reinterpret_cast<void*>(fn),  // nm_priv
822       nullptr                       // nm_link
823   };
824   AddLinkedBinding(env, mod);
825 }
826 
827 static std::atomic<uint64_t> next_thread_id{0};
828 
AllocateEnvironmentThreadId()829 ThreadId AllocateEnvironmentThreadId() {
830   return ThreadId { next_thread_id++ };
831 }
832 
DefaultProcessExitHandler(Environment * env,int exit_code)833 void DefaultProcessExitHandler(Environment* env, int exit_code) {
834   env->set_stopping(true);
835   env->set_can_call_into_js(false);
836   env->stop_sub_worker_contexts();
837   env->isolate()->DumpAndResetStats();
838   // The tracing agent could be in the process of writing data using the
839   // threadpool. Stop it before shutting down libuv. The rest of the tracing
840   // agent disposal will be performed in DisposePlatform().
841   per_process::v8_platform.StopTracingAgent();
842   // When the process exits, the tasks in the thread pool may also need to
843   // access the data of V8Platform, such as trace agent, or a field
844   // added in the future. So make sure the thread pool exits first.
845   // And make sure V8Platform don not call into Libuv threadpool, see Dispose
846   // in node_v8_platform-inl.h
847   uv_library_shutdown();
848   DisposePlatform();
849   exit(exit_code);
850 }
851 
852 
SetProcessExitHandler(Environment * env,std::function<void (Environment *,int)> && handler)853 void SetProcessExitHandler(Environment* env,
854                            std::function<void(Environment*, int)>&& handler) {
855   env->set_process_exit_handler(std::move(handler));
856 }
857 
858 }  // namespace node
859