Line data Source code
1 : // Copyright 2016 the V8 project authors. All rights reserved.
2 : // Use of this source code is governed by a BSD-style license that can be
3 : // found in the LICENSE file.
4 :
5 : #include "src/flags.h"
6 :
7 : #include "test/cctest/cctest.h"
8 :
9 : namespace {
10 :
11 : using v8::Context;
12 : using v8::HandleScope;
13 : using v8::Isolate;
14 : using v8::Local;
15 : using v8::MaybeLocal;
16 : using v8::Module;
17 : using v8::ScriptCompiler;
18 : using v8::ScriptOrigin;
19 : using v8::String;
20 : using v8::Value;
21 :
22 : ScriptOrigin ModuleOrigin(Local<v8::Value> resource_name, Isolate* isolate) {
23 : ScriptOrigin origin(resource_name, Local<v8::Integer>(), Local<v8::Integer>(),
24 : Local<v8::Boolean>(), Local<v8::Integer>(),
25 : Local<v8::Value>(), Local<v8::Boolean>(),
26 : Local<v8::Boolean>(), True(isolate));
27 : return origin;
28 : }
29 :
30 : static Local<Module> dep1;
31 : static Local<Module> dep2;
32 60 : MaybeLocal<Module> ResolveCallback(Local<Context> context,
33 : Local<String> specifier,
34 : Local<Module> referrer) {
35 60 : Isolate* isolate = CcTest::isolate();
36 120 : if (specifier->StrictEquals(v8_str("./dep1.js"))) {
37 20 : return dep1;
38 80 : } else if (specifier->StrictEquals(v8_str("./dep2.js"))) {
39 20 : return dep2;
40 : } else {
41 40 : isolate->ThrowException(v8_str("boom"));
42 20 : return MaybeLocal<Module>();
43 : }
44 : }
45 :
46 26644 : TEST(ModuleInstantiationFailures1) {
47 5 : Isolate* isolate = CcTest::isolate();
48 10 : HandleScope scope(isolate);
49 5 : LocalContext env;
50 10 : v8::TryCatch try_catch(isolate);
51 :
52 : Local<Module> module;
53 : {
54 : Local<String> source_text = v8_str(
55 : "import './foo.js';\n"
56 5 : "export {} from './bar.js';");
57 5 : ScriptOrigin origin = ModuleOrigin(v8_str("file.js"), CcTest::isolate());
58 : ScriptCompiler::Source source(source_text, origin);
59 5 : module = ScriptCompiler::CompileModule(isolate, &source).ToLocalChecked();
60 5 : CHECK_EQ(Module::kUninstantiated, module->GetStatus());
61 5 : CHECK_EQ(2, module->GetModuleRequestsLength());
62 15 : CHECK(v8_str("./foo.js")->StrictEquals(module->GetModuleRequest(0)));
63 5 : v8::Location loc = module->GetModuleRequestLocation(0);
64 5 : CHECK_EQ(0, loc.GetLineNumber());
65 5 : CHECK_EQ(7, loc.GetColumnNumber());
66 15 : CHECK(v8_str("./bar.js")->StrictEquals(module->GetModuleRequest(1)));
67 5 : loc = module->GetModuleRequestLocation(1);
68 5 : CHECK_EQ(1, loc.GetLineNumber());
69 5 : CHECK_EQ(15, loc.GetColumnNumber());
70 : }
71 :
72 : // Instantiation should fail.
73 : {
74 10 : v8::TryCatch inner_try_catch(isolate);
75 10 : CHECK(module->InstantiateModule(env.local(), ResolveCallback).IsNothing());
76 5 : CHECK(inner_try_catch.HasCaught());
77 15 : CHECK(inner_try_catch.Exception()->StrictEquals(v8_str("boom")));
78 5 : CHECK_EQ(Module::kUninstantiated, module->GetStatus());
79 : }
80 :
81 : // Start over again...
82 : {
83 : Local<String> source_text = v8_str(
84 : "import './dep1.js';\n"
85 5 : "export {} from './bar.js';");
86 5 : ScriptOrigin origin = ModuleOrigin(v8_str("file.js"), CcTest::isolate());
87 : ScriptCompiler::Source source(source_text, origin);
88 5 : module = ScriptCompiler::CompileModule(isolate, &source).ToLocalChecked();
89 : }
90 :
91 : // dep1.js
92 : {
93 5 : Local<String> source_text = v8_str("");
94 5 : ScriptOrigin origin = ModuleOrigin(v8_str("dep1.js"), CcTest::isolate());
95 : ScriptCompiler::Source source(source_text, origin);
96 10 : dep1 = ScriptCompiler::CompileModule(isolate, &source).ToLocalChecked();
97 : }
98 :
99 : // Instantiation should fail because a sub-module fails to resolve.
100 : {
101 10 : v8::TryCatch inner_try_catch(isolate);
102 10 : CHECK(module->InstantiateModule(env.local(), ResolveCallback).IsNothing());
103 5 : CHECK(inner_try_catch.HasCaught());
104 15 : CHECK(inner_try_catch.Exception()->StrictEquals(v8_str("boom")));
105 5 : CHECK_EQ(Module::kUninstantiated, module->GetStatus());
106 : }
107 :
108 5 : CHECK(!try_catch.HasCaught());
109 5 : }
110 :
111 26644 : TEST(ModuleInstantiationFailures2) {
112 5 : Isolate* isolate = CcTest::isolate();
113 10 : HandleScope scope(isolate);
114 5 : LocalContext env;
115 10 : v8::TryCatch try_catch(isolate);
116 :
117 : // root1.js
118 : Local<Module> root;
119 : {
120 : Local<String> source_text =
121 5 : v8_str("import './dep1.js'; import './dep2.js'");
122 5 : ScriptOrigin origin = ModuleOrigin(v8_str("root1.js"), CcTest::isolate());
123 : ScriptCompiler::Source source(source_text, origin);
124 5 : root = ScriptCompiler::CompileModule(isolate, &source).ToLocalChecked();
125 : }
126 :
127 : // dep1.js
128 : {
129 5 : Local<String> source_text = v8_str("export let x = 42");
130 5 : ScriptOrigin origin = ModuleOrigin(v8_str("dep1.js"), CcTest::isolate());
131 : ScriptCompiler::Source source(source_text, origin);
132 10 : dep1 = ScriptCompiler::CompileModule(isolate, &source).ToLocalChecked();
133 : }
134 :
135 : // dep2.js
136 : {
137 5 : Local<String> source_text = v8_str("import {foo} from './dep3.js'");
138 5 : ScriptOrigin origin = ModuleOrigin(v8_str("dep2.js"), CcTest::isolate());
139 : ScriptCompiler::Source source(source_text, origin);
140 10 : dep2 = ScriptCompiler::CompileModule(isolate, &source).ToLocalChecked();
141 : }
142 :
143 : {
144 10 : v8::TryCatch inner_try_catch(isolate);
145 10 : CHECK(root->InstantiateModule(env.local(), ResolveCallback).IsNothing());
146 5 : CHECK(inner_try_catch.HasCaught());
147 15 : CHECK(inner_try_catch.Exception()->StrictEquals(v8_str("boom")));
148 5 : CHECK_EQ(Module::kUninstantiated, root->GetStatus());
149 5 : CHECK_EQ(Module::kUninstantiated, dep1->GetStatus());
150 5 : CHECK_EQ(Module::kUninstantiated, dep2->GetStatus());
151 : }
152 :
153 : // Change dep2.js
154 : {
155 5 : Local<String> source_text = v8_str("import {foo} from './dep2.js'");
156 5 : ScriptOrigin origin = ModuleOrigin(v8_str("dep2.js"), CcTest::isolate());
157 : ScriptCompiler::Source source(source_text, origin);
158 10 : dep2 = ScriptCompiler::CompileModule(isolate, &source).ToLocalChecked();
159 : }
160 :
161 : {
162 10 : v8::TryCatch inner_try_catch(isolate);
163 10 : CHECK(root->InstantiateModule(env.local(), ResolveCallback).IsNothing());
164 5 : CHECK(inner_try_catch.HasCaught());
165 15 : CHECK(!inner_try_catch.Exception()->StrictEquals(v8_str("boom")));
166 5 : CHECK_EQ(Module::kUninstantiated, root->GetStatus());
167 5 : CHECK_EQ(Module::kInstantiated, dep1->GetStatus());
168 5 : CHECK_EQ(Module::kUninstantiated, dep2->GetStatus());
169 : }
170 :
171 : // Change dep2.js again
172 : {
173 5 : Local<String> source_text = v8_str("import {foo} from './dep3.js'");
174 5 : ScriptOrigin origin = ModuleOrigin(v8_str("dep2.js"), CcTest::isolate());
175 : ScriptCompiler::Source source(source_text, origin);
176 10 : dep2 = ScriptCompiler::CompileModule(isolate, &source).ToLocalChecked();
177 : }
178 :
179 : {
180 10 : v8::TryCatch inner_try_catch(isolate);
181 10 : CHECK(root->InstantiateModule(env.local(), ResolveCallback).IsNothing());
182 5 : CHECK(inner_try_catch.HasCaught());
183 15 : CHECK(inner_try_catch.Exception()->StrictEquals(v8_str("boom")));
184 5 : CHECK_EQ(Module::kUninstantiated, root->GetStatus());
185 5 : CHECK_EQ(Module::kInstantiated, dep1->GetStatus());
186 5 : CHECK_EQ(Module::kUninstantiated, dep2->GetStatus());
187 : }
188 5 : }
189 :
190 45 : static MaybeLocal<Module> CompileSpecifierAsModuleResolveCallback(
191 : Local<Context> context, Local<String> specifier, Local<Module> referrer) {
192 45 : ScriptOrigin origin = ModuleOrigin(v8_str("module.js"), CcTest::isolate());
193 : ScriptCompiler::Source source(specifier, origin);
194 45 : return ScriptCompiler::CompileModule(CcTest::isolate(), &source)
195 90 : .ToLocalChecked();
196 : }
197 :
198 26644 : TEST(ModuleEvaluation) {
199 5 : Isolate* isolate = CcTest::isolate();
200 10 : HandleScope scope(isolate);
201 5 : LocalContext env;
202 10 : v8::TryCatch try_catch(isolate);
203 :
204 : Local<String> source_text = v8_str(
205 : "import 'Object.expando = 5';"
206 5 : "import 'Object.expando *= 2';");
207 5 : ScriptOrigin origin = ModuleOrigin(v8_str("file.js"), CcTest::isolate());
208 : ScriptCompiler::Source source(source_text, origin);
209 : Local<Module> module =
210 5 : ScriptCompiler::CompileModule(isolate, &source).ToLocalChecked();
211 5 : CHECK_EQ(Module::kUninstantiated, module->GetStatus());
212 10 : CHECK(module
213 : ->InstantiateModule(env.local(),
214 : CompileSpecifierAsModuleResolveCallback)
215 : .FromJust());
216 5 : CHECK_EQ(Module::kInstantiated, module->GetStatus());
217 10 : CHECK(!module->Evaluate(env.local()).IsEmpty());
218 5 : CHECK_EQ(Module::kEvaluated, module->GetStatus());
219 5 : ExpectInt32("Object.expando", 10);
220 :
221 5 : CHECK(!try_catch.HasCaught());
222 5 : }
223 :
224 26644 : TEST(ModuleEvaluationError) {
225 5 : Isolate* isolate = CcTest::isolate();
226 10 : HandleScope scope(isolate);
227 5 : LocalContext env;
228 10 : v8::TryCatch try_catch(isolate);
229 :
230 : Local<String> source_text =
231 5 : v8_str("Object.x = (Object.x || 0) + 1; throw 'boom';");
232 5 : ScriptOrigin origin = ModuleOrigin(v8_str("file.js"), CcTest::isolate());
233 : ScriptCompiler::Source source(source_text, origin);
234 : Local<Module> module =
235 5 : ScriptCompiler::CompileModule(isolate, &source).ToLocalChecked();
236 5 : CHECK_EQ(Module::kUninstantiated, module->GetStatus());
237 10 : CHECK(module
238 : ->InstantiateModule(env.local(),
239 : CompileSpecifierAsModuleResolveCallback)
240 : .FromJust());
241 5 : CHECK_EQ(Module::kInstantiated, module->GetStatus());
242 :
243 : {
244 10 : v8::TryCatch inner_try_catch(isolate);
245 10 : CHECK(module->Evaluate(env.local()).IsEmpty());
246 5 : CHECK(inner_try_catch.HasCaught());
247 15 : CHECK(inner_try_catch.Exception()->StrictEquals(v8_str("boom")));
248 5 : CHECK_EQ(Module::kErrored, module->GetStatus());
249 5 : Local<Value> exception = module->GetException();
250 10 : CHECK(exception->StrictEquals(v8_str("boom")));
251 5 : ExpectInt32("Object.x", 1);
252 : }
253 :
254 : {
255 10 : v8::TryCatch inner_try_catch(isolate);
256 10 : CHECK(module->Evaluate(env.local()).IsEmpty());
257 5 : CHECK(inner_try_catch.HasCaught());
258 15 : CHECK(inner_try_catch.Exception()->StrictEquals(v8_str("boom")));
259 5 : CHECK_EQ(Module::kErrored, module->GetStatus());
260 5 : Local<Value> exception = module->GetException();
261 10 : CHECK(exception->StrictEquals(v8_str("boom")));
262 5 : ExpectInt32("Object.x", 1);
263 : }
264 :
265 5 : CHECK(!try_catch.HasCaught());
266 5 : }
267 :
268 26644 : TEST(ModuleEvaluationCompletion1) {
269 5 : Isolate* isolate = CcTest::isolate();
270 10 : HandleScope scope(isolate);
271 5 : LocalContext env;
272 10 : v8::TryCatch try_catch(isolate);
273 :
274 : const char* sources[] = {
275 : "",
276 : "var a = 1",
277 : "import '42'",
278 : "export * from '42'",
279 : "export {} from '42'",
280 : "export {}",
281 : "var a = 1; export {a}",
282 : "export function foo() {}",
283 : "export class C extends null {}",
284 : "export let a = 1",
285 : "export default 1",
286 : "export default function foo() {}",
287 : "export default function () {}",
288 : "export default (function () {})",
289 : "export default class C extends null {}",
290 : "export default (class C extends null {})",
291 : "for (var i = 0; i < 5; ++i) {}",
292 5 : };
293 :
294 175 : for (auto src : sources) {
295 85 : Local<String> source_text = v8_str(src);
296 85 : ScriptOrigin origin = ModuleOrigin(v8_str("file.js"), CcTest::isolate());
297 : ScriptCompiler::Source source(source_text, origin);
298 : Local<Module> module =
299 85 : ScriptCompiler::CompileModule(isolate, &source).ToLocalChecked();
300 85 : CHECK_EQ(Module::kUninstantiated, module->GetStatus());
301 170 : CHECK(module
302 : ->InstantiateModule(env.local(),
303 : CompileSpecifierAsModuleResolveCallback)
304 : .FromJust());
305 85 : CHECK_EQ(Module::kInstantiated, module->GetStatus());
306 170 : CHECK(module->Evaluate(env.local()).ToLocalChecked()->IsUndefined());
307 85 : CHECK_EQ(Module::kEvaluated, module->GetStatus());
308 170 : CHECK(module->Evaluate(env.local()).ToLocalChecked()->IsUndefined());
309 85 : CHECK_EQ(Module::kEvaluated, module->GetStatus());
310 : }
311 :
312 5 : CHECK(!try_catch.HasCaught());
313 5 : }
314 :
315 26644 : TEST(ModuleEvaluationCompletion2) {
316 5 : Isolate* isolate = CcTest::isolate();
317 10 : HandleScope scope(isolate);
318 5 : LocalContext env;
319 10 : v8::TryCatch try_catch(isolate);
320 :
321 : const char* sources[] = {
322 : "'gaga'; ",
323 : "'gaga'; var a = 1",
324 : "'gaga'; import '42'",
325 : "'gaga'; export * from '42'",
326 : "'gaga'; export {} from '42'",
327 : "'gaga'; export {}",
328 : "'gaga'; var a = 1; export {a}",
329 : "'gaga'; export function foo() {}",
330 : "'gaga'; export class C extends null {}",
331 : "'gaga'; export let a = 1",
332 : "'gaga'; export default 1",
333 : "'gaga'; export default function foo() {}",
334 : "'gaga'; export default function () {}",
335 : "'gaga'; export default (function () {})",
336 : "'gaga'; export default class C extends null {}",
337 : "'gaga'; export default (class C extends null {})",
338 5 : };
339 :
340 165 : for (auto src : sources) {
341 80 : Local<String> source_text = v8_str(src);
342 80 : ScriptOrigin origin = ModuleOrigin(v8_str("file.js"), CcTest::isolate());
343 : ScriptCompiler::Source source(source_text, origin);
344 : Local<Module> module =
345 80 : ScriptCompiler::CompileModule(isolate, &source).ToLocalChecked();
346 80 : CHECK_EQ(Module::kUninstantiated, module->GetStatus());
347 160 : CHECK(module
348 : ->InstantiateModule(env.local(),
349 : CompileSpecifierAsModuleResolveCallback)
350 : .FromJust());
351 80 : CHECK_EQ(Module::kInstantiated, module->GetStatus());
352 240 : CHECK(module->Evaluate(env.local())
353 : .ToLocalChecked()
354 : ->StrictEquals(v8_str("gaga")));
355 80 : CHECK_EQ(Module::kEvaluated, module->GetStatus());
356 160 : CHECK(module->Evaluate(env.local()).ToLocalChecked()->IsUndefined());
357 80 : CHECK_EQ(Module::kEvaluated, module->GetStatus());
358 : }
359 :
360 5 : CHECK(!try_catch.HasCaught());
361 5 : }
362 :
363 26644 : TEST(ModuleNamespace) {
364 5 : Isolate* isolate = CcTest::isolate();
365 10 : HandleScope scope(isolate);
366 5 : LocalContext env;
367 10 : v8::TryCatch try_catch(isolate);
368 :
369 : Local<v8::Object> ReferenceError =
370 10 : CompileRun("ReferenceError")->ToObject(env.local()).ToLocalChecked();
371 :
372 : Local<String> source_text = v8_str(
373 : "import {a, b} from 'export var a = 1; export let b = 2';"
374 : "export function geta() {return a};"
375 : "export function getb() {return b};"
376 : "export let radio = 3;"
377 5 : "export var gaga = 4;");
378 5 : ScriptOrigin origin = ModuleOrigin(v8_str("file.js"), CcTest::isolate());
379 : ScriptCompiler::Source source(source_text, origin);
380 : Local<Module> module =
381 5 : ScriptCompiler::CompileModule(isolate, &source).ToLocalChecked();
382 5 : CHECK_EQ(Module::kUninstantiated, module->GetStatus());
383 10 : CHECK(module
384 : ->InstantiateModule(env.local(),
385 : CompileSpecifierAsModuleResolveCallback)
386 : .FromJust());
387 5 : CHECK_EQ(Module::kInstantiated, module->GetStatus());
388 5 : Local<Value> ns = module->GetModuleNamespace();
389 5 : CHECK_EQ(Module::kInstantiated, module->GetStatus());
390 5 : Local<v8::Object> nsobj = ns->ToObject(env.local()).ToLocalChecked();
391 :
392 : // a, b
393 15 : CHECK(nsobj->Get(env.local(), v8_str("a")).ToLocalChecked()->IsUndefined());
394 15 : CHECK(nsobj->Get(env.local(), v8_str("b")).ToLocalChecked()->IsUndefined());
395 :
396 : // geta
397 : {
398 15 : auto geta = nsobj->Get(env.local(), v8_str("geta")).ToLocalChecked();
399 : auto a = geta.As<v8::Function>()
400 5 : ->Call(env.local(), geta, 0, nullptr)
401 : .ToLocalChecked();
402 5 : CHECK(a->IsUndefined());
403 : }
404 :
405 : // getb
406 : {
407 10 : v8::TryCatch inner_try_catch(isolate);
408 15 : auto getb = nsobj->Get(env.local(), v8_str("getb")).ToLocalChecked();
409 10 : CHECK(
410 : getb.As<v8::Function>()->Call(env.local(), getb, 0, nullptr).IsEmpty());
411 5 : CHECK(inner_try_catch.HasCaught());
412 15 : CHECK(inner_try_catch.Exception()
413 : ->InstanceOf(env.local(), ReferenceError)
414 : .FromJust());
415 : }
416 :
417 : // radio
418 : {
419 10 : v8::TryCatch inner_try_catch(isolate);
420 : // https://bugs.chromium.org/p/v8/issues/detail?id=7235
421 : // CHECK(nsobj->Get(env.local(), v8_str("radio")).IsEmpty());
422 15 : CHECK(nsobj->Get(env.local(), v8_str("radio"))
423 : .ToLocalChecked()
424 : ->IsUndefined());
425 5 : CHECK(inner_try_catch.HasCaught());
426 15 : CHECK(inner_try_catch.Exception()
427 : ->InstanceOf(env.local(), ReferenceError)
428 : .FromJust());
429 : }
430 :
431 : // gaga
432 : {
433 15 : auto gaga = nsobj->Get(env.local(), v8_str("gaga")).ToLocalChecked();
434 5 : CHECK(gaga->IsUndefined());
435 : }
436 :
437 5 : CHECK(!try_catch.HasCaught());
438 5 : CHECK_EQ(Module::kInstantiated, module->GetStatus());
439 5 : module->Evaluate(env.local()).ToLocalChecked();
440 5 : CHECK_EQ(Module::kEvaluated, module->GetStatus());
441 :
442 : // geta
443 : {
444 15 : auto geta = nsobj->Get(env.local(), v8_str("geta")).ToLocalChecked();
445 : auto a = geta.As<v8::Function>()
446 5 : ->Call(env.local(), geta, 0, nullptr)
447 : .ToLocalChecked();
448 10 : CHECK_EQ(1, a->Int32Value(env.local()).FromJust());
449 : }
450 :
451 : // getb
452 : {
453 15 : auto getb = nsobj->Get(env.local(), v8_str("getb")).ToLocalChecked();
454 : auto b = getb.As<v8::Function>()
455 5 : ->Call(env.local(), getb, 0, nullptr)
456 : .ToLocalChecked();
457 10 : CHECK_EQ(2, b->Int32Value(env.local()).FromJust());
458 : }
459 :
460 : // radio
461 : {
462 15 : auto radio = nsobj->Get(env.local(), v8_str("radio")).ToLocalChecked();
463 10 : CHECK_EQ(3, radio->Int32Value(env.local()).FromJust());
464 : }
465 :
466 : // gaga
467 : {
468 15 : auto gaga = nsobj->Get(env.local(), v8_str("gaga")).ToLocalChecked();
469 10 : CHECK_EQ(4, gaga->Int32Value(env.local()).FromJust());
470 : }
471 :
472 5 : CHECK(!try_catch.HasCaught());
473 5 : }
474 79917 : } // anonymous namespace
|