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) {
module->set_hash(isolate()->GenerateIdentityHash(Smi::kMaxValue));
module->set_module_namespace(isolate()->heap()->undefined_value());
module->set_requested_modules(*requested_modules);
module->set_status(Module::kUnprepared);
DCHECK(!module->instantiated());
DCHECK(!module->evaluated());
return module;
......
......@@ -1100,6 +1100,7 @@ void Module::ModuleVerify() {
VerifyPointer(module_namespace());
VerifyPointer(requested_modules());
VerifySmiField(kHashOffset);
VerifySmiField(kStatusOffset);
CHECK((!instantiated() && code()->IsSharedFunctionInfo()) ||
(instantiated() && !evaluated() && code()->IsJSFunction()) ||
......@@ -1108,12 +1109,17 @@ void Module::ModuleVerify() {
CHECK(module_namespace()->IsUndefined(GetIsolate()) ||
module_namespace()->IsJSModuleNamespace());
if (module_namespace()->IsJSModuleNamespace()) {
CHECK(instantiated());
CHECK_EQ(JSModuleNamespace::cast(module_namespace())->module(), this);
}
CHECK_EQ(requested_modules()->length(), info()->module_requests()->length());
CHECK_NE(hash(), 0);
CHECK_LE(kUnprepared, status());
CHECK_LE(status(), kPrepared);
CHECK_IMPLIES(instantiated(), status() == kPrepared);
}
void PrototypeInfo::PrototypeInfoVerify() {
......
......@@ -5620,6 +5620,7 @@ ACCESSORS(Module, regular_exports, FixedArray, kRegularExportsOffset)
ACCESSORS(Module, regular_imports, FixedArray, kRegularImportsOffset)
ACCESSORS(Module, module_namespace, HeapObject, kModuleNamespaceOffset)
ACCESSORS(Module, requested_modules, FixedArray, kRequestedModulesOffset)
SMI_ACCESSORS(Module, status, kStatusOffset)
SMI_ACCESSORS(Module, hash, kHashOffset)
bool Module::evaluated() const { return code()->IsModuleInfo(); }
......
......@@ -19737,6 +19737,7 @@ MaybeHandle<Cell> Module::ResolveExport(Handle<Module> module,
Handle<String> name,
MessageLocation loc, bool must_resolve,
Module::ResolveSet* resolve_set) {
DCHECK_EQ(module->status(), kPrepared);
Isolate* isolate = module->GetIsolate();
Handle<Object> object(module->exports()->Lookup(name), isolate);
if (object->IsCell()) {
......@@ -19858,20 +19859,45 @@ MaybeHandle<Cell> Module::ResolveExportUsingStarExports(
bool Module::Instantiate(Handle<Module> module, v8::Local<v8::Context> context,
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();
Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(module->code()),
isolate);
Handle<JSFunction> function =
isolate->factory()->NewFunctionFromSharedFunctionInfo(
shared,
handle(Utils::OpenHandle(*context)->native_context(), isolate));
module->set_code(*function);
DCHECK(module->instantiated());
Handle<ModuleInfo> module_info(module->info(), isolate);
Handle<FixedArray> module_requests(module_info->module_requests(), isolate);
Handle<FixedArray> requested_modules(module->requested_modules(), isolate);
for (int i = 0, length = module_requests->length(); i < length; ++i) {
Handle<String> specifier(String::cast(module_requests->get(i)), isolate);
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
// 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(),
isolate);
// 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);
if (!PrepareInstantiate(requested_module, context, callback)) return false;
}
// Set up local exports.
// 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,
CreateIndirectExport(module, Handle<String>::cast(export_name), entry);
}
Handle<FixedArray> module_requests(module_info->module_requests(), isolate);
for (int i = 0, length = module_requests->length(); i < length; ++i) {
Handle<String> specifier(String::cast(module_requests->get(i)), isolate);
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
// 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);
module->requested_modules()->set(i, *requested_module);
if (!Instantiate(requested_module, context, callback)) {
return false;
}
DCHECK_EQ(module->status(), kPrepared);
DCHECK(!module->instantiated());
return true;
}
bool Module::FinishInstantiate(Handle<Module> module,
v8::Local<v8::Context> context) {
DCHECK_EQ(module->status(), kPrepared);
if (module->instantiated()) return true;
// Instantiate SharedFunctionInfo and mark module as instantiated for
// the recursion.
Isolate* isolate = module->GetIsolate();
Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(module->code()),
isolate);
Handle<JSFunction> function =
isolate->factory()->NewFunctionFromSharedFunctionInfo(
shared,
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);
// Resolve imports.
Handle<ModuleInfo> module_info(shared->scope_info()->ModuleDescriptorInfo(),
isolate);
Handle<FixedArray> regular_imports(module_info->regular_imports(), isolate);
for (int i = 0, n = regular_imports->length(); i < n; ++i) {
Handle<ModuleInfoEntry> entry(
......@@ -19941,6 +19977,7 @@ bool Module::Instantiate(Handle<Module> module, v8::Local<v8::Context> context,
}
// Resolve indirect exports.
Handle<FixedArray> special_exports(module_info->special_exports(), isolate);
for (int i = 0, n = special_exports->length(); i < n; ++i) {
Handle<ModuleInfoEntry> entry(
ModuleInfoEntry::cast(special_exports->get(i)), isolate);
......
......@@ -7782,6 +7782,10 @@ class Module : public Struct {
// Hash for this object (a random non-zero Smi).
DECL_INT_ACCESSORS(hash)
// Internal instantiation status.
DECL_INT_ACCESSORS(status)
enum InstantiationStatus { kUnprepared, kPrepared };
// The namespace object (or undefined).
DECL_ACCESSORS(module_namespace, HeapObject)
......@@ -7795,7 +7799,6 @@ class Module : public Struct {
inline bool instantiated() const;
inline bool evaluated() const;
inline void set_evaluated();
// Implementation of spec operation ModuleDeclarationInstantiation.
// Returns false if an exception occurred during instantiation, true
......@@ -7824,7 +7827,8 @@ class Module : public Struct {
static const int kModuleNamespaceOffset = kHashOffset + kPointerSize;
static const int kRequestedModulesOffset =
kModuleNamespaceOffset + kPointerSize;
static const int kSize = kRequestedModulesOffset + kPointerSize;
static const int kStatusOffset = kRequestedModulesOffset + kPointerSize;
static const int kSize = kStatusOffset + kPointerSize;
private:
static void CreateExport(Handle<Module> module, int cell_index,
......@@ -7857,6 +7861,14 @@ class Module : public Struct {
Handle<Module> module, Handle<String> name, MessageLocation loc,
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);
};
......
// 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