GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: api/environment.cc Lines: 350 403 86.8 %
Date: 2022-12-31 04:22:30 Branches: 119 198 60.1 %

Line Branch Exec Source
1
#include <cstdlib>
2
#include "node.h"
3
#include "node_builtins.h"
4
#include "node_context_data.h"
5
#include "node_errors.h"
6
#include "node_exit_code.h"
7
#include "node_internals.h"
8
#include "node_options-inl.h"
9
#include "node_platform.h"
10
#include "node_realm-inl.h"
11
#include "node_shadow_realm.h"
12
#include "node_v8_platform-inl.h"
13
#include "node_wasm_web_api.h"
14
#include "uv.h"
15
#ifdef NODE_ENABLE_VTUNE_PROFILING
16
#include "../deps/v8/src/third_party/vtune/v8-vtune.h"
17
#endif
18
#if HAVE_INSPECTOR
19
#include "inspector/worker_inspector.h"  // ParentInspectorHandle
20
#endif
21
22
namespace node {
23
using errors::TryCatchScope;
24
using v8::Array;
25
using v8::Context;
26
using v8::EscapableHandleScope;
27
using v8::Function;
28
using v8::FunctionCallbackInfo;
29
using v8::HandleScope;
30
using v8::Isolate;
31
using v8::Just;
32
using v8::Local;
33
using v8::Maybe;
34
using v8::MaybeLocal;
35
using v8::Nothing;
36
using v8::Null;
37
using v8::Object;
38
using v8::ObjectTemplate;
39
using v8::Private;
40
using v8::PropertyDescriptor;
41
using v8::SealHandleScope;
42
using v8::String;
43
using v8::Value;
44
45
408
bool AllowWasmCodeGenerationCallback(Local<Context> context,
46
                                     Local<String>) {
47
  Local<Value> wasm_code_gen =
48
816
      context->GetEmbedderData(ContextEmbedderIndex::kAllowWasmCodeGeneration);
49

1224
  return wasm_code_gen->IsUndefined() || wasm_code_gen->IsTrue();
50
}
51
52
34
bool ShouldAbortOnUncaughtException(Isolate* isolate) {
53
34
  DebugSealHandleScope scope(isolate);
54
34
  Environment* env = Environment::GetCurrent(isolate);
55
34
  return env != nullptr &&
56

70
         (env->is_main_thread() || !env->is_stopping()) &&
57
63
         env->abort_on_uncaught_exception() &&
58
98
         env->should_abort_on_uncaught_toggle()[0] &&
59
35
         !env->inside_should_not_abort_on_uncaught_scope();
60
}
61
62
68499
MaybeLocal<Value> PrepareStackTraceCallback(Local<Context> context,
63
                                            Local<Value> exception,
64
                                            Local<Array> trace) {
65
68499
  Environment* env = Environment::GetCurrent(context);
66
68499
  if (env == nullptr) {
67
    return exception->ToString(context).FromMaybe(Local<Value>());
68
  }
69
68499
  Local<Function> prepare = env->prepare_stack_trace_callback();
70
68499
  if (prepare.IsEmpty()) {
71
    return exception->ToString(context).FromMaybe(Local<Value>());
72
  }
73
  Local<Value> args[] = {
74
      context->Global(),
75
      exception,
76
      trace,
77
136998
  };
78
  // This TryCatch + Rethrow is required by V8 due to details around exception
79
  // handling there. For C++ callbacks, V8 expects a scheduled exception (which
80
  // is what ReThrow gives us). Just returning the empty MaybeLocal would leave
81
  // us with a pending exception.
82
68499
  TryCatchScope try_catch(env);
83
  MaybeLocal<Value> result = prepare->Call(
84
136998
      context, Undefined(env->isolate()), arraysize(args), args);
85

68499
  if (try_catch.HasCaught() && !try_catch.HasTerminated()) {
86
2
    try_catch.ReThrow();
87
  }
88
68499
  return result;
89
}
90
91
370613
void* NodeArrayBufferAllocator::Allocate(size_t size) {
92
  void* ret;
93

370613
  if (zero_fill_field_ || per_process::cli_options->zero_fill_all_buffers)
94
52456
    ret = allocator_->Allocate(size);
95
  else
96
318157
    ret = allocator_->AllocateUninitialized(size);
97
370613
  if (LIKELY(ret != nullptr))
98
370613
    total_mem_usage_.fetch_add(size, std::memory_order_relaxed);
99
370613
  return ret;
100
}
101
102
153674
void* NodeArrayBufferAllocator::AllocateUninitialized(size_t size) {
103
153674
  void* ret = allocator_->AllocateUninitialized(size);
104
153674
  if (LIKELY(ret != nullptr))
105
153674
    total_mem_usage_.fetch_add(size, std::memory_order_relaxed);
106
153674
  return ret;
107
}
108
109
62982
void* NodeArrayBufferAllocator::Reallocate(
110
    void* data, size_t old_size, size_t size) {
111
62982
  void* ret = allocator_->Reallocate(data, old_size, size);
112

62982
  if (LIKELY(ret != nullptr) || UNLIKELY(size == 0))
113
62982
    total_mem_usage_.fetch_add(size - old_size, std::memory_order_relaxed);
114
62982
  return ret;
115
}
116
117
505447
void NodeArrayBufferAllocator::Free(void* data, size_t size) {
118
505447
  total_mem_usage_.fetch_sub(size, std::memory_order_relaxed);
119
505447
  allocator_->Free(data, size);
120
505447
}
121
122
12
DebuggingArrayBufferAllocator::~DebuggingArrayBufferAllocator() {
123
6
  CHECK(allocations_.empty());
124
12
}
125
126
70
void* DebuggingArrayBufferAllocator::Allocate(size_t size) {
127
70
  Mutex::ScopedLock lock(mutex_);
128
70
  void* data = NodeArrayBufferAllocator::Allocate(size);
129
70
  RegisterPointerInternal(data, size);
130
70
  return data;
131
}
132
133
38
void* DebuggingArrayBufferAllocator::AllocateUninitialized(size_t size) {
134
38
  Mutex::ScopedLock lock(mutex_);
135
38
  void* data = NodeArrayBufferAllocator::AllocateUninitialized(size);
136
38
  RegisterPointerInternal(data, size);
137
38
  return data;
138
}
139
140
108
void DebuggingArrayBufferAllocator::Free(void* data, size_t size) {
141
216
  Mutex::ScopedLock lock(mutex_);
142
108
  UnregisterPointerInternal(data, size);
143
108
  NodeArrayBufferAllocator::Free(data, size);
144
108
}
145
146
void* DebuggingArrayBufferAllocator::Reallocate(void* data,
147
                                                size_t old_size,
148
                                                size_t size) {
149
  Mutex::ScopedLock lock(mutex_);
150
  void* ret = NodeArrayBufferAllocator::Reallocate(data, old_size, size);
151
  if (ret == nullptr) {
152
    if (size == 0) {  // i.e. equivalent to free().
153
      // suppress coverity warning as data is used as key versus as pointer
154
      // in UnregisterPointerInternal
155
      // coverity[pass_freed_arg]
156
      UnregisterPointerInternal(data, old_size);
157
    }
158
    return nullptr;
159
  }
160
161
  if (data != nullptr) {
162
    auto it = allocations_.find(data);
163
    CHECK_NE(it, allocations_.end());
164
    allocations_.erase(it);
165
  }
166
167
  RegisterPointerInternal(ret, size);
168
  return ret;
169
}
170
171
void DebuggingArrayBufferAllocator::RegisterPointer(void* data, size_t size) {
172
  Mutex::ScopedLock lock(mutex_);
173
  NodeArrayBufferAllocator::RegisterPointer(data, size);
174
  RegisterPointerInternal(data, size);
175
}
176
177
void DebuggingArrayBufferAllocator::UnregisterPointer(void* data, size_t size) {
178
  Mutex::ScopedLock lock(mutex_);
179
  NodeArrayBufferAllocator::UnregisterPointer(data, size);
180
  UnregisterPointerInternal(data, size);
181
}
182
183
108
void DebuggingArrayBufferAllocator::UnregisterPointerInternal(void* data,
184
                                                              size_t size) {
185
108
  if (data == nullptr) return;
186
108
  auto it = allocations_.find(data);
187
108
  CHECK_NE(it, allocations_.end());
188
108
  if (size > 0) {
189
    // We allow allocations with size 1 for 0-length buffers to avoid having
190
    // to deal with nullptr values.
191
108
    CHECK_EQ(it->second, size);
192
  }
193
108
  allocations_.erase(it);
194
}
195
196
108
void DebuggingArrayBufferAllocator::RegisterPointerInternal(void* data,
197
                                                            size_t size) {
198
108
  if (data == nullptr) return;
199
108
  CHECK_EQ(allocations_.count(data), 0);
200
108
  allocations_[data] = size;
201
}
202
203
6550
std::unique_ptr<ArrayBufferAllocator> ArrayBufferAllocator::Create(bool debug) {
204

6550
  if (debug || per_process::cli_options->debug_arraybuffer_allocations)
205
3
    return std::make_unique<DebuggingArrayBufferAllocator>();
206
  else
207
6547
    return std::make_unique<NodeArrayBufferAllocator>();
208
}
209
210
61
ArrayBufferAllocator* CreateArrayBufferAllocator() {
211
61
  return ArrayBufferAllocator::Create().release();
212
}
213
214
61
void FreeArrayBufferAllocator(ArrayBufferAllocator* allocator) {
215
61
  delete allocator;
216
61
}
217
218
7316
void SetIsolateCreateParamsForNode(Isolate::CreateParams* params) {
219
7316
  const uint64_t constrained_memory = uv_get_constrained_memory();
220
14606
  const uint64_t total_memory = constrained_memory > 0 ?
221
7290
      std::min(uv_get_total_memory(), constrained_memory) :
222
26
      uv_get_total_memory();
223

14632
  if (total_memory > 0 &&
224
7316
      params->constraints.max_old_generation_size_in_bytes() == 0) {
225
    // V8 defaults to 700MB or 1.4GB on 32 and 64 bit platforms respectively.
226
    // This default is based on browser use-cases. Tell V8 to configure the
227
    // heap based on the actual physical memory.
228
6548
    params->constraints.ConfigureDefaults(total_memory, 0);
229
  }
230
7316
  params->embedder_wrapper_object_index = BaseObject::InternalFields::kSlot;
231
7316
  params->embedder_wrapper_type_index = std::numeric_limits<int>::max();
232
233
#ifdef NODE_ENABLE_VTUNE_PROFILING
234
  params->code_event_handler = vTune::GetVtuneCodeEventHandler();
235
#endif
236
7316
}
237
238
6549
void SetIsolateErrorHandlers(v8::Isolate* isolate, const IsolateSettings& s) {
239
6549
  if (s.flags & MESSAGE_LISTENER_WITH_ERROR_LEVEL)
240
6549
    isolate->AddMessageListenerWithErrorLevel(
241
            errors::PerIsolateMessageListener,
242
            Isolate::MessageErrorLevel::kMessageError |
243
                Isolate::MessageErrorLevel::kMessageWarning);
244
245
6549
  auto* abort_callback = s.should_abort_on_uncaught_exception_callback ?
246
      s.should_abort_on_uncaught_exception_callback :
247
      ShouldAbortOnUncaughtException;
248
6549
  isolate->SetAbortOnUncaughtExceptionCallback(abort_callback);
249
250
6549
  auto* fatal_error_cb = s.fatal_error_callback ?
251
      s.fatal_error_callback : OnFatalError;
252
6549
  isolate->SetFatalErrorHandler(fatal_error_cb);
253
6549
  isolate->SetOOMErrorHandler(OOMErrorHandler);
254
255
6549
  if ((s.flags & SHOULD_NOT_SET_PREPARE_STACK_TRACE_CALLBACK) == 0) {
256
6549
    auto* prepare_stack_trace_cb = s.prepare_stack_trace_callback ?
257
        s.prepare_stack_trace_callback : PrepareStackTraceCallback;
258
6549
    isolate->SetPrepareStackTraceCallback(prepare_stack_trace_cb);
259
  }
260
6549
}
261
262
7324
void SetIsolateMiscHandlers(v8::Isolate* isolate, const IsolateSettings& s) {
263
7324
  isolate->SetMicrotasksPolicy(s.policy);
264
265
7324
  auto* allow_wasm_codegen_cb = s.allow_wasm_code_generation_callback ?
266
    s.allow_wasm_code_generation_callback : AllowWasmCodeGenerationCallback;
267
7324
  isolate->SetAllowWasmCodeGenerationCallback(allow_wasm_codegen_cb);
268
7324
  isolate->SetModifyCodeGenerationFromStringsCallback(
269
      ModifyCodeGenerationFromStrings);
270
271
14648
  Mutex::ScopedLock lock(node::per_process::cli_options_mutex);
272
7324
  if (per_process::cli_options->get_per_isolate_options()
273
7324
          ->get_per_env_options()
274
7324
          ->experimental_fetch) {
275
7323
    isolate->SetWasmStreamingCallback(wasm_web_api::StartStreamingCompilation);
276
  }
277
278
7324
  if (per_process::cli_options->get_per_isolate_options()
279
7324
          ->experimental_shadow_realm) {
280
1
    isolate->SetHostCreateShadowRealmContextCallback(
281
        shadow_realm::HostCreateShadowRealmContextCallback);
282
  }
283
284
7324
  if ((s.flags & SHOULD_NOT_SET_PROMISE_REJECTION_CALLBACK) == 0) {
285
7324
    auto* promise_reject_cb = s.promise_reject_callback ?
286
      s.promise_reject_callback : PromiseRejectCallback;
287
7324
    isolate->SetPromiseRejectCallback(promise_reject_cb);
288
  }
289
290
7324
  if (s.flags & DETAILED_SOURCE_POSITIONS_FOR_PROFILING)
291
7324
    v8::CpuProfiler::UseDetailedSourcePositionsForProfiling(isolate);
292
7324
}
293
294
841
void SetIsolateUpForNode(v8::Isolate* isolate,
295
                         const IsolateSettings& settings) {
296
841
  SetIsolateErrorHandlers(isolate, settings);
297
841
  SetIsolateMiscHandlers(isolate, settings);
298
841
}
299
300
841
void SetIsolateUpForNode(v8::Isolate* isolate) {
301
841
  IsolateSettings settings;
302
841
  SetIsolateUpForNode(isolate, settings);
303
841
}
304
305
// TODO(joyeecheung): we may want to expose this, but then we need to be
306
// careful about what we override in the params.
307
6548
Isolate* NewIsolate(Isolate::CreateParams* params,
308
                    uv_loop_t* event_loop,
309
                    MultiIsolatePlatform* platform,
310
                    bool has_snapshot_data) {
311
6548
  Isolate* isolate = Isolate::Allocate();
312
6548
  if (isolate == nullptr) return nullptr;
313
#ifdef NODE_V8_SHARED_RO_HEAP
314
  {
315
    // In shared-readonly-heap mode, V8 requires all snapshots used for
316
    // creating Isolates to be identical. This isn't really memory-safe
317
    // but also otherwise just doesn't work, and the only real alternative
318
    // is disabling shared-readonly-heap mode altogether.
319

6548
    static Isolate::CreateParams first_params = *params;
320
6548
    params->snapshot_blob = first_params.snapshot_blob;
321
6548
    params->external_references = first_params.external_references;
322
  }
323
#endif
324
325
  // Register the isolate on the platform before the isolate gets initialized,
326
  // so that the isolate can access the platform during initialization.
327
6548
  platform->RegisterIsolate(isolate, event_loop);
328
329
6548
  SetIsolateCreateParamsForNode(params);
330
6548
  Isolate::Initialize(isolate, *params);
331
6548
  if (!has_snapshot_data) {
332
    // If in deserialize mode, delay until after the deserialization is
333
    // complete.
334
73
    SetIsolateUpForNode(isolate);
335
  } else {
336
6475
    SetIsolateMiscHandlers(isolate, {});
337
  }
338
339
6548
  return isolate;
340
}
341
342
59
Isolate* NewIsolate(ArrayBufferAllocator* allocator,
343
                    uv_loop_t* event_loop,
344
                    MultiIsolatePlatform* platform) {
345
118
  Isolate::CreateParams params;
346
59
  if (allocator != nullptr) params.array_buffer_allocator = allocator;
347
59
  return NewIsolate(&params, event_loop, platform);
348
}
349
350
10
Isolate* NewIsolate(std::shared_ptr<ArrayBufferAllocator> allocator,
351
                    uv_loop_t* event_loop,
352
                    MultiIsolatePlatform* platform) {
353
20
  Isolate::CreateParams params;
354
10
  if (allocator) params.array_buffer_allocator_shared = allocator;
355
10
  return NewIsolate(&params, event_loop, platform);
356
}
357
358
820
IsolateData* CreateIsolateData(Isolate* isolate,
359
                               uv_loop_t* loop,
360
                               MultiIsolatePlatform* platform,
361
                               ArrayBufferAllocator* allocator) {
362
820
  return new IsolateData(isolate, loop, platform, allocator);
363
}
364
365
818
void FreeIsolateData(IsolateData* isolate_data) {
366
818
  delete isolate_data;
367
818
}
368
369
1968
InspectorParentHandle::~InspectorParentHandle() {}
370
371
// Hide the internal handle class from the public API.
372
#if HAVE_INSPECTOR
373
struct InspectorParentHandleImpl : public InspectorParentHandle {
374
  std::unique_ptr<inspector::ParentInspectorHandle> impl;
375
376
984
  explicit InspectorParentHandleImpl(
377
      std::unique_ptr<inspector::ParentInspectorHandle>&& impl)
378
984
    : impl(std::move(impl)) {}
379
};
380
#endif
381
382
827
Environment* CreateEnvironment(
383
    IsolateData* isolate_data,
384
    Local<Context> context,
385
    const std::vector<std::string>& args,
386
    const std::vector<std::string>& exec_args,
387
    EnvironmentFlags::Flags flags,
388
    ThreadId thread_id,
389
    std::unique_ptr<InspectorParentHandle> inspector_parent_handle) {
390
827
  Isolate* isolate = context->GetIsolate();
391
1654
  HandleScope handle_scope(isolate);
392
827
  Context::Scope context_scope(context);
393
  // TODO(addaleax): This is a much better place for parsing per-Environment
394
  // options than the global parse call.
395
  Environment* env = new Environment(
396
827
      isolate_data, context, args, exec_args, nullptr, flags, thread_id);
397
398
#if HAVE_INSPECTOR
399
827
  if (env->should_create_inspector()) {
400
819
    if (inspector_parent_handle) {
401
765
      env->InitializeInspector(std::move(
402
765
          static_cast<InspectorParentHandleImpl*>(inspector_parent_handle.get())
403
765
              ->impl));
404
    } else {
405
54
      env->InitializeInspector({});
406
    }
407
  }
408
#endif
409
410
1654
  if (env->principal_realm()->RunBootstrapping().IsEmpty()) {
411
    FreeEnvironment(env);
412
    return nullptr;
413
  }
414
415
827
  return env;
416
}
417
418
5922
void FreeEnvironment(Environment* env) {
419
5922
  Isolate* isolate = env->isolate();
420
  Isolate::DisallowJavascriptExecutionScope disallow_js(isolate,
421
11844
      Isolate::DisallowJavascriptExecutionScope::THROW_ON_FAILURE);
422
  {
423
11844
    HandleScope handle_scope(isolate);  // For env->context().
424
5922
    Context::Scope context_scope(env->context());
425
11844
    SealHandleScope seal_handle_scope(isolate);
426
427
5922
    env->set_stopping(true);
428
5922
    env->stop_sub_worker_contexts();
429
5922
    env->RunCleanup();
430
5922
    RunAtExit(env);
431
  }
432
433
  // This call needs to be made while the `Environment` is still alive
434
  // because we assume that it is available for async tracking in the
435
  // NodePlatform implementation.
436
5922
  MultiIsolatePlatform* platform = env->isolate_data()->platform();
437
5922
  if (platform != nullptr)
438
5922
    platform->DrainTasks(isolate);
439
440
5922
  delete env;
441
5922
}
442
443
984
NODE_EXTERN std::unique_ptr<InspectorParentHandle> GetInspectorParentHandle(
444
    Environment* env,
445
    ThreadId thread_id,
446
    const char* url) {
447
984
  CHECK_NOT_NULL(env);
448
984
  CHECK_NE(thread_id.id, static_cast<uint64_t>(-1));
449
#if HAVE_INSPECTOR
450
1968
  return std::make_unique<InspectorParentHandleImpl>(
451
2952
      env->inspector_agent()->GetParentHandle(thread_id.id, url));
452
#else
453
  return {};
454
#endif
455
}
456
457
6494
MaybeLocal<Value> LoadEnvironment(
458
    Environment* env,
459
    StartExecutionCallback cb) {
460
6494
  env->InitializeLibuv();
461
6494
  env->InitializeDiagnostics();
462
463
6494
  return StartExecution(env, cb);
464
}
465
466
20
MaybeLocal<Value> LoadEnvironment(
467
    Environment* env,
468
    const char* main_script_source_utf8) {
469
20
  CHECK_NOT_NULL(main_script_source_utf8);
470
20
  Isolate* isolate = env->isolate();
471
  return LoadEnvironment(
472
      env,
473
20
      [&](const StartExecutionCallbackInfo& info) -> MaybeLocal<Value> {
474
        // This is a slightly hacky way to convert UTF-8 to UTF-16.
475
        Local<String> str =
476
40
            String::NewFromUtf8(isolate,
477
20
                                main_script_source_utf8).ToLocalChecked();
478
38
        auto main_utf16 = std::make_unique<String::Value>(isolate, str);
479
480
        // TODO(addaleax): Avoid having a global table for all scripts.
481
38
        std::string name = "embedder_main_" + std::to_string(env->thread_id());
482
20
        builtins::BuiltinLoader::Add(
483
20
            name.c_str(), UnionBytes(**main_utf16, main_utf16->length()));
484
20
        env->set_main_utf16(std::move(main_utf16));
485
20
        Realm* realm = env->principal_realm();
486
487
20
        return realm->ExecuteBootstrapper(name.c_str());
488
20
      });
489
}
490
491
11
Environment* GetCurrentEnvironment(Local<Context> context) {
492
11
  return Environment::GetCurrent(context);
493
}
494
495
1
IsolateData* GetEnvironmentIsolateData(Environment* env) {
496
1
  return env->isolate_data();
497
}
498
499
1
ArrayBufferAllocator* GetArrayBufferAllocator(IsolateData* isolate_data) {
500
1
  return isolate_data->node_allocator();
501
}
502
503
6126
MultiIsolatePlatform* GetMultiIsolatePlatform(Environment* env) {
504
6126
  return GetMultiIsolatePlatform(env->isolate_data());
505
}
506
507
6126
MultiIsolatePlatform* GetMultiIsolatePlatform(IsolateData* env) {
508
6126
  return env->platform();
509
}
510
511
MultiIsolatePlatform* CreatePlatform(
512
    int thread_pool_size,
513
    node::tracing::TracingController* tracing_controller) {
514
  return CreatePlatform(
515
      thread_pool_size,
516
      static_cast<v8::TracingController*>(tracing_controller));
517
}
518
519
MultiIsolatePlatform* CreatePlatform(
520
    int thread_pool_size,
521
    v8::TracingController* tracing_controller) {
522
  return MultiIsolatePlatform::Create(thread_pool_size,
523
                                      tracing_controller)
524
      .release();
525
}
526
527
void FreePlatform(MultiIsolatePlatform* platform) {
528
  delete platform;
529
}
530
531
7
std::unique_ptr<MultiIsolatePlatform> MultiIsolatePlatform::Create(
532
    int thread_pool_size,
533
    v8::TracingController* tracing_controller,
534
    v8::PageAllocator* page_allocator) {
535
14
  return std::make_unique<NodePlatform>(thread_pool_size,
536
                                        tracing_controller,
537
7
                                        page_allocator);
538
}
539
540
36686
MaybeLocal<Object> GetPerContextExports(Local<Context> context) {
541
36686
  Isolate* isolate = context->GetIsolate();
542
36686
  EscapableHandleScope handle_scope(isolate);
543
544
36686
  Local<Object> global = context->Global();
545
  Local<Private> key = Private::ForApi(isolate,
546
36686
      FIXED_ONE_BYTE_STRING(isolate, "node:per_context_binding_exports"));
547
548
  Local<Value> existing_value;
549
73372
  if (!global->GetPrivate(context, key).ToLocal(&existing_value))
550
    return MaybeLocal<Object>();
551
36686
  if (existing_value->IsObject())
552
36608
    return handle_scope.Escape(existing_value.As<Object>());
553
554
78
  Local<Object> exports = Object::New(isolate);
555
312
  if (context->Global()->SetPrivate(context, key, exports).IsNothing() ||
556

234
      InitializePrimordials(context).IsNothing())
557
    return MaybeLocal<Object>();
558
78
  return handle_scope.Escape(exports);
559
}
560
561
// Any initialization logic should be performed in
562
// InitializeContext, because embedders don't necessarily
563
// call NewContext and so they will experience breakages.
564
71
Local<Context> NewContext(Isolate* isolate,
565
                          Local<ObjectTemplate> object_template) {
566
142
  auto context = Context::New(isolate, nullptr, object_template);
567
71
  if (context.IsEmpty()) return context;
568
569
142
  if (InitializeContext(context).IsNothing()) {
570
    return Local<Context>();
571
  }
572
573
71
  return context;
574
}
575
576
8
void ProtoThrower(const FunctionCallbackInfo<Value>& info) {
577
8
  THROW_ERR_PROTO_ACCESS(info.GetIsolate());
578
8
}
579
580
// This runs at runtime, regardless of whether the context
581
// is created from a snapshot.
582
7216
Maybe<bool> InitializeContextRuntime(Local<Context> context) {
583
7216
  Isolate* isolate = context->GetIsolate();
584
14432
  HandleScope handle_scope(isolate);
585
586
  // When `IsCodeGenerationFromStringsAllowed` is true, V8 takes the fast path
587
  // and ignores the ModifyCodeGenerationFromStrings callback. Set it to false
588
  // to delegate the code generation validation to
589
  // node::ModifyCodeGenerationFromStrings.
590
  // The `IsCodeGenerationFromStringsAllowed` can be refreshed by V8 according
591
  // to the runtime flags, propagate the value to the embedder data.
592
  bool is_code_generation_from_strings_allowed =
593
7216
      context->IsCodeGenerationFromStringsAllowed();
594
7216
  context->AllowCodeGenerationFromStrings(false);
595
14432
  context->SetEmbedderData(
596
      ContextEmbedderIndex::kAllowCodeGenerationFromStrings,
597
      is_code_generation_from_strings_allowed ? True(isolate) : False(isolate));
598
599
7216
  if (per_process::cli_options->disable_proto == "") {
600
7208
    return Just(true);
601
  }
602
603
  // Remove __proto__
604
  // https://github.com/nodejs/node/issues/31951
605
  Local<Object> prototype;
606
  {
607
    Local<String> object_string =
608
8
      FIXED_ONE_BYTE_STRING(isolate, "Object");
609
    Local<String> prototype_string =
610
8
      FIXED_ONE_BYTE_STRING(isolate, "prototype");
611
612
    Local<Value> object_v;
613
8
    if (!context->Global()
614
8
        ->Get(context, object_string)
615
8
        .ToLocal(&object_v)) {
616
      return Nothing<bool>();
617
    }
618
619
    Local<Value> prototype_v;
620
8
    if (!object_v.As<Object>()
621
8
        ->Get(context, prototype_string)
622
8
        .ToLocal(&prototype_v)) {
623
      return Nothing<bool>();
624
    }
625
626
8
    prototype = prototype_v.As<Object>();
627
  }
628
629
  Local<String> proto_string =
630
8
    FIXED_ONE_BYTE_STRING(isolate, "__proto__");
631
632
8
  if (per_process::cli_options->disable_proto == "delete") {
633
4
    if (prototype
634
4
        ->Delete(context, proto_string)
635
4
        .IsNothing()) {
636
      return Nothing<bool>();
637
    }
638
4
  } else if (per_process::cli_options->disable_proto == "throw") {
639
    Local<Value> thrower;
640
4
    if (!Function::New(context, ProtoThrower)
641
4
        .ToLocal(&thrower)) {
642
      return Nothing<bool>();
643
    }
644
645
4
    PropertyDescriptor descriptor(thrower, thrower);
646
4
    descriptor.set_enumerable(false);
647
4
    descriptor.set_configurable(true);
648
4
    if (prototype
649
4
        ->DefineProperty(context, proto_string, descriptor)
650
4
        .IsNothing()) {
651
      return Nothing<bool>();
652
    }
653
  } else if (per_process::cli_options->disable_proto != "") {
654
    // Validated in ProcessGlobalArgs
655
    FatalError("InitializeContextRuntime()",
656
               "invalid --disable-proto mode");
657
  }
658
659
8
  return Just(true);
660
}
661
662
82
Maybe<bool> InitializeBaseContextForSnapshot(Local<Context> context) {
663
82
  Isolate* isolate = context->GetIsolate();
664
164
  HandleScope handle_scope(isolate);
665
666
  // Delete `Intl.v8BreakIterator`
667
  // https://github.com/nodejs/node/issues/14909
668
  {
669
82
    Context::Scope context_scope(context);
670
82
    Local<String> intl_string = FIXED_ONE_BYTE_STRING(isolate, "Intl");
671
    Local<String> break_iter_string =
672
82
        FIXED_ONE_BYTE_STRING(isolate, "v8BreakIterator");
673
674
    Local<Value> intl_v;
675
246
    if (!context->Global()->Get(context, intl_string).ToLocal(&intl_v)) {
676
      return Nothing<bool>();
677
    }
678
679
164
    if (intl_v->IsObject() &&
680

246
        intl_v.As<Object>()->Delete(context, break_iter_string).IsNothing()) {
681
      return Nothing<bool>();
682
    }
683
  }
684
82
  return Just(true);
685
}
686
687
72
Maybe<bool> InitializeMainContextForSnapshot(Local<Context> context) {
688
72
  Isolate* isolate = context->GetIsolate();
689
144
  HandleScope handle_scope(isolate);
690
691
  // Initialize the default values.
692
144
  context->SetEmbedderData(ContextEmbedderIndex::kAllowWasmCodeGeneration,
693
                           True(isolate));
694
144
  context->SetEmbedderData(
695
      ContextEmbedderIndex::kAllowCodeGenerationFromStrings, True(isolate));
696
697
144
  if (InitializeBaseContextForSnapshot(context).IsNothing()) {
698
    return Nothing<bool>();
699
  }
700
72
  return InitializePrimordials(context);
701
}
702
703
150
Maybe<bool> InitializePrimordials(Local<Context> context) {
704
  // Run per-context JS files.
705
150
  Isolate* isolate = context->GetIsolate();
706
150
  Context::Scope context_scope(context);
707
  Local<Object> exports;
708
709
  Local<String> primordials_string =
710
150
      FIXED_ONE_BYTE_STRING(isolate, "primordials");
711
712
  // Create primordials first and make it available to per-context scripts.
713
150
  Local<Object> primordials = Object::New(isolate);
714
300
  if (primordials->SetPrototype(context, Null(isolate)).IsNothing() ||
715

450
      !GetPerContextExports(context).ToLocal(&exports) ||
716

450
      exports->Set(context, primordials_string, primordials).IsNothing()) {
717
    return Nothing<bool>();
718
  }
719
720
  static const char* context_files[] = {"internal/per_context/primordials",
721
                                        "internal/per_context/domexception",
722
                                        "internal/per_context/messageport",
723
                                        nullptr};
724
725
600
  for (const char** module = context_files; *module != nullptr; module++) {
726
450
    Local<Value> arguments[] = {exports, primordials};
727
450
    if (builtins::BuiltinLoader::CompileAndCall(
728
450
            context, *module, arraysize(arguments), arguments, nullptr)
729
450
            .IsEmpty()) {
730
      // Execution failed during context creation.
731
      return Nothing<bool>();
732
    }
733
  }
734
735
150
  return Just(true);
736
}
737
738
// This initializes the main context (i.e. vm contexts are not included).
739
72
Maybe<bool> InitializeContext(Local<Context> context) {
740
144
  if (InitializeMainContextForSnapshot(context).IsNothing()) {
741
    return Nothing<bool>();
742
  }
743
744
72
  return InitializeContextRuntime(context);
745
}
746
747
10
uv_loop_t* GetCurrentEventLoop(Isolate* isolate) {
748
20
  HandleScope handle_scope(isolate);
749
10
  Local<Context> context = isolate->GetCurrentContext();
750
10
  if (context.IsEmpty()) return nullptr;
751
10
  Environment* env = Environment::GetCurrent(context);
752
10
  if (env == nullptr) return nullptr;
753
10
  return env->event_loop();
754
}
755
756
9
void AddLinkedBinding(Environment* env, const node_module& mod) {
757
9
  CHECK_NOT_NULL(env);
758
18
  Mutex::ScopedLock lock(env->extra_linked_bindings_mutex());
759
760
9
  node_module* prev_tail = env->extra_linked_bindings_tail();
761
9
  env->extra_linked_bindings()->push_back(mod);
762
9
  if (prev_tail != nullptr)
763
5
    prev_tail->nm_link = &env->extra_linked_bindings()->back();
764
9
}
765
766
3
void AddLinkedBinding(Environment* env, const napi_module& mod) {
767
3
  AddLinkedBinding(env, napi_module_to_node_module(&mod));
768
3
}
769
770
6
void AddLinkedBinding(Environment* env,
771
                      const char* name,
772
                      addon_context_register_func fn,
773
                      void* priv) {
774
6
  node_module mod = {
775
    NODE_MODULE_VERSION,
776
    NM_F_LINKED,
777
    nullptr,  // nm_dso_handle
778
    nullptr,  // nm_filename
779
    nullptr,  // nm_register_func
780
    fn,
781
    name,
782
    priv,
783
    nullptr   // nm_link
784
6
  };
785
6
  AddLinkedBinding(env, mod);
786
6
}
787
788
static std::atomic<uint64_t> next_thread_id{0};
789
790
6754
ThreadId AllocateEnvironmentThreadId() {
791
6754
  return ThreadId { next_thread_id++ };
792
}
793
794
609
[[noreturn]] void Exit(ExitCode exit_code) {
795
609
  exit(static_cast<int>(exit_code));
796
}
797
798
605
void DefaultProcessExitHandlerInternal(Environment* env, ExitCode exit_code) {
799
605
  env->set_can_call_into_js(false);
800
605
  env->stop_sub_worker_contexts();
801
605
  env->isolate()->DumpAndResetStats();
802
  // When the process exits, the tasks in the thread pool may also need to
803
  // access the data of V8Platform, such as trace agent, or a field
804
  // added in the future. So make sure the thread pool exits first.
805
  // And make sure V8Platform don not call into Libuv threadpool, see Dispose
806
  // in node_v8_platform-inl.h
807
605
  uv_library_shutdown();
808
605
  DisposePlatform();
809
605
  Exit(exit_code);
810
}
811
812
void DefaultProcessExitHandler(Environment* env, int exit_code) {
813
  DefaultProcessExitHandlerInternal(env, static_cast<ExitCode>(exit_code));
814
}
815
816
void SetProcessExitHandler(
817
    Environment* env, std::function<void(Environment*, ExitCode)>&& handler) {
818
  env->set_process_exit_handler(std::move(handler));
819
}
820
821
759
void SetProcessExitHandler(Environment* env,
822
                           std::function<void(Environment*, int)>&& handler) {
823
759
  auto movedHandler = std::move(handler);
824
759
  env->set_process_exit_handler([=](Environment* env, ExitCode exit_code) {
825
64
    movedHandler(env, static_cast<int>(exit_code));
826
64
  });
827
759
}
828
829
}  // namespace node