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