GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: node_native_module.cc Lines: 104 117 88.9 %
Date: 2022-05-23 04:15:47 Branches: 47 56 83.9 %

Line Branch Exec Source
1
#include "node_native_module.h"
2
#include "debug_utils-inl.h"
3
#include "node_internals.h"
4
#include "util-inl.h"
5
6
namespace node {
7
namespace native_module {
8
9
using v8::Context;
10
using v8::EscapableHandleScope;
11
using v8::Function;
12
using v8::Isolate;
13
using v8::Local;
14
using v8::MaybeLocal;
15
using v8::Object;
16
using v8::ScriptCompiler;
17
using v8::ScriptOrigin;
18
using v8::String;
19
20
NativeModuleLoader NativeModuleLoader::instance_;
21
22
5255
NativeModuleLoader::NativeModuleLoader() : config_(GetConfig()) {
23
5255
  LoadJavaScriptSource();
24
5255
}
25
26
415454
NativeModuleLoader* NativeModuleLoader::GetInstance() {
27
415454
  return &instance_;
28
}
29
30
17
bool NativeModuleLoader::Exists(const char* id) {
31
17
  return source_.find(id) != source_.end();
32
}
33
34
17
bool NativeModuleLoader::Add(const char* id, const UnionBytes& source) {
35
17
  if (Exists(id)) {
36
    return false;
37
  }
38
17
  source_.emplace(id, source);
39
17
  return true;
40
}
41
42
41
Local<Object> NativeModuleLoader::GetSourceObject(Local<Context> context) {
43
41
  Isolate* isolate = context->GetIsolate();
44
41
  Local<Object> out = Object::New(isolate);
45
12259
  for (auto const& x : source_) {
46
12218
    Local<String> key = OneByteString(isolate, x.first.c_str(), x.first.size());
47
36654
    out->Set(context, key, x.second.ToStringChecked(isolate)).FromJust();
48
  }
49
41
  return out;
50
}
51
52
888
Local<String> NativeModuleLoader::GetConfigString(Isolate* isolate) {
53
888
  return config_.ToStringChecked(isolate);
54
}
55
56
891
std::vector<std::string> NativeModuleLoader::GetModuleIds() {
57
891
  std::vector<std::string> ids;
58
891
  ids.reserve(source_.size());
59
266564
  for (auto const& x : source_) {
60
265673
    ids.emplace_back(x.first);
61
  }
62
891
  return ids;
63
}
64
65
1790
void NativeModuleLoader::InitializeModuleCategories() {
66
1790
  if (module_categories_.is_initialized) {
67
    DCHECK(!module_categories_.can_be_required.empty());
68
1783
    return;
69
  }
70
71
  std::vector<std::string> prefixes = {
72
#if !HAVE_OPENSSL
73
    "internal/crypto/",
74
    "internal/debugger/",
75
#endif  // !HAVE_OPENSSL
76
77
    "internal/bootstrap/",
78
    "internal/per_context/",
79
    "internal/deps/",
80
    "internal/main/"
81
49
  };
82
83
  module_categories_.can_be_required.emplace(
84
7
      "internal/deps/cjs-module-lexer/lexer");
85
86
49
  module_categories_.cannot_be_required = std::set<std::string> {
87
#if !HAVE_INSPECTOR
88
      "inspector",
89
      "internal/util/inspector",
90
#endif  // !HAVE_INSPECTOR
91
92
#if !NODE_USE_V8_PLATFORM || !defined(NODE_HAVE_I18N_SUPPORT)
93
      "trace_events",
94
#endif  // !NODE_USE_V8_PLATFORM || !defined(NODE_HAVE_I18N_SUPPORT)
95
96
#if !HAVE_OPENSSL
97
      "crypto",
98
      "crypto/promises",
99
      "https",
100
      "http2",
101
      "tls",
102
      "_tls_common",
103
      "_tls_wrap",
104
      "internal/tls/secure-pair",
105
      "internal/tls/parse-cert-string",
106
      "internal/tls/secure-context",
107
      "internal/http2/core",
108
      "internal/http2/compat",
109
      "internal/policy/manifest",
110
      "internal/process/policy",
111
      "internal/streams/lazy_transform",
112
#endif  // !HAVE_OPENSSL
113
      "sys",  // Deprecated.
114
      "wasi",  // Experimental.
115
      "internal/test/binding",
116
      "internal/v8_prof_polyfill",
117
      "internal/v8_prof_processor",
118
42
  };
119
120
2093
  for (auto const& x : source_) {
121
2086
    const std::string& id = x.first;
122
10430
    for (auto const& prefix : prefixes) {
123
8344
      if (prefix.length() > id.length()) {
124
2443
        continue;
125
      }
126

6174
      if (id.find(prefix) == 0 &&
127
273
          module_categories_.can_be_required.count(id) == 0) {
128
266
        module_categories_.cannot_be_required.emplace(id);
129
      }
130
    }
131
  }
132
133
2093
  for (auto const& x : source_) {
134
2086
    const std::string& id = x.first;
135
2086
    if (0 == module_categories_.cannot_be_required.count(id)) {
136
1785
      module_categories_.can_be_required.emplace(id);
137
    }
138
  }
139
140
7
  module_categories_.is_initialized = true;
141
}
142
143
1
const std::set<std::string>& NativeModuleLoader::GetCannotBeRequired() {
144
1
  InitializeModuleCategories();
145
1
  return module_categories_.cannot_be_required;
146
}
147
148
1789
const std::set<std::string>& NativeModuleLoader::GetCanBeRequired() {
149
1789
  InitializeModuleCategories();
150
1789
  return module_categories_.can_be_required;
151
}
152
153
1788
bool NativeModuleLoader::CanBeRequired(const char* id) {
154
1788
  return GetCanBeRequired().count(id) == 1;
155
}
156
157
bool NativeModuleLoader::CannotBeRequired(const char* id) {
158
  return GetCannotBeRequired().count(id) == 1;
159
}
160
161
5187
NativeModuleCacheMap* NativeModuleLoader::code_cache() {
162
5187
  return &code_cache_;
163
}
164
165
ScriptCompiler::CachedData* NativeModuleLoader::GetCodeCache(
166
    const char* id) const {
167
  Mutex::ScopedLock lock(code_cache_mutex_);
168
  const auto it = code_cache_.find(id);
169
  if (it == code_cache_.end()) {
170
    // The module has not been compiled before.
171
    return nullptr;
172
  }
173
  return it->second.get();
174
}
175
176
399318
MaybeLocal<Function> NativeModuleLoader::CompileAsModule(
177
    Local<Context> context,
178
    const char* id,
179
    NativeModuleLoader::Result* result) {
180
399318
  Isolate* isolate = context->GetIsolate();
181
  std::vector<Local<String>> parameters = {
182
399318
      FIXED_ONE_BYTE_STRING(isolate, "exports"),
183
399318
      FIXED_ONE_BYTE_STRING(isolate, "require"),
184
399318
      FIXED_ONE_BYTE_STRING(isolate, "module"),
185
399318
      FIXED_ONE_BYTE_STRING(isolate, "process"),
186
399318
      FIXED_ONE_BYTE_STRING(isolate, "internalBinding"),
187
798636
      FIXED_ONE_BYTE_STRING(isolate, "primordials")};
188
399318
  return LookupAndCompile(context, id, &parameters, result);
189
}
190
191
#ifdef NODE_BUILTIN_MODULES_PATH
192
static std::string OnDiskFileName(const char* id) {
193
  std::string filename = NODE_BUILTIN_MODULES_PATH;
194
  filename += "/";
195
196
  if (strncmp(id, "internal/deps", strlen("internal/deps")) == 0) {
197
    id += strlen("internal/");
198
  } else {
199
    filename += "lib/";
200
  }
201
  filename += id;
202
  filename += ".js";
203
204
  return filename;
205
}
206
#endif  // NODE_BUILTIN_MODULES_PATH
207
208
409958
MaybeLocal<String> NativeModuleLoader::LoadBuiltinModuleSource(Isolate* isolate,
209
                                                               const char* id) {
210
#ifdef NODE_BUILTIN_MODULES_PATH
211
  if (strncmp(id, "embedder_main_", strlen("embedder_main_")) == 0) {
212
#endif  // NODE_BUILTIN_MODULES_PATH
213
409958
    const auto source_it = source_.find(id);
214
409958
    if (UNLIKELY(source_it == source_.end())) {
215
      fprintf(stderr, "Cannot find native builtin: \"%s\".\n", id);
216
      ABORT();
217
    }
218
819916
    return source_it->second.ToStringChecked(isolate);
219
#ifdef NODE_BUILTIN_MODULES_PATH
220
  }
221
  std::string filename = OnDiskFileName(id);
222
223
  std::string contents;
224
  int r = ReadFileSync(&contents, filename.c_str());
225
  if (r != 0) {
226
    const std::string buf = SPrintF("Cannot read local builtin. %s: %s \"%s\"",
227
                                    uv_err_name(r),
228
                                    uv_strerror(r),
229
                                    filename);
230
    Local<String> message = OneByteString(isolate, buf.c_str());
231
    isolate->ThrowException(v8::Exception::Error(message));
232
    return MaybeLocal<String>();
233
  }
234
  return String::NewFromUtf8(
235
      isolate, contents.c_str(), v8::NewStringType::kNormal, contents.length());
236
#endif  // NODE_BUILTIN_MODULES_PATH
237
}
238
239
// Returns Local<Function> of the compiled module if return_code_cache
240
// is false (we are only compiling the function).
241
// Otherwise return a Local<Object> containing the cache.
242
409958
MaybeLocal<Function> NativeModuleLoader::LookupAndCompile(
243
    Local<Context> context,
244
    const char* id,
245
    std::vector<Local<String>>* parameters,
246
    NativeModuleLoader::Result* result) {
247
409958
  Isolate* isolate = context->GetIsolate();
248
409958
  EscapableHandleScope scope(isolate);
249
250
  Local<String> source;
251
819916
  if (!LoadBuiltinModuleSource(isolate, id).ToLocal(&source)) {
252
    return {};
253
  }
254
255
1229874
  std::string filename_s = std::string("node:") + id;
256
  Local<String> filename =
257
409958
      OneByteString(isolate, filename_s.c_str(), filename_s.size());
258
409958
  ScriptOrigin origin(isolate, filename, 0, 0, true);
259
260
409958
  ScriptCompiler::CachedData* cached_data = nullptr;
261
  {
262
    // Note: The lock here should not extend into the
263
    // `CompileFunctionInContext()` call below, because this function may
264
    // recurse if there is a syntax error during bootstrap (because the fatal
265
    // exception handler is invoked, which may load built-in modules).
266
819916
    Mutex::ScopedLock lock(code_cache_mutex_);
267
409958
    auto cache_it = code_cache_.find(id);
268
409958
    if (cache_it != code_cache_.end()) {
269
      // Transfer ownership to ScriptCompiler::Source later.
270
393792
      cached_data = cache_it->second.release();
271
393792
      code_cache_.erase(cache_it);
272
    }
273
  }
274
275
409958
  const bool has_cache = cached_data != nullptr;
276
409958
  ScriptCompiler::CompileOptions options =
277
409958
      has_cache ? ScriptCompiler::kConsumeCodeCache
278
                : ScriptCompiler::kEagerCompile;
279
409958
  ScriptCompiler::Source script_source(source, origin, cached_data);
280
281
  per_process::Debug(DebugCategory::CODE_CACHE,
282
                     "Compiling %s %s code cache\n",
283
                     id,
284
409958
                     has_cache ? "with" : "without");
285
286
  MaybeLocal<Function> maybe_fun =
287
      ScriptCompiler::CompileFunctionInContext(context,
288
                                               &script_source,
289
                                               parameters->size(),
290
                                               parameters->data(),
291
                                               0,
292
                                               nullptr,
293
409958
                                               options);
294
295
  // This could fail when there are early errors in the native modules,
296
  // e.g. the syntax errors
297
  Local<Function> fun;
298
409958
  if (!maybe_fun.ToLocal(&fun)) {
299
    // In the case of early errors, v8 is already capable of
300
    // decorating the stack for us - note that we use CompileFunctionInContext
301
    // so there is no need to worry about wrappers.
302
    return MaybeLocal<Function>();
303
  }
304
305
  // XXX(joyeecheung): this bookkeeping is not exactly accurate because
306
  // it only starts after the Environment is created, so the per_context.js
307
  // will never be in any of these two sets, but the two sets are only for
308
  // testing anyway.
309
310
393792
  *result = (has_cache && !script_source.GetCachedData()->rejected)
311
803750
                ? Result::kWithCache
312
                : Result::kWithoutCache;
313
314
409958
  if (has_cache) {
315
393792
    per_process::Debug(DebugCategory::CODE_CACHE,
316
                       "Code cache of %s (%s) %s\n",
317
                       id,
318
393792
                       script_source.GetCachedData()->buffer_policy ==
319
                               ScriptCompiler::CachedData::BufferNotOwned
320
787584
                           ? "BufferNotOwned"
321
                           : "BufferOwned",
322
393792
                       script_source.GetCachedData()->rejected ? "is rejected"
323
                                                               : "is accepted");
324
  }
325
326
  // Generate new cache for next compilation
327
  std::unique_ptr<ScriptCompiler::CachedData> new_cached_data(
328
409958
      ScriptCompiler::CreateCodeCacheForFunction(fun));
329
409958
  CHECK_NOT_NULL(new_cached_data);
330
331
  {
332
819916
    Mutex::ScopedLock lock(code_cache_mutex_);
333
409958
    const auto it = code_cache_.find(id);
334
    // TODO(joyeecheung): it's safer for each thread to have its own
335
    // copy of the code cache map.
336
409958
    if (it == code_cache_.end()) {
337
408211
      code_cache_.emplace(id, std::move(new_cached_data));
338
    } else {
339
1747
      it->second.reset(new_cached_data.release());
340
    }
341
  }
342
343
409958
  return scope.Escape(fun);
344
}
345
346
}  // namespace native_module
347
}  // namespace node