diff options
Diffstat (limited to 'deps/v8/test/cctest/test-modules.cc')
-rw-r--r-- | deps/v8/test/cctest/test-modules.cc | 819 |
1 files changed, 617 insertions, 202 deletions
diff --git a/deps/v8/test/cctest/test-modules.cc b/deps/v8/test/cctest/test-modules.cc index 0f2bfd2a5f..d7cb6e610e 100644 --- a/deps/v8/test/cctest/test-modules.cc +++ b/deps/v8/test/cctest/test-modules.cc @@ -14,6 +14,7 @@ using v8::Isolate; using v8::Local; using v8::MaybeLocal; using v8::Module; +using v8::Promise; using v8::ScriptCompiler; using v8::ScriptOrigin; using v8::String; @@ -196,99 +197,480 @@ static MaybeLocal<Module> CompileSpecifierAsModuleResolveCallback( } TEST(ModuleEvaluation) { - Isolate* isolate = CcTest::isolate(); - HandleScope scope(isolate); - LocalContext env; - v8::TryCatch try_catch(isolate); + bool prev_top_level_await = i::FLAG_harmony_top_level_await; + for (auto top_level_await : {true, false}) { + i::FLAG_harmony_top_level_await = top_level_await; - 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); + Isolate* isolate = CcTest::isolate(); + HandleScope scope(isolate); + LocalContext env; + v8::TryCatch try_catch(isolate); - CHECK(!try_catch.HasCaught()); + 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()); + + MaybeLocal<Value> result = module->Evaluate(env.local()); + CHECK_EQ(Module::kEvaluated, module->GetStatus()); + if (i::FLAG_harmony_top_level_await) { + Local<Promise> promise = Local<Promise>::Cast(result.ToLocalChecked()); + CHECK_EQ(promise->State(), v8::Promise::kFulfilled); + CHECK(promise->Result()->IsUndefined()); + } else { + CHECK(!result.IsEmpty()); + ExpectInt32("Object.expando", 10); + } + CHECK(!try_catch.HasCaught()); + } + i::FLAG_harmony_top_level_await = prev_top_level_await; } -TEST(ModuleEvaluationError) { - Isolate* isolate = CcTest::isolate(); - HandleScope scope(isolate); - LocalContext env; - v8::TryCatch try_catch(isolate); +TEST(ModuleEvaluationError1) { + bool prev_top_level_await = i::FLAG_harmony_top_level_await; + for (auto top_level_await : {true, false}) { + i::FLAG_harmony_top_level_await = top_level_await; - 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()); + Isolate* isolate = CcTest::isolate(); + HandleScope scope(isolate); + LocalContext env; + v8::TryCatch try_catch(isolate); - { - 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); + 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()); + + MaybeLocal<Value> result_1; + { + v8::TryCatch inner_try_catch(isolate); + result_1 = module->Evaluate(env.local()); + CHECK_EQ(Module::kErrored, module->GetStatus()); + Local<Value> exception = module->GetException(); + CHECK(exception->StrictEquals(v8_str("boom"))); + ExpectInt32("Object.x", 1); + CHECK(inner_try_catch.HasCaught()); + CHECK(inner_try_catch.Exception()->StrictEquals(v8_str("boom"))); + } + + MaybeLocal<Value> result_2; + { + v8::TryCatch inner_try_catch(isolate); + result_2 = module->Evaluate(env.local()); + CHECK_EQ(Module::kErrored, module->GetStatus()); + Local<Value> exception = module->GetException(); + CHECK(exception->StrictEquals(v8_str("boom"))); + ExpectInt32("Object.x", 1); + + if (i::FLAG_harmony_top_level_await) { + // With top level await we do not rethrow the exception. + CHECK(!inner_try_catch.HasCaught()); + } else { + CHECK(inner_try_catch.HasCaught()); + CHECK(inner_try_catch.Exception()->StrictEquals(v8_str("boom"))); + } + } + if (i::FLAG_harmony_top_level_await) { + // With top level await, errored evaluation returns a rejected promise + // with the exception. + Local<Promise> promise_1 = + Local<Promise>::Cast(result_1.ToLocalChecked()); + Local<Promise> promise_2 = + Local<Promise>::Cast(result_2.ToLocalChecked()); + CHECK_EQ(promise_1->State(), v8::Promise::kRejected); + CHECK_EQ(promise_2->State(), v8::Promise::kRejected); + CHECK_EQ(promise_1->Result(), module->GetException()); + CHECK_EQ(promise_2->Result(), module->GetException()); + } else { + CHECK(result_1.IsEmpty() && result_2.IsEmpty()); + } + + CHECK(!try_catch.HasCaught()); } + i::FLAG_harmony_top_level_await = prev_top_level_await; +} - { - 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); +static Local<Module> failure_module; +static Local<Module> dependent_module; +MaybeLocal<Module> ResolveCallbackForModuleEvaluationError2( + Local<Context> context, Local<String> specifier, Local<Module> referrer) { + if (specifier->StrictEquals(v8_str("./failure.js"))) { + return failure_module; + } else { + CHECK(specifier->StrictEquals(v8_str("./dependent.js"))); + return dependent_module; } +} - CHECK(!try_catch.HasCaught()); +TEST(ModuleEvaluationError2) { + bool prev_top_level_await = i::FLAG_harmony_top_level_await; + for (auto top_level_await : {true, false}) { + i::FLAG_harmony_top_level_await = top_level_await; + + Isolate* isolate = CcTest::isolate(); + HandleScope scope(isolate); + LocalContext env; + v8::TryCatch try_catch(isolate); + + Local<String> failure_text = v8_str("throw 'boom';"); + ScriptOrigin failure_origin = + ModuleOrigin(v8_str("failure.js"), CcTest::isolate()); + ScriptCompiler::Source failure_source(failure_text, failure_origin); + failure_module = ScriptCompiler::CompileModule(isolate, &failure_source) + .ToLocalChecked(); + CHECK_EQ(Module::kUninstantiated, failure_module->GetStatus()); + CHECK(failure_module + ->InstantiateModule(env.local(), + ResolveCallbackForModuleEvaluationError2) + .FromJust()); + CHECK_EQ(Module::kInstantiated, failure_module->GetStatus()); + + MaybeLocal<Value> result_1; + { + v8::TryCatch inner_try_catch(isolate); + result_1 = failure_module->Evaluate(env.local()); + CHECK_EQ(Module::kErrored, failure_module->GetStatus()); + Local<Value> exception = failure_module->GetException(); + CHECK(exception->StrictEquals(v8_str("boom"))); + CHECK(inner_try_catch.HasCaught()); + CHECK(inner_try_catch.Exception()->StrictEquals(v8_str("boom"))); + } + + Local<String> dependent_text = + v8_str("import './failure.js'; export const c = 123;"); + ScriptOrigin dependent_origin = + ModuleOrigin(v8_str("dependent.js"), CcTest::isolate()); + ScriptCompiler::Source dependent_source(dependent_text, dependent_origin); + dependent_module = ScriptCompiler::CompileModule(isolate, &dependent_source) + .ToLocalChecked(); + CHECK_EQ(Module::kUninstantiated, dependent_module->GetStatus()); + CHECK(dependent_module + ->InstantiateModule(env.local(), + ResolveCallbackForModuleEvaluationError2) + .FromJust()); + CHECK_EQ(Module::kInstantiated, dependent_module->GetStatus()); + + MaybeLocal<Value> result_2; + { + v8::TryCatch inner_try_catch(isolate); + result_2 = dependent_module->Evaluate(env.local()); + CHECK_EQ(Module::kErrored, dependent_module->GetStatus()); + Local<Value> exception = dependent_module->GetException(); + CHECK(exception->StrictEquals(v8_str("boom"))); + CHECK_EQ(exception, failure_module->GetException()); + + if (i::FLAG_harmony_top_level_await) { + // With top level await we do not rethrow the exception. + CHECK(!inner_try_catch.HasCaught()); + } else { + CHECK(inner_try_catch.HasCaught()); + CHECK(inner_try_catch.Exception()->StrictEquals(v8_str("boom"))); + } + } + + if (i::FLAG_harmony_top_level_await) { + // With top level await, errored evaluation returns a rejected promise + // with the exception. + Local<Promise> promise_1 = + Local<Promise>::Cast(result_1.ToLocalChecked()); + Local<Promise> promise_2 = + Local<Promise>::Cast(result_2.ToLocalChecked()); + CHECK_EQ(promise_1->State(), v8::Promise::kRejected); + CHECK_EQ(promise_2->State(), v8::Promise::kRejected); + CHECK_EQ(promise_1->Result(), failure_module->GetException()); + CHECK_EQ(promise_2->Result(), failure_module->GetException()); + } else { + CHECK(result_1.IsEmpty() && result_2.IsEmpty()); + } + + CHECK(!try_catch.HasCaught()); + } + i::FLAG_harmony_top_level_await = prev_top_level_await; } TEST(ModuleEvaluationCompletion1) { + bool prev_top_level_await = i::FLAG_harmony_top_level_await; + for (auto top_level_await : {true, false}) { + i::FLAG_harmony_top_level_await = top_level_await; + + 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()); + + // Evaluate twice. + Local<Value> result_1 = module->Evaluate(env.local()).ToLocalChecked(); + CHECK_EQ(Module::kEvaluated, module->GetStatus()); + Local<Value> result_2 = module->Evaluate(env.local()).ToLocalChecked(); + CHECK_EQ(Module::kEvaluated, module->GetStatus()); + + if (i::FLAG_harmony_top_level_await) { + Local<Promise> promise = Local<Promise>::Cast(result_1); + CHECK_EQ(promise->State(), v8::Promise::kFulfilled); + CHECK(promise->Result()->IsUndefined()); + + // Second evaluation should return the same promise. + Local<Promise> promise_too = Local<Promise>::Cast(result_2); + CHECK_EQ(promise, promise_too); + CHECK_EQ(promise_too->State(), v8::Promise::kFulfilled); + CHECK(promise_too->Result()->IsUndefined()); + } else { + CHECK(result_1->IsUndefined()); + CHECK(result_2->IsUndefined()); + } + } + CHECK(!try_catch.HasCaught()); + } + i::FLAG_harmony_top_level_await = prev_top_level_await; +} + +TEST(ModuleEvaluationCompletion2) { + bool prev_top_level_await = i::FLAG_harmony_top_level_await; + for (auto top_level_await : {true, false}) { + i::FLAG_harmony_top_level_await = top_level_await; + + 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()); + + Local<Value> result_1 = module->Evaluate(env.local()).ToLocalChecked(); + CHECK_EQ(Module::kEvaluated, module->GetStatus()); + + Local<Value> result_2 = module->Evaluate(env.local()).ToLocalChecked(); + CHECK_EQ(Module::kEvaluated, module->GetStatus()); + if (i::FLAG_harmony_top_level_await) { + Local<Promise> promise = Local<Promise>::Cast(result_1); + CHECK_EQ(promise->State(), v8::Promise::kFulfilled); + CHECK(promise->Result()->IsUndefined()); + + // Second Evaluation should return the same promise. + Local<Promise> promise_too = Local<Promise>::Cast(result_2); + CHECK_EQ(promise, promise_too); + CHECK_EQ(promise_too->State(), v8::Promise::kFulfilled); + CHECK(promise_too->Result()->IsUndefined()); + } else { + CHECK(result_1->StrictEquals(v8_str("gaga"))); + CHECK(result_2->IsUndefined()); + } + } + CHECK(!try_catch.HasCaught()); + } + i::FLAG_harmony_top_level_await = prev_top_level_await; +} + +TEST(ModuleNamespace) { + bool prev_top_level_await = i::FLAG_harmony_top_level_await; + for (auto top_level_await : {true, false}) { + i::FLAG_harmony_top_level_await = top_level_await; + + Isolate* isolate = CcTest::isolate(); + HandleScope scope(isolate); + LocalContext env; + v8::TryCatch try_catch(isolate); + + Local<v8::Object> ReferenceError = + CompileRun("ReferenceError")->ToObject(env.local()).ToLocalChecked(); + + Local<String> source_text = v8_str( + "import {a, b} from 'export var a = 1; export let b = 2';" + "export function geta() {return a};" + "export function getb() {return b};" + "export let radio = 3;" + "export var gaga = 4;"); + 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()); + Local<Value> ns = module->GetModuleNamespace(); + CHECK_EQ(Module::kInstantiated, module->GetStatus()); + Local<v8::Object> nsobj = ns->ToObject(env.local()).ToLocalChecked(); + CHECK_EQ(nsobj->CreationContext(), env.local()); + + // a, b + CHECK(nsobj->Get(env.local(), v8_str("a")).ToLocalChecked()->IsUndefined()); + CHECK(nsobj->Get(env.local(), v8_str("b")).ToLocalChecked()->IsUndefined()); + + // geta + { + auto geta = nsobj->Get(env.local(), v8_str("geta")).ToLocalChecked(); + auto a = geta.As<v8::Function>() + ->Call(env.local(), geta, 0, nullptr) + .ToLocalChecked(); + CHECK(a->IsUndefined()); + } + + // getb + { + v8::TryCatch inner_try_catch(isolate); + auto getb = nsobj->Get(env.local(), v8_str("getb")).ToLocalChecked(); + CHECK(getb.As<v8::Function>() + ->Call(env.local(), getb, 0, nullptr) + .IsEmpty()); + CHECK(inner_try_catch.HasCaught()); + CHECK(inner_try_catch.Exception() + ->InstanceOf(env.local(), ReferenceError) + .FromJust()); + } + + // radio + { + v8::TryCatch inner_try_catch(isolate); + // https://bugs.chromium.org/p/v8/issues/detail?id=7235 + // CHECK(nsobj->Get(env.local(), v8_str("radio")).IsEmpty()); + CHECK(nsobj->Get(env.local(), v8_str("radio")) + .ToLocalChecked() + ->IsUndefined()); + CHECK(inner_try_catch.HasCaught()); + CHECK(inner_try_catch.Exception() + ->InstanceOf(env.local(), ReferenceError) + .FromJust()); + } + + // gaga + { + auto gaga = nsobj->Get(env.local(), v8_str("gaga")).ToLocalChecked(); + CHECK(gaga->IsUndefined()); + } + + CHECK(!try_catch.HasCaught()); + CHECK_EQ(Module::kInstantiated, module->GetStatus()); + module->Evaluate(env.local()).ToLocalChecked(); + CHECK_EQ(Module::kEvaluated, module->GetStatus()); + + // geta + { + auto geta = nsobj->Get(env.local(), v8_str("geta")).ToLocalChecked(); + auto a = geta.As<v8::Function>() + ->Call(env.local(), geta, 0, nullptr) + .ToLocalChecked(); + CHECK_EQ(1, a->Int32Value(env.local()).FromJust()); + } + + // getb + { + auto getb = nsobj->Get(env.local(), v8_str("getb")).ToLocalChecked(); + auto b = getb.As<v8::Function>() + ->Call(env.local(), getb, 0, nullptr) + .ToLocalChecked(); + CHECK_EQ(2, b->Int32Value(env.local()).FromJust()); + } + + // radio + { + auto radio = nsobj->Get(env.local(), v8_str("radio")).ToLocalChecked(); + CHECK_EQ(3, radio->Int32Value(env.local()).FromJust()); + } + + // gaga + { + auto gaga = nsobj->Get(env.local(), v8_str("gaga")).ToLocalChecked(); + CHECK_EQ(4, gaga->Int32Value(env.local()).FromJust()); + } + CHECK(!try_catch.HasCaught()); + } + i::FLAG_harmony_top_level_await = prev_top_level_await; +} + +TEST(ModuleEvaluationTopLevelAwait) { + bool previous_top_level_await_flag_value = i::FLAG_harmony_top_level_await; + i::FLAG_harmony_top_level_await = true; 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) {}", + "await 42", + "import 'await 42';", + "import '42'; import 'await 42';", }; for (auto src : sources) { @@ -303,41 +685,30 @@ TEST(ModuleEvaluationCompletion1) { 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()); + Local<Promise> promise = + Local<Promise>::Cast(module->Evaluate(env.local()).ToLocalChecked()); CHECK_EQ(Module::kEvaluated, module->GetStatus()); + CHECK_EQ(promise->State(), v8::Promise::kFulfilled); + CHECK(promise->Result()->IsUndefined()); + CHECK(!try_catch.HasCaught()); } - - CHECK(!try_catch.HasCaught()); + i::FLAG_harmony_top_level_await = previous_top_level_await_flag_value; } -TEST(ModuleEvaluationCompletion2) { +TEST(ModuleEvaluationTopLevelAwaitError) { + bool previous_top_level_await_flag_value = i::FLAG_harmony_top_level_await; + i::FLAG_harmony_top_level_await = true; 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 {})", + "await 42; throw 'boom';", + "import 'await 42; throw \"boom\";';", + "import '42'; import 'await 42; throw \"boom\";';", }; for (auto src : sources) { + v8::TryCatch try_catch(isolate); Local<String> source_text = v8_str(src); ScriptOrigin origin = ModuleOrigin(v8_str("file.js"), CcTest::isolate()); ScriptCompiler::Source source(source_text, origin); @@ -349,126 +720,170 @@ TEST(ModuleEvaluationCompletion2) { 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()); + Local<Promise> promise = + Local<Promise>::Cast(module->Evaluate(env.local()).ToLocalChecked()); + CHECK_EQ(Module::kErrored, module->GetStatus()); + CHECK_EQ(promise->State(), v8::Promise::kRejected); + CHECK(promise->Result()->StrictEquals(v8_str("boom"))); + CHECK(module->GetException()->StrictEquals(v8_str("boom"))); + + // TODO(joshualitt) I am not sure, but this might not be supposed to throw + // because it is async. + CHECK(!try_catch.HasCaught()); } + i::FLAG_harmony_top_level_await = previous_top_level_await_flag_value; +} - CHECK(!try_catch.HasCaught()); +namespace { +struct DynamicImportData { + DynamicImportData(Isolate* isolate_, Local<Promise::Resolver> resolver_, + Local<Context> context_, bool should_resolve_) + : isolate(isolate_), should_resolve(should_resolve_) { + resolver.Reset(isolate, resolver_); + context.Reset(isolate, context_); + } + + Isolate* isolate; + v8::Global<Promise::Resolver> resolver; + v8::Global<Context> context; + bool should_resolve; +}; + +void DoHostImportModuleDynamically(void* import_data) { + std::unique_ptr<DynamicImportData> import_data_( + static_cast<DynamicImportData*>(import_data)); + Isolate* isolate(import_data_->isolate); + HandleScope handle_scope(isolate); + + Local<Promise::Resolver> resolver(import_data_->resolver.Get(isolate)); + Local<Context> realm(import_data_->context.Get(isolate)); + Context::Scope context_scope(realm); + + if (import_data_->should_resolve) { + resolver->Resolve(realm, True(isolate)).ToChecked(); + } else { + resolver->Reject(realm, v8_str("boom")).ToChecked(); + } } -TEST(ModuleNamespace) { +v8::MaybeLocal<v8::Promise> HostImportModuleDynamicallyCallbackResolve( + Local<Context> context, Local<v8::ScriptOrModule> referrer, + Local<String> specifier) { + Isolate* isolate = context->GetIsolate(); + Local<v8::Promise::Resolver> resolver = + v8::Promise::Resolver::New(context).ToLocalChecked(); + + DynamicImportData* data = + new DynamicImportData(isolate, resolver, context, true); + isolate->EnqueueMicrotask(DoHostImportModuleDynamically, data); + return resolver->GetPromise(); +} + +v8::MaybeLocal<v8::Promise> HostImportModuleDynamicallyCallbackReject( + Local<Context> context, Local<v8::ScriptOrModule> referrer, + Local<String> specifier) { + Isolate* isolate = context->GetIsolate(); + Local<v8::Promise::Resolver> resolver = + v8::Promise::Resolver::New(context).ToLocalChecked(); + + DynamicImportData* data = + new DynamicImportData(isolate, resolver, context, false); + isolate->EnqueueMicrotask(DoHostImportModuleDynamically, data); + return resolver->GetPromise(); +} + +} // namespace + +TEST(ModuleEvaluationTopLevelAwaitDynamicImport) { + bool previous_top_level_await_flag_value = i::FLAG_harmony_top_level_await; + bool previous_dynamic_import_flag_value = i::FLAG_harmony_dynamic_import; + i::FLAG_harmony_top_level_await = true; + i::FLAG_harmony_dynamic_import = true; Isolate* isolate = CcTest::isolate(); HandleScope scope(isolate); + isolate->SetMicrotasksPolicy(v8::MicrotasksPolicy::kExplicit); + isolate->SetHostImportModuleDynamicallyCallback( + HostImportModuleDynamicallyCallbackResolve); LocalContext env; v8::TryCatch try_catch(isolate); + const char* sources[] = { + "await import('foo');", + "import 'await import(\"foo\");';", + "import '42'; import 'await import(\"foo\");';", + }; - Local<v8::Object> ReferenceError = - CompileRun("ReferenceError")->ToObject(env.local()).ToLocalChecked(); - - Local<String> source_text = v8_str( - "import {a, b} from 'export var a = 1; export let b = 2';" - "export function geta() {return a};" - "export function getb() {return b};" - "export let radio = 3;" - "export var gaga = 4;"); - 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()); - Local<Value> ns = module->GetModuleNamespace(); - CHECK_EQ(Module::kInstantiated, module->GetStatus()); - Local<v8::Object> nsobj = ns->ToObject(env.local()).ToLocalChecked(); - - // a, b - CHECK(nsobj->Get(env.local(), v8_str("a")).ToLocalChecked()->IsUndefined()); - CHECK(nsobj->Get(env.local(), v8_str("b")).ToLocalChecked()->IsUndefined()); - - // geta - { - auto geta = nsobj->Get(env.local(), v8_str("geta")).ToLocalChecked(); - auto a = geta.As<v8::Function>() - ->Call(env.local(), geta, 0, nullptr) - .ToLocalChecked(); - CHECK(a->IsUndefined()); - } - - // getb - { - v8::TryCatch inner_try_catch(isolate); - auto getb = nsobj->Get(env.local(), v8_str("getb")).ToLocalChecked(); - CHECK( - getb.As<v8::Function>()->Call(env.local(), getb, 0, nullptr).IsEmpty()); - CHECK(inner_try_catch.HasCaught()); - CHECK(inner_try_catch.Exception() - ->InstanceOf(env.local(), ReferenceError) + 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()); - // radio - { - v8::TryCatch inner_try_catch(isolate); - // https://bugs.chromium.org/p/v8/issues/detail?id=7235 - // CHECK(nsobj->Get(env.local(), v8_str("radio")).IsEmpty()); - CHECK(nsobj->Get(env.local(), v8_str("radio")) - .ToLocalChecked() - ->IsUndefined()); - CHECK(inner_try_catch.HasCaught()); - CHECK(inner_try_catch.Exception() - ->InstanceOf(env.local(), ReferenceError) - .FromJust()); - } + Local<Promise> promise = + Local<Promise>::Cast(module->Evaluate(env.local()).ToLocalChecked()); + CHECK_EQ(Module::kEvaluated, module->GetStatus()); + CHECK_EQ(promise->State(), v8::Promise::kPending); + CHECK(!try_catch.HasCaught()); - // gaga - { - auto gaga = nsobj->Get(env.local(), v8_str("gaga")).ToLocalChecked(); - CHECK(gaga->IsUndefined()); + isolate->RunMicrotasks(); + CHECK_EQ(promise->State(), v8::Promise::kFulfilled); } + i::FLAG_harmony_top_level_await = previous_top_level_await_flag_value; + i::FLAG_harmony_dynamic_import = previous_dynamic_import_flag_value; +} - CHECK(!try_catch.HasCaught()); - CHECK_EQ(Module::kInstantiated, module->GetStatus()); - module->Evaluate(env.local()).ToLocalChecked(); - CHECK_EQ(Module::kEvaluated, module->GetStatus()); - - // geta - { - auto geta = nsobj->Get(env.local(), v8_str("geta")).ToLocalChecked(); - auto a = geta.As<v8::Function>() - ->Call(env.local(), geta, 0, nullptr) - .ToLocalChecked(); - CHECK_EQ(1, a->Int32Value(env.local()).FromJust()); - } +TEST(ModuleEvaluationTopLevelAwaitDynamicImportError) { + bool previous_top_level_await_flag_value = i::FLAG_harmony_top_level_await; + bool previous_dynamic_import_flag_value = i::FLAG_harmony_dynamic_import; + i::FLAG_harmony_top_level_await = true; + i::FLAG_harmony_dynamic_import = true; + Isolate* isolate = CcTest::isolate(); + HandleScope scope(isolate); + isolate->SetMicrotasksPolicy(v8::MicrotasksPolicy::kExplicit); + isolate->SetHostImportModuleDynamicallyCallback( + HostImportModuleDynamicallyCallbackReject); + LocalContext env; + v8::TryCatch try_catch(isolate); + const char* sources[] = { + "await import('foo');", + "import 'await import(\"foo\");';", + "import '42'; import 'await import(\"foo\");';", + }; - // getb - { - auto getb = nsobj->Get(env.local(), v8_str("getb")).ToLocalChecked(); - auto b = getb.As<v8::Function>() - ->Call(env.local(), getb, 0, nullptr) - .ToLocalChecked(); - CHECK_EQ(2, b->Int32Value(env.local()).FromJust()); - } + 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()); - // radio - { - auto radio = nsobj->Get(env.local(), v8_str("radio")).ToLocalChecked(); - CHECK_EQ(3, radio->Int32Value(env.local()).FromJust()); - } + Local<Promise> promise = + Local<Promise>::Cast(module->Evaluate(env.local()).ToLocalChecked()); + CHECK_EQ(Module::kEvaluated, module->GetStatus()); + CHECK_EQ(promise->State(), v8::Promise::kPending); + CHECK(!try_catch.HasCaught()); - // gaga - { - auto gaga = nsobj->Get(env.local(), v8_str("gaga")).ToLocalChecked(); - CHECK_EQ(4, gaga->Int32Value(env.local()).FromJust()); + isolate->RunMicrotasks(); + CHECK_EQ(Module::kErrored, module->GetStatus()); + CHECK_EQ(promise->State(), v8::Promise::kRejected); + CHECK(promise->Result()->StrictEquals(v8_str("boom"))); + CHECK(module->GetException()->StrictEquals(v8_str("boom"))); + CHECK(!try_catch.HasCaught()); } - - CHECK(!try_catch.HasCaught()); + i::FLAG_harmony_top_level_await = previous_top_level_await_flag_value; + i::FLAG_harmony_dynamic_import = previous_dynamic_import_flag_value; } + } // anonymous namespace |