| // Copyright 2016 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "src/flags.h" |
| |
| #include "test/cctest/cctest.h" |
| |
| namespace { |
| |
| using v8::Context; |
| using v8::HandleScope; |
| using v8::Isolate; |
| using v8::Local; |
| using v8::MaybeLocal; |
| using v8::Module; |
| using v8::ScriptCompiler; |
| using v8::ScriptOrigin; |
| using v8::String; |
| using v8::Value; |
| |
| ScriptOrigin ModuleOrigin(Local<v8::Value> resource_name, Isolate* isolate) { |
| ScriptOrigin origin(resource_name, Local<v8::Integer>(), Local<v8::Integer>(), |
| Local<v8::Boolean>(), Local<v8::Integer>(), |
| Local<v8::Value>(), Local<v8::Boolean>(), |
| Local<v8::Boolean>(), True(isolate)); |
| return origin; |
| } |
| |
| MaybeLocal<Module> FailAlwaysResolveCallback(Local<Context> context, |
| Local<String> specifier, |
| Local<Module> referrer) { |
| Isolate* isolate = context->GetIsolate(); |
| isolate->ThrowException(v8_str("boom")); |
| return MaybeLocal<Module>(); |
| } |
| |
| static int g_count = 0; |
| MaybeLocal<Module> FailOnSecondCallResolveCallback(Local<Context> context, |
| Local<String> specifier, |
| Local<Module> referrer) { |
| Isolate* isolate = CcTest::isolate(); |
| if (g_count++ > 0) { |
| isolate->ThrowException(v8_str("booom")); |
| return MaybeLocal<Module>(); |
| } |
| Local<String> source_text = v8_str(""); |
| ScriptOrigin origin = ModuleOrigin(v8_str("module.js"), isolate); |
| ScriptCompiler::Source source(source_text, origin); |
| return ScriptCompiler::CompileModule(isolate, &source).ToLocalChecked(); |
| } |
| |
| TEST(ModuleInstantiationFailures) { |
| Isolate* isolate = CcTest::isolate(); |
| HandleScope scope(isolate); |
| LocalContext env; |
| v8::TryCatch try_catch(isolate); |
| |
| Local<String> source_text = v8_str( |
| "import './foo.js';\n" |
| "export {} from './bar.js';"); |
| ScriptOrigin origin = ModuleOrigin(v8_str("file.js"), CcTest::isolate()); |
| ScriptCompiler::Source source(source_text, origin); |
| Local<Module> module = |
| ScriptCompiler::CompileModule(isolate, &source).ToLocalChecked(); |
| CHECK_EQ(Module::kUninstantiated, module->GetStatus()); |
| CHECK_EQ(2, module->GetModuleRequestsLength()); |
| CHECK(v8_str("./foo.js")->StrictEquals(module->GetModuleRequest(0))); |
| v8::Location loc = module->GetModuleRequestLocation(0); |
| CHECK_EQ(0, loc.GetLineNumber()); |
| CHECK_EQ(7, loc.GetColumnNumber()); |
| |
| CHECK(v8_str("./bar.js")->StrictEquals(module->GetModuleRequest(1))); |
| loc = module->GetModuleRequestLocation(1); |
| CHECK_EQ(1, loc.GetLineNumber()); |
| CHECK_EQ(15, loc.GetColumnNumber()); |
| |
| // Instantiation should fail. |
| { |
| v8::TryCatch inner_try_catch(isolate); |
| CHECK(module->InstantiateModule(env.local(), FailAlwaysResolveCallback) |
| .IsNothing()); |
| CHECK(inner_try_catch.HasCaught()); |
| CHECK(inner_try_catch.Exception()->StrictEquals(v8_str("boom"))); |
| CHECK_EQ(Module::kErrored, module->GetStatus()); |
| Local<Value> exception = module->GetException(); |
| CHECK(exception->StrictEquals(v8_str("boom"))); |
| // TODO(neis): Check object identity. |
| } |
| |
| // Start over again... |
| module = ScriptCompiler::CompileModule(isolate, &source).ToLocalChecked(); |
| |
| // Instantiation should fail if a sub-module fails to resolve. |
| g_count = 0; |
| { |
| v8::TryCatch inner_try_catch(isolate); |
| CHECK( |
| module->InstantiateModule(env.local(), FailOnSecondCallResolveCallback) |
| .IsNothing()); |
| CHECK(inner_try_catch.HasCaught()); |
| CHECK(inner_try_catch.Exception()->StrictEquals(v8_str("booom"))); |
| CHECK_EQ(Module::kErrored, module->GetStatus()); |
| Local<Value> exception = module->GetException(); |
| CHECK(exception->StrictEquals(v8_str("booom"))); |
| } |
| |
| CHECK(!try_catch.HasCaught()); |
| } |
| |
| static MaybeLocal<Module> CompileSpecifierAsModuleResolveCallback( |
| Local<Context> context, Local<String> specifier, Local<Module> referrer) { |
| ScriptOrigin origin = ModuleOrigin(v8_str("module.js"), CcTest::isolate()); |
| ScriptCompiler::Source source(specifier, origin); |
| return ScriptCompiler::CompileModule(CcTest::isolate(), &source) |
| .ToLocalChecked(); |
| } |
| |
| TEST(ModuleEvaluation) { |
| Isolate* isolate = CcTest::isolate(); |
| HandleScope scope(isolate); |
| LocalContext env; |
| v8::TryCatch try_catch(isolate); |
| |
| Local<String> source_text = v8_str( |
| "import 'Object.expando = 5';" |
| "import 'Object.expando *= 2';"); |
| ScriptOrigin origin = ModuleOrigin(v8_str("file.js"), CcTest::isolate()); |
| ScriptCompiler::Source source(source_text, origin); |
| Local<Module> module = |
| ScriptCompiler::CompileModule(isolate, &source).ToLocalChecked(); |
| CHECK_EQ(Module::kUninstantiated, module->GetStatus()); |
| CHECK(module |
| ->InstantiateModule(env.local(), |
| CompileSpecifierAsModuleResolveCallback) |
| .FromJust()); |
| CHECK_EQ(Module::kInstantiated, module->GetStatus()); |
| CHECK(!module->Evaluate(env.local()).IsEmpty()); |
| CHECK_EQ(Module::kEvaluated, module->GetStatus()); |
| ExpectInt32("Object.expando", 10); |
| |
| CHECK(!try_catch.HasCaught()); |
| } |
| |
| TEST(ModuleEvaluationError) { |
| Isolate* isolate = CcTest::isolate(); |
| HandleScope scope(isolate); |
| LocalContext env; |
| v8::TryCatch try_catch(isolate); |
| |
| Local<String> source_text = |
| v8_str("Object.x = (Object.x || 0) + 1; throw 'boom';"); |
| ScriptOrigin origin = ModuleOrigin(v8_str("file.js"), CcTest::isolate()); |
| ScriptCompiler::Source source(source_text, origin); |
| Local<Module> module = |
| ScriptCompiler::CompileModule(isolate, &source).ToLocalChecked(); |
| CHECK_EQ(Module::kUninstantiated, module->GetStatus()); |
| CHECK(module |
| ->InstantiateModule(env.local(), |
| CompileSpecifierAsModuleResolveCallback) |
| .FromJust()); |
| CHECK_EQ(Module::kInstantiated, module->GetStatus()); |
| |
| { |
| v8::TryCatch inner_try_catch(isolate); |
| CHECK(module->Evaluate(env.local()).IsEmpty()); |
| CHECK(inner_try_catch.HasCaught()); |
| CHECK(inner_try_catch.Exception()->StrictEquals(v8_str("boom"))); |
| CHECK_EQ(Module::kErrored, module->GetStatus()); |
| Local<Value> exception = module->GetException(); |
| CHECK(exception->StrictEquals(v8_str("boom"))); |
| ExpectInt32("Object.x", 1); |
| } |
| |
| { |
| v8::TryCatch inner_try_catch(isolate); |
| CHECK(module->Evaluate(env.local()).IsEmpty()); |
| CHECK(inner_try_catch.HasCaught()); |
| CHECK(inner_try_catch.Exception()->StrictEquals(v8_str("boom"))); |
| CHECK_EQ(Module::kErrored, module->GetStatus()); |
| Local<Value> exception = module->GetException(); |
| CHECK(exception->StrictEquals(v8_str("boom"))); |
| ExpectInt32("Object.x", 1); |
| } |
| |
| CHECK(!try_catch.HasCaught()); |
| } |
| |
| TEST(ModuleEvaluationCompletion1) { |
| Isolate* isolate = CcTest::isolate(); |
| HandleScope scope(isolate); |
| LocalContext env; |
| v8::TryCatch try_catch(isolate); |
| |
| const char* sources[] = { |
| "", |
| "var a = 1", |
| "import '42'", |
| "export * from '42'", |
| "export {} from '42'", |
| "export {}", |
| "var a = 1; export {a}", |
| "export function foo() {}", |
| "export class C extends null {}", |
| "export let a = 1", |
| "export default 1", |
| "export default function foo() {}", |
| "export default function () {}", |
| "export default (function () {})", |
| "export default class C extends null {}", |
| "export default (class C extends null {})", |
| "for (var i = 0; i < 5; ++i) {}", |
| }; |
| |
| for (auto src : sources) { |
| Local<String> source_text = v8_str(src); |
| ScriptOrigin origin = ModuleOrigin(v8_str("file.js"), CcTest::isolate()); |
| ScriptCompiler::Source source(source_text, origin); |
| Local<Module> module = |
| ScriptCompiler::CompileModule(isolate, &source).ToLocalChecked(); |
| CHECK_EQ(Module::kUninstantiated, module->GetStatus()); |
| CHECK(module |
| ->InstantiateModule(env.local(), |
| CompileSpecifierAsModuleResolveCallback) |
| .FromJust()); |
| CHECK_EQ(Module::kInstantiated, module->GetStatus()); |
| CHECK(module->Evaluate(env.local()).ToLocalChecked()->IsUndefined()); |
| CHECK_EQ(Module::kEvaluated, module->GetStatus()); |
| CHECK(module->Evaluate(env.local()).ToLocalChecked()->IsUndefined()); |
| CHECK_EQ(Module::kEvaluated, module->GetStatus()); |
| } |
| |
| CHECK(!try_catch.HasCaught()); |
| } |
| |
| TEST(ModuleEvaluationCompletion2) { |
| Isolate* isolate = CcTest::isolate(); |
| HandleScope scope(isolate); |
| LocalContext env; |
| v8::TryCatch try_catch(isolate); |
| |
| const char* sources[] = { |
| "'gaga'; ", |
| "'gaga'; var a = 1", |
| "'gaga'; import '42'", |
| "'gaga'; export * from '42'", |
| "'gaga'; export {} from '42'", |
| "'gaga'; export {}", |
| "'gaga'; var a = 1; export {a}", |
| "'gaga'; export function foo() {}", |
| "'gaga'; export class C extends null {}", |
| "'gaga'; export let a = 1", |
| "'gaga'; export default 1", |
| "'gaga'; export default function foo() {}", |
| "'gaga'; export default function () {}", |
| "'gaga'; export default (function () {})", |
| "'gaga'; export default class C extends null {}", |
| "'gaga'; export default (class C extends null {})", |
| }; |
| |
| for (auto src : sources) { |
| Local<String> source_text = v8_str(src); |
| ScriptOrigin origin = ModuleOrigin(v8_str("file.js"), CcTest::isolate()); |
| ScriptCompiler::Source source(source_text, origin); |
| Local<Module> module = |
| ScriptCompiler::CompileModule(isolate, &source).ToLocalChecked(); |
| CHECK_EQ(Module::kUninstantiated, module->GetStatus()); |
| CHECK(module |
| ->InstantiateModule(env.local(), |
| CompileSpecifierAsModuleResolveCallback) |
| .FromJust()); |
| CHECK_EQ(Module::kInstantiated, module->GetStatus()); |
| CHECK(module->Evaluate(env.local()) |
| .ToLocalChecked() |
| ->StrictEquals(v8_str("gaga"))); |
| CHECK_EQ(Module::kEvaluated, module->GetStatus()); |
| CHECK(module->Evaluate(env.local()).ToLocalChecked()->IsUndefined()); |
| CHECK_EQ(Module::kEvaluated, module->GetStatus()); |
| } |
| |
| CHECK(!try_catch.HasCaught()); |
| } |
| |
| } // anonymous namespace |