GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
1 |
#include "module_wrap.h" |
||
2 |
|||
3 |
#include "env.h" |
||
4 |
#include "memory_tracker-inl.h" |
||
5 |
#include "node_errors.h" |
||
6 |
#include "node_url.h" |
||
7 |
#include "util-inl.h" |
||
8 |
#include "node_contextify.h" |
||
9 |
#include "node_watchdog.h" |
||
10 |
|||
11 |
#include <sys/stat.h> // S_IFDIR |
||
12 |
|||
13 |
#include <algorithm> |
||
14 |
#include <climits> // PATH_MAX |
||
15 |
|||
16 |
namespace node { |
||
17 |
namespace loader { |
||
18 |
|||
19 |
using errors::TryCatchScope; |
||
20 |
|||
21 |
using node::contextify::ContextifyContext; |
||
22 |
using node::url::URL; |
||
23 |
using node::url::URL_FLAGS_FAILED; |
||
24 |
using v8::Array; |
||
25 |
using v8::Context; |
||
26 |
using v8::Function; |
||
27 |
using v8::FunctionCallbackInfo; |
||
28 |
using v8::FunctionTemplate; |
||
29 |
using v8::Global; |
||
30 |
using v8::HandleScope; |
||
31 |
using v8::Integer; |
||
32 |
using v8::IntegrityLevel; |
||
33 |
using v8::Isolate; |
||
34 |
using v8::Just; |
||
35 |
using v8::Local; |
||
36 |
using v8::Maybe; |
||
37 |
using v8::MaybeLocal; |
||
38 |
using v8::Module; |
||
39 |
using v8::Nothing; |
||
40 |
using v8::Number; |
||
41 |
using v8::Object; |
||
42 |
using v8::PrimitiveArray; |
||
43 |
using v8::Promise; |
||
44 |
using v8::ScriptCompiler; |
||
45 |
using v8::ScriptOrigin; |
||
46 |
using v8::String; |
||
47 |
using v8::Undefined; |
||
48 |
using v8::Value; |
||
49 |
|||
50 |
static const char* const EXTENSIONS[] = { |
||
51 |
".mjs", |
||
52 |
".cjs", |
||
53 |
".js", |
||
54 |
".json", |
||
55 |
".node" |
||
56 |
}; |
||
57 |
|||
58 |
429 |
ModuleWrap::ModuleWrap(Environment* env, |
|
59 |
Local<Object> object, |
||
60 |
Local<Module> module, |
||
61 |
Local<String> url) : |
||
62 |
BaseObject(env, object), |
||
63 |
1716 |
id_(env->get_next_module_id()) { |
|
64 |
429 |
module_.Reset(env->isolate(), module); |
|
65 |
429 |
url_.Reset(env->isolate(), url); |
|
66 |
429 |
env->id_to_module_map.emplace(id_, this); |
|
67 |
429 |
} |
|
68 |
|||
69 |
2436 |
ModuleWrap::~ModuleWrap() { |
|
70 |
406 |
HandleScope scope(env()->isolate()); |
|
71 |
812 |
Local<Module> module = module_.Get(env()->isolate()); |
|
72 |
406 |
env()->id_to_module_map.erase(id_); |
|
73 |
406 |
auto range = env()->hash_to_module_map.equal_range(module->GetIdentityHash()); |
|
74 |
✓✗ | 406 |
for (auto it = range.first; it != range.second; ++it) { |
75 |
✓✗ | 406 |
if (it->second == this) { |
76 |
406 |
env()->hash_to_module_map.erase(it); |
|
77 |
406 |
break; |
|
78 |
} |
||
79 |
406 |
} |
|
80 |
✗✓ | 812 |
} |
81 |
|||
82 |
370 |
ModuleWrap* ModuleWrap::GetFromModule(Environment* env, |
|
83 |
Local<Module> module) { |
||
84 |
370 |
auto range = env->hash_to_module_map.equal_range(module->GetIdentityHash()); |
|
85 |
✓✗ | 370 |
for (auto it = range.first; it != range.second; ++it) { |
86 |
✓✗ | 740 |
if (it->second->module_ == module) { |
87 |
370 |
return it->second; |
|
88 |
} |
||
89 |
} |
||
90 |
return nullptr; |
||
91 |
} |
||
92 |
|||
93 |
38 |
ModuleWrap* ModuleWrap::GetFromID(Environment* env, uint32_t id) { |
|
94 |
38 |
auto module_wrap_it = env->id_to_module_map.find(id); |
|
95 |
✗✓ | 38 |
if (module_wrap_it == env->id_to_module_map.end()) { |
96 |
return nullptr; |
||
97 |
} |
||
98 |
38 |
return module_wrap_it->second; |
|
99 |
} |
||
100 |
|||
101 |
435 |
void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) { |
|
102 |
435 |
Environment* env = Environment::GetCurrent(args); |
|
103 |
435 |
Isolate* isolate = env->isolate(); |
|
104 |
|||
105 |
✗✓ | 435 |
CHECK(args.IsConstructCall()); |
106 |
435 |
Local<Object> that = args.This(); |
|
107 |
|||
108 |
435 |
const int argc = args.Length(); |
|
109 |
✗✓ | 435 |
CHECK_GE(argc, 2); |
110 |
|||
111 |
✗✓ | 1305 |
CHECK(args[0]->IsString()); |
112 |
870 |
Local<String> source_text = args[0].As<String>(); |
|
113 |
|||
114 |
✗✓ | 1305 |
CHECK(args[1]->IsString()); |
115 |
870 |
Local<String> url = args[1].As<String>(); |
|
116 |
|||
117 |
Local<Context> context; |
||
118 |
Local<Integer> line_offset; |
||
119 |
Local<Integer> column_offset; |
||
120 |
|||
121 |
✓✓ | 435 |
if (argc == 5) { |
122 |
// new ModuleWrap(source, url, context?, lineOffset, columnOffset) |
||
123 |
✓✓ | 180 |
if (args[2]->IsUndefined()) { |
124 |
53 |
context = that->CreationContext(); |
|
125 |
} else { |
||
126 |
✗✓ | 14 |
CHECK(args[2]->IsObject()); |
127 |
ContextifyContext* sandbox = |
||
128 |
ContextifyContext::ContextFromContextifiedSandbox( |
||
129 |
14 |
env, args[2].As<Object>()); |
|
130 |
✗✓ | 7 |
CHECK_NOT_NULL(sandbox); |
131 |
7 |
context = sandbox->context(); |
|
132 |
} |
||
133 |
|||
134 |
✗✓ | 120 |
CHECK(args[3]->IsNumber()); |
135 |
120 |
line_offset = args[3].As<Integer>(); |
|
136 |
|||
137 |
✗✓ | 120 |
CHECK(args[4]->IsNumber()); |
138 |
120 |
column_offset = args[4].As<Integer>(); |
|
139 |
} else { |
||
140 |
// new ModuleWrap(source, url) |
||
141 |
375 |
context = that->CreationContext(); |
|
142 |
375 |
line_offset = Integer::New(isolate, 0); |
|
143 |
375 |
column_offset = Integer::New(isolate, 0); |
|
144 |
} |
||
145 |
|||
146 |
435 |
ShouldNotAbortOnUncaughtScope no_abort_scope(env); |
|
147 |
✓✓ | 864 |
TryCatchScope try_catch(env); |
148 |
Local<Module> module; |
||
149 |
|||
150 |
Local<PrimitiveArray> host_defined_options = |
||
151 |
435 |
PrimitiveArray::New(isolate, HostDefinedOptions::kLength); |
|
152 |
host_defined_options->Set(isolate, HostDefinedOptions::kType, |
||
153 |
870 |
Number::New(isolate, ScriptType::kModule)); |
|
154 |
|||
155 |
// compile |
||
156 |
{ |
||
157 |
ScriptOrigin origin(url, |
||
158 |
line_offset, // line offset |
||
159 |
column_offset, // column offset |
||
160 |
True(isolate), // is cross origin |
||
161 |
Local<Integer>(), // script id |
||
162 |
Local<Value>(), // source map URL |
||
163 |
False(isolate), // is opaque (?) |
||
164 |
False(isolate), // is WASM |
||
165 |
True(isolate), // is ES Module |
||
166 |
435 |
host_defined_options); |
|
167 |
Context::Scope context_scope(context); |
||
168 |
429 |
ScriptCompiler::Source source(source_text, origin); |
|
169 |
✓✓ | 870 |
if (!ScriptCompiler::CompileModule(isolate, &source).ToLocal(&module)) { |
170 |
✓✗✓✗ ✓✗ |
6 |
if (try_catch.HasCaught() && !try_catch.HasTerminated()) { |
171 |
✗✓ | 12 |
CHECK(!try_catch.Message().IsEmpty()); |
172 |
✗✓ | 12 |
CHECK(!try_catch.Exception().IsEmpty()); |
173 |
AppendExceptionLine(env, try_catch.Exception(), try_catch.Message(), |
||
174 |
6 |
ErrorHandlingMode::MODULE_ERROR); |
|
175 |
6 |
try_catch.ReThrow(); |
|
176 |
} |
||
177 |
6 |
return; |
|
178 |
429 |
} |
|
179 |
} |
||
180 |
|||
181 |
✗✓ | 1716 |
if (!that->Set(context, env->url_string(), url).FromMaybe(false)) { |
182 |
return; |
||
183 |
} |
||
184 |
|||
185 |
429 |
ModuleWrap* obj = new ModuleWrap(env, that, module, url); |
|
186 |
429 |
obj->context_.Reset(isolate, context); |
|
187 |
|||
188 |
429 |
env->hash_to_module_map.emplace(module->GetIdentityHash(), obj); |
|
189 |
|||
190 |
host_defined_options->Set(isolate, HostDefinedOptions::kID, |
||
191 |
858 |
Number::New(isolate, obj->id())); |
|
192 |
|||
193 |
429 |
that->SetIntegrityLevel(context, IntegrityLevel::kFrozen); |
|
194 |
✓✓ | 1287 |
args.GetReturnValue().Set(that); |
195 |
} |
||
196 |
|||
197 |
406 |
void ModuleWrap::Link(const FunctionCallbackInfo<Value>& args) { |
|
198 |
406 |
Environment* env = Environment::GetCurrent(args); |
|
199 |
406 |
Isolate* isolate = args.GetIsolate(); |
|
200 |
|||
201 |
✗✓ | 406 |
CHECK_EQ(args.Length(), 1); |
202 |
✗✓ | 812 |
CHECK(args[0]->IsFunction()); |
203 |
|||
204 |
406 |
Local<Object> that = args.This(); |
|
205 |
|||
206 |
ModuleWrap* obj; |
||
207 |
✗✓ | 406 |
ASSIGN_OR_RETURN_UNWRAP(&obj, that); |
208 |
|||
209 |
✗✓ | 406 |
if (obj->linked_) |
210 |
return; |
||
211 |
406 |
obj->linked_ = true; |
|
212 |
|||
213 |
812 |
Local<Function> resolver_arg = args[0].As<Function>(); |
|
214 |
|||
215 |
812 |
Local<Context> mod_context = obj->context_.Get(isolate); |
|
216 |
812 |
Local<Module> module = obj->module_.Get(isolate); |
|
217 |
|||
218 |
Local<Array> promises = Array::New(isolate, |
||
219 |
406 |
module->GetModuleRequestsLength()); |
|
220 |
|||
221 |
// call the dependency resolve callbacks |
||
222 |
✓✓ | 1220 |
for (int i = 0; i < module->GetModuleRequestsLength(); i++) { |
223 |
204 |
Local<String> specifier = module->GetModuleRequest(i); |
|
224 |
204 |
Utf8Value specifier_utf8(env->isolate(), specifier); |
|
225 |
✓✗ | 408 |
std::string specifier_std(*specifier_utf8, specifier_utf8.length()); |
226 |
|||
227 |
Local<Value> argv[] = { |
||
228 |
specifier |
||
229 |
408 |
}; |
|
230 |
|||
231 |
MaybeLocal<Value> maybe_resolve_return_value = |
||
232 |
204 |
resolver_arg->Call(mod_context, that, 1, argv); |
|
233 |
✗✓ | 204 |
if (maybe_resolve_return_value.IsEmpty()) { |
234 |
return; |
||
235 |
} |
||
236 |
Local<Value> resolve_return_value = |
||
237 |
204 |
maybe_resolve_return_value.ToLocalChecked(); |
|
238 |
✗✓ | 204 |
if (!resolve_return_value->IsPromise()) { |
239 |
env->ThrowError("linking error, expected resolver to return a promise"); |
||
240 |
} |
||
241 |
204 |
Local<Promise> resolve_promise = resolve_return_value.As<Promise>(); |
|
242 |
204 |
obj->resolve_cache_[specifier_std].Reset(env->isolate(), resolve_promise); |
|
243 |
|||
244 |
✓✗ | 612 |
promises->Set(mod_context, i, resolve_promise).Check(); |
245 |
204 |
} |
|
246 |
|||
247 |
812 |
args.GetReturnValue().Set(promises); |
|
248 |
} |
||
249 |
|||
250 |
131 |
void ModuleWrap::Instantiate(const FunctionCallbackInfo<Value>& args) { |
|
251 |
131 |
Environment* env = Environment::GetCurrent(args); |
|
252 |
131 |
Isolate* isolate = args.GetIsolate(); |
|
253 |
ModuleWrap* obj; |
||
254 |
✗✓ | 134 |
ASSIGN_OR_RETURN_UNWRAP(&obj, args.This()); |
255 |
262 |
Local<Context> context = obj->context_.Get(isolate); |
|
256 |
262 |
Local<Module> module = obj->module_.Get(isolate); |
|
257 |
131 |
TryCatchScope try_catch(env); |
|
258 |
131 |
USE(module->InstantiateModule(context, ResolveCallback)); |
|
259 |
|||
260 |
// clear resolve cache on instantiate |
||
261 |
131 |
obj->resolve_cache_.clear(); |
|
262 |
|||
263 |
✓✓✓✗ ✓✓ |
131 |
if (try_catch.HasCaught() && !try_catch.HasTerminated()) { |
264 |
✗✓ | 6 |
CHECK(!try_catch.Message().IsEmpty()); |
265 |
✗✓ | 6 |
CHECK(!try_catch.Exception().IsEmpty()); |
266 |
AppendExceptionLine(env, try_catch.Exception(), try_catch.Message(), |
||
267 |
3 |
ErrorHandlingMode::MODULE_ERROR); |
|
268 |
3 |
try_catch.ReThrow(); |
|
269 |
✓✓ | 3 |
return; |
270 |
128 |
} |
|
271 |
} |
||
272 |
|||
273 |
140 |
void ModuleWrap::Evaluate(const FunctionCallbackInfo<Value>& args) { |
|
274 |
140 |
Environment* env = Environment::GetCurrent(args); |
|
275 |
140 |
Isolate* isolate = env->isolate(); |
|
276 |
ModuleWrap* obj; |
||
277 |
✗✓ | 152 |
ASSIGN_OR_RETURN_UNWRAP(&obj, args.This()); |
278 |
280 |
Local<Context> context = obj->context_.Get(isolate); |
|
279 |
280 |
Local<Module> module = obj->module_.Get(isolate); |
|
280 |
|||
281 |
// module.evaluate(timeout, breakOnSigint) |
||
282 |
✗✓ | 140 |
CHECK_EQ(args.Length(), 2); |
283 |
|||
284 |
✗✓ | 280 |
CHECK(args[0]->IsNumber()); |
285 |
560 |
int64_t timeout = args[0]->IntegerValue(env->context()).FromJust(); |
|
286 |
|||
287 |
✗✓ | 280 |
CHECK(args[1]->IsBoolean()); |
288 |
280 |
bool break_on_sigint = args[1]->IsTrue(); |
|
289 |
|||
290 |
140 |
ShouldNotAbortOnUncaughtScope no_abort_scope(env); |
|
291 |
✓✓ | 267 |
TryCatchScope try_catch(env); |
292 |
|||
293 |
140 |
bool timed_out = false; |
|
294 |
140 |
bool received_signal = false; |
|
295 |
MaybeLocal<Value> result; |
||
296 |
✗✓✗✗ |
140 |
if (break_on_sigint && timeout != -1) { |
297 |
Watchdog wd(isolate, timeout, &timed_out); |
||
298 |
SigintWatchdog swd(isolate, &received_signal); |
||
299 |
result = module->Evaluate(context); |
||
300 |
✗✓ | 140 |
} else if (break_on_sigint) { |
301 |
SigintWatchdog swd(isolate, &received_signal); |
||
302 |
result = module->Evaluate(context); |
||
303 |
✓✓ | 140 |
} else if (timeout != -1) { |
304 |
1 |
Watchdog wd(isolate, timeout, &timed_out); |
|
305 |
1 |
result = module->Evaluate(context); |
|
306 |
} else { |
||
307 |
139 |
result = module->Evaluate(context); |
|
308 |
} |
||
309 |
|||
310 |
// Convert the termination exception into a regular exception. |
||
311 |
✓✓✗✓ |
139 |
if (timed_out || received_signal) { |
312 |
✗✓✗✗ ✗✓ |
1 |
if (!env->is_main_thread() && env->is_stopping()) |
313 |
return; |
||
314 |
1 |
env->isolate()->CancelTerminateExecution(); |
|
315 |
// It is possible that execution was terminated by another timeout in |
||
316 |
// which this timeout is nested, so check whether one of the watchdogs |
||
317 |
// from this invocation is responsible for termination. |
||
318 |
✓✗ | 1 |
if (timed_out) { |
319 |
1 |
THROW_ERR_SCRIPT_EXECUTION_TIMEOUT(env, timeout); |
|
320 |
} else if (received_signal) { |
||
321 |
THROW_ERR_SCRIPT_EXECUTION_INTERRUPTED(env); |
||
322 |
} |
||
323 |
} |
||
324 |
|||
325 |
✓✓ | 139 |
if (try_catch.HasCaught()) { |
326 |
✓✓ | 12 |
if (!try_catch.HasTerminated()) |
327 |
11 |
try_catch.ReThrow(); |
|
328 |
12 |
return; |
|
329 |
} |
||
330 |
|||
331 |
✓✓ | 381 |
args.GetReturnValue().Set(result.ToLocalChecked()); |
332 |
} |
||
333 |
|||
334 |
117 |
void ModuleWrap::Namespace(const FunctionCallbackInfo<Value>& args) { |
|
335 |
117 |
Environment* env = Environment::GetCurrent(args); |
|
336 |
117 |
Isolate* isolate = args.GetIsolate(); |
|
337 |
ModuleWrap* obj; |
||
338 |
✗✓ | 117 |
ASSIGN_OR_RETURN_UNWRAP(&obj, args.This()); |
339 |
|||
340 |
234 |
Local<Module> module = obj->module_.Get(isolate); |
|
341 |
|||
342 |
✗✓ | 117 |
switch (module->GetStatus()) { |
343 |
default: |
||
344 |
return env->ThrowError( |
||
345 |
"cannot get namespace, Module has not been instantiated"); |
||
346 |
case v8::Module::Status::kInstantiated: |
||
347 |
case v8::Module::Status::kEvaluating: |
||
348 |
case v8::Module::Status::kEvaluated: |
||
349 |
117 |
break; |
|
350 |
} |
||
351 |
|||
352 |
117 |
Local<Value> result = module->GetModuleNamespace(); |
|
353 |
234 |
args.GetReturnValue().Set(result); |
|
354 |
} |
||
355 |
|||
356 |
112 |
void ModuleWrap::GetStatus(const FunctionCallbackInfo<Value>& args) { |
|
357 |
112 |
Isolate* isolate = args.GetIsolate(); |
|
358 |
ModuleWrap* obj; |
||
359 |
✗✓ | 224 |
ASSIGN_OR_RETURN_UNWRAP(&obj, args.This()); |
360 |
|||
361 |
224 |
Local<Module> module = obj->module_.Get(isolate); |
|
362 |
|||
363 |
336 |
args.GetReturnValue().Set(module->GetStatus()); |
|
364 |
} |
||
365 |
|||
366 |
2 |
void ModuleWrap::GetStaticDependencySpecifiers( |
|
367 |
const FunctionCallbackInfo<Value>& args) { |
||
368 |
2 |
Environment* env = Environment::GetCurrent(args); |
|
369 |
ModuleWrap* obj; |
||
370 |
✗✓ | 4 |
ASSIGN_OR_RETURN_UNWRAP(&obj, args.This()); |
371 |
|||
372 |
4 |
Local<Module> module = obj->module_.Get(env->isolate()); |
|
373 |
|||
374 |
2 |
int count = module->GetModuleRequestsLength(); |
|
375 |
|||
376 |
2 |
Local<Array> specifiers = Array::New(env->isolate(), count); |
|
377 |
|||
378 |
✓✓ | 3 |
for (int i = 0; i < count; i++) |
379 |
4 |
specifiers->Set(env->context(), i, module->GetModuleRequest(i)).Check(); |
|
380 |
|||
381 |
4 |
args.GetReturnValue().Set(specifiers); |
|
382 |
} |
||
383 |
|||
384 |
1 |
void ModuleWrap::GetError(const FunctionCallbackInfo<Value>& args) { |
|
385 |
1 |
Isolate* isolate = args.GetIsolate(); |
|
386 |
ModuleWrap* obj; |
||
387 |
✗✓ | 2 |
ASSIGN_OR_RETURN_UNWRAP(&obj, args.This()); |
388 |
|||
389 |
2 |
Local<Module> module = obj->module_.Get(isolate); |
|
390 |
3 |
args.GetReturnValue().Set(module->GetException()); |
|
391 |
} |
||
392 |
|||
393 |
194 |
MaybeLocal<Module> ModuleWrap::ResolveCallback(Local<Context> context, |
|
394 |
Local<String> specifier, |
||
395 |
Local<Module> referrer) { |
||
396 |
194 |
Environment* env = Environment::GetCurrent(context); |
|
397 |
✗✓ | 194 |
CHECK_NOT_NULL(env); // TODO(addaleax): Handle nullptr here. |
398 |
194 |
Isolate* isolate = env->isolate(); |
|
399 |
|||
400 |
194 |
ModuleWrap* dependent = GetFromModule(env, referrer); |
|
401 |
✗✓ | 194 |
if (dependent == nullptr) { |
402 |
env->ThrowError("linking error, null dep"); |
||
403 |
return MaybeLocal<Module>(); |
||
404 |
} |
||
405 |
|||
406 |
194 |
Utf8Value specifier_utf8(isolate, specifier); |
|
407 |
388 |
std::string specifier_std(*specifier_utf8, specifier_utf8.length()); |
|
408 |
|||
409 |
✗✓ | 194 |
if (dependent->resolve_cache_.count(specifier_std) != 1) { |
410 |
env->ThrowError("linking error, not in local cache"); |
||
411 |
return MaybeLocal<Module>(); |
||
412 |
} |
||
413 |
|||
414 |
Local<Promise> resolve_promise = |
||
415 |
388 |
dependent->resolve_cache_[specifier_std].Get(isolate); |
|
416 |
|||
417 |
✗✓ | 194 |
if (resolve_promise->State() != Promise::kFulfilled) { |
418 |
env->ThrowError("linking error, dependency promises must be resolved on " |
||
419 |
"instantiate"); |
||
420 |
return MaybeLocal<Module>(); |
||
421 |
} |
||
422 |
|||
423 |
388 |
Local<Object> module_object = resolve_promise->Result().As<Object>(); |
|
424 |
✓✗✗✓ ✗✓ |
388 |
if (module_object.IsEmpty() || !module_object->IsObject()) { |
425 |
env->ThrowError("linking error, expected a valid module object from " |
||
426 |
"resolver"); |
||
427 |
return MaybeLocal<Module>(); |
||
428 |
} |
||
429 |
|||
430 |
ModuleWrap* module; |
||
431 |
✗✓ | 194 |
ASSIGN_OR_RETURN_UNWRAP(&module, module_object, MaybeLocal<Module>()); |
432 |
582 |
return module->module_.Get(isolate); |
|
433 |
} |
||
434 |
|||
435 |
namespace { |
||
436 |
|||
437 |
// Tests whether a path starts with /, ./ or ../ |
||
438 |
// In WhatWG terminology, the alternative case is called a "bare" specifier |
||
439 |
// (e.g. in `import "jquery"`). |
||
440 |
231 |
inline bool ShouldBeTreatedAsRelativeOrAbsolutePath( |
|
441 |
const std::string& specifier) { |
||
442 |
231 |
size_t len = specifier.length(); |
|
443 |
✗✓ | 231 |
if (len == 0) |
444 |
return false; |
||
445 |
✓✓ | 231 |
if (specifier[0] == '/') { |
446 |
87 |
return true; |
|
447 |
✓✓ | 144 |
} else if (specifier[0] == '.') { |
448 |
✓✗✓✓ ✓✓ |
106 |
if (len == 1 || specifier[1] == '/') |
449 |
32 |
return true; |
|
450 |
✓✗ | 74 |
if (specifier[1] == '.') { |
451 |
✓✗✓✗ ✓✗ |
74 |
if (len == 2 || specifier[2] == '/') |
452 |
74 |
return true; |
|
453 |
} |
||
454 |
} |
||
455 |
38 |
return false; |
|
456 |
} |
||
457 |
|||
458 |
18 |
std::string ReadFile(uv_file file) { |
|
459 |
18 |
std::string contents; |
|
460 |
uv_fs_t req; |
||
461 |
char buffer_memory[4096]; |
||
462 |
18 |
uv_buf_t buf = uv_buf_init(buffer_memory, sizeof(buffer_memory)); |
|
463 |
|||
464 |
do { |
||
465 |
const int r = uv_fs_read(nullptr, |
||
466 |
&req, |
||
467 |
file, |
||
468 |
&buf, |
||
469 |
1, |
||
470 |
36 |
contents.length(), // offset |
|
471 |
36 |
nullptr); |
|
472 |
36 |
uv_fs_req_cleanup(&req); |
|
473 |
|||
474 |
✓✓ | 36 |
if (r <= 0) |
475 |
18 |
break; |
|
476 |
18 |
contents.append(buf.base, r); |
|
477 |
} while (true); |
||
478 |
18 |
return contents; |
|
479 |
} |
||
480 |
|||
481 |
enum DescriptorType { |
||
482 |
FILE, |
||
483 |
DIRECTORY, |
||
484 |
NONE |
||
485 |
}; |
||
486 |
|||
487 |
// When DescriptorType cache is added, this can also return |
||
488 |
// Nothing for the "null" cache entries. |
||
489 |
1150 |
inline Maybe<uv_file> OpenDescriptor(const std::string& path) { |
|
490 |
uv_fs_t fs_req; |
||
491 |
1150 |
uv_file fd = uv_fs_open(nullptr, &fs_req, path.c_str(), O_RDONLY, 0, nullptr); |
|
492 |
1150 |
uv_fs_req_cleanup(&fs_req); |
|
493 |
✓✓ | 1150 |
if (fd < 0) return Nothing<uv_file>(); |
494 |
245 |
return Just(fd); |
|
495 |
} |
||
496 |
|||
497 |
245 |
inline void CloseDescriptor(uv_file fd) { |
|
498 |
uv_fs_t fs_req; |
||
499 |
✗✓ | 245 |
CHECK_EQ(0, uv_fs_close(nullptr, &fs_req, fd, nullptr)); |
500 |
245 |
uv_fs_req_cleanup(&fs_req); |
|
501 |
245 |
} |
|
502 |
|||
503 |
245 |
inline DescriptorType CheckDescriptorAtFile(uv_file fd) { |
|
504 |
uv_fs_t fs_req; |
||
505 |
245 |
int rc = uv_fs_fstat(nullptr, &fs_req, fd, nullptr); |
|
506 |
✓✗ | 245 |
if (rc == 0) { |
507 |
245 |
uint64_t is_directory = fs_req.statbuf.st_mode & S_IFDIR; |
|
508 |
245 |
uv_fs_req_cleanup(&fs_req); |
|
509 |
✓✓ | 245 |
return is_directory ? DIRECTORY : FILE; |
510 |
} |
||
511 |
uv_fs_req_cleanup(&fs_req); |
||
512 |
return NONE; |
||
513 |
} |
||
514 |
|||
515 |
// TODO(@guybedford): Add a DescriptorType cache layer here. |
||
516 |
// Should be directory based -> if path/to/dir doesn't exist |
||
517 |
// then the cache should early-fail any path/to/dir/file check. |
||
518 |
310 |
DescriptorType CheckDescriptorAtPath(const std::string& path) { |
|
519 |
310 |
Maybe<uv_file> fd = OpenDescriptor(path); |
|
520 |
✓✓ | 310 |
if (fd.IsNothing()) return NONE; |
521 |
227 |
DescriptorType type = CheckDescriptorAtFile(fd.FromJust()); |
|
522 |
227 |
CloseDescriptor(fd.FromJust()); |
|
523 |
227 |
return type; |
|
524 |
} |
||
525 |
|||
526 |
840 |
Maybe<std::string> ReadIfFile(const std::string& path) { |
|
527 |
840 |
Maybe<uv_file> fd = OpenDescriptor(path); |
|
528 |
✓✓ | 840 |
if (fd.IsNothing()) return Nothing<std::string>(); |
529 |
18 |
DescriptorType type = CheckDescriptorAtFile(fd.FromJust()); |
|
530 |
✗✓ | 18 |
if (type != FILE) return Nothing<std::string>(); |
531 |
18 |
std::string source = ReadFile(fd.FromJust()); |
|
532 |
18 |
CloseDescriptor(fd.FromJust()); |
|
533 |
18 |
return Just(source); |
|
534 |
} |
||
535 |
|||
536 |
using Exists = PackageConfig::Exists; |
||
537 |
using IsValid = PackageConfig::IsValid; |
||
538 |
using HasMain = PackageConfig::HasMain; |
||
539 |
using PackageType = PackageConfig::PackageType; |
||
540 |
|||
541 |
1683 |
Maybe<const PackageConfig*> GetPackageConfig(Environment* env, |
|
542 |
const std::string& path, |
||
543 |
const URL& base) { |
||
544 |
1683 |
auto existing = env->package_json_cache.find(path); |
|
545 |
✓✓ | 1683 |
if (existing != env->package_json_cache.end()) { |
546 |
843 |
const PackageConfig* pcfg = &existing->second; |
|
547 |
✗✓ | 843 |
if (pcfg->is_valid == IsValid::No) { |
548 |
std::string msg = "Invalid JSON in " + path + |
||
549 |
" imported from " + base.ToFilePath(); |
||
550 |
node::THROW_ERR_INVALID_PACKAGE_CONFIG(env, msg.c_str()); |
||
551 |
return Nothing<const PackageConfig*>(); |
||
552 |
} |
||
553 |
843 |
return Just(pcfg); |
|
554 |
} |
||
555 |
|||
556 |
840 |
Maybe<std::string> source = ReadIfFile(path); |
|
557 |
|||
558 |
✓✓ | 840 |
if (source.IsNothing()) { |
559 |
auto entry = env->package_json_cache.emplace(path, |
||
560 |
PackageConfig { Exists::No, IsValid::Yes, HasMain::No, "", |
||
561 |
1644 |
PackageType::None, Global<Value>() }); |
|
562 |
822 |
return Just(&entry.first->second); |
|
563 |
} |
||
564 |
|||
565 |
18 |
std::string pkg_src = source.FromJust(); |
|
566 |
|||
567 |
18 |
Isolate* isolate = env->isolate(); |
|
568 |
36 |
v8::HandleScope handle_scope(isolate); |
|
569 |
|||
570 |
Local<Object> pkg_json; |
||
571 |
{ |
||
572 |
Local<Value> src; |
||
573 |
Local<Value> pkg_json_v; |
||
574 |
18 |
Local<Context> context = env->context(); |
|
575 |
|||
576 |
✓✗✓✗ ✗✓ |
108 |
if (!ToV8Value(context, pkg_src).ToLocal(&src) || |
577 |
✓✗✗✓ ✓✗ |
144 |
!v8::JSON::Parse(context, src.As<String>()).ToLocal(&pkg_json_v) || |
578 |
✓✗ | 54 |
!pkg_json_v->ToObject(context).ToLocal(&pkg_json)) { |
579 |
env->package_json_cache.emplace(path, |
||
580 |
PackageConfig { Exists::Yes, IsValid::No, HasMain::No, "", |
||
581 |
PackageType::None, Global<Value>() }); |
||
582 |
std::string msg = "Invalid JSON in " + path + |
||
583 |
" imported from " + base.ToFilePath(); |
||
584 |
node::THROW_ERR_INVALID_PACKAGE_CONFIG(env, msg.c_str()); |
||
585 |
return Nothing<const PackageConfig*>(); |
||
586 |
} |
||
587 |
} |
||
588 |
|||
589 |
Local<Value> pkg_main; |
||
590 |
18 |
HasMain has_main = HasMain::No; |
|
591 |
36 |
std::string main_std; |
|
592 |
✓✗ | 72 |
if (pkg_json->Get(env->context(), env->main_string()).ToLocal(&pkg_main)) { |
593 |
✓✓ | 36 |
if (pkg_main->IsString()) { |
594 |
13 |
has_main = HasMain::Yes; |
|
595 |
} |
||
596 |
18 |
Utf8Value main_utf8(isolate, pkg_main); |
|
597 |
18 |
main_std.assign(std::string(*main_utf8, main_utf8.length())); |
|
598 |
} |
||
599 |
|||
600 |
18 |
PackageType pkg_type = PackageType::None; |
|
601 |
Local<Value> type_v; |
||
602 |
✓✗ | 72 |
if (pkg_json->Get(env->context(), env->type_string()).ToLocal(&type_v)) { |
603 |
✓✓ | 36 |
if (type_v->StrictEquals(env->module_string())) { |
604 |
6 |
pkg_type = PackageType::Module; |
|
605 |
✓✓ | 24 |
} else if (type_v->StrictEquals(env->commonjs_string())) { |
606 |
4 |
pkg_type = PackageType::CommonJS; |
|
607 |
} |
||
608 |
// ignore unknown types for forwards compatibility |
||
609 |
} |
||
610 |
|||
611 |
Local<Value> exports_v; |
||
612 |
✓✗✓✗ ✓✓ |
108 |
if (env->options()->experimental_exports && |
613 |
pkg_json->Get(env->context(), |
||
614 |
✓✗✓✓ ✓✗✓✗ |
144 |
env->exports_string()).ToLocal(&exports_v) && |
615 |
36 |
!exports_v->IsNullOrUndefined()) { |
|
616 |
Global<Value> exports; |
||
617 |
2 |
exports.Reset(env->isolate(), exports_v); |
|
618 |
|||
619 |
auto entry = env->package_json_cache.emplace(path, |
||
620 |
PackageConfig { Exists::Yes, IsValid::Yes, has_main, main_std, |
||
621 |
4 |
pkg_type, std::move(exports) }); |
|
622 |
2 |
return Just(&entry.first->second); |
|
623 |
} |
||
624 |
|||
625 |
auto entry = env->package_json_cache.emplace(path, |
||
626 |
PackageConfig { Exists::Yes, IsValid::Yes, has_main, main_std, |
||
627 |
32 |
pkg_type, Global<Value>() }); |
|
628 |
856 |
return Just(&entry.first->second); |
|
629 |
} |
||
630 |
|||
631 |
194 |
Maybe<const PackageConfig*> GetPackageScopeConfig(Environment* env, |
|
632 |
const URL& resolved, |
||
633 |
const URL& base) { |
||
634 |
194 |
URL pjson_url("./package.json", &resolved); |
|
635 |
while (true) { |
||
636 |
1657 |
std::string pjson_url_path = pjson_url.path(); |
|
637 |
✓✓✓✓ ✓✓ |
6142 |
if (pjson_url_path.length() > 25 && |
638 |
✓✓ | 5170 |
pjson_url_path.substr(pjson_url_path.length() - 25, 25) == |
639 |
"node_modules/package.json") { |
||
640 |
2 |
break; |
|
641 |
} |
||
642 |
Maybe<const PackageConfig*> pkg_cfg = |
||
643 |
1655 |
GetPackageConfig(env, pjson_url.ToFilePath(), base); |
|
644 |
✗✓ | 1655 |
if (pkg_cfg.IsNothing()) return pkg_cfg; |
645 |
✓✓ | 1655 |
if (pkg_cfg.FromJust()->exists == Exists::Yes) return pkg_cfg; |
646 |
|||
647 |
✓✓✓ | 3088 |
URL last_pjson_url = pjson_url; |
648 |
1625 |
pjson_url = URL("../package.json", pjson_url); |
|
649 |
|||
650 |
// Terminates at root where ../package.json equals ../../package.json |
||
651 |
// (can't just check "/package.json" for Windows support). |
||
652 |
✓✓✓✓ |
1625 |
if (pjson_url.path() == last_pjson_url.path()) break; |
653 |
1463 |
} |
|
654 |
auto entry = env->package_json_cache.emplace(pjson_url.ToFilePath(), |
||
655 |
PackageConfig { Exists::No, IsValid::Yes, HasMain::No, "", |
||
656 |
328 |
PackageType::None, Global<Value>() }); |
|
657 |
164 |
const PackageConfig* pcfg = &entry.first->second; |
|
658 |
164 |
return Just(pcfg); |
|
659 |
} |
||
660 |
|||
661 |
/* |
||
662 |
* Legacy CommonJS main resolution: |
||
663 |
* 1. let M = pkg_url + (json main field) |
||
664 |
* 2. TRY(M, M.js, M.json, M.node) |
||
665 |
* 3. TRY(M/index.js, M/index.json, M/index.node) |
||
666 |
* 4. TRY(pkg_url/index.js, pkg_url/index.json, pkg_url/index.node) |
||
667 |
* 5. NOT_FOUND |
||
668 |
*/ |
||
669 |
56 |
inline bool FileExists(const URL& url) { |
|
670 |
56 |
return CheckDescriptorAtPath(url.ToFilePath()) == FILE; |
|
671 |
} |
||
672 |
Maybe<URL> LegacyMainResolve(const URL& pjson_url, |
||
673 |
const PackageConfig& pcfg) { |
||
674 |
URL guess; |
||
675 |
if (pcfg.has_main == HasMain::Yes) { |
||
676 |
// Note: fs check redundances will be handled by Descriptor cache here. |
||
677 |
if (FileExists(guess = URL("./" + pcfg.main, pjson_url))) { |
||
678 |
return Just(guess); |
||
679 |
} |
||
680 |
if (FileExists(guess = URL("./" + pcfg.main + ".js", pjson_url))) { |
||
681 |
return Just(guess); |
||
682 |
} |
||
683 |
if (FileExists(guess = URL("./" + pcfg.main + ".json", pjson_url))) { |
||
684 |
return Just(guess); |
||
685 |
} |
||
686 |
if (FileExists(guess = URL("./" + pcfg.main + ".node", pjson_url))) { |
||
687 |
return Just(guess); |
||
688 |
} |
||
689 |
if (FileExists(guess = URL("./" + pcfg.main + "/index.js", pjson_url))) { |
||
690 |
return Just(guess); |
||
691 |
} |
||
692 |
// Such stat. |
||
693 |
if (FileExists(guess = URL("./" + pcfg.main + "/index.json", pjson_url))) { |
||
694 |
return Just(guess); |
||
695 |
} |
||
696 |
if (FileExists(guess = URL("./" + pcfg.main + "/index.node", pjson_url))) { |
||
697 |
return Just(guess); |
||
698 |
} |
||
699 |
// Fallthrough. |
||
700 |
} |
||
701 |
if (FileExists(guess = URL("./index.js", pjson_url))) { |
||
702 |
return Just(guess); |
||
703 |
} |
||
704 |
// So fs. |
||
705 |
if (FileExists(guess = URL("./index.json", pjson_url))) { |
||
706 |
return Just(guess); |
||
707 |
} |
||
708 |
if (FileExists(guess = URL("./index.node", pjson_url))) { |
||
709 |
return Just(guess); |
||
710 |
} |
||
711 |
// Not found. |
||
712 |
return Nothing<URL>(); |
||
713 |
} |
||
714 |
|||
715 |
enum ResolveExtensionsOptions { |
||
716 |
TRY_EXACT_NAME, |
||
717 |
ONLY_VIA_EXTENSIONS |
||
718 |
}; |
||
719 |
|||
720 |
template <ResolveExtensionsOptions options> |
||
721 |
22 |
Maybe<URL> ResolveExtensions(const URL& search) { |
|
722 |
if (options == TRY_EXACT_NAME) { |
||
723 |
✓✓ | 17 |
if (FileExists(search)) { |
724 |
9 |
return Just(search); |
|
725 |
} |
||
726 |
} |
||
727 |
|||
728 |
✓✓✓✓ |
45 |
for (const char* extension : EXTENSIONS) { |
729 |
39 |
URL guess(search.path() + extension, &search); |
|
730 |
✓✓✓✓ |
39 |
if (FileExists(guess)) { |
731 |
✓✓✓✓ |
7 |
return Just(guess); |
732 |
} |
||
733 |
} |
||
734 |
|||
735 |
6 |
return Nothing<URL>(); |
|
736 |
} |
||
737 |
|||
738 |
5 |
inline Maybe<URL> ResolveIndex(const URL& search) { |
|
739 |
5 |
return ResolveExtensions<ONLY_VIA_EXTENSIONS>(URL("index", search)); |
|
740 |
} |
||
741 |
|||
742 |
209 |
Maybe<URL> FinalizeResolution(Environment* env, |
|
743 |
const URL& resolved, |
||
744 |
const URL& base) { |
||
745 |
✓✓ | 209 |
if (env->options()->es_module_specifier_resolution == "node") { |
746 |
17 |
Maybe<URL> file = ResolveExtensions<TRY_EXACT_NAME>(resolved); |
|
747 |
✓✓ | 17 |
if (!file.IsNothing()) { |
748 |
12 |
return file; |
|
749 |
} |
||
750 |
✓✓ | 5 |
if (resolved.path().back() != '/') { |
751 |
4 |
file = ResolveIndex(URL(resolved.path() + "/", &base)); |
|
752 |
} else { |
||
753 |
1 |
file = ResolveIndex(resolved); |
|
754 |
} |
||
755 |
✓✓ | 5 |
if (!file.IsNothing()) { |
756 |
4 |
return file; |
|
757 |
} |
||
758 |
2 |
std::string msg = "Cannot find module " + resolved.path() + |
|
759 |
4 |
" imported from " + base.ToFilePath(); |
|
760 |
1 |
node::THROW_ERR_MODULE_NOT_FOUND(env, msg.c_str()); |
|
761 |
18 |
return Nothing<URL>(); |
|
762 |
} |
||
763 |
|||
764 |
192 |
const std::string& path = resolved.ToFilePath(); |
|
765 |
✓✓ | 192 |
if (CheckDescriptorAtPath(path) != FILE) { |
766 |
✓✓ | 30 |
std::string msg = "Cannot find module " + |
767 |
45 |
(path.length() != 0 ? path : resolved.path()) + |
|
768 |
45 |
" imported from " + base.ToFilePath(); |
|
769 |
15 |
node::THROW_ERR_MODULE_NOT_FOUND(env, msg.c_str()); |
|
770 |
15 |
return Nothing<URL>(); |
|
771 |
} |
||
772 |
|||
773 |
177 |
return Just(resolved); |
|
774 |
} |
||
775 |
|||
776 |
2 |
void ThrowExportsNotFound(Environment* env, |
|
777 |
const std::string& subpath, |
||
778 |
const URL& pjson_url, |
||
779 |
const URL& base) { |
||
780 |
4 |
const std::string msg = "Package exports for " + |
|
781 |
4 |
pjson_url.ToFilePath() + " do not define a '" + subpath + |
|
782 |
6 |
"' subpath, imported from " + base.ToFilePath(); |
|
783 |
2 |
node::THROW_ERR_MODULE_NOT_FOUND(env, msg.c_str()); |
|
784 |
2 |
} |
|
785 |
|||
786 |
18 |
void ThrowExportsInvalid(Environment* env, |
|
787 |
const std::string& subpath, |
||
788 |
const std::string& target, |
||
789 |
const URL& pjson_url, |
||
790 |
const URL& base) { |
||
791 |
36 |
const std::string msg = "Cannot resolve package exports target '" + target + |
|
792 |
72 |
"' matched for '" + subpath + "' in " + pjson_url.ToFilePath() + |
|
793 |
54 |
", imported from " + base.ToFilePath(); |
|
794 |
18 |
node::THROW_ERR_MODULE_NOT_FOUND(env, msg.c_str()); |
|
795 |
18 |
} |
|
796 |
|||
797 |
8 |
void ThrowExportsInvalid(Environment* env, |
|
798 |
const std::string& subpath, |
||
799 |
Local<Value> target, |
||
800 |
const URL& pjson_url, |
||
801 |
const URL& base) { |
||
802 |
Local<String> target_string; |
||
803 |
✓✗ | 24 |
if (target->ToString(env->context()).ToLocal(&target_string)) { |
804 |
8 |
Utf8Value target_utf8(env->isolate(), target_string); |
|
805 |
16 |
std::string target_str(*target_utf8, target_utf8.length()); |
|
806 |
✓✓ | 8 |
if (target->IsArray()) { |
807 |
1 |
target_str = '[' + target_str + ']'; |
|
808 |
} |
||
809 |
16 |
ThrowExportsInvalid(env, subpath, target_str, pjson_url, base); |
|
810 |
} |
||
811 |
8 |
} |
|
812 |
|||
813 |
19 |
Maybe<URL> ResolveExportsTarget(Environment* env, |
|
814 |
const std::string& target, |
||
815 |
const std::string& subpath, |
||
816 |
const std::string& match, |
||
817 |
const URL& pjson_url, |
||
818 |
const URL& base, |
||
819 |
bool throw_invalid = true) { |
||
820 |
✓✓ | 19 |
if (target.substr(0, 2) != "./") { |
821 |
✓✓ | 8 |
if (throw_invalid) { |
822 |
5 |
ThrowExportsInvalid(env, match, target, pjson_url, base); |
|
823 |
} |
||
824 |
8 |
return Nothing<URL>(); |
|
825 |
} |
||
826 |
✓✓✓✓ ✓✓ |
11 |
if (subpath.length() > 0 && target.back() != '/') { |
827 |
✗✓ | 1 |
if (throw_invalid) { |
828 |
ThrowExportsInvalid(env, match, target, pjson_url, base); |
||
829 |
} |
||
830 |
1 |
return Nothing<URL>(); |
|
831 |
} |
||
832 |
10 |
URL resolved(target, pjson_url); |
|
833 |
20 |
std::string resolved_path = resolved.path(); |
|
834 |
20 |
std::string pkg_path = URL(".", pjson_url).path(); |
|
835 |
✓✗✓✓ ✓✓ |
20 |
if (resolved_path.find(pkg_path) != 0 || |
836 |
10 |
resolved_path.find("/node_modules/", pkg_path.length() - 1) != |
|
837 |
std::string::npos) { |
||
838 |
✓✗ | 1 |
if (throw_invalid) { |
839 |
1 |
ThrowExportsInvalid(env, match, target, pjson_url, base); |
|
840 |
} |
||
841 |
1 |
return Nothing<URL>(); |
|
842 |
} |
||
843 |
✓✓ | 9 |
if (subpath.length() == 0) return Just(resolved); |
844 |
10 |
URL subpath_resolved(subpath, resolved); |
|
845 |
10 |
std::string subpath_resolved_path = subpath_resolved.path(); |
|
846 |
✓✓✗✓ ✓✓ |
9 |
if (subpath_resolved_path.find(resolved_path) != 0 || |
847 |
4 |
subpath_resolved_path.find("/node_modules/", pkg_path.length() - 1) |
|
848 |
!= std::string::npos) { |
||
849 |
✓✗ | 1 |
if (throw_invalid) { |
850 |
1 |
ThrowExportsInvalid(env, match, target + subpath, pjson_url, base); |
|
851 |
} |
||
852 |
1 |
return Nothing<URL>(); |
|
853 |
} |
||
854 |
14 |
return Just(subpath_resolved); |
|
855 |
} |
||
856 |
|||
857 |
5 |
Maybe<URL> PackageMainResolve(Environment* env, |
|
858 |
const URL& pjson_url, |
||
859 |
const PackageConfig& pcfg, |
||
860 |
const URL& base) { |
||
861 |
✓✗ | 5 |
if (pcfg.exists == Exists::Yes) { |
862 |
5 |
Isolate* isolate = env->isolate(); |
|
863 |
5 |
Local<Context> context = env->context(); |
|
864 |
✓✓ | 10 |
if (!pcfg.exports.IsEmpty()) { |
865 |
2 |
Local<Value> exports = pcfg.exports.Get(isolate); |
|
866 |
✓✗✗✓ ✗✗✓✗ |
3 |
if (exports->IsString() || exports->IsObject() || exports->IsArray()) { |
867 |
Local<Value> target; |
||
868 |
✗✓ | 1 |
if (!exports->IsObject()) { |
869 |
target = exports; |
||
870 |
} else { |
||
871 |
1 |
Local<Object> exports_obj = exports.As<Object>(); |
|
872 |
Local<String> dot_string = String::NewFromUtf8(env->isolate(), ".", |
||
873 |
2 |
v8::NewStringType::kNormal).ToLocalChecked(); |
|
874 |
target = |
||
875 |
3 |
exports_obj->Get(env->context(), dot_string).ToLocalChecked(); |
|
876 |
} |
||
877 |
✓✗ | 2 |
if (target->IsString()) { |
878 |
1 |
Utf8Value target_utf8(isolate, target.As<v8::String>()); |
|
879 |
2 |
std::string target(*target_utf8, target_utf8.length()); |
|
880 |
Maybe<URL> resolved = ResolveExportsTarget(env, target, "", ".", |
||
881 |
2 |
pjson_url, base); |
|
882 |
✗✓ | 1 |
if (resolved.IsNothing()) { |
883 |
ThrowExportsInvalid(env, ".", target, pjson_url, base); |
||
884 |
return Nothing<URL>(); |
||
885 |
} |
||
886 |
2 |
return FinalizeResolution(env, resolved.FromJust(), base); |
|
887 |
} else if (target->IsArray()) { |
||
888 |
Local<Array> target_arr = target.As<Array>(); |
||
889 |
const uint32_t length = target_arr->Length(); |
||
890 |
if (length == 0) { |
||
891 |
ThrowExportsInvalid(env, ".", target, pjson_url, base); |
||
892 |
return Nothing<URL>(); |
||
893 |
} |
||
894 |
for (uint32_t i = 0; i < length; i++) { |
||
895 |
auto target_item = target_arr->Get(context, i).ToLocalChecked(); |
||
896 |
if (target_item->IsString()) { |
||
897 |
Utf8Value target_utf8(isolate, target_item.As<v8::String>()); |
||
898 |
std::string target_str(*target_utf8, target_utf8.length()); |
||
899 |
Maybe<URL> resolved = ResolveExportsTarget(env, target_str, "", |
||
900 |
".", pjson_url, base, false); |
||
901 |
if (resolved.IsNothing()) continue; |
||
902 |
return FinalizeResolution(env, resolved.FromJust(), base); |
||
903 |
} |
||
904 |
} |
||
905 |
auto invalid = target_arr->Get(context, length - 1).ToLocalChecked(); |
||
906 |
if (!invalid->IsString()) { |
||
907 |
ThrowExportsInvalid(env, ".", invalid, pjson_url, base); |
||
908 |
return Nothing<URL>(); |
||
909 |
} |
||
910 |
Utf8Value invalid_utf8(isolate, invalid.As<v8::String>()); |
||
911 |
std::string invalid_str(*invalid_utf8, invalid_utf8.length()); |
||
912 |
Maybe<URL> resolved = ResolveExportsTarget(env, invalid_str, "", |
||
913 |
".", pjson_url, base); |
||
914 |
CHECK(resolved.IsNothing()); |
||
915 |
return Nothing<URL>(); |
||
916 |
} else { |
||
917 |
ThrowExportsInvalid(env, ".", target, pjson_url, base); |
||
918 |
return Nothing<URL>(); |
||
919 |
} |
||
920 |
} |
||
921 |
} |
||
922 |
✓✗ | 4 |
if (pcfg.has_main == HasMain::Yes) { |
923 |
4 |
URL resolved(pcfg.main, pjson_url); |
|
924 |
✓✓ | 7 |
const std::string& path = resolved.ToFilePath(); |
925 |
✓✓ | 4 |
if (CheckDescriptorAtPath(path) == FILE) { |
926 |
✓✓ | 1 |
return Just(resolved); |
927 |
3 |
} |
|
928 |
} |
||
929 |
✓✗ | 3 |
if (env->options()->es_module_specifier_resolution == "node") { |
930 |
✓✗ | 3 |
if (pcfg.has_main == HasMain::Yes) { |
931 |
3 |
return FinalizeResolution(env, URL(pcfg.main, pjson_url), base); |
|
932 |
} else { |
||
933 |
return FinalizeResolution(env, URL("index", pjson_url), base); |
||
934 |
} |
||
935 |
} |
||
936 |
if (pcfg.type != PackageType::Module) { |
||
937 |
Maybe<URL> resolved = LegacyMainResolve(pjson_url, pcfg); |
||
938 |
if (!resolved.IsNothing()) { |
||
939 |
return resolved; |
||
940 |
} |
||
941 |
} |
||
942 |
} |
||
943 |
std::string msg = "Cannot find main entry point for " + |
||
944 |
URL(".", pjson_url).ToFilePath() + " imported from " + |
||
945 |
base.ToFilePath(); |
||
946 |
node::THROW_ERR_MODULE_NOT_FOUND(env, msg.c_str()); |
||
947 |
return Nothing<URL>(); |
||
948 |
} |
||
949 |
|||
950 |
21 |
Maybe<URL> PackageExportsResolve(Environment* env, |
|
951 |
const URL& pjson_url, |
||
952 |
const std::string& pkg_subpath, |
||
953 |
const PackageConfig& pcfg, |
||
954 |
const URL& base) { |
||
955 |
✗✓ | 21 |
CHECK(env->options()->experimental_exports); |
956 |
21 |
Isolate* isolate = env->isolate(); |
|
957 |
21 |
Local<Context> context = env->context(); |
|
958 |
42 |
Local<Value> exports = pcfg.exports.Get(isolate); |
|
959 |
✓✓ | 21 |
if (!exports->IsObject()) { |
960 |
1 |
ThrowExportsNotFound(env, pkg_subpath, pjson_url, base); |
|
961 |
1 |
return Nothing<URL>(); |
|
962 |
} |
||
963 |
20 |
Local<Object> exports_obj = exports.As<Object>(); |
|
964 |
Local<String> subpath = String::NewFromUtf8(isolate, |
||
965 |
40 |
pkg_subpath.c_str(), v8::NewStringType::kNormal).ToLocalChecked(); |
|
966 |
|||
967 |
✓✓ | 40 |
if (exports_obj->HasOwnProperty(context, subpath).FromJust()) { |
968 |
24 |
Local<Value> target = exports_obj->Get(context, subpath).ToLocalChecked(); |
|
969 |
✓✓ | 24 |
if (target->IsString()) { |
970 |
5 |
Utf8Value target_utf8(isolate, target.As<v8::String>()); |
|
971 |
10 |
std::string target_str(*target_utf8, target_utf8.length()); |
|
972 |
Maybe<URL> resolved = ResolveExportsTarget(env, target_str, "", |
||
973 |
10 |
pkg_subpath, pjson_url, base); |
|
974 |
✓✓ | 5 |
if (resolved.IsNothing()) { |
975 |
3 |
ThrowExportsInvalid(env, pkg_subpath, target, pjson_url, base); |
|
976 |
3 |
return Nothing<URL>(); |
|
977 |
} |
||
978 |
7 |
return FinalizeResolution(env, resolved.FromJust(), base); |
|
979 |
✓✓ | 7 |
} else if (target->IsArray()) { |
980 |
3 |
Local<Array> target_arr = target.As<Array>(); |
|
981 |
3 |
const uint32_t length = target_arr->Length(); |
|
982 |
✓✓ | 3 |
if (length == 0) { |
983 |
1 |
ThrowExportsInvalid(env, pkg_subpath, target, pjson_url, base); |
|
984 |
1 |
return Nothing<URL>(); |
|
985 |
} |
||
986 |
✓✓ | 9 |
for (uint32_t i = 0; i < length; i++) { |
987 |
16 |
auto target_item = target_arr->Get(context, i).ToLocalChecked(); |
|
988 |
✓✓ | 16 |
if (target_item->IsString()) { |
989 |
3 |
Utf8Value target_utf8(isolate, target_item.As<v8::String>()); |
|
990 |
3 |
std::string target(*target_utf8, target_utf8.length()); |
|
991 |
Maybe<URL> resolved = ResolveExportsTarget(env, target, "", |
||
992 |
3 |
pkg_subpath, pjson_url, base, false); |
|
993 |
✓✓ | 3 |
if (resolved.IsNothing()) continue; |
994 |
✓✓✓✓ ✓✓ |
1 |
return FinalizeResolution(env, resolved.FromJust(), base); |
995 |
} |
||
996 |
} |
||
997 |
3 |
auto invalid = target_arr->Get(context, length - 1).ToLocalChecked(); |
|
998 |
✗✓ | 2 |
if (!invalid->IsString()) { |
999 |
ThrowExportsInvalid(env, pkg_subpath, invalid, pjson_url, base); |
||
1000 |
return Nothing<URL>(); |
||
1001 |
} |
||
1002 |
1 |
Utf8Value invalid_utf8(isolate, invalid.As<v8::String>()); |
|
1003 |
2 |
std::string invalid_str(*invalid_utf8, invalid_utf8.length()); |
|
1004 |
Maybe<URL> resolved = ResolveExportsTarget(env, invalid_str, "", |
||
1005 |
2 |
pkg_subpath, pjson_url, base); |
|
1006 |
✗✓ | 1 |
CHECK(resolved.IsNothing()); |
1007 |
2 |
return Nothing<URL>(); |
|
1008 |
} else { |
||
1009 |
4 |
ThrowExportsInvalid(env, pkg_subpath, target, pjson_url, base); |
|
1010 |
4 |
return Nothing<URL>(); |
|
1011 |
} |
||
1012 |
} |
||
1013 |
|||
1014 |
Local<String> best_match; |
||
1015 |
8 |
std::string best_match_str = ""; |
|
1016 |
Local<Array> keys = |
||
1017 |
16 |
exports_obj->GetOwnPropertyNames(context).ToLocalChecked(); |
|
1018 |
✓✓ | 288 |
for (uint32_t i = 0; i < keys->Length(); ++i) { |
1019 |
408 |
Local<String> key = keys->Get(context, i).ToLocalChecked().As<String>(); |
|
1020 |
136 |
Utf8Value key_utf8(isolate, key); |
|
1021 |
✓✓ | 168 |
std::string key_str(*key_utf8, key_utf8.length()); |
1022 |
✓✓ | 136 |
if (key_str.back() != '/') continue; |
1023 |
✓✓✓✗ ✓✗✓✓ |
39 |
if (pkg_subpath.substr(0, key_str.length()) == key_str && |
1024 |
7 |
key_str.length() > best_match_str.length()) { |
|
1025 |
7 |
best_match = key; |
|
1026 |
✓✓ | 7 |
best_match_str = key_str; |
1027 |
} |
||
1028 |
32 |
} |
|
1029 |
|||
1030 |
✓✓ | 8 |
if (best_match_str.length() > 0) { |
1031 |
14 |
auto target = exports_obj->Get(context, best_match).ToLocalChecked(); |
|
1032 |
7 |
std::string subpath = pkg_subpath.substr(best_match_str.length()); |
|
1033 |
✓✓ | 14 |
if (target->IsString()) { |
1034 |
6 |
Utf8Value target_utf8(isolate, target.As<v8::String>()); |
|
1035 |
12 |
std::string target(*target_utf8, target_utf8.length()); |
|
1036 |
Maybe<URL> resolved = ResolveExportsTarget(env, target, subpath, |
||
1037 |
12 |
pkg_subpath, pjson_url, base); |
|
1038 |
✓✓ | 6 |
if (resolved.IsNothing()) { |
1039 |
3 |
ThrowExportsInvalid(env, pkg_subpath, target, pjson_url, base); |
|
1040 |
3 |
return Nothing<URL>(); |
|
1041 |
} |
||
1042 |
9 |
return FinalizeResolution(env, URL(subpath, resolved.FromJust()), base); |
|
1043 |
✓✗ | 1 |
} else if (target->IsArray()) { |
1044 |
1 |
Local<Array> target_arr = target.As<Array>(); |
|
1045 |
1 |
const uint32_t length = target_arr->Length(); |
|
1046 |
✗✓ | 1 |
if (length == 0) { |
1047 |
ThrowExportsInvalid(env, pkg_subpath, target, pjson_url, base); |
||
1048 |
return Nothing<URL>(); |
||
1049 |
} |
||
1050 |
✓✗ | 6 |
for (uint32_t i = 0; i < length; i++) { |
1051 |
12 |
auto target_item = target_arr->Get(context, i).ToLocalChecked(); |
|
1052 |
✓✓ | 12 |
if (target_item->IsString()) { |
1053 |
3 |
Utf8Value target_utf8(isolate, target_item.As<v8::String>()); |
|
1054 |
3 |
std::string target_str(*target_utf8, target_utf8.length()); |
|
1055 |
Maybe<URL> resolved = ResolveExportsTarget(env, target_str, subpath, |
||
1056 |
3 |
pkg_subpath, pjson_url, base, false); |
|
1057 |
✓✓ | 3 |
if (resolved.IsNothing()) continue; |
1058 |
✓✓✓✓ ✓✓ |
1 |
return FinalizeResolution(env, resolved.FromJust(), base); |
1059 |
} |
||
1060 |
} |
||
1061 |
auto invalid = target_arr->Get(context, length - 1).ToLocalChecked(); |
||
1062 |
if (!invalid->IsString()) { |
||
1063 |
ThrowExportsInvalid(env, pkg_subpath, invalid, pjson_url, base); |
||
1064 |
return Nothing<URL>(); |
||
1065 |
} |
||
1066 |
Utf8Value invalid_utf8(isolate, invalid.As<v8::String>()); |
||
1067 |
std::string invalid_str(*invalid_utf8, invalid_utf8.length()); |
||
1068 |
Maybe<URL> resolved = ResolveExportsTarget(env, invalid_str, subpath, |
||
1069 |
pkg_subpath, pjson_url, base); |
||
1070 |
CHECK(resolved.IsNothing()); |
||
1071 |
return Nothing<URL>(); |
||
1072 |
} else { |
||
1073 |
ThrowExportsInvalid(env, pkg_subpath, target, pjson_url, base); |
||
1074 |
return Nothing<URL>(); |
||
1075 |
7 |
} |
|
1076 |
} |
||
1077 |
|||
1078 |
1 |
ThrowExportsNotFound(env, pkg_subpath, pjson_url, base); |
|
1079 |
1 |
return Nothing<URL>(); |
|
1080 |
} |
||
1081 |
|||
1082 |
35 |
Maybe<URL> PackageResolve(Environment* env, |
|
1083 |
const std::string& specifier, |
||
1084 |
const URL& base) { |
||
1085 |
35 |
size_t sep_index = specifier.find('/'); |
|
1086 |
35 |
bool valid_package_name = true; |
|
1087 |
35 |
bool scope = false; |
|
1088 |
✓✓ | 35 |
if (specifier[0] == '@') { |
1089 |
1 |
scope = true; |
|
1090 |
✗✓✗✗ ✓✗ |
1 |
if (sep_index == std::string::npos || specifier.length() == 0) { |
1091 |
1 |
valid_package_name = false; |
|
1092 |
} else { |
||
1093 |
sep_index = specifier.find('/', sep_index + 1); |
||
1094 |
} |
||
1095 |
✗✓ | 34 |
} else if (specifier[0] == '.') { |
1096 |
valid_package_name = false; |
||
1097 |
} |
||
1098 |
std::string pkg_name = specifier.substr(0, |
||
1099 |
35 |
sep_index == std::string::npos ? std::string::npos : sep_index); |
|
1100 |
// Package name cannot have leading . and cannot have percent-encoding or |
||
1101 |
// separators. |
||
1102 |
✓✓ | 375 |
for (size_t i = 0; i < pkg_name.length(); i++) { |
1103 |
343 |
char c = pkg_name[i]; |
|
1104 |
✓✓✓✓ |
343 |
if (c == '%' || c == '\\') { |
1105 |
3 |
valid_package_name = false; |
|
1106 |
3 |
break; |
|
1107 |
} |
||
1108 |
} |
||
1109 |
✓✓ | 35 |
if (!valid_package_name) { |
1110 |
8 |
std::string msg = "Invalid package name '" + specifier + |
|
1111 |
12 |
"' imported from " + base.ToFilePath(); |
|
1112 |
4 |
node::THROW_ERR_INVALID_MODULE_SPECIFIER(env, msg.c_str()); |
|
1113 |
4 |
return Nothing<URL>(); |
|
1114 |
} |
||
1115 |
62 |
std::string pkg_subpath; |
|
1116 |
✓✓✗✓ ✓✓ |
54 |
if ((sep_index == std::string::npos || |
1117 |
23 |
sep_index == specifier.length() - 1)) { |
|
1118 |
8 |
pkg_subpath = ""; |
|
1119 |
} else { |
||
1120 |
23 |
pkg_subpath = "." + specifier.substr(sep_index); |
|
1121 |
} |
||
1122 |
62 |
URL pjson_url("./node_modules/" + pkg_name + "/package.json", &base); |
|
1123 |
62 |
std::string pjson_path = pjson_url.ToFilePath(); |
|
1124 |
62 |
std::string last_path; |
|
1125 |
✓✓ | 30 |
do { |
1126 |
DescriptorType check = |
||
1127 |
58 |
CheckDescriptorAtPath(pjson_path.substr(0, pjson_path.length() - 13)); |
|
1128 |
✓✓ | 58 |
if (check != DIRECTORY) { |
1129 |
30 |
last_path = pjson_path; |
|
1130 |
60 |
pjson_url = URL((scope ? |
|
1131 |
✗✓ | 60 |
"../../../../node_modules/" : "../../../node_modules/") + |
1132 |
90 |
pkg_name + "/package.json", &pjson_url); |
|
1133 |
30 |
pjson_path = pjson_url.ToFilePath(); |
|
1134 |
30 |
continue; |
|
1135 |
} |
||
1136 |
|||
1137 |
// Package match. |
||
1138 |
28 |
Maybe<const PackageConfig*> pcfg = GetPackageConfig(env, pjson_path, base); |
|
1139 |
// Invalid package configuration error. |
||
1140 |
✗✓ | 56 |
if (pcfg.IsNothing()) return Nothing<URL>(); |
1141 |
✓✓ | 28 |
if (!pkg_subpath.length()) { |
1142 |
5 |
return PackageMainResolve(env, pjson_url, *pcfg.FromJust(), base); |
|
1143 |
} else { |
||
1144 |
✓✓ | 46 |
if (!pcfg.FromJust()->exports.IsEmpty()) { |
1145 |
return PackageExportsResolve(env, pjson_url, pkg_subpath, |
||
1146 |
21 |
*pcfg.FromJust(), base); |
|
1147 |
} else { |
||
1148 |
2 |
return FinalizeResolution(env, URL(pkg_subpath, pjson_url), base); |
|
1149 |
} |
||
1150 |
} |
||
1151 |
CHECK(false); |
||
1152 |
// Cross-platform root check. |
||
1153 |
30 |
} while (pjson_path.length() != last_path.length()); |
|
1154 |
|||
1155 |
6 |
std::string msg = "Cannot find package '" + pkg_name + |
|
1156 |
12 |
"' imported from " + base.ToFilePath(); |
|
1157 |
3 |
node::THROW_ERR_MODULE_NOT_FOUND(env, msg.c_str()); |
|
1158 |
38 |
return Nothing<URL>(); |
|
1159 |
} |
||
1160 |
|||
1161 |
} // anonymous namespace |
||
1162 |
|||
1163 |
231 |
Maybe<URL> Resolve(Environment* env, |
|
1164 |
const std::string& specifier, |
||
1165 |
const URL& base) { |
||
1166 |
// Order swapped from spec for minor perf gain. |
||
1167 |
// Ok since relative URLs cannot parse as URLs. |
||
1168 |
231 |
URL resolved; |
|
1169 |
✓✓ | 231 |
if (ShouldBeTreatedAsRelativeOrAbsolutePath(specifier)) { |
1170 |
193 |
resolved = URL(specifier, base); |
|
1171 |
} else { |
||
1172 |
38 |
URL pure_url(specifier); |
|
1173 |
✓✓ | 38 |
if (!(pure_url.flags() & URL_FLAGS_FAILED)) { |
1174 |
3 |
resolved = pure_url; |
|
1175 |
} else { |
||
1176 |
✓✓ | 35 |
return PackageResolve(env, specifier, base); |
1177 |
3 |
} |
|
1178 |
} |
||
1179 |
196 |
return FinalizeResolution(env, resolved, base); |
|
1180 |
} |
||
1181 |
|||
1182 |
231 |
void ModuleWrap::Resolve(const FunctionCallbackInfo<Value>& args) { |
|
1183 |
231 |
Environment* env = Environment::GetCurrent(args); |
|
1184 |
|||
1185 |
// module.resolve(specifier, url) |
||
1186 |
✗✓ | 231 |
CHECK_EQ(args.Length(), 2); |
1187 |
|||
1188 |
✗✓ | 693 |
CHECK(args[0]->IsString()); |
1189 |
231 |
Utf8Value specifier_utf8(env->isolate(), args[0]); |
|
1190 |
✓✓ | 425 |
std::string specifier_std(*specifier_utf8, specifier_utf8.length()); |
1191 |
|||
1192 |
✗✓ | 693 |
CHECK(args[1]->IsString()); |
1193 |
✓✓ | 425 |
Utf8Value url_utf8(env->isolate(), args[1]); |
1194 |
✓✓ | 425 |
URL url(*url_utf8, url_utf8.length()); |
1195 |
|||
1196 |
✗✓ | 231 |
if (url.flags() & URL_FLAGS_FAILED) { |
1197 |
return node::THROW_ERR_INVALID_ARG_TYPE( |
||
1198 |
env, "second argument is not a URL string"); |
||
1199 |
} |
||
1200 |
|||
1201 |
Maybe<URL> result = |
||
1202 |
node::loader::Resolve(env, |
||
1203 |
specifier_std, |
||
1204 |
✓✓ | 425 |
url); |
1205 |
✓✓ | 231 |
if (result.IsNothing()) { |
1206 |
37 |
return; |
|
1207 |
} |
||
1208 |
|||
1209 |
✓✓ | 194 |
URL resolution = result.FromJust(); |
1210 |
✗✓ | 194 |
CHECK(!(resolution.flags() & URL_FLAGS_FAILED)); |
1211 |
|||
1212 |
Local<Value> resolution_obj; |
||
1213 |
✓✗ | 388 |
if (resolution.ToObject(env).ToLocal(&resolution_obj)) |
1214 |
582 |
args.GetReturnValue().Set(resolution_obj); |
|
1215 |
} |
||
1216 |
|||
1217 |
194 |
void ModuleWrap::GetPackageType(const FunctionCallbackInfo<Value>& args) { |
|
1218 |
194 |
Environment* env = Environment::GetCurrent(args); |
|
1219 |
|||
1220 |
// module.getPackageType(url) |
||
1221 |
✗✓ | 194 |
CHECK_EQ(args.Length(), 1); |
1222 |
|||
1223 |
✗✓ | 582 |
CHECK(args[0]->IsString()); |
1224 |
194 |
Utf8Value url_utf8(env->isolate(), args[0]); |
|
1225 |
388 |
URL url(*url_utf8, url_utf8.length()); |
|
1226 |
|||
1227 |
194 |
PackageType pkg_type = PackageType::None; |
|
1228 |
Maybe<const PackageConfig*> pcfg = |
||
1229 |
194 |
GetPackageScopeConfig(env, url, url); |
|
1230 |
✓✗ | 194 |
if (!pcfg.IsNothing()) { |
1231 |
194 |
pkg_type = pcfg.FromJust()->type; |
|
1232 |
} |
||
1233 |
|||
1234 |
776 |
args.GetReturnValue().Set(Integer::New(env->isolate(), pkg_type)); |
|
1235 |
194 |
} |
|
1236 |
|||
1237 |
61 |
static MaybeLocal<Promise> ImportModuleDynamically( |
|
1238 |
Local<Context> context, |
||
1239 |
Local<v8::ScriptOrModule> referrer, |
||
1240 |
Local<String> specifier) { |
||
1241 |
61 |
Isolate* iso = context->GetIsolate(); |
|
1242 |
61 |
Environment* env = Environment::GetCurrent(context); |
|
1243 |
✗✓ | 61 |
CHECK_NOT_NULL(env); // TODO(addaleax): Handle nullptr here. |
1244 |
61 |
v8::EscapableHandleScope handle_scope(iso); |
|
1245 |
|||
1246 |
Local<Function> import_callback = |
||
1247 |
61 |
env->host_import_module_dynamically_callback(); |
|
1248 |
|||
1249 |
61 |
Local<PrimitiveArray> options = referrer->GetHostDefinedOptions(); |
|
1250 |
✗✓ | 61 |
if (options->Length() != HostDefinedOptions::kLength) { |
1251 |
Local<Promise::Resolver> resolver = |
||
1252 |
Promise::Resolver::New(context).ToLocalChecked(); |
||
1253 |
resolver |
||
1254 |
->Reject(context, |
||
1255 |
v8::Exception::TypeError(FIXED_ONE_BYTE_STRING( |
||
1256 |
context->GetIsolate(), "Invalid host defined options"))) |
||
1257 |
.ToChecked(); |
||
1258 |
return handle_scope.Escape(resolver->GetPromise()); |
||
1259 |
} |
||
1260 |
|||
1261 |
Local<Value> object; |
||
1262 |
|||
1263 |
61 |
int type = options->Get(iso, HostDefinedOptions::kType) |
|
1264 |
122 |
.As<Number>() |
|
1265 |
183 |
->Int32Value(context) |
|
1266 |
122 |
.ToChecked(); |
|
1267 |
61 |
uint32_t id = options->Get(iso, HostDefinedOptions::kID) |
|
1268 |
122 |
.As<Number>() |
|
1269 |
183 |
->Uint32Value(context) |
|
1270 |
122 |
.ToChecked(); |
|
1271 |
✓✓ | 61 |
if (type == ScriptType::kScript) { |
1272 |
3 |
contextify::ContextifyScript* wrap = env->id_to_script_map.find(id)->second; |
|
1273 |
6 |
object = wrap->object(); |
|
1274 |
✓✓ | 58 |
} else if (type == ScriptType::kModule) { |
1275 |
38 |
ModuleWrap* wrap = ModuleWrap::GetFromID(env, id); |
|
1276 |
76 |
object = wrap->object(); |
|
1277 |
✓✗ | 20 |
} else if (type == ScriptType::kFunction) { |
1278 |
20 |
auto it = env->id_to_function_map.find(id); |
|
1279 |
✗✓ | 20 |
CHECK_NE(it, env->id_to_function_map.end()); |
1280 |
40 |
object = it->second->object(); |
|
1281 |
} else { |
||
1282 |
UNREACHABLE(); |
||
1283 |
} |
||
1284 |
|||
1285 |
Local<Value> import_args[] = { |
||
1286 |
object, |
||
1287 |
Local<Value>(specifier), |
||
1288 |
122 |
}; |
|
1289 |
|||
1290 |
Local<Value> result; |
||
1291 |
✓✗ | 122 |
if (import_callback->Call( |
1292 |
context, |
||
1293 |
v8::Undefined(iso), |
||
1294 |
61 |
arraysize(import_args), |
|
1295 |
183 |
import_args).ToLocal(&result)) { |
|
1296 |
✗✓ | 61 |
CHECK(result->IsPromise()); |
1297 |
61 |
return handle_scope.Escape(result.As<Promise>()); |
|
1298 |
} |
||
1299 |
|||
1300 |
return MaybeLocal<Promise>(); |
||
1301 |
} |
||
1302 |
|||
1303 |
109 |
void ModuleWrap::SetImportModuleDynamicallyCallback( |
|
1304 |
const FunctionCallbackInfo<Value>& args) { |
||
1305 |
109 |
Isolate* iso = args.GetIsolate(); |
|
1306 |
109 |
Environment* env = Environment::GetCurrent(args); |
|
1307 |
109 |
HandleScope handle_scope(iso); |
|
1308 |
|||
1309 |
✗✓ | 109 |
CHECK_EQ(args.Length(), 1); |
1310 |
✗✓ | 218 |
CHECK(args[0]->IsFunction()); |
1311 |
218 |
Local<Function> import_callback = args[0].As<Function>(); |
|
1312 |
109 |
env->set_host_import_module_dynamically_callback(import_callback); |
|
1313 |
|||
1314 |
109 |
iso->SetHostImportModuleDynamicallyCallback(ImportModuleDynamically); |
|
1315 |
109 |
} |
|
1316 |
|||
1317 |
176 |
void ModuleWrap::HostInitializeImportMetaObjectCallback( |
|
1318 |
Local<Context> context, Local<Module> module, Local<Object> meta) { |
||
1319 |
176 |
Environment* env = Environment::GetCurrent(context); |
|
1320 |
✗✓ | 176 |
CHECK_NOT_NULL(env); // TODO(addaleax): Handle nullptr here. |
1321 |
176 |
ModuleWrap* module_wrap = GetFromModule(env, module); |
|
1322 |
|||
1323 |
✗✓ | 176 |
if (module_wrap == nullptr) { |
1324 |
176 |
return; |
|
1325 |
} |
||
1326 |
|||
1327 |
176 |
Local<Object> wrap = module_wrap->object(); |
|
1328 |
Local<Function> callback = |
||
1329 |
176 |
env->host_initialize_import_meta_object_callback(); |
|
1330 |
528 |
Local<Value> args[] = { wrap, meta }; |
|
1331 |
528 |
callback->Call(context, Undefined(env->isolate()), arraysize(args), args) |
|
1332 |
352 |
.ToLocalChecked(); |
|
1333 |
} |
||
1334 |
|||
1335 |
109 |
void ModuleWrap::SetInitializeImportMetaObjectCallback( |
|
1336 |
const FunctionCallbackInfo<Value>& args) { |
||
1337 |
109 |
Environment* env = Environment::GetCurrent(args); |
|
1338 |
109 |
Isolate* isolate = env->isolate(); |
|
1339 |
|||
1340 |
✗✓ | 109 |
CHECK_EQ(args.Length(), 1); |
1341 |
✗✓ | 218 |
CHECK(args[0]->IsFunction()); |
1342 |
218 |
Local<Function> import_meta_callback = args[0].As<Function>(); |
|
1343 |
109 |
env->set_host_initialize_import_meta_object_callback(import_meta_callback); |
|
1344 |
|||
1345 |
isolate->SetHostInitializeImportMetaObjectCallback( |
||
1346 |
109 |
HostInitializeImportMetaObjectCallback); |
|
1347 |
109 |
} |
|
1348 |
|||
1349 |
5091 |
void ModuleWrap::Initialize(Local<Object> target, |
|
1350 |
Local<Value> unused, |
||
1351 |
Local<Context> context, |
||
1352 |
void* priv) { |
||
1353 |
5091 |
Environment* env = Environment::GetCurrent(context); |
|
1354 |
5091 |
Isolate* isolate = env->isolate(); |
|
1355 |
|||
1356 |
5091 |
Local<FunctionTemplate> tpl = env->NewFunctionTemplate(New); |
|
1357 |
10182 |
tpl->SetClassName(FIXED_ONE_BYTE_STRING(isolate, "ModuleWrap")); |
|
1358 |
10182 |
tpl->InstanceTemplate()->SetInternalFieldCount(1); |
|
1359 |
|||
1360 |
5091 |
env->SetProtoMethod(tpl, "link", Link); |
|
1361 |
5091 |
env->SetProtoMethod(tpl, "instantiate", Instantiate); |
|
1362 |
5091 |
env->SetProtoMethod(tpl, "evaluate", Evaluate); |
|
1363 |
5091 |
env->SetProtoMethodNoSideEffect(tpl, "namespace", Namespace); |
|
1364 |
5091 |
env->SetProtoMethodNoSideEffect(tpl, "getStatus", GetStatus); |
|
1365 |
5091 |
env->SetProtoMethodNoSideEffect(tpl, "getError", GetError); |
|
1366 |
env->SetProtoMethodNoSideEffect(tpl, "getStaticDependencySpecifiers", |
||
1367 |
5091 |
GetStaticDependencySpecifiers); |
|
1368 |
|||
1369 |
target->Set(env->context(), FIXED_ONE_BYTE_STRING(isolate, "ModuleWrap"), |
||
1370 |
25455 |
tpl->GetFunction(context).ToLocalChecked()).Check(); |
|
1371 |
5091 |
env->SetMethod(target, "resolve", Resolve); |
|
1372 |
5091 |
env->SetMethod(target, "getPackageType", GetPackageType); |
|
1373 |
env->SetMethod(target, |
||
1374 |
"setImportModuleDynamicallyCallback", |
||
1375 |
5091 |
SetImportModuleDynamicallyCallback); |
|
1376 |
env->SetMethod(target, |
||
1377 |
"setInitializeImportMetaObjectCallback", |
||
1378 |
5091 |
SetInitializeImportMetaObjectCallback); |
|
1379 |
|||
1380 |
#define V(name) \ |
||
1381 |
target->Set(context, \ |
||
1382 |
FIXED_ONE_BYTE_STRING(env->isolate(), #name), \ |
||
1383 |
Integer::New(env->isolate(), Module::Status::name)) \ |
||
1384 |
.FromJust() |
||
1385 |
20364 |
V(kUninstantiated); |
|
1386 |
20364 |
V(kInstantiating); |
|
1387 |
20364 |
V(kInstantiated); |
|
1388 |
20364 |
V(kEvaluating); |
|
1389 |
20364 |
V(kEvaluated); |
|
1390 |
20364 |
V(kErrored); |
|
1391 |
#undef V |
||
1392 |
5091 |
} |
|
1393 |
|||
1394 |
} // namespace loader |
||
1395 |
} // namespace node |
||
1396 |
|||
1397 |
4953 |
NODE_MODULE_CONTEXT_AWARE_INTERNAL(module_wrap, |
|
1398 |
node::loader::ModuleWrap::Initialize) |
Generated by: GCOVR (Version 3.4) |