[api,modules] Allow GetModuleNamespace on unevaluated modules.

Bug: v8:7217
Cq-Include-Trybots: master.tryserver.chromium.linux:linux_chromium_rel_ng
Change-Id: I97b067254355eb91e12b92eba92631cbc3ce8000
Reviewed-on: https://chromium-review.googlesource.com/839280
Commit-Queue: Georg Neis <neis@chromium.org>
Reviewed-by: Adam Klein <adamk@chromium.org>
Cr-Commit-Position: refs/heads/master@{#50395}
diff --git a/src/api.cc b/src/api.cc
index 218e0e1..60793df 100644
--- a/src/api.cc
+++ b/src/api.cc
@@ -2262,9 +2262,8 @@
 
 Local<Value> Module::GetModuleNamespace() {
   Utils::ApiCheck(
-      GetStatus() == kEvaluated, "v8::Module::GetModuleNamespace",
-      "v8::Module::GetModuleNamespace can only be used on a module with "
-      "status kEvaluated");
+      GetStatus() >= kInstantiated, "v8::Module::GetModuleNamespace",
+      "v8::Module::GetModuleNamespace must be used on an instantiated module");
   i::Handle<i::Module> self = Utils::OpenHandle(this);
   i::Handle<i::JSModuleNamespace> module_namespace =
       i::Module::GetModuleNamespace(self);
diff --git a/test/cctest/test-modules.cc b/test/cctest/test-modules.cc
index e6473b2..2523b83 100644
--- a/test/cctest/test-modules.cc
+++ b/test/cctest/test-modules.cc
@@ -360,4 +360,115 @@
   CHECK(!try_catch.HasCaught());
 }
 
+TEST(ModuleNamespace) {
+  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();
+
+  // 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());
+}
 }  // anonymous namespace