GCC Code Coverage Report
Directory: ../ Exec Total Coverage
File: /home/iojs/build/workspace/node-test-commit-linux-coverage-daily/nodes/benchmark/out/../src/node_native_module.cc Lines: 127 130 97.7 %
Date: 2019-02-13 22:28:58 Branches: 30 44 68.2 %

Line Branch Exec Source
1
#include "node_native_module.h"
2
#include "node_errors.h"
3
4
namespace node {
5
6
namespace per_process {
7
4314
native_module::NativeModuleLoader native_module_loader;
8
}  // namespace per_process
9
10
namespace native_module {
11
12
using v8::Array;
13
using v8::ArrayBuffer;
14
using v8::ArrayBufferCreationMode;
15
using v8::Context;
16
using v8::DEFAULT;
17
using v8::EscapableHandleScope;
18
using v8::Function;
19
using v8::FunctionCallbackInfo;
20
using v8::HandleScope;
21
using v8::Integer;
22
using v8::IntegrityLevel;
23
using v8::Isolate;
24
using v8::Local;
25
using v8::Maybe;
26
using v8::MaybeLocal;
27
using v8::Name;
28
using v8::None;
29
using v8::Object;
30
using v8::PropertyCallbackInfo;
31
using v8::Script;
32
using v8::ScriptCompiler;
33
using v8::ScriptOrigin;
34
using v8::Set;
35
using v8::SideEffectType;
36
using v8::String;
37
using v8::Uint8Array;
38
using v8::Value;
39
40
// TODO(joyeecheung): make these more general and put them into util.h
41
63
Local<Object> MapToObject(Local<Context> context,
42
                          const NativeModuleRecordMap& in) {
43
63
  Isolate* isolate = context->GetIsolate();
44
63
  Local<Object> out = Object::New(isolate);
45
12474
  for (auto const& x : in) {
46
12411
    Local<String> key = OneByteString(isolate, x.first.c_str(), x.first.size());
47
37233
    out->Set(context, key, x.second.ToStringChecked(isolate)).FromJust();
48
  }
49
63
  return out;
50
}
51
52
2
Local<Set> ToJsSet(Local<Context> context,
53
                   const std::set<std::string>& in) {
54
2
  Isolate* isolate = context->GetIsolate();
55
2
  Local<Set> out = Set::New(isolate);
56
171
  for (auto const& x : in) {
57
507
    out->Add(context, OneByteString(isolate, x.c_str(), x.size()))
58
338
        .ToLocalChecked();
59
  }
60
2
  return out;
61
}
62
63
4265
bool NativeModuleLoader::Exists(const char* id) {
64
4265
  return source_.find(id) != source_.end();
65
}
66
67
1
void NativeModuleLoader::GetCacheUsage(
68
    const FunctionCallbackInfo<Value>& args) {
69
1
  Environment* env = Environment::GetCurrent(args);
70
1
  Isolate* isolate = env->isolate();
71
1
  Local<Context> context = env->context();
72
1
  Local<Object> result = Object::New(isolate);
73
  result
74
      ->Set(env->context(),
75
            OneByteString(isolate, "compiledWithCache"),
76
5
            ToJsSet(context, env->native_modules_with_cache))
77
2
      .FromJust();
78
  result
79
      ->Set(env->context(),
80
            OneByteString(isolate, "compiledWithoutCache"),
81
5
            ToJsSet(context, env->native_modules_without_cache))
82
2
      .FromJust();
83
2
  args.GetReturnValue().Set(result);
84
1
}
85
86
4416
void NativeModuleLoader::ModuleIdsGetter(
87
    Local<Name> property, const PropertyCallbackInfo<Value>& info) {
88
4416
  Isolate* isolate = info.GetIsolate();
89
90
  const NativeModuleRecordMap& source_ =
91
4416
      per_process::native_module_loader.source_;
92
4416
  std::vector<Local<Value>> ids;
93
4416
  ids.reserve(source_.size());
94
95
874368
  for (auto const& x : source_) {
96
1739904
    ids.push_back(OneByteString(isolate, x.first.c_str(), x.first.size()));
97
  }
98
99
13248
  info.GetReturnValue().Set(Array::New(isolate, ids.data(), ids.size()));
100
4416
}
101
102
4411
void NativeModuleLoader::ConfigStringGetter(
103
    Local<Name> property, const PropertyCallbackInfo<Value>& info) {
104
  info.GetReturnValue().Set(
105
13233
      per_process::native_module_loader.GetConfigString(info.GetIsolate()));
106
4411
}
107
108
63
Local<Object> NativeModuleLoader::GetSourceObject(
109
    Local<Context> context) const {
110
63
  return MapToObject(context, source_);
111
}
112
113
4474
Local<String> NativeModuleLoader::GetConfigString(Isolate* isolate) const {
114
4474
  return config_.ToStringChecked(isolate);
115
}
116
117
4314
NativeModuleLoader::NativeModuleLoader() : config_(GetConfig()) {
118
4314
  LoadJavaScriptSource();
119
4314
  LoadCodeCache();
120
4314
}
121
122
// This is supposed to be run only by the main thread in
123
// tools/generate_code_cache.js
124
162
void NativeModuleLoader::GetCodeCache(const FunctionCallbackInfo<Value>& args) {
125
162
  Environment* env = Environment::GetCurrent(args);
126
162
  Isolate* isolate = env->isolate();
127
162
  CHECK(env->is_main_thread());
128
129
486
  CHECK(args[0]->IsString());
130
324
  node::Utf8Value id_v(isolate, args[0].As<String>());
131
162
  const char* id = *id_v;
132
133
162
  const NativeModuleLoader& loader = per_process::native_module_loader;
134
162
  MaybeLocal<Uint8Array> ret = loader.GetCodeCache(isolate, id);
135
162
  if (!ret.IsEmpty()) {
136
324
    args.GetReturnValue().Set(ret.ToLocalChecked());
137
162
  }
138
162
}
139
140
// This is supposed to be run only by the main thread in
141
// tools/generate_code_cache.js
142
162
MaybeLocal<Uint8Array> NativeModuleLoader::GetCodeCache(Isolate* isolate,
143
                                                        const char* id) const {
144
162
  EscapableHandleScope scope(isolate);
145
324
  Mutex::ScopedLock lock(code_cache_mutex_);
146
147
162
  ScriptCompiler::CachedData* cached_data = nullptr;
148
162
  const auto it = code_cache_.find(id);
149
162
  if (it == code_cache_.end()) {
150
    // The module has not been compiled before.
151
    return MaybeLocal<Uint8Array>();
152
  }
153
154
162
  cached_data = it->second.get();
155
156
324
  MallocedBuffer<uint8_t> copied(cached_data->length);
157
162
  memcpy(copied.data, cached_data->data, cached_data->length);
158
  Local<ArrayBuffer> buf =
159
      ArrayBuffer::New(isolate,
160
162
                       copied.release(),
161
                       cached_data->length,
162
324
                       ArrayBufferCreationMode::kInternalized);
163
324
  return scope.Escape(Uint8Array::New(buf, 0, cached_data->length));
164
}
165
166
348474
void NativeModuleLoader::CompileFunction(
167
    const FunctionCallbackInfo<Value>& args) {
168
348474
  Environment* env = Environment::GetCurrent(args);
169
1045422
  CHECK(args[0]->IsString());
170
696948
  node::Utf8Value id(env->isolate(), args[0].As<String>());
171
172
348475
  MaybeLocal<Function> result = CompileAsModule(env, *id);
173
348475
  if (!result.IsEmpty()) {
174
696950
    args.GetReturnValue().Set(result.ToLocalChecked());
175
348475
  }
176
348475
}
177
178
348473
MaybeLocal<Function> NativeModuleLoader::CompileAsModule(Environment* env,
179
                                                         const char* id) {
180
  std::vector<Local<String>> parameters = {env->exports_string(),
181
                                           env->require_string(),
182
                                           env->module_string(),
183
                                           env->process_string(),
184
                                           env->internal_binding_string(),
185
348473
                                           env->primordials_string()};
186
  return per_process::native_module_loader.LookupAndCompile(
187
348474
      env->context(), id, &parameters, env);
188
}
189
190
// Returns Local<Function> of the compiled module if return_code_cache
191
// is false (we are only compiling the function).
192
// Otherwise return a Local<Object> containing the cache.
193
375069
MaybeLocal<Function> NativeModuleLoader::LookupAndCompile(
194
    Local<Context> context,
195
    const char* id,
196
    std::vector<Local<String>>* parameters,
197
    Environment* optional_env) {
198
375069
  Isolate* isolate = context->GetIsolate();
199
375072
  EscapableHandleScope scope(isolate);
200
  Local<Value> ret;  // Used to convert to MaybeLocal before return
201
202
375071
  const auto source_it = source_.find(id);
203
375070
  CHECK_NE(source_it, source_.end());
204
375070
  Local<String> source = source_it->second.ToStringChecked(isolate);
205
206
750142
  std::string filename_s = id + std::string(".js");
207
  Local<String> filename =
208
375070
      OneByteString(isolate, filename_s.c_str(), filename_s.size());
209
375069
  Local<Integer> line_offset = Integer::New(isolate, 0);
210
375064
  Local<Integer> column_offset = Integer::New(isolate, 0);
211
375071
  ScriptOrigin origin(filename, line_offset, column_offset, True(isolate));
212
213
750143
  Mutex::ScopedLock lock(code_cache_mutex_);
214
215
375072
  ScriptCompiler::CachedData* cached_data = nullptr;
216
  {
217
375072
    auto cache_it = code_cache_.find(id);
218
375072
    if (cache_it != code_cache_.end()) {
219
      // Transfer ownership to ScriptCompiler::Source later.
220
14478
      cached_data = cache_it->second.release();
221
14478
      code_cache_.erase(cache_it);
222
    }
223
  }
224
225
375072
  const bool use_cache = cached_data != nullptr;
226
  ScriptCompiler::CompileOptions options =
227
      use_cache ? ScriptCompiler::kConsumeCodeCache
228
375072
                : ScriptCompiler::kEagerCompile;
229
  ScriptCompiler::Source script_source(source, origin, cached_data);
230
231
  MaybeLocal<Function> maybe_fun =
232
      ScriptCompiler::CompileFunctionInContext(context,
233
                                               &script_source,
234
                                               parameters->size(),
235
                                               parameters->data(),
236
                                               0,
237
                                               nullptr,
238
375072
                                               options);
239
240
  // This could fail when there are early errors in the native modules,
241
  // e.g. the syntax errors
242
375072
  if (maybe_fun.IsEmpty()) {
243
    // In the case of early errors, v8 is already capable of
244
    // decorating the stack for us - note that we use CompileFunctionInContext
245
    // so there is no need to worry about wrappers.
246
    return MaybeLocal<Function>();
247
  }
248
249
375072
  Local<Function> fun = maybe_fun.ToLocalChecked();
250
  // XXX(joyeecheung): this bookkeeping is not exactly accurate because
251
  // it only starts after the Environment is created, so the per_context.js
252
  // will never be in any of these two sets, but the two sets are only for
253
  // testing anyway.
254
375072
  if (use_cache) {
255
14478
    if (optional_env != nullptr) {
256
      // This could happen when Node is run with any v8 flag, but
257
      // the cache is not generated with one
258
9801
      if (script_source.GetCachedData()->rejected) {
259
        optional_env->native_modules_without_cache.insert(id);
260
      } else {
261
9801
        optional_env->native_modules_with_cache.insert(id);
262
      }
263
    }
264
  } else {
265
360594
    if (optional_env != nullptr) {
266
356328
      optional_env->native_modules_without_cache.insert(id);
267
    }
268
  }
269
270
  // Generate new cache for next compilation
271
  std::unique_ptr<ScriptCompiler::CachedData> new_cached_data(
272
750144
      ScriptCompiler::CreateCodeCacheForFunction(fun));
273
375072
  CHECK_NOT_NULL(new_cached_data);
274
275
  // The old entry should've been erased by now so we can just emplace
276
375072
  code_cache_.emplace(id, std::move(new_cached_data));
277
278
375072
  return scope.Escape(fun);
279
}
280
281
4416
void NativeModuleLoader::Initialize(Local<Object> target,
282
                                    Local<Value> unused,
283
                                    Local<Context> context,
284
                                    void* priv) {
285
4416
  Environment* env = Environment::GetCurrent(context);
286
287
17664
  CHECK(target
288
            ->SetAccessor(env->context(),
289
                          env->config_string(),
290
                          ConfigStringGetter,
291
                          nullptr,
292
                          MaybeLocal<Value>(),
293
                          DEFAULT,
294
                          None,
295
                          SideEffectType::kHasNoSideEffect)
296
            .FromJust());
297
17664
  CHECK(target
298
            ->SetAccessor(env->context(),
299
                          FIXED_ONE_BYTE_STRING(env->isolate(), "moduleIds"),
300
                          ModuleIdsGetter,
301
                          nullptr,
302
                          MaybeLocal<Value>(),
303
                          DEFAULT,
304
                          None,
305
                          SideEffectType::kHasNoSideEffect)
306
            .FromJust());
307
308
  env->SetMethod(
309
4416
      target, "getCacheUsage", NativeModuleLoader::GetCacheUsage);
310
  env->SetMethod(
311
4416
      target, "compileFunction", NativeModuleLoader::CompileFunction);
312
4416
  env->SetMethod(target, "getCodeCache", NativeModuleLoader::GetCodeCache);
313
  // internalBinding('native_module') should be frozen
314
8832
  target->SetIntegrityLevel(context, IntegrityLevel::kFrozen).FromJust();
315
4416
}
316
317
}  // namespace native_module
318
}  // namespace node
319
320

17256
NODE_MODULE_CONTEXT_AWARE_INTERNAL(
321
    native_module, node::native_module::NativeModuleLoader::Initialize)