blob: 6a0f26a89580752df51f2d4aaefe2fea8cda40a1 [file] [log] [blame]
// 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"
namespace v8 {
namespace internal {
void ModuleDescriptor::AddImport(
const AstRawString* import_name, const AstRawString* local_name,
const AstRawString* module_request, Scanner::Location 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);
AddRegularImport(entry);
}
void ModuleDescriptor::AddStarImport(
const AstRawString* local_name, const AstRawString* module_request,
Scanner::Location loc, Zone* zone) {
DCHECK_NOT_NULL(local_name);
Entry* entry = new (zone) Entry(loc);
entry->local_name = local_name;
entry->module_request = AddModuleRequest(module_request);
AddSpecialImport(entry, zone);
}
void ModuleDescriptor::AddEmptyImport(
const AstRawString* module_request, Scanner::Location loc, Zone* zone) {
Entry* entry = new (zone) Entry(loc);
entry->module_request = AddModuleRequest(module_request);
AddSpecialImport(entry, zone);
}
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, Scanner::Location 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);
AddSpecialExport(entry, zone);
}
void ModuleDescriptor::AddStarExport(
const AstRawString* module_request, Scanner::Location loc, Zone* zone) {
Entry* entry = new (zone) Entry(loc);
entry->module_request = AddModuleRequest(module_request);
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),
Handle<Object>(Smi::FromInt(module_request), isolate));
}
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 = Smi::cast(entry->module_request())->value();
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(zone);
data.reserve(2 * regular_exports_.size());
for (auto it = regular_exports_.begin(); it != regular_exports_.end();) {
// Find out how many export names this local name has.
auto next = it;
int size = 0;
do {
++next;
++size;
} while (next != regular_exports_.end() && next->first == it->first);
Handle<FixedArray> export_names = isolate->factory()->NewFixedArray(size);
data.push_back(it->second->local_name->string());
data.push_back(export_names);
// Collect the export names.
int i = 0;
for (; it != next; ++it) {
export_names->set(i++, *it->second->export_name->string());
}
DCHECK_EQ(i, size);
// Continue with the next distinct key.
DCHECK(it == next);
}
// We cannot create the FixedArray earlier because we only now know the
// precise size (the number of unique keys in regular_exports).
int size = static_cast<int>(data.size());
Handle<FixedArray> result = isolate->factory()->NewFixedArray(size);
for (int i = 0; i < size; ++i) {
result->set(i, *data[i]);
}
return result;
}
void ModuleDescriptor::DeserializeRegularExports(Isolate* isolate,
AstValueFactory* avfactory,
Handle<FixedArray> data) {
for (int i = 0, length_i = data->length(); i < length_i;) {
Handle<String> local_name(String::cast(data->get(i++)), isolate);
Handle<FixedArray> export_names(FixedArray::cast(data->get(i++)), isolate);
for (int j = 0, length_j = export_names->length(); j < length_j; ++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);
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;
entry->local_name = nullptr;
AddSpecialExport(entry, zone);
it = regular_exports_.erase(it);
} else {
it++;
}
}
}
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);
return true;
}
} // namespace internal
} // namespace v8