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