GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: api/hooks.cc Lines: 102 107 95.3 %
Date: 2022-05-17 04:15:46 Branches: 32 48 66.7 %

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
6109
void RunAtExit(Environment* env) {
22
6109
  env->RunAtExitCallbacks();
23
6109
}
24
25
12034
void AtExit(Environment* env, void (*cb)(void* arg), void* arg) {
26
12034
  CHECK_NOT_NULL(env);
27
12034
  env->AtExit(cb, arg);
28
12034
}
29
30
void EmitBeforeExit(Environment* env) {
31
  USE(EmitProcessBeforeExit(env));
32
}
33
34
5121
Maybe<bool> EmitProcessBeforeExit(Environment* env) {
35

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

20330
      !process_object->Get(context, exit_code).ToLocal(&code_v) ||
85

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