Commit 59c9e6ff authored by Georg Neis's avatar Georg Neis Committed by Commit Bot

[modules] Fix bug in Module::Instantiate.

The order in which things were done wasn't quite correct and lead
to wrong behaviour for certain circular module graphs.

BUG=v8:1569,chromium:694566

Change-Id: I291186e261268c853a30ad891ff362904e0b28ef
Reviewed-on: https://chromium-review.googlesource.com/447399Reviewed-by: 's avatarAdam Klein <adamk@chromium.org>
Commit-Queue: Georg Neis <neis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#43497}
parent a813525a
...@@ -1939,6 +1939,7 @@ Handle<Module> Factory::NewModule(Handle<SharedFunctionInfo> code) { ...@@ -1939,6 +1939,7 @@ Handle<Module> Factory::NewModule(Handle<SharedFunctionInfo> code) {
module->set_hash(isolate()->GenerateIdentityHash(Smi::kMaxValue)); module->set_hash(isolate()->GenerateIdentityHash(Smi::kMaxValue));
module->set_module_namespace(isolate()->heap()->undefined_value()); module->set_module_namespace(isolate()->heap()->undefined_value());
module->set_requested_modules(*requested_modules); module->set_requested_modules(*requested_modules);
module->set_status(Module::kUnprepared);
DCHECK(!module->instantiated()); DCHECK(!module->instantiated());
DCHECK(!module->evaluated()); DCHECK(!module->evaluated());
return module; return module;
......
...@@ -1100,6 +1100,7 @@ void Module::ModuleVerify() { ...@@ -1100,6 +1100,7 @@ void Module::ModuleVerify() {
VerifyPointer(module_namespace()); VerifyPointer(module_namespace());
VerifyPointer(requested_modules()); VerifyPointer(requested_modules());
VerifySmiField(kHashOffset); VerifySmiField(kHashOffset);
VerifySmiField(kStatusOffset);
CHECK((!instantiated() && code()->IsSharedFunctionInfo()) || CHECK((!instantiated() && code()->IsSharedFunctionInfo()) ||
(instantiated() && !evaluated() && code()->IsJSFunction()) || (instantiated() && !evaluated() && code()->IsJSFunction()) ||
...@@ -1108,12 +1109,17 @@ void Module::ModuleVerify() { ...@@ -1108,12 +1109,17 @@ void Module::ModuleVerify() {
CHECK(module_namespace()->IsUndefined(GetIsolate()) || CHECK(module_namespace()->IsUndefined(GetIsolate()) ||
module_namespace()->IsJSModuleNamespace()); module_namespace()->IsJSModuleNamespace());
if (module_namespace()->IsJSModuleNamespace()) { if (module_namespace()->IsJSModuleNamespace()) {
CHECK(instantiated());
CHECK_EQ(JSModuleNamespace::cast(module_namespace())->module(), this); CHECK_EQ(JSModuleNamespace::cast(module_namespace())->module(), this);
} }
CHECK_EQ(requested_modules()->length(), info()->module_requests()->length()); CHECK_EQ(requested_modules()->length(), info()->module_requests()->length());
CHECK_NE(hash(), 0); CHECK_NE(hash(), 0);
CHECK_LE(kUnprepared, status());
CHECK_LE(status(), kPrepared);
CHECK_IMPLIES(instantiated(), status() == kPrepared);
} }
void PrototypeInfo::PrototypeInfoVerify() { void PrototypeInfo::PrototypeInfoVerify() {
......
...@@ -5620,6 +5620,7 @@ ACCESSORS(Module, regular_exports, FixedArray, kRegularExportsOffset) ...@@ -5620,6 +5620,7 @@ ACCESSORS(Module, regular_exports, FixedArray, kRegularExportsOffset)
ACCESSORS(Module, regular_imports, FixedArray, kRegularImportsOffset) ACCESSORS(Module, regular_imports, FixedArray, kRegularImportsOffset)
ACCESSORS(Module, module_namespace, HeapObject, kModuleNamespaceOffset) ACCESSORS(Module, module_namespace, HeapObject, kModuleNamespaceOffset)
ACCESSORS(Module, requested_modules, FixedArray, kRequestedModulesOffset) ACCESSORS(Module, requested_modules, FixedArray, kRequestedModulesOffset)
SMI_ACCESSORS(Module, status, kStatusOffset)
SMI_ACCESSORS(Module, hash, kHashOffset) SMI_ACCESSORS(Module, hash, kHashOffset)
bool Module::evaluated() const { return code()->IsModuleInfo(); } bool Module::evaluated() const { return code()->IsModuleInfo(); }
......
...@@ -19737,6 +19737,7 @@ MaybeHandle<Cell> Module::ResolveExport(Handle<Module> module, ...@@ -19737,6 +19737,7 @@ MaybeHandle<Cell> Module::ResolveExport(Handle<Module> module,
Handle<String> name, Handle<String> name,
MessageLocation loc, bool must_resolve, MessageLocation loc, bool must_resolve,
Module::ResolveSet* resolve_set) { Module::ResolveSet* resolve_set) {
DCHECK_EQ(module->status(), kPrepared);
Isolate* isolate = module->GetIsolate(); Isolate* isolate = module->GetIsolate();
Handle<Object> object(module->exports()->Lookup(name), isolate); Handle<Object> object(module->exports()->Lookup(name), isolate);
if (object->IsCell()) { if (object->IsCell()) {
...@@ -19858,20 +19859,45 @@ MaybeHandle<Cell> Module::ResolveExportUsingStarExports( ...@@ -19858,20 +19859,45 @@ MaybeHandle<Cell> Module::ResolveExportUsingStarExports(
bool Module::Instantiate(Handle<Module> module, v8::Local<v8::Context> context, bool Module::Instantiate(Handle<Module> module, v8::Local<v8::Context> context,
v8::Module::ResolveCallback callback) { v8::Module::ResolveCallback callback) {
if (module->instantiated()) return true; return PrepareInstantiate(module, context, callback) &&
FinishInstantiate(module, context);
}
bool Module::PrepareInstantiate(Handle<Module> module,
v8::Local<v8::Context> context,
v8::Module::ResolveCallback callback) {
if (module->status() == kPrepared) return true;
// Obtain requested modules.
Isolate* isolate = module->GetIsolate(); Isolate* isolate = module->GetIsolate();
Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(module->code()), Handle<ModuleInfo> module_info(module->info(), isolate);
isolate); Handle<FixedArray> module_requests(module_info->module_requests(), isolate);
Handle<JSFunction> function = Handle<FixedArray> requested_modules(module->requested_modules(), isolate);
isolate->factory()->NewFunctionFromSharedFunctionInfo( for (int i = 0, length = module_requests->length(); i < length; ++i) {
shared, Handle<String> specifier(String::cast(module_requests->get(i)), isolate);
handle(Utils::OpenHandle(*context)->native_context(), isolate)); v8::Local<v8::Module> api_requested_module;
module->set_code(*function); // TODO(adamk): Revisit these failure cases once d8 knows how to
DCHECK(module->instantiated()); // persist a module_map across multiple top-level module loads, as
// the current module is left in a "half-instantiated" state.
if (!callback(context, v8::Utils::ToLocal(specifier),
v8::Utils::ToLocal(module))
.ToLocal(&api_requested_module)) {
// TODO(adamk): Give this a better error message. But this is a
// misuse of the API anyway.
isolate->ThrowIllegalOperation();
return false;
}
Handle<Module> requested_module = Utils::OpenHandle(*api_requested_module);
requested_modules->set(i, *requested_module);
}
Handle<ModuleInfo> module_info(shared->scope_info()->ModuleDescriptorInfo(), // Recurse.
module->set_status(kPrepared);
for (int i = 0, length = requested_modules->length(); i < length; ++i) {
Handle<Module> requested_module(Module::cast(requested_modules->get(i)),
isolate); isolate);
if (!PrepareInstantiate(requested_module, context, callback)) return false;
}
// Set up local exports. // Set up local exports.
// TODO(neis): Create regular_exports array here instead of in factory method? // TODO(neis): Create regular_exports array here instead of in factory method?
...@@ -19896,31 +19922,41 @@ bool Module::Instantiate(Handle<Module> module, v8::Local<v8::Context> context, ...@@ -19896,31 +19922,41 @@ bool Module::Instantiate(Handle<Module> module, v8::Local<v8::Context> context,
CreateIndirectExport(module, Handle<String>::cast(export_name), entry); CreateIndirectExport(module, Handle<String>::cast(export_name), entry);
} }
Handle<FixedArray> module_requests(module_info->module_requests(), isolate); DCHECK_EQ(module->status(), kPrepared);
for (int i = 0, length = module_requests->length(); i < length; ++i) { DCHECK(!module->instantiated());
Handle<String> specifier(String::cast(module_requests->get(i)), isolate); return true;
v8::Local<v8::Module> api_requested_module; }
// TODO(adamk): Revisit these failure cases once d8 knows how to
// persist a module_map across multiple top-level module loads, as bool Module::FinishInstantiate(Handle<Module> module,
// the current module is left in a "half-instantiated" state. v8::Local<v8::Context> context) {
if (!callback(context, v8::Utils::ToLocal(specifier), DCHECK_EQ(module->status(), kPrepared);
v8::Utils::ToLocal(module)) if (module->instantiated()) return true;
.ToLocal(&api_requested_module)) {
// TODO(adamk): Give this a better error message. But this is a // Instantiate SharedFunctionInfo and mark module as instantiated for
// misuse of the API anyway. // the recursion.
isolate->ThrowIllegalOperation(); Isolate* isolate = module->GetIsolate();
return false; Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(module->code()),
} isolate);
Handle<Module> requested_module = Utils::OpenHandle(*api_requested_module); Handle<JSFunction> function =
module->requested_modules()->set(i, *requested_module); isolate->factory()->NewFunctionFromSharedFunctionInfo(
if (!Instantiate(requested_module, context, callback)) { shared,
return false; handle(Utils::OpenHandle(*context)->native_context(), isolate));
} module->set_code(*function);
DCHECK(module->instantiated());
// Recurse.
Handle<FixedArray> requested_modules(module->requested_modules(), isolate);
for (int i = 0, length = requested_modules->length(); i < length; ++i) {
Handle<Module> requested_module(Module::cast(requested_modules->get(i)),
isolate);
if (!FinishInstantiate(requested_module, context)) return false;
} }
Zone zone(isolate->allocator(), ZONE_NAME); Zone zone(isolate->allocator(), ZONE_NAME);
// Resolve imports. // Resolve imports.
Handle<ModuleInfo> module_info(shared->scope_info()->ModuleDescriptorInfo(),
isolate);
Handle<FixedArray> regular_imports(module_info->regular_imports(), isolate); Handle<FixedArray> regular_imports(module_info->regular_imports(), isolate);
for (int i = 0, n = regular_imports->length(); i < n; ++i) { for (int i = 0, n = regular_imports->length(); i < n; ++i) {
Handle<ModuleInfoEntry> entry( Handle<ModuleInfoEntry> entry(
...@@ -19941,6 +19977,7 @@ bool Module::Instantiate(Handle<Module> module, v8::Local<v8::Context> context, ...@@ -19941,6 +19977,7 @@ bool Module::Instantiate(Handle<Module> module, v8::Local<v8::Context> context,
} }
// Resolve indirect exports. // Resolve indirect exports.
Handle<FixedArray> special_exports(module_info->special_exports(), isolate);
for (int i = 0, n = special_exports->length(); i < n; ++i) { for (int i = 0, n = special_exports->length(); i < n; ++i) {
Handle<ModuleInfoEntry> entry( Handle<ModuleInfoEntry> entry(
ModuleInfoEntry::cast(special_exports->get(i)), isolate); ModuleInfoEntry::cast(special_exports->get(i)), isolate);
......
...@@ -7782,6 +7782,10 @@ class Module : public Struct { ...@@ -7782,6 +7782,10 @@ class Module : public Struct {
// Hash for this object (a random non-zero Smi). // Hash for this object (a random non-zero Smi).
DECL_INT_ACCESSORS(hash) DECL_INT_ACCESSORS(hash)
// Internal instantiation status.
DECL_INT_ACCESSORS(status)
enum InstantiationStatus { kUnprepared, kPrepared };
// The namespace object (or undefined). // The namespace object (or undefined).
DECL_ACCESSORS(module_namespace, HeapObject) DECL_ACCESSORS(module_namespace, HeapObject)
...@@ -7795,7 +7799,6 @@ class Module : public Struct { ...@@ -7795,7 +7799,6 @@ class Module : public Struct {
inline bool instantiated() const; inline bool instantiated() const;
inline bool evaluated() const; inline bool evaluated() const;
inline void set_evaluated();
// Implementation of spec operation ModuleDeclarationInstantiation. // Implementation of spec operation ModuleDeclarationInstantiation.
// Returns false if an exception occurred during instantiation, true // Returns false if an exception occurred during instantiation, true
...@@ -7824,7 +7827,8 @@ class Module : public Struct { ...@@ -7824,7 +7827,8 @@ class Module : public Struct {
static const int kModuleNamespaceOffset = kHashOffset + kPointerSize; static const int kModuleNamespaceOffset = kHashOffset + kPointerSize;
static const int kRequestedModulesOffset = static const int kRequestedModulesOffset =
kModuleNamespaceOffset + kPointerSize; kModuleNamespaceOffset + kPointerSize;
static const int kSize = kRequestedModulesOffset + kPointerSize; static const int kStatusOffset = kRequestedModulesOffset + kPointerSize;
static const int kSize = kStatusOffset + kPointerSize;
private: private:
static void CreateExport(Handle<Module> module, int cell_index, static void CreateExport(Handle<Module> module, int cell_index,
...@@ -7857,6 +7861,14 @@ class Module : public Struct { ...@@ -7857,6 +7861,14 @@ class Module : public Struct {
Handle<Module> module, Handle<String> name, MessageLocation loc, Handle<Module> module, Handle<String> name, MessageLocation loc,
bool must_resolve, ResolveSet* resolve_set); bool must_resolve, ResolveSet* resolve_set);
inline void set_evaluated();
static MUST_USE_RESULT bool PrepareInstantiate(
Handle<Module> module, v8::Local<v8::Context> context,
v8::Module::ResolveCallback callback);
static MUST_USE_RESULT bool FinishInstantiate(Handle<Module> module,
v8::Local<v8::Context> context);
DISALLOW_IMPLICIT_CONSTRUCTORS(Module); DISALLOW_IMPLICIT_CONSTRUCTORS(Module);
}; };
......
// Copyright 2017 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.
//
// MODULE
import "modules-skip-cycle5.js";
export {foo} from "modules-cycle5.js";
*%(basename)s:8: SyntaxError: Detected cycle while resolving name 'foo'
export {foo} from "modules-cycle5.js";
^^^
SyntaxError: Detected cycle while resolving name 'foo'
// Copyright 2017 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.
//
// MODULE
import "modules-skip-cycle6.js";
export * from "modules-cycle6.js";
*modules-skip-cycle6.js:5: SyntaxError: The requested module does not provide an export named 'foo'
export {foo} from "modules-cycle6.js";
^^^
SyntaxError: The requested module does not provide an export named 'foo'
// Copyright 2017 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.
export {foo} from "modules-cycle5.js";
// Copyright 2017 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.
export {foo} from "modules-cycle6.js";
// Copyright 2017 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.
//
// MODULE
import "modules-skip-cycle.js";
export {a as foo} from "modules-skip-1.js"
// Copyright 2017 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.
export {foo} from "modules-cycle.js";
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment