• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include "node_native_module.h"
2 #include "util-inl.h"
3 
4 namespace node {
5 namespace native_module {
6 
7 using v8::Context;
8 using v8::EscapableHandleScope;
9 using v8::Function;
10 using v8::Integer;
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 
NativeModuleLoader()21 NativeModuleLoader::NativeModuleLoader() : config_(GetConfig()) {
22   LoadJavaScriptSource();
23 }
24 
GetInstance()25 NativeModuleLoader* NativeModuleLoader::GetInstance() {
26   return &instance_;
27 }
28 
Exists(const char * id)29 bool NativeModuleLoader::Exists(const char* id) {
30   return source_.find(id) != source_.end();
31 }
32 
Add(const char * id,const UnionBytes & source)33 bool NativeModuleLoader::Add(const char* id, const UnionBytes& source) {
34   if (Exists(id)) {
35     return false;
36   }
37   source_.emplace(id, source);
38   return true;
39 }
40 
GetSourceObject(Local<Context> context)41 Local<Object> NativeModuleLoader::GetSourceObject(Local<Context> context) {
42   Isolate* isolate = context->GetIsolate();
43   Local<Object> out = Object::New(isolate);
44   for (auto const& x : source_) {
45     Local<String> key = OneByteString(isolate, x.first.c_str(), x.first.size());
46     out->Set(context, key, x.second.ToStringChecked(isolate)).FromJust();
47   }
48   return out;
49 }
50 
GetConfigString(Isolate * isolate)51 Local<String> NativeModuleLoader::GetConfigString(Isolate* isolate) {
52   return config_.ToStringChecked(isolate);
53 }
54 
GetModuleIds()55 std::vector<std::string> NativeModuleLoader::GetModuleIds() {
56   std::vector<std::string> ids;
57   ids.reserve(source_.size());
58   for (auto const& x : source_) {
59     ids.emplace_back(x.first);
60   }
61   return ids;
62 }
63 
InitializeModuleCategories()64 void NativeModuleLoader::InitializeModuleCategories() {
65   if (module_categories_.is_initialized) {
66     DCHECK(!module_categories_.can_be_required.empty());
67     return;
68   }
69 
70   std::vector<std::string> prefixes = {
71 #if !HAVE_OPENSSL
72     "internal/crypto/",
73 #endif  // !HAVE_OPENSSL
74 
75     "internal/bootstrap/",
76     "internal/per_context/",
77     "internal/deps/",
78     "internal/main/"
79   };
80 
81   module_categories_.can_be_required.emplace(
82       "internal/deps/cjs-module-lexer/lexer");
83 
84   module_categories_.cannot_be_required = std::set<std::string> {
85 #if !HAVE_INSPECTOR
86       "inspector",
87       "internal/util/inspector",
88 #endif  // !HAVE_INSPECTOR
89 
90 #if !NODE_USE_V8_PLATFORM || !defined(NODE_HAVE_I18N_SUPPORT)
91       "trace_events",
92 #endif  // !NODE_USE_V8_PLATFORM
93 
94 #if !HAVE_OPENSSL
95       "crypto",
96       "https",
97       "http2",
98       "tls",
99       "_tls_common",
100       "_tls_wrap",
101       "internal/http2/core",
102       "internal/http2/compat",
103       "internal/policy/manifest",
104       "internal/process/policy",
105       "internal/streams/lazy_transform",
106 #endif  // !HAVE_OPENSSL
107 
108       "sys",  // Deprecated.
109       "wasi",  // Experimental.
110       "internal/test/binding",
111       "internal/v8_prof_polyfill",
112       "internal/v8_prof_processor",
113   };
114 
115   for (auto const& x : source_) {
116     const std::string& id = x.first;
117     for (auto const& prefix : prefixes) {
118       if (prefix.length() > id.length()) {
119         continue;
120       }
121       if (id.find(prefix) == 0 &&
122           module_categories_.can_be_required.count(id) == 0) {
123         module_categories_.cannot_be_required.emplace(id);
124       }
125     }
126   }
127 
128   for (auto const& x : source_) {
129     const std::string& id = x.first;
130     if (0 == module_categories_.cannot_be_required.count(id)) {
131       module_categories_.can_be_required.emplace(id);
132     }
133   }
134 
135   module_categories_.is_initialized = true;
136 }
137 
GetCannotBeRequired()138 const std::set<std::string>& NativeModuleLoader::GetCannotBeRequired() {
139   InitializeModuleCategories();
140   return module_categories_.cannot_be_required;
141 }
142 
GetCanBeRequired()143 const std::set<std::string>& NativeModuleLoader::GetCanBeRequired() {
144   InitializeModuleCategories();
145   return module_categories_.can_be_required;
146 }
147 
CanBeRequired(const char * id)148 bool NativeModuleLoader::CanBeRequired(const char* id) {
149   return GetCanBeRequired().count(id) == 1;
150 }
151 
CannotBeRequired(const char * id)152 bool NativeModuleLoader::CannotBeRequired(const char* id) {
153   return GetCannotBeRequired().count(id) == 1;
154 }
155 
code_cache()156 NativeModuleCacheMap* NativeModuleLoader::code_cache() {
157   return &code_cache_;
158 }
159 
GetCodeCache(const char * id) const160 ScriptCompiler::CachedData* NativeModuleLoader::GetCodeCache(
161     const char* id) const {
162   Mutex::ScopedLock lock(code_cache_mutex_);
163   const auto it = code_cache_.find(id);
164   if (it == code_cache_.end()) {
165     // The module has not been compiled before.
166     return nullptr;
167   }
168   return it->second.get();
169 }
170 
CompileAsModule(Local<Context> context,const char * id,NativeModuleLoader::Result * result)171 MaybeLocal<Function> NativeModuleLoader::CompileAsModule(
172     Local<Context> context,
173     const char* id,
174     NativeModuleLoader::Result* result) {
175   Isolate* isolate = context->GetIsolate();
176   std::vector<Local<String>> parameters = {
177       FIXED_ONE_BYTE_STRING(isolate, "exports"),
178       FIXED_ONE_BYTE_STRING(isolate, "require"),
179       FIXED_ONE_BYTE_STRING(isolate, "module"),
180       FIXED_ONE_BYTE_STRING(isolate, "process"),
181       FIXED_ONE_BYTE_STRING(isolate, "internalBinding"),
182       FIXED_ONE_BYTE_STRING(isolate, "primordials")};
183   return LookupAndCompile(context, id, &parameters, result);
184 }
185 
186 #ifdef NODE_BUILTIN_MODULES_PATH
OnDiskFileName(const char * id)187 static std::string OnDiskFileName(const char* id) {
188   std::string filename = NODE_BUILTIN_MODULES_PATH;
189   filename += "/";
190 
191   if (strncmp(id, "internal/deps", strlen("internal/deps")) == 0) {
192     id += strlen("internal/");
193   } else {
194     filename += "lib/";
195   }
196   filename += id;
197   filename += ".js";
198 
199   return filename;
200 }
201 #endif  // NODE_BUILTIN_MODULES_PATH
202 
LoadBuiltinModuleSource(Isolate * isolate,const char * id)203 MaybeLocal<String> NativeModuleLoader::LoadBuiltinModuleSource(Isolate* isolate,
204                                                                const char* id) {
205 #ifdef NODE_BUILTIN_MODULES_PATH
206   std::string filename = OnDiskFileName(id);
207 
208   uv_fs_t req;
209   uv_file file =
210       uv_fs_open(nullptr, &req, filename.c_str(), O_RDONLY, 0, nullptr);
211   CHECK_GE(req.result, 0);
212   uv_fs_req_cleanup(&req);
213 
214   auto defer_close = OnScopeLeave([file]() {
215     uv_fs_t close_req;
216     CHECK_EQ(0, uv_fs_close(nullptr, &close_req, file, nullptr));
217     uv_fs_req_cleanup(&close_req);
218   });
219 
220   std::string contents;
221   char buffer[4096];
222   uv_buf_t buf = uv_buf_init(buffer, sizeof(buffer));
223 
224   while (true) {
225     const int r =
226         uv_fs_read(nullptr, &req, file, &buf, 1, contents.length(), nullptr);
227     CHECK_GE(req.result, 0);
228     uv_fs_req_cleanup(&req);
229     if (r <= 0) {
230       break;
231     }
232     contents.append(buf.base, r);
233   }
234 
235   return String::NewFromUtf8(
236       isolate, contents.c_str(), v8::NewStringType::kNormal, contents.length());
237 #else
238   const auto source_it = source_.find(id);
239   CHECK_NE(source_it, source_.end());
240   return source_it->second.ToStringChecked(isolate);
241 #endif  // NODE_BUILTIN_MODULES_PATH
242 }
243 
244 // Returns Local<Function> of the compiled module if return_code_cache
245 // is false (we are only compiling the function).
246 // Otherwise return a Local<Object> containing the cache.
LookupAndCompile(Local<Context> context,const char * id,std::vector<Local<String>> * parameters,NativeModuleLoader::Result * result)247 MaybeLocal<Function> NativeModuleLoader::LookupAndCompile(
248     Local<Context> context,
249     const char* id,
250     std::vector<Local<String>>* parameters,
251     NativeModuleLoader::Result* result) {
252   Isolate* isolate = context->GetIsolate();
253   EscapableHandleScope scope(isolate);
254 
255   Local<String> source;
256   if (!LoadBuiltinModuleSource(isolate, id).ToLocal(&source)) {
257     return {};
258   }
259 
260   std::string filename_s = id + std::string(".js");
261   Local<String> filename =
262       OneByteString(isolate, filename_s.c_str(), filename_s.size());
263   Local<Integer> line_offset = Integer::New(isolate, 0);
264   Local<Integer> column_offset = Integer::New(isolate, 0);
265   ScriptOrigin origin(filename, line_offset, column_offset, True(isolate));
266 
267   ScriptCompiler::CachedData* cached_data = nullptr;
268   {
269     // Note: The lock here should not extend into the
270     // `CompileFunctionInContext()` call below, because this function may
271     // recurse if there is a syntax error during bootstrap (because the fatal
272     // exception handler is invoked, which may load built-in modules).
273     Mutex::ScopedLock lock(code_cache_mutex_);
274     auto cache_it = code_cache_.find(id);
275     if (cache_it != code_cache_.end()) {
276       // Transfer ownership to ScriptCompiler::Source later.
277       cached_data = cache_it->second.release();
278       code_cache_.erase(cache_it);
279     }
280   }
281 
282   const bool has_cache = cached_data != nullptr;
283   ScriptCompiler::CompileOptions options =
284       has_cache ? ScriptCompiler::kConsumeCodeCache
285                 : ScriptCompiler::kEagerCompile;
286   ScriptCompiler::Source script_source(source, origin, cached_data);
287 
288   MaybeLocal<Function> maybe_fun =
289       ScriptCompiler::CompileFunctionInContext(context,
290                                                &script_source,
291                                                parameters->size(),
292                                                parameters->data(),
293                                                0,
294                                                nullptr,
295                                                options);
296 
297   // This could fail when there are early errors in the native modules,
298   // e.g. the syntax errors
299   Local<Function> fun;
300   if (!maybe_fun.ToLocal(&fun)) {
301     // In the case of early errors, v8 is already capable of
302     // decorating the stack for us - note that we use CompileFunctionInContext
303     // so there is no need to worry about wrappers.
304     return MaybeLocal<Function>();
305   }
306 
307   // XXX(joyeecheung): this bookkeeping is not exactly accurate because
308   // it only starts after the Environment is created, so the per_context.js
309   // will never be in any of these two sets, but the two sets are only for
310   // testing anyway.
311 
312   *result = (has_cache && !script_source.GetCachedData()->rejected)
313                 ? Result::kWithCache
314                 : Result::kWithoutCache;
315   // Generate new cache for next compilation
316   std::unique_ptr<ScriptCompiler::CachedData> new_cached_data(
317       ScriptCompiler::CreateCodeCacheForFunction(fun));
318   CHECK_NOT_NULL(new_cached_data);
319 
320   {
321     Mutex::ScopedLock lock(code_cache_mutex_);
322     // The old entry should've been erased by now so we can just emplace.
323     // If another thread did the same thing in the meantime, that should not
324     // be an issue.
325     code_cache_.emplace(id, std::move(new_cached_data));
326   }
327 
328   return scope.Escape(fun);
329 }
330 
331 }  // namespace native_module
332 }  // namespace node
333