// Copyright 2012 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.
|
|
#include "src/ast/modules.h"
|
#include "src/ast/ast-value-factory.h"
|
#include "src/ast/scopes.h"
|
#include "src/objects-inl.h"
|
#include "src/objects/module-inl.h"
|
#include "src/pending-compilation-error-handler.h"
|
|
namespace v8 {
|
namespace internal {
|
|
bool ModuleDescriptor::AstRawStringComparer::operator()(
|
const AstRawString* lhs, const AstRawString* rhs) const {
|
// Fast path for equal pointers: a pointer is not strictly less than itself.
|
if (lhs == rhs) return false;
|
|
// Order by contents (ordering by hash is unstable across runs).
|
if (lhs->is_one_byte() != rhs->is_one_byte()) {
|
return lhs->is_one_byte();
|
}
|
if (lhs->byte_length() != rhs->byte_length()) {
|
return lhs->byte_length() < rhs->byte_length();
|
}
|
return memcmp(lhs->raw_data(), rhs->raw_data(), lhs->byte_length()) < 0;
|
}
|
|
void ModuleDescriptor::AddImport(const AstRawString* import_name,
|
const AstRawString* local_name,
|
const AstRawString* module_request,
|
const Scanner::Location loc,
|
const Scanner::Location specifier_loc,
|
Zone* zone) {
|
Entry* entry = new (zone) Entry(loc);
|
entry->local_name = local_name;
|
entry->import_name = import_name;
|
entry->module_request = AddModuleRequest(module_request, specifier_loc);
|
AddRegularImport(entry);
|
}
|
|
void ModuleDescriptor::AddStarImport(const AstRawString* local_name,
|
const AstRawString* module_request,
|
const Scanner::Location loc,
|
const Scanner::Location specifier_loc,
|
Zone* zone) {
|
Entry* entry = new (zone) Entry(loc);
|
entry->local_name = local_name;
|
entry->module_request = AddModuleRequest(module_request, specifier_loc);
|
AddNamespaceImport(entry, zone);
|
}
|
|
void ModuleDescriptor::AddEmptyImport(const AstRawString* module_request,
|
const Scanner::Location specifier_loc) {
|
AddModuleRequest(module_request, specifier_loc);
|
}
|
|
|
void ModuleDescriptor::AddExport(
|
const AstRawString* local_name, const AstRawString* export_name,
|
Scanner::Location loc, Zone* zone) {
|
Entry* entry = new (zone) Entry(loc);
|
entry->export_name = export_name;
|
entry->local_name = local_name;
|
AddRegularExport(entry);
|
}
|
|
void ModuleDescriptor::AddExport(const AstRawString* import_name,
|
const AstRawString* export_name,
|
const AstRawString* module_request,
|
const Scanner::Location loc,
|
const Scanner::Location specifier_loc,
|
Zone* zone) {
|
DCHECK_NOT_NULL(import_name);
|
DCHECK_NOT_NULL(export_name);
|
Entry* entry = new (zone) Entry(loc);
|
entry->export_name = export_name;
|
entry->import_name = import_name;
|
entry->module_request = AddModuleRequest(module_request, specifier_loc);
|
AddSpecialExport(entry, zone);
|
}
|
|
void ModuleDescriptor::AddStarExport(const AstRawString* module_request,
|
const Scanner::Location loc,
|
const Scanner::Location specifier_loc,
|
Zone* zone) {
|
Entry* entry = new (zone) Entry(loc);
|
entry->module_request = AddModuleRequest(module_request, specifier_loc);
|
AddSpecialExport(entry, zone);
|
}
|
|
namespace {
|
|
Handle<Object> ToStringOrUndefined(Isolate* isolate, const AstRawString* s) {
|
return (s == nullptr)
|
? Handle<Object>::cast(isolate->factory()->undefined_value())
|
: Handle<Object>::cast(s->string());
|
}
|
|
const AstRawString* FromStringOrUndefined(Isolate* isolate,
|
AstValueFactory* avfactory,
|
Handle<Object> object) {
|
if (object->IsUndefined(isolate)) return nullptr;
|
return avfactory->GetString(Handle<String>::cast(object));
|
}
|
|
} // namespace
|
|
Handle<ModuleInfoEntry> ModuleDescriptor::Entry::Serialize(
|
Isolate* isolate) const {
|
CHECK(Smi::IsValid(module_request)); // TODO(neis): Check earlier?
|
return ModuleInfoEntry::New(
|
isolate, ToStringOrUndefined(isolate, export_name),
|
ToStringOrUndefined(isolate, local_name),
|
ToStringOrUndefined(isolate, import_name), module_request, cell_index,
|
location.beg_pos, location.end_pos);
|
}
|
|
ModuleDescriptor::Entry* ModuleDescriptor::Entry::Deserialize(
|
Isolate* isolate, AstValueFactory* avfactory,
|
Handle<ModuleInfoEntry> entry) {
|
Entry* result = new (avfactory->zone()) Entry(Scanner::Location::invalid());
|
result->export_name = FromStringOrUndefined(
|
isolate, avfactory, handle(entry->export_name(), isolate));
|
result->local_name = FromStringOrUndefined(
|
isolate, avfactory, handle(entry->local_name(), isolate));
|
result->import_name = FromStringOrUndefined(
|
isolate, avfactory, handle(entry->import_name(), isolate));
|
result->module_request = entry->module_request();
|
result->cell_index = entry->cell_index();
|
return result;
|
}
|
|
Handle<FixedArray> ModuleDescriptor::SerializeRegularExports(Isolate* isolate,
|
Zone* zone) const {
|
// We serialize regular exports in a way that lets us later iterate over their
|
// local names and for each local name immediately access all its export
|
// names. (Regular exports have neither import name nor module request.)
|
|
ZoneVector<Handle<Object>> data(
|
ModuleInfo::kRegularExportLength * regular_exports_.size(), zone);
|
int index = 0;
|
|
for (auto it = regular_exports_.begin(); it != regular_exports_.end();) {
|
// Find out how many export names this local name has.
|
auto next = it;
|
int count = 0;
|
do {
|
DCHECK_EQ(it->second->local_name, next->second->local_name);
|
DCHECK_EQ(it->second->cell_index, next->second->cell_index);
|
++next;
|
++count;
|
} while (next != regular_exports_.end() && next->first == it->first);
|
|
Handle<FixedArray> export_names = isolate->factory()->NewFixedArray(count);
|
data[index + ModuleInfo::kRegularExportLocalNameOffset] =
|
it->second->local_name->string();
|
data[index + ModuleInfo::kRegularExportCellIndexOffset] =
|
handle(Smi::FromInt(it->second->cell_index), isolate);
|
data[index + ModuleInfo::kRegularExportExportNamesOffset] = export_names;
|
index += ModuleInfo::kRegularExportLength;
|
|
// Collect the export names.
|
int i = 0;
|
for (; it != next; ++it) {
|
export_names->set(i++, *it->second->export_name->string());
|
}
|
DCHECK_EQ(i, count);
|
|
// Continue with the next distinct key.
|
DCHECK(it == next);
|
}
|
DCHECK_LE(index, static_cast<int>(data.size()));
|
data.resize(index);
|
|
// We cannot create the FixedArray earlier because we only now know the
|
// precise size.
|
Handle<FixedArray> result = isolate->factory()->NewFixedArray(index);
|
for (int i = 0; i < index; ++i) {
|
result->set(i, *data[i]);
|
}
|
return result;
|
}
|
|
void ModuleDescriptor::DeserializeRegularExports(
|
Isolate* isolate, AstValueFactory* avfactory,
|
Handle<ModuleInfo> module_info) {
|
for (int i = 0, count = module_info->RegularExportCount(); i < count; ++i) {
|
Handle<String> local_name(module_info->RegularExportLocalName(i), isolate);
|
int cell_index = module_info->RegularExportCellIndex(i);
|
Handle<FixedArray> export_names(module_info->RegularExportExportNames(i),
|
isolate);
|
|
for (int j = 0, length = export_names->length(); j < length; ++j) {
|
Handle<String> export_name(String::cast(export_names->get(j)), isolate);
|
|
Entry* entry =
|
new (avfactory->zone()) Entry(Scanner::Location::invalid());
|
entry->local_name = avfactory->GetString(local_name);
|
entry->export_name = avfactory->GetString(export_name);
|
entry->cell_index = cell_index;
|
|
AddRegularExport(entry);
|
}
|
}
|
}
|
|
void ModuleDescriptor::MakeIndirectExportsExplicit(Zone* zone) {
|
for (auto it = regular_exports_.begin(); it != regular_exports_.end();) {
|
Entry* entry = it->second;
|
DCHECK_NOT_NULL(entry->local_name);
|
auto import = regular_imports_.find(entry->local_name);
|
if (import != regular_imports_.end()) {
|
// Found an indirect export. Patch export entry and move it from regular
|
// to special.
|
DCHECK_NULL(entry->import_name);
|
DCHECK_LT(entry->module_request, 0);
|
DCHECK_NOT_NULL(import->second->import_name);
|
DCHECK_LE(0, import->second->module_request);
|
DCHECK_LT(import->second->module_request,
|
static_cast<int>(module_requests_.size()));
|
entry->import_name = import->second->import_name;
|
entry->module_request = import->second->module_request;
|
// Hack: When the indirect export cannot be resolved, we want the error
|
// message to point at the import statement, not at the export statement.
|
// Therefore we overwrite [entry]'s location here. Note that Validate()
|
// has already checked for duplicate exports, so it's guaranteed that we
|
// won't need to report any error pointing at the (now lost) export
|
// location.
|
entry->location = import->second->location;
|
entry->local_name = nullptr;
|
AddSpecialExport(entry, zone);
|
it = regular_exports_.erase(it);
|
} else {
|
it++;
|
}
|
}
|
}
|
|
ModuleDescriptor::CellIndexKind ModuleDescriptor::GetCellIndexKind(
|
int cell_index) {
|
if (cell_index > 0) return kExport;
|
if (cell_index < 0) return kImport;
|
return kInvalid;
|
}
|
|
void ModuleDescriptor::AssignCellIndices() {
|
int export_index = 1;
|
for (auto it = regular_exports_.begin(); it != regular_exports_.end();) {
|
auto current_key = it->first;
|
// This local name may be exported under multiple export names. Assign the
|
// same index to each such entry.
|
do {
|
Entry* entry = it->second;
|
DCHECK_NOT_NULL(entry->local_name);
|
DCHECK_NULL(entry->import_name);
|
DCHECK_LT(entry->module_request, 0);
|
DCHECK_EQ(entry->cell_index, 0);
|
entry->cell_index = export_index;
|
it++;
|
} while (it != regular_exports_.end() && it->first == current_key);
|
export_index++;
|
}
|
|
int import_index = -1;
|
for (const auto& elem : regular_imports_) {
|
Entry* entry = elem.second;
|
DCHECK_NOT_NULL(entry->local_name);
|
DCHECK_NOT_NULL(entry->import_name);
|
DCHECK_LE(0, entry->module_request);
|
DCHECK_EQ(entry->cell_index, 0);
|
entry->cell_index = import_index;
|
import_index--;
|
}
|
}
|
|
namespace {
|
|
const ModuleDescriptor::Entry* BetterDuplicate(
|
const ModuleDescriptor::Entry* candidate,
|
ZoneMap<const AstRawString*, const ModuleDescriptor::Entry*>& export_names,
|
const ModuleDescriptor::Entry* current_duplicate) {
|
DCHECK_NOT_NULL(candidate->export_name);
|
DCHECK(candidate->location.IsValid());
|
auto insert_result =
|
export_names.insert(std::make_pair(candidate->export_name, candidate));
|
if (insert_result.second) return current_duplicate;
|
if (current_duplicate == nullptr) {
|
current_duplicate = insert_result.first->second;
|
}
|
return (candidate->location.beg_pos > current_duplicate->location.beg_pos)
|
? candidate
|
: current_duplicate;
|
}
|
|
} // namespace
|
|
const ModuleDescriptor::Entry* ModuleDescriptor::FindDuplicateExport(
|
Zone* zone) const {
|
const ModuleDescriptor::Entry* duplicate = nullptr;
|
ZoneMap<const AstRawString*, const ModuleDescriptor::Entry*> export_names(
|
zone);
|
for (const auto& elem : regular_exports_) {
|
duplicate = BetterDuplicate(elem.second, export_names, duplicate);
|
}
|
for (auto entry : special_exports_) {
|
if (entry->export_name == nullptr) continue; // Star export.
|
duplicate = BetterDuplicate(entry, export_names, duplicate);
|
}
|
return duplicate;
|
}
|
|
bool ModuleDescriptor::Validate(ModuleScope* module_scope,
|
PendingCompilationErrorHandler* error_handler,
|
Zone* zone) {
|
DCHECK_EQ(this, module_scope->module());
|
DCHECK_NOT_NULL(error_handler);
|
|
// Report error iff there are duplicate exports.
|
{
|
const Entry* entry = FindDuplicateExport(zone);
|
if (entry != nullptr) {
|
error_handler->ReportMessageAt(
|
entry->location.beg_pos, entry->location.end_pos,
|
MessageTemplate::kDuplicateExport, entry->export_name);
|
return false;
|
}
|
}
|
|
// Report error iff there are exports of non-existent local names.
|
for (const auto& elem : regular_exports_) {
|
const Entry* entry = elem.second;
|
DCHECK_NOT_NULL(entry->local_name);
|
if (module_scope->LookupLocal(entry->local_name) == nullptr) {
|
error_handler->ReportMessageAt(
|
entry->location.beg_pos, entry->location.end_pos,
|
MessageTemplate::kModuleExportUndefined, entry->local_name);
|
return false;
|
}
|
}
|
|
MakeIndirectExportsExplicit(zone);
|
AssignCellIndices();
|
return true;
|
}
|
|
} // namespace internal
|
} // namespace v8
|