GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: node_native_module.cc Lines: 100 108 92.6 %
Date: 2021-10-02 04:13:01 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
4932
NativeModuleLoader::NativeModuleLoader() : config_(GetConfig()) {
22
4932
  LoadJavaScriptSource();
23
4932
}
24
25
405916
NativeModuleLoader* NativeModuleLoader::GetInstance() {
26
405916
  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
10152
  for (auto const& x : source_) {
45
10116
    Local<String> key = OneByteString(isolate, x.first.c_str(), x.first.size());
46
30348
    out->Set(context, key, x.second.ToStringChecked(isolate)).FromJust();
47
  }
48
36
  return out;
49
}
50
51
656
Local<String> NativeModuleLoader::GetConfigString(Isolate* isolate) {
52
656
  return config_.ToStringChecked(isolate);
53
}
54
55
659
std::vector<std::string> NativeModuleLoader::GetModuleIds() {
56
659
  std::vector<std::string> ids;
57
659
  ids.reserve(source_.size());
58
185993
  for (auto const& x : source_) {
59
185334
    ids.emplace_back(x.first);
60
  }
61
659
  return ids;
62
}
63
64
1688
void NativeModuleLoader::InitializeModuleCategories() {
65
1688
  if (module_categories_.is_initialized) {
66
    DCHECK(!module_categories_.can_be_required.empty());
67
1681
    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
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
1974
  for (auto const& x : source_) {
120
1967
    const std::string& id = x.first;
121
9835
    for (auto const& prefix : prefixes) {
122
7868
      if (prefix.length() > id.length()) {
123
2415
        continue;
124
      }
125

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