Coverage Report

Created: 2025-09-05 10:05

/src/node/src/api/hooks.cc
Line
Count
Source (jump to first uncovered line)
1
#include "env-inl.h"
2
#include "node_internals.h"
3
#include "node_process-inl.h"
4
#include "async_wrap.h"
5
6
namespace node {
7
8
using v8::Context;
9
using v8::HandleScope;
10
using v8::Integer;
11
using v8::Isolate;
12
using v8::Just;
13
using v8::Local;
14
using v8::Maybe;
15
using v8::NewStringType;
16
using v8::Nothing;
17
using v8::Object;
18
using v8::String;
19
20
211k
void RunAtExit(Environment* env) {
21
211k
  env->RunAtExitCallbacks();
22
211k
}
23
24
254k
void AtExit(Environment* env, void (*cb)(void* arg), void* arg) {
25
254k
  CHECK_NOT_NULL(env);
26
254k
  env->AtExit(cb, arg);
27
254k
}
28
29
0
void EmitBeforeExit(Environment* env) {
30
0
  USE(EmitProcessBeforeExit(env));
31
0
}
32
33
0
Maybe<bool> EmitProcessBeforeExit(Environment* env) {
34
0
  TRACE_EVENT0(TRACING_CATEGORY_NODE1(environment), "BeforeExit");
35
0
  if (!env->destroy_async_id_list()->empty())
36
0
    AsyncWrap::DestroyAsyncIdsCallback(env);
37
38
0
  Isolate* isolate = env->isolate();
39
0
  HandleScope handle_scope(isolate);
40
0
  Context::Scope context_scope(env->context());
41
42
0
  if (!env->can_call_into_js()) {
43
0
    return Nothing<bool>();
44
0
  }
45
46
0
  Local<Integer> exit_code = Integer::New(
47
0
      isolate, static_cast<int32_t>(env->exit_code(ExitCode::kNoFailure)));
48
49
0
  return ProcessEmit(env, "beforeExit", exit_code).IsEmpty() ?
50
0
      Nothing<bool>() : Just(true);
51
0
}
52
53
0
static ExitCode EmitExitInternal(Environment* env) {
54
0
  return EmitProcessExitInternal(env).FromMaybe(ExitCode::kGenericUserError);
55
0
}
56
57
0
int EmitExit(Environment* env) {
58
0
  return static_cast<int>(EmitExitInternal(env));
59
0
}
60
61
0
Maybe<ExitCode> EmitProcessExitInternal(Environment* env) {
62
  // process.emit('exit')
63
0
  Isolate* isolate = env->isolate();
64
0
  HandleScope handle_scope(isolate);
65
0
  Context::Scope context_scope(env->context());
66
67
0
  env->set_exiting(true);
68
69
0
  if (!env->can_call_into_js()) {
70
0
    return Nothing<ExitCode>();
71
0
  }
72
73
0
  Local<Integer> exit_code = Integer::New(
74
0
      isolate, static_cast<int32_t>(env->exit_code(ExitCode::kNoFailure)));
75
76
0
  if (ProcessEmit(env, "exit", exit_code).IsEmpty()) {
77
0
    return Nothing<ExitCode>();
78
0
  }
79
  // Reload exit code, it may be changed by `emit('exit')`
80
0
  return Just(env->exit_code(ExitCode::kNoFailure));
81
0
}
82
83
0
Maybe<int> EmitProcessExit(Environment* env) {
84
0
  Maybe<ExitCode> result = EmitProcessExitInternal(env);
85
0
  if (result.IsNothing()) {
86
0
    return Nothing<int>();
87
0
  }
88
0
  return Just(static_cast<int>(result.FromJust()));
89
0
}
90
91
typedef void (*CleanupHook)(void* arg);
92
typedef void (*AsyncCleanupHook)(void* arg, void(*)(void*), void*);
93
94
struct AsyncCleanupHookInfo final {
95
  Environment* env;
96
  AsyncCleanupHook fun;
97
  void* arg;
98
  bool started = false;
99
  // Use a self-reference to make sure the storage is kept alive while the
100
  // cleanup hook is registered but not yet finished.
101
  std::shared_ptr<AsyncCleanupHookInfo> self;
102
};
103
104
// Opaque type that is basically an alias for `shared_ptr<AsyncCleanupHookInfo>`
105
// (but not publicly so for easier ABI/API changes). In particular,
106
// std::shared_ptr does not generally maintain a consistent ABI even on a
107
// specific platform.
108
struct ACHHandle final {
109
  std::shared_ptr<AsyncCleanupHookInfo> info;
110
};
111
// This is implemented as an operator on a struct because otherwise you can't
112
// default-initialize AsyncCleanupHookHandle, because in C++ for a
113
// std::unique_ptr to be default-initializable the deleter type also needs
114
// to be default-initializable; in particular, function types don't satisfy
115
// this.
116
0
void DeleteACHHandle::operator ()(ACHHandle* handle) const { delete handle; }
117
118
void AddEnvironmentCleanupHook(Isolate* isolate,
119
                               CleanupHook fun,
120
0
                               void* arg) {
121
0
  Environment* env = Environment::GetCurrent(isolate);
122
0
  CHECK_NOT_NULL(env);
123
0
  env->AddCleanupHook(fun, arg);
124
0
}
125
126
void RemoveEnvironmentCleanupHook(Isolate* isolate,
127
                                  CleanupHook fun,
128
0
                                  void* arg) {
129
0
  Environment* env = Environment::GetCurrent(isolate);
130
0
  CHECK_NOT_NULL(env);
131
0
  env->RemoveCleanupHook(fun, arg);
132
0
}
133
134
0
static void FinishAsyncCleanupHook(void* arg) {
135
0
  AsyncCleanupHookInfo* info = static_cast<AsyncCleanupHookInfo*>(arg);
136
0
  std::shared_ptr<AsyncCleanupHookInfo> keep_alive = info->self;
137
138
0
  info->env->DecreaseWaitingRequestCounter();
139
0
  info->self.reset();
140
0
}
141
142
0
static void RunAsyncCleanupHook(void* arg) {
143
0
  AsyncCleanupHookInfo* info = static_cast<AsyncCleanupHookInfo*>(arg);
144
0
  info->env->IncreaseWaitingRequestCounter();
145
0
  info->started = true;
146
0
  info->fun(info->arg, FinishAsyncCleanupHook, info);
147
0
}
148
149
ACHHandle* AddEnvironmentCleanupHookInternal(
150
    Isolate* isolate,
151
    AsyncCleanupHook fun,
152
0
    void* arg) {
153
0
  Environment* env = Environment::GetCurrent(isolate);
154
0
  CHECK_NOT_NULL(env);
155
0
  auto info = std::make_shared<AsyncCleanupHookInfo>();
156
0
  info->env = env;
157
0
  info->fun = fun;
158
0
  info->arg = arg;
159
0
  info->self = info;
160
0
  env->AddCleanupHook(RunAsyncCleanupHook, info.get());
161
0
  return new ACHHandle { info };
162
0
}
163
164
void RemoveEnvironmentCleanupHookInternal(
165
0
    ACHHandle* handle) {
166
0
  if (handle->info->started) return;
167
0
  handle->info->self.reset();
168
0
  handle->info->env->RemoveCleanupHook(RunAsyncCleanupHook, handle->info.get());
169
0
}
170
171
0
void RequestInterrupt(Environment* env, void (*fun)(void* arg), void* arg) {
172
0
  env->RequestInterrupt([fun, arg](Environment* env) {
173
    // Disallow JavaScript execution during interrupt.
174
0
    Isolate::DisallowJavascriptExecutionScope scope(
175
0
        env->isolate(),
176
0
        Isolate::DisallowJavascriptExecutionScope::CRASH_ON_FAILURE);
177
0
    fun(arg);
178
0
  });
179
0
}
180
181
0
async_id AsyncHooksGetExecutionAsyncId(Isolate* isolate) {
182
0
  Environment* env = Environment::GetCurrent(isolate);
183
0
  if (env == nullptr) return -1;
184
0
  return env->execution_async_id();
185
0
}
186
187
0
async_id AsyncHooksGetTriggerAsyncId(Isolate* isolate) {
188
0
  Environment* env = Environment::GetCurrent(isolate);
189
0
  if (env == nullptr) return -1;
190
0
  return env->trigger_async_id();
191
0
}
192
193
194
async_context EmitAsyncInit(Isolate* isolate,
195
                            Local<Object> resource,
196
                            const char* name,
197
0
                            async_id trigger_async_id) {
198
0
  HandleScope handle_scope(isolate);
199
0
  Local<String> type =
200
0
      String::NewFromUtf8(isolate, name, NewStringType::kInternalized)
201
0
          .ToLocalChecked();
202
0
  return EmitAsyncInit(isolate, resource, type, trigger_async_id);
203
0
}
204
205
async_context EmitAsyncInit(Isolate* isolate,
206
                            Local<Object> resource,
207
                            Local<String> name,
208
0
                            async_id trigger_async_id) {
209
0
  DebugSealHandleScope handle_scope(isolate);
210
0
  Environment* env = Environment::GetCurrent(isolate);
211
0
  CHECK_NOT_NULL(env);
212
213
  // Initialize async context struct
214
0
  if (trigger_async_id == -1)
215
0
    trigger_async_id = env->get_default_trigger_async_id();
216
217
0
  async_context context = {
218
0
    env->new_async_id(),  // async_id_
219
0
    trigger_async_id  // trigger_async_id_
220
0
  };
221
222
  // Run init hooks
223
0
  AsyncWrap::EmitAsyncInit(env, resource, name, context.async_id,
224
0
                           context.trigger_async_id);
225
226
0
  return context;
227
0
}
228
229
0
void EmitAsyncDestroy(Isolate* isolate, async_context asyncContext) {
230
0
  EmitAsyncDestroy(Environment::GetCurrent(isolate), asyncContext);
231
0
}
232
233
0
void EmitAsyncDestroy(Environment* env, async_context asyncContext) {
234
0
  AsyncWrap::EmitDestroy(env, asyncContext.async_id);
235
0
}
236
237
}  // namespace node