GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: api/hooks.cc Lines: 106 118 89.8 %
Date: 2022-10-07 04:22:37 Branches: 31 48 64.6 %

Line Branch Exec Source
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
using v8::Value;
20
21
6377
void RunAtExit(Environment* env) {
22
6377
  env->RunAtExitCallbacks();
23
6377
}
24
25
12598
void AtExit(Environment* env, void (*cb)(void* arg), void* arg) {
26
12598
  CHECK_NOT_NULL(env);
27
12598
  env->AtExit(cb, arg);
28
12598
}
29
30
void EmitBeforeExit(Environment* env) {
31
  USE(EmitProcessBeforeExit(env));
32
}
33
34
5385
Maybe<bool> EmitProcessBeforeExit(Environment* env) {
35

15738
  TRACE_EVENT0(TRACING_CATEGORY_NODE1(environment), "BeforeExit");
36
5385
  if (!env->destroy_async_id_list()->empty())
37
247
    AsyncWrap::DestroyAsyncIdsCallback(env);
38
39
10768
  HandleScope handle_scope(env->isolate());
40
5385
  Local<Context> context = env->context();
41
5385
  Context::Scope context_scope(context);
42
43
  Local<Value> exit_code_v;
44
16155
  if (!env->process_object()->Get(context, env->exit_code_string())
45
5385
      .ToLocal(&exit_code_v)) return Nothing<bool>();
46
47
  Local<Integer> exit_code;
48
10768
  if (!exit_code_v->ToInteger(context).ToLocal(&exit_code)) {
49
    return Nothing<bool>();
50
  }
51
52
5384
  return ProcessEmit(env, "beforeExit", exit_code).IsEmpty() ?
53
5382
      Nothing<bool>() : Just(true);
54
}
55
56
static ExitCode EmitExitInternal(Environment* env) {
57
  return EmitProcessExitInternal(env).FromMaybe(ExitCode::kGenericUserError);
58
}
59
60
int EmitExit(Environment* env) {
61
  return static_cast<int>(EmitExitInternal(env));
62
}
63
64
5344
Maybe<ExitCode> EmitProcessExitInternal(Environment* env) {
65
  // process.emit('exit')
66
5344
  Isolate* isolate = env->isolate();
67
10678
  HandleScope handle_scope(isolate);
68
5344
  Local<Context> context = env->context();
69
5344
  Context::Scope context_scope(context);
70
5344
  Local<Object> process_object = env->process_object();
71
72
  // TODO(addaleax): It might be nice to share process.exitCode via
73
  // getter/setter pairs that pass data directly to the native side, so that we
74
  // don't manually have to read and write JS properties here. These getters
75
  // could use e.g. a typed array for performance.
76
5344
  env->set_exiting(true);
77
78
5344
  Local<String> exit_code = env->exit_code_string();
79
  Local<Value> code_v;
80
  int code;
81
5344
  if (!process_object->Get(context, exit_code).ToLocal(&code_v) ||
82
10688
      !code_v->Int32Value(context).To(&code) ||
83
16022
      ProcessEmit(env, "exit", Integer::New(isolate, code)).IsEmpty() ||
84
      // Reload exit code, it may be changed by `emit('exit')`
85

21336
      !process_object->Get(context, exit_code).ToLocal(&code_v) ||
86

15992
      !code_v->Int32Value(context).To(&code)) {
87
5
    return Nothing<ExitCode>();
88
  }
89
90
5329
  return Just(static_cast<ExitCode>(code));
91
}
92
93
Maybe<int> EmitProcessExit(Environment* env) {
94
  Maybe<ExitCode> result = EmitProcessExitInternal(env);
95
  if (result.IsNothing()) {
96
    return Nothing<int>();
97
  }
98
  return Just(static_cast<int>(result.FromJust()));
99
}
100
101
typedef void (*CleanupHook)(void* arg);
102
typedef void (*AsyncCleanupHook)(void* arg, void(*)(void*), void*);
103
104
struct AsyncCleanupHookInfo final {
105
  Environment* env;
106
  AsyncCleanupHook fun;
107
  void* arg;
108
  bool started = false;
109
  // Use a self-reference to make sure the storage is kept alive while the
110
  // cleanup hook is registered but not yet finished.
111
  std::shared_ptr<AsyncCleanupHookInfo> self;
112
};
113
114
// Opaque type that is basically an alias for `shared_ptr<AsyncCleanupHookInfo>`
115
// (but not publicly so for easier ABI/API changes). In particular,
116
// std::shared_ptr does not generally maintain a consistent ABI even on a
117
// specific platform.
118
struct ACHHandle final {
119
  std::shared_ptr<AsyncCleanupHookInfo> info;
120
};
121
// This is implemented as an operator on a struct because otherwise you can't
122
// default-initialize AsyncCleanupHookHandle, because in C++ for a
123
// std::unique_ptr to be default-initializable the deleter type also needs
124
// to be default-initializable; in particular, function types don't satisfy
125
// this.
126
10
void DeleteACHHandle::operator ()(ACHHandle* handle) const { delete handle; }
127
128
41
void AddEnvironmentCleanupHook(Isolate* isolate,
129
                               CleanupHook fun,
130
                               void* arg) {
131
41
  Environment* env = Environment::GetCurrent(isolate);
132
41
  CHECK_NOT_NULL(env);
133
41
  env->AddCleanupHook(fun, arg);
134
41
}
135
136
27
void RemoveEnvironmentCleanupHook(Isolate* isolate,
137
                                  CleanupHook fun,
138
                                  void* arg) {
139
27
  Environment* env = Environment::GetCurrent(isolate);
140
27
  CHECK_NOT_NULL(env);
141
27
  env->RemoveCleanupHook(fun, arg);
142
27
}
143
144
6
static void FinishAsyncCleanupHook(void* arg) {
145
6
  AsyncCleanupHookInfo* info = static_cast<AsyncCleanupHookInfo*>(arg);
146
12
  std::shared_ptr<AsyncCleanupHookInfo> keep_alive = info->self;
147
148
6
  info->env->DecreaseWaitingRequestCounter();
149
6
  info->self.reset();
150
6
}
151
152
6
static void RunAsyncCleanupHook(void* arg) {
153
6
  AsyncCleanupHookInfo* info = static_cast<AsyncCleanupHookInfo*>(arg);
154
6
  info->env->IncreaseWaitingRequestCounter();
155
6
  info->started = true;
156
6
  info->fun(info->arg, FinishAsyncCleanupHook, info);
157
6
}
158
159
10
ACHHandle* AddEnvironmentCleanupHookInternal(
160
    Isolate* isolate,
161
    AsyncCleanupHook fun,
162
    void* arg) {
163
10
  Environment* env = Environment::GetCurrent(isolate);
164
10
  CHECK_NOT_NULL(env);
165
10
  auto info = std::make_shared<AsyncCleanupHookInfo>();
166
10
  info->env = env;
167
10
  info->fun = fun;
168
10
  info->arg = arg;
169
10
  info->self = info;
170
10
  env->AddCleanupHook(RunAsyncCleanupHook, info.get());
171
10
  return new ACHHandle { info };
172
}
173
174
10
void RemoveEnvironmentCleanupHookInternal(
175
    ACHHandle* handle) {
176
10
  if (handle->info->started) return;
177
4
  handle->info->self.reset();
178
4
  handle->info->env->RemoveCleanupHook(RunAsyncCleanupHook, handle->info.get());
179
}
180
181
3
void RequestInterrupt(Environment* env, void (*fun)(void* arg), void* arg) {
182
3
  env->RequestInterrupt([fun, arg](Environment* env) {
183
    // Disallow JavaScript execution during interrupt.
184
    Isolate::DisallowJavascriptExecutionScope scope(
185
        env->isolate(),
186
4
        Isolate::DisallowJavascriptExecutionScope::CRASH_ON_FAILURE);
187
3
    fun(arg);
188
1
  });
189
3
}
190
191
2
async_id AsyncHooksGetExecutionAsyncId(Isolate* isolate) {
192
2
  Environment* env = Environment::GetCurrent(isolate);
193
2
  if (env == nullptr) return -1;
194
2
  return env->execution_async_id();
195
}
196
197
2
async_id AsyncHooksGetTriggerAsyncId(Isolate* isolate) {
198
2
  Environment* env = Environment::GetCurrent(isolate);
199
2
  if (env == nullptr) return -1;
200
2
  return env->trigger_async_id();
201
}
202
203
204
542
async_context EmitAsyncInit(Isolate* isolate,
205
                            Local<Object> resource,
206
                            const char* name,
207
                            async_id trigger_async_id) {
208
1084
  HandleScope handle_scope(isolate);
209
  Local<String> type =
210
542
      String::NewFromUtf8(isolate, name, NewStringType::kInternalized)
211
542
          .ToLocalChecked();
212
542
  return EmitAsyncInit(isolate, resource, type, trigger_async_id);
213
}
214
215
542
async_context EmitAsyncInit(Isolate* isolate,
216
                            Local<Object> resource,
217
                            Local<String> name,
218
                            async_id trigger_async_id) {
219
542
  DebugSealHandleScope handle_scope(isolate);
220
542
  Environment* env = Environment::GetCurrent(isolate);
221
542
  CHECK_NOT_NULL(env);
222
223
  // Initialize async context struct
224
542
  if (trigger_async_id == -1)
225
539
    trigger_async_id = env->get_default_trigger_async_id();
226
227
  async_context context = {
228
542
    env->new_async_id(),  // async_id_
229
    trigger_async_id  // trigger_async_id_
230
542
  };
231
232
  // Run init hooks
233
542
  AsyncWrap::EmitAsyncInit(env, resource, name, context.async_id,
234
                           context.trigger_async_id);
235
236
542
  return context;
237
}
238
239
3
void EmitAsyncDestroy(Isolate* isolate, async_context asyncContext) {
240
3
  EmitAsyncDestroy(Environment::GetCurrent(isolate), asyncContext);
241
3
}
242
243
538
void EmitAsyncDestroy(Environment* env, async_context asyncContext) {
244
538
  AsyncWrap::EmitDestroy(env, asyncContext.async_id);
245
538
}
246
247
}  // namespace node