GCC Code Coverage Report
Directory: ../ Exec Total Coverage
File: /home/iojs/build/workspace/node-test-commit-linux-coverage/nodes/benchmark/out/../src/node_native_module.cc Lines: 126 130 96.9 %
Date: 2019-01-07 12:15:22 Branches: 29 44 65.9 %

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

14387
NODE_MODULE_CONTEXT_AWARE_INTERNAL(
328
    native_module, node::native_module::NativeModuleLoader::Initialize)