GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: node_native_module.cc Lines: 100 108 92.6 %
Date: 2022-03-11 04:15:03 Branches: 39 46 84.8 %

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

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