Coverage Report

Created: 2026-01-21 08:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/node/src/api/embed_helpers.cc
Line
Count
Source
1
#include "debug_utils-inl.h"
2
#include "env-inl.h"
3
#include "node.h"
4
#include "node_internals.h"
5
#include "node_snapshot_builder.h"
6
7
using v8::Context;
8
using v8::Function;
9
using v8::Global;
10
using v8::HandleScope;
11
using v8::Isolate;
12
using v8::Just;
13
using v8::Local;
14
using v8::Locker;
15
using v8::Maybe;
16
using v8::Nothing;
17
using v8::SealHandleScope;
18
using v8::SnapshotCreator;
19
using v8::TryCatch;
20
21
namespace node {
22
23
0
Maybe<ExitCode> SpinEventLoopInternal(Environment* env) {
24
0
  CHECK_NOT_NULL(env);
25
0
  MultiIsolatePlatform* platform = GetMultiIsolatePlatform(env);
26
0
  CHECK_NOT_NULL(platform);
27
28
0
  Isolate* isolate = env->isolate();
29
0
  HandleScope handle_scope(isolate);
30
0
  Context::Scope context_scope(env->context());
31
0
  SealHandleScope seal(isolate);
32
33
0
  if (env->is_stopping()) return Nothing<ExitCode>();
34
35
0
  env->set_trace_sync_io(env->options()->trace_sync_io);
36
0
  {
37
0
    bool more;
38
0
    env->performance_state()->Mark(
39
0
        node::performance::NODE_PERFORMANCE_MILESTONE_LOOP_START);
40
0
    do {
41
0
      if (env->is_stopping()) break;
42
0
      uv_run(env->event_loop(), UV_RUN_DEFAULT);
43
0
      if (env->is_stopping()) break;
44
45
0
      platform->DrainTasks(isolate);
46
47
0
      more = uv_loop_alive(env->event_loop());
48
0
      if (more && !env->is_stopping()) continue;
49
50
0
      if (EmitProcessBeforeExit(env).IsNothing())
51
0
        break;
52
53
0
      {
54
0
        HandleScope handle_scope(isolate);
55
0
        if (env->RunSnapshotSerializeCallback().IsEmpty()) {
56
0
          break;
57
0
        }
58
0
      }
59
60
      // Emit `beforeExit` if the loop became alive either after emitting
61
      // event, or after running some callbacks.
62
0
      more = uv_loop_alive(env->event_loop());
63
0
    } while (more == true && !env->is_stopping());
64
0
    env->performance_state()->Mark(
65
0
        node::performance::NODE_PERFORMANCE_MILESTONE_LOOP_EXIT);
66
0
  }
67
0
  if (env->is_stopping()) return Nothing<ExitCode>();
68
69
0
  env->set_trace_sync_io(false);
70
  // Clear the serialize callback even though the JS-land queue should
71
  // be empty this point so that the deserialized instance won't
72
  // attempt to call into JS again.
73
0
  env->set_snapshot_serialize_callback(Local<Function>());
74
75
0
  env->PrintInfoForSnapshotIfDebug();
76
0
  env->ForEachRealm([](Realm* realm) { realm->VerifyNoStrongBaseObjects(); });
77
0
  return EmitProcessExitInternal(env);
78
0
}
79
80
struct CommonEnvironmentSetup::Impl {
81
  MultiIsolatePlatform* platform = nullptr;
82
  uv_loop_t loop;
83
  std::shared_ptr<ArrayBufferAllocator> allocator;
84
  std::optional<SnapshotCreator> snapshot_creator;
85
  Isolate* isolate = nullptr;
86
  DeleteFnPtr<IsolateData, FreeIsolateData> isolate_data;
87
  DeleteFnPtr<Environment, FreeEnvironment> env;
88
  Global<Context> main_context;
89
};
90
91
CommonEnvironmentSetup::CommonEnvironmentSetup(
92
    MultiIsolatePlatform* platform,
93
    std::vector<std::string>* errors,
94
    const EmbedderSnapshotData* snapshot_data,
95
    uint32_t flags,
96
    std::function<Environment*(const CommonEnvironmentSetup*)> make_env,
97
    const SnapshotConfig* snapshot_config)
98
0
    : impl_(new Impl()) {
99
0
  CHECK_NOT_NULL(platform);
100
0
  CHECK_NOT_NULL(errors);
101
102
0
  impl_->platform = platform;
103
0
  uv_loop_t* loop = &impl_->loop;
104
  // Use `data` to tell the destructor whether the loop was initialized or not.
105
0
  loop->data = nullptr;
106
0
  int ret = uv_loop_init(loop);
107
0
  if (ret != 0) {
108
0
    errors->push_back(
109
0
        SPrintF("Failed to initialize loop: %s", uv_err_name(ret)));
110
0
    return;
111
0
  }
112
0
  loop->data = this;
113
114
0
  impl_->allocator = ArrayBufferAllocator::Create();
115
0
  const std::vector<intptr_t>& external_references =
116
0
      SnapshotBuilder::CollectExternalReferences();
117
0
  Isolate::CreateParams params;
118
0
  params.array_buffer_allocator = impl_->allocator.get();
119
0
  params.external_references = external_references.data();
120
0
  params.cpp_heap =
121
0
      v8::CppHeap::Create(platform, v8::CppHeapCreateParams{{}}).release();
122
123
0
  Isolate* isolate;
124
125
  // Isolates created for snapshotting should be set up differently since
126
  // it will be owned by the snapshot creator and needs to be cleaned up
127
  // before serialization.
128
0
  if (flags & Flags::kIsForSnapshotting) {
129
    // The isolate must be registered before the SnapshotCreator initializes the
130
    // isolate, so that the memory reducer can be initialized.
131
0
    isolate = impl_->isolate = Isolate::Allocate(GetOrCreateIsolateGroup());
132
0
    platform->RegisterIsolate(isolate, loop);
133
134
0
    impl_->snapshot_creator.emplace(isolate, params);
135
0
    isolate->SetCaptureStackTraceForUncaughtExceptions(
136
0
        true,
137
0
        static_cast<int>(
138
0
            per_process::cli_options->per_isolate->stack_trace_limit),
139
0
        v8::StackTrace::StackTraceOptions::kDetailed);
140
0
    SetIsolateMiscHandlers(isolate, {});
141
0
  } else {
142
0
    isolate = impl_->isolate =
143
0
        NewIsolate(&params,
144
0
                   &impl_->loop,
145
0
                   platform,
146
0
                   SnapshotData::FromEmbedderWrapper(snapshot_data));
147
0
  }
148
149
0
  {
150
0
    Locker locker(isolate);
151
0
    Isolate::Scope isolate_scope(isolate);
152
0
    HandleScope handle_scope(isolate);
153
154
0
    TryCatch bootstrapCatch(isolate);
155
0
    auto print_Exception = OnScopeLeave([&]() {
156
0
      if (bootstrapCatch.HasCaught()) {
157
0
        errors->push_back(FormatCaughtException(
158
0
            isolate, isolate->GetCurrentContext(), bootstrapCatch));
159
0
      }
160
0
    });
161
162
0
    impl_->isolate_data.reset(CreateIsolateData(
163
0
        isolate, loop, platform, impl_->allocator.get(), snapshot_data));
164
0
    impl_->isolate_data->set_snapshot_config(snapshot_config);
165
166
0
    if (snapshot_data) {
167
0
      impl_->env.reset(make_env(this));
168
0
      if (impl_->env) {
169
0
        impl_->main_context.Reset(isolate, impl_->env->context());
170
0
      }
171
0
      return;
172
0
    }
173
174
0
    Local<Context> context = NewContext(isolate);
175
0
    impl_->main_context.Reset(isolate, context);
176
0
    if (context.IsEmpty()) {
177
0
      errors->push_back("Failed to initialize V8 Context");
178
0
      return;
179
0
    }
180
181
0
    Context::Scope context_scope(context);
182
0
    impl_->env.reset(make_env(this));
183
0
  }
184
0
}
185
186
CommonEnvironmentSetup::CommonEnvironmentSetup(
187
    MultiIsolatePlatform* platform,
188
    std::vector<std::string>* errors,
189
    std::function<Environment*(const CommonEnvironmentSetup*)> make_env)
190
0
    : CommonEnvironmentSetup(platform, errors, nullptr, false, make_env) {}
191
192
std::unique_ptr<CommonEnvironmentSetup>
193
CommonEnvironmentSetup::CreateForSnapshotting(
194
    MultiIsolatePlatform* platform,
195
    std::vector<std::string>* errors,
196
    const std::vector<std::string>& args,
197
    const std::vector<std::string>& exec_args,
198
0
    const SnapshotConfig& snapshot_config) {
199
  // It's not guaranteed that a context that goes through
200
  // v8_inspector::V8Inspector::contextCreated() is runtime-independent,
201
  // so do not start the inspector on the main context when building
202
  // the default snapshot.
203
0
  uint64_t env_flags =
204
0
      EnvironmentFlags::kDefaultFlags | EnvironmentFlags::kNoCreateInspector;
205
206
0
  auto ret = std::unique_ptr<CommonEnvironmentSetup>(new CommonEnvironmentSetup(
207
0
      platform,
208
0
      errors,
209
0
      nullptr,
210
0
      true,
211
0
      [&](const CommonEnvironmentSetup* setup) -> Environment* {
212
0
        return CreateEnvironment(
213
0
            setup->isolate_data(),
214
0
            setup->context(),
215
0
            args,
216
0
            exec_args,
217
0
            static_cast<EnvironmentFlags::Flags>(env_flags));
218
0
      },
219
0
      &snapshot_config));
220
0
  if (!errors->empty()) ret.reset();
221
0
  return ret;
222
0
}
223
224
0
CommonEnvironmentSetup::~CommonEnvironmentSetup() {
225
0
  if (impl_->isolate != nullptr) {
226
0
    Isolate* isolate = impl_->isolate;
227
0
    {
228
0
      Locker locker(isolate);
229
0
      Isolate::Scope isolate_scope(isolate);
230
231
0
      impl_->main_context.Reset();
232
0
      impl_->env.reset();
233
0
      impl_->isolate_data.reset();
234
0
    }
235
236
0
    bool platform_finished = false;
237
0
    impl_->platform->AddIsolateFinishedCallback(
238
0
        isolate,
239
0
        [](void* data) {
240
0
          bool* ptr = static_cast<bool*>(data);
241
0
          *ptr = true;
242
0
        },
243
0
        &platform_finished);
244
0
    if (impl_->snapshot_creator.has_value()) {
245
0
      impl_->snapshot_creator.reset();
246
0
    }
247
0
    impl_->platform->DisposeIsolate(isolate);
248
249
    // Wait until the platform has cleaned up all relevant resources.
250
0
    while (!platform_finished)
251
0
      uv_run(&impl_->loop, UV_RUN_ONCE);
252
0
  }
253
254
0
  if (impl_->isolate || impl_->loop.data != nullptr)
255
0
    CheckedUvLoopClose(&impl_->loop);
256
257
0
  delete impl_;
258
0
}
259
260
0
EmbedderSnapshotData::Pointer CommonEnvironmentSetup::CreateSnapshot() {
261
0
  CHECK_NOT_NULL(snapshot_creator());
262
0
  SnapshotData* snapshot_data = new SnapshotData();
263
0
  EmbedderSnapshotData::Pointer result{
264
0
      new EmbedderSnapshotData(snapshot_data, true)};
265
266
0
  auto exit_code = SnapshotBuilder::CreateSnapshot(snapshot_data, this);
267
0
  if (exit_code != ExitCode::kNoFailure) return {};
268
269
0
  return result;
270
0
}
271
272
0
Maybe<int> SpinEventLoop(Environment* env) {
273
0
  Maybe<ExitCode> result = SpinEventLoopInternal(env);
274
0
  if (result.IsNothing()) {
275
0
    return Nothing<int>();
276
0
  }
277
0
  return Just(static_cast<int>(result.FromJust()));
278
0
}
279
280
0
uv_loop_t* CommonEnvironmentSetup::event_loop() const {
281
0
  return &impl_->loop;
282
0
}
283
284
std::shared_ptr<ArrayBufferAllocator>
285
0
CommonEnvironmentSetup::array_buffer_allocator() const {
286
0
  return impl_->allocator;
287
0
}
288
289
0
Isolate* CommonEnvironmentSetup::isolate() const {
290
0
  return impl_->isolate;
291
0
}
292
293
0
IsolateData* CommonEnvironmentSetup::isolate_data() const {
294
0
  return impl_->isolate_data.get();
295
0
}
296
297
0
Environment* CommonEnvironmentSetup::env() const {
298
0
  return impl_->env.get();
299
0
}
300
301
0
v8::Local<v8::Context> CommonEnvironmentSetup::context() const {
302
0
  return impl_->main_context.Get(impl_->isolate);
303
0
}
304
305
0
v8::SnapshotCreator* CommonEnvironmentSetup::snapshot_creator() {
306
0
  return impl_->snapshot_creator ? &impl_->snapshot_creator.value() : nullptr;
307
0
}
308
309
void EmbedderSnapshotData::DeleteSnapshotData::operator()(
310
0
    const EmbedderSnapshotData* data) const {
311
0
  CHECK_IMPLIES(data->owns_impl_, data->impl_);
312
0
  if (data->owns_impl_ &&
313
0
      data->impl_->data_ownership == SnapshotData::DataOwnership::kOwned) {
314
0
    delete data->impl_;
315
0
  }
316
0
  delete data;
317
0
}
318
319
0
EmbedderSnapshotData::Pointer EmbedderSnapshotData::BuiltinSnapshotData() {
320
0
  return EmbedderSnapshotData::Pointer{new EmbedderSnapshotData(
321
0
      SnapshotBuilder::GetEmbeddedSnapshotData(), false)};
322
0
}
323
324
EmbedderSnapshotData::Pointer EmbedderSnapshotData::FromBlob(
325
0
    const std::vector<char>& in) {
326
0
  return FromBlob(std::string_view(in.data(), in.size()));
327
0
}
328
329
EmbedderSnapshotData::Pointer EmbedderSnapshotData::FromBlob(
330
0
    std::string_view in) {
331
0
  SnapshotData* snapshot_data = new SnapshotData();
332
0
  CHECK_EQ(snapshot_data->data_ownership, SnapshotData::DataOwnership::kOwned);
333
0
  EmbedderSnapshotData::Pointer result{
334
0
      new EmbedderSnapshotData(snapshot_data, true)};
335
0
  if (!SnapshotData::FromBlob(snapshot_data, in)) {
336
0
    return {};
337
0
  }
338
0
  return result;
339
0
}
340
341
0
EmbedderSnapshotData::Pointer EmbedderSnapshotData::FromFile(FILE* in) {
342
0
  return FromBlob(ReadFileSync(in));
343
0
}
344
345
0
std::vector<char> EmbedderSnapshotData::ToBlob() const {
346
0
  return impl_->ToBlob();
347
0
}
348
349
0
void EmbedderSnapshotData::ToFile(FILE* out) const {
350
0
  impl_->ToFile(out);
351
0
}
352
353
EmbedderSnapshotData::EmbedderSnapshotData(const SnapshotData* impl,
354
                                           bool owns_impl)
355
0
    : impl_(impl), owns_impl_(owns_impl) {}
356
357
0
bool EmbedderSnapshotData::CanUseCustomSnapshotPerIsolate() {
358
0
  return false;
359
0
}
360
361
}  // namespace node