Commit d7858e35 authored by neis's avatar neis Committed by Commit bot

[modules] Do basic linking.

Resolve imports and indirect exports at instantiation time.

With this CL we have some basic functionality for modules working.  Not yet
supported: star exports, namespace imports, cycle detection, proper variable
initialisation in mutually recursive modules.

BUG=v8:1569

Review-Url: https://codereview.chromium.org/2362083002
Cr-Commit-Position: refs/heads/master@{#39689}
parent c6363348
......@@ -1892,31 +1892,15 @@ Local<UnboundScript> Script::GetUnboundScript() {
int Module::GetModuleRequestsLength() const {
i::Handle<i::Module> self = Utils::OpenHandle(this);
i::Isolate* isolate = self->GetIsolate();
i::Handle<i::SharedFunctionInfo> shared;
if (self->code()->IsSharedFunctionInfo()) {
shared = i::handle(i::SharedFunctionInfo::cast(self->code()), isolate);
} else {
shared = i::handle(i::JSFunction::cast(self->code())->shared(), isolate);
}
return shared->scope_info()
->ModuleDescriptorInfo()
->module_requests()
->length();
return self->info()->module_requests()->length();
}
Local<String> Module::GetModuleRequest(int i) const {
CHECK_GE(i, 0);
i::Handle<i::Module> self = Utils::OpenHandle(this);
i::Isolate* isolate = self->GetIsolate();
i::Handle<i::SharedFunctionInfo> shared;
if (self->code()->IsSharedFunctionInfo()) {
shared = i::handle(i::SharedFunctionInfo::cast(self->code()), isolate);
} else {
shared = i::handle(i::JSFunction::cast(self->code())->shared(), isolate);
}
i::Handle<i::FixedArray> module_requests(
shared->scope_info()->ModuleDescriptorInfo()->module_requests(), isolate);
i::Handle<i::FixedArray> module_requests(self->info()->module_requests(),
isolate);
CHECK_LT(i, module_requests->length());
return ToApiHandle<String>(i::handle(module_requests->get(i), isolate));
}
......@@ -1950,33 +1934,73 @@ static bool InstantiateModule(Local<Module> v8_module,
shared, handle(context->native_context(), isolate));
module->set_code(*function);
i::Handle<i::FixedArray> regular_exports = i::handle(
shared->scope_info()->ModuleDescriptorInfo()->regular_exports(), isolate);
i::Handle<i::FixedArray> regular_imports = i::handle(
shared->scope_info()->ModuleDescriptorInfo()->regular_imports(), isolate);
i::Handle<i::FixedArray> special_exports = i::handle(
shared->scope_info()->ModuleDescriptorInfo()->special_exports(), isolate);
// Set up local exports.
for (int i = 0, n = regular_exports->length(); i < n; i += 2) {
i::Handle<i::FixedArray> export_names(
i::FixedArray::cast(regular_exports->get(i + 1)), isolate);
i::Module::CreateExport(module, export_names);
}
// Partially set up indirect exports.
// For each indirect export, we create the appropriate slot in the export
// table and store its ModuleInfoEntry there. When we later find the correct
// Cell in the module that actually provides the value, we replace the
// ModuleInfoEntry by that Cell (see ResolveExport).
for (int i = 0, n = special_exports->length(); i < n; ++i) {
i::Handle<i::ModuleInfoEntry> entry(
i::ModuleInfoEntry::cast(special_exports->get(i)), isolate);
i::Handle<i::Object> export_name(entry->export_name(), isolate);
if (export_name->IsUndefined(isolate)) continue; // Star export.
i::Module::CreateIndirectExport(
module, i::Handle<i::String>::cast(export_name), entry);
}
for (int i = 0, length = v8_module->GetModuleRequestsLength(); i < length;
++i) {
Local<Module> import;
Local<Module> 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(v8_context, v8_module->GetModuleRequest(i), v8_module,
callback_data)
.ToLocal(&import)) {
.ToLocal(&requested_module)) {
// TODO(adamk): Give this a better error message. But this is a
// misuse of the API anyway.
isolate->ThrowIllegalOperation();
return false;
}
if (!InstantiateModule(import, v8_context, callback, callback_data)) {
if (!requested_module->Instantiate(v8_context, callback, callback_data)) {
return false;
}
module->requested_modules()->set(i, *Utils::OpenHandle(*import));
module->requested_modules()->set(i, *Utils::OpenHandle(*requested_module));
}
// Set up local exports.
i::Handle<i::FixedArray> regular_exports = i::handle(
shared->scope_info()->ModuleDescriptorInfo()->regular_exports(), isolate);
for (int i = 0, n = regular_exports->length(); i < n; i += 2) {
i::Handle<i::FixedArray> export_names(
i::FixedArray::cast(regular_exports->get(i + 1)), isolate);
i::Module::CreateExport(module, export_names);
// Resolve imports.
for (int i = 0, n = regular_imports->length(); i < n; ++i) {
i::Handle<i::ModuleInfoEntry> entry(
i::ModuleInfoEntry::cast(regular_imports->get(i)), isolate);
i::Handle<i::String> name(i::String::cast(entry->import_name()), isolate);
int module_request = i::Smi::cast(entry->module_request())->value();
if (i::Module::ResolveImport(module, name, module_request).is_null()) {
return false;
}
}
// Resolve indirect exports.
for (int i = 0, n = special_exports->length(); i < n; ++i) {
i::Handle<i::ModuleInfoEntry> entry(
i::ModuleInfoEntry::cast(special_exports->get(i)), isolate);
i::Handle<i::String> name(i::String::cast(entry->export_name()), isolate);
if (i::Module::ResolveExport(module, name).is_null()) {
return false;
}
}
return true;
......
......@@ -190,7 +190,7 @@ void ModuleDescriptor::MakeIndirectExportsExplicit(Zone* zone) {
entry->import_name = import->second->import_name;
entry->module_request = import->second->module_request;
entry->local_name = nullptr;
special_exports_.Add(entry, zone);
AddSpecialExport(entry, zone);
it = regular_exports_.erase(it);
} else {
it++;
......
......@@ -136,6 +136,7 @@ class ModuleDescriptor : public ZoneObject {
}
void AddSpecialExport(const Entry* entry, Zone* zone) {
DCHECK_NULL(entry->local_name);
DCHECK_LE(0, entry->module_request);
special_exports_.Add(entry, zone);
}
......
......@@ -611,6 +611,7 @@ class ErrorUtils : public AllStatic {
T(UnexpectedTokenString, "Unexpected string") \
T(UnexpectedTokenRegExp, "Unexpected regular expression") \
T(UnknownLabel, "Undefined label '%'") \
T(UnresolvableExport, "Module does not provide an export named '%'") \
T(UnterminatedArgList, "missing ) after argument list") \
T(UnterminatedRegExp, "Invalid regular expression: missing /") \
T(UnterminatedTemplate, "Unterminated template literal") \
......
......@@ -7961,6 +7961,14 @@ Object* ModuleInfoEntry::module_request() const {
return get(kModuleRequestIndex);
}
ModuleInfo* Module::info() const {
DisallowHeapAllocation no_gc;
SharedFunctionInfo* shared = code()->IsSharedFunctionInfo()
? SharedFunctionInfo::cast(code())
: JSFunction::cast(code())->shared();
return shared->scope_info()->ModuleDescriptorInfo();
}
FixedArray* ModuleInfo::module_requests() const {
return FixedArray::cast(get(kModuleRequestsIndex));
}
......
......@@ -19580,6 +19580,15 @@ bool JSReceiver::HasProxyInPrototype(Isolate* isolate) {
return false;
}
void Module::CreateIndirectExport(Handle<Module> module, Handle<String> name,
Handle<ModuleInfoEntry> entry) {
Isolate* isolate = module->GetIsolate();
Handle<ObjectHashTable> exports(module->exports(), isolate);
DCHECK(exports->Lookup(name)->IsTheHole(isolate));
exports = ObjectHashTable::Put(exports, name, entry);
module->set_exports(*exports);
}
void Module::CreateExport(Handle<Module> module, Handle<FixedArray> names) {
DCHECK_LT(0, names->length());
Isolate* isolate = module->GetIsolate();
......@@ -19596,16 +19605,19 @@ void Module::CreateExport(Handle<Module> module, Handle<FixedArray> names) {
void Module::StoreExport(Handle<Module> module, Handle<String> name,
Handle<Object> value) {
Handle<ObjectHashTable> exports(module->exports());
Handle<Cell> cell(Cell::cast(exports->Lookup(name)));
Handle<Cell> cell(Cell::cast(module->exports()->Lookup(name)));
cell->set_value(*value);
}
Handle<Object> Module::LoadExport(Handle<Module> module, Handle<String> name) {
Isolate* isolate = module->GetIsolate();
Handle<ObjectHashTable> exports(module->exports(), isolate);
Handle<Cell> cell(Cell::cast(exports->Lookup(name)));
return handle(cell->value(), isolate);
Handle<Object> object(module->exports()->Lookup(name), isolate);
// TODO(neis): Star exports and namespace imports are not yet implemented.
// Trying to use these features may crash here.
if (!object->IsCell()) UNIMPLEMENTED();
return handle(Handle<Cell>::cast(object)->value(), isolate);
}
Handle<Object> Module::LoadImport(Handle<Module> module, Handle<String> name,
......@@ -19613,10 +19625,58 @@ Handle<Object> Module::LoadImport(Handle<Module> module, Handle<String> name,
Isolate* isolate = module->GetIsolate();
Handle<Module> requested_module(
Module::cast(module->requested_modules()->get(module_request)), isolate);
Handle<ObjectHashTable> exports(requested_module->exports(), isolate);
Object* object = exports->Lookup(name);
if (!object->IsCell()) UNIMPLEMENTED();
return handle(Cell::cast(object)->value(), isolate);
return Module::LoadExport(requested_module, name);
}
MaybeHandle<Cell> Module::ResolveImport(Handle<Module> module,
Handle<String> name,
int module_request) {
Isolate* isolate = module->GetIsolate();
Handle<Module> requested_module(
Module::cast(module->requested_modules()->get(module_request)), isolate);
return Module::ResolveExport(requested_module, name);
}
MaybeHandle<Cell> Module::ResolveExport(Handle<Module> module,
Handle<String> name) {
// TODO(neis): Detect cycles.
Isolate* isolate = module->GetIsolate();
Handle<Object> object(module->exports()->Lookup(name), isolate);
if (object->IsCell()) {
// Already resolved (e.g. because it's a local export).
return Handle<Cell>::cast(object);
}
if (object->IsModuleInfoEntry()) {
// Not yet resolved indirect export.
Handle<ModuleInfoEntry> entry = Handle<ModuleInfoEntry>::cast(object);
int module_request = Smi::cast(entry->module_request())->value();
Handle<String> import_name(String::cast(entry->import_name()), isolate);
Handle<Cell> cell;
if (!ResolveImport(module, import_name, module_request).ToHandle(&cell)) {
return MaybeHandle<Cell>();
}
// The export table may have changed but the entry in question should be
// unchanged.
Handle<ObjectHashTable> exports(module->exports(), isolate);
DCHECK(exports->Lookup(name)->IsModuleInfoEntry());
exports = ObjectHashTable::Put(exports, name, cell);
module->set_exports(*exports);
return cell;
}
DCHECK(object->IsTheHole(isolate));
// TODO(neis): Implement star exports.
// Unresolvable.
THROW_NEW_ERROR(isolate,
NewSyntaxError(MessageTemplate::kUnresolvableExport, name),
Cell);
}
} // namespace internal
......
......@@ -7948,14 +7948,26 @@ class Module : public Struct {
// Embedder-specified data
DECL_ACCESSORS(embedder_data, Object)
// Get the ModuleInfo associated with the code.
inline ModuleInfo* info() const;
static void CreateExport(Handle<Module> module, Handle<FixedArray> names);
static Handle<Object> LoadExport(Handle<Module> module, Handle<String> name);
static void StoreExport(Handle<Module> module, Handle<String> name,
Handle<Object> value);
static void CreateIndirectExport(Handle<Module> module, Handle<String> name,
Handle<ModuleInfoEntry> entry);
static Handle<Object> LoadImport(Handle<Module> module, Handle<String> name,
int module_request);
static MUST_USE_RESULT MaybeHandle<Cell> ResolveImport(Handle<Module> module,
Handle<String> name,
int module_request);
static MUST_USE_RESULT MaybeHandle<Cell> ResolveExport(Handle<Module> module,
Handle<String> name);
static const int kCodeOffset = HeapObject::kHeaderSize;
static const int kExportsOffset = kCodeOffset + kPointerSize;
static const int kRequestedModulesOffset = kExportsOffset + kPointerSize;
......
......@@ -688,6 +688,15 @@
'smi-mul-const': [FAIL],
'smi-mul': [FAIL],
'unary-minus-deopt': [FAIL],
# Modules for which execution must fail (e.g. because of unresolved imports).
# Eventually we should test for the precise error message, but for now we only
# ensure that there is an error.
'modules-fail*': [FAIL],
# Modules which are only meant to be imported from by other tests, not to be
# tested standalone.
'modules-skip*': [SKIP],
}], # variant == ignition
['variant == ignition and arch == arm64', {
......
// 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.
//
// MODULE
import foo from "modules-skip-1.js";
assertEquals(42, foo);
import {default as gaga} from "modules-skip-1.js";
assertEquals(42, gaga);
// 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.
//
// MODULE
import {a} from "modules-fail-1.js";
// 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.
//
// MODULE
import {a as b} from "modules-fail-2.js";
// 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.
//
// MODULE
import foo from "modules-fail-3.js";
// 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.
//
// MODULE
import {a as b} from "modules-fail-4.js";
export {c as a} from "modules-fail-4.js";
// 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.
//
// MODULE
import {a as b} from "modules-fail-5.js";
export {c as a} from "modules-fail-5.js";
import {c} from "modules-fail-5.js";
// 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.
//
// MODULE
import {a as x, set_a as set_x} from "modules-skip-1.js"
let get_x = () => x;
assertEquals(1, x);
assertEquals(1, (() => x)());
assertEquals(1, eval('x'));
assertEquals(1, get_x());
assertThrows(() => x = 666, TypeError);
assertEquals(1, x);
assertEquals(1, (() => x)());
assertEquals(1, eval('x'));
assertEquals(1, get_x());
set_x("foo");
assertEquals("foo", x);
assertEquals("foo", (() => x)());
assertEquals("foo", eval('x'));
assertEquals("foo", get_x());
// 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.
//
// MODULE
let get_x = () => x;
assertEquals(1, x);
assertEquals(1, (() => x)());
assertEquals(1, eval('x'));
assertEquals(1, get_x());
assertThrows(() => x = 666, TypeError);
assertEquals(1, x);
assertEquals(1, (() => x)());
assertEquals(1, eval('x'));
assertEquals(1, get_x());
set_x("foo");
assertEquals("foo", x);
assertEquals("foo", (() => x)());
assertEquals("foo", eval('x'));
assertEquals("foo", get_x());
import {a as x, set_a as set_x} from "modules-skip-1.js"
// 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.
//
// MODULE
import {a as x, a as y} from "modules-skip-1.js";
import {b as z, get_a, set_a} from "modules-skip-1.js";
assertEquals(1, get_a());
assertEquals(1, x);
assertEquals(1, y);
assertEquals(1, z);
set_a(2);
assertEquals(2, get_a());
assertEquals(2, x);
assertEquals(2, y);
assertEquals(2, z);
assertThrows(() => x = 3, TypeError);
assertThrows(() => y = 3, TypeError);
assertThrows(() => z = 3, TypeError);
assertEquals(2, get_a());
assertEquals(2, x);
assertEquals(2, y);
assertEquals(2, z);
assertEquals(2, eval('get_a()'));
assertEquals(2, eval('x'));
assertEquals(2, eval('y'));
assertEquals(2, eval('z'));
assertEquals(2, (() => get_a())());
assertEquals(2, (() => x)());
assertEquals(2, (() => y)());
assertEquals(2, (() => z)());
// 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.
//
// MODULE
import {b, c} from "modules-skip-2.js";
import {a, set_a} from "modules-skip-1.js";
import x from "modules-skip-2.js";
assertEquals(42, x);
assertEquals(1, a);
assertEquals(1, b);
assertEquals(1, c);
set_a(2);
assertEquals(2, a);
assertEquals(2, b);
assertEquals(2, c);
assertThrows(() => a = 3, TypeError);
assertThrows(() => b = 3, TypeError);
assertThrows(() => c = 3, TypeError);
assertEquals(2, a);
assertEquals(2, b);
assertEquals(2, c);
assertThrows(() => x = 43, TypeError);
assertEquals(42, x);
// 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.
//
// MODULE
import {a} from "modules-skip-3.js";
export var b = 20;
assertEquals(42, a+b);
// 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.
export default 42;
export let a = 1;
export {a as b};
export function set_a(x) { a = x };
export function get_a() { return a };
// 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.
export {a as b, default} from "modules-skip-1.js";
import {a as tmp} from "modules-skip-1.js";
export {tmp as c};
// 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.
import {b} from "modules-imports5.js";
export let a = 22;
assertSame(undefined, b);
assertEquals(22, a);
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