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(¶ms, 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(¶ms, 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