blob: 3c9a5080adfb3b82c98c95770cf56f5416d4bb76 [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"
#include "src/heap/local-factory-inl.h"
#include "src/objects/module-inl.h"
#include "src/objects/objects-inl.h"
#include "src/parsing/pending-compilation-error-handler.h"
namespace v8 {
namespace internal {
bool SourceTextModuleDescriptor::AstRawStringComparer::operator()(
const AstRawString* lhs, const AstRawString* rhs) const {
return ThreeWayCompare(lhs, rhs) < 0;
}
int SourceTextModuleDescriptor::AstRawStringComparer::ThreeWayCompare(
const AstRawString* lhs, const AstRawString* rhs) {
// 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() ? -1 : 1;
}
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());
}
bool SourceTextModuleDescriptor::ModuleRequestComparer::operator()(
const AstModuleRequest* lhs, const AstModuleRequest* rhs) const {
if (int specifier_comparison = AstRawStringComparer::ThreeWayCompare(
lhs->specifier(), rhs->specifier()))
return specifier_comparison < 0;
if (lhs->import_assertions()->size() != rhs->import_assertions()->size())
return (lhs->import_assertions()->size() <
rhs->import_assertions()->size());
auto lhsIt = lhs->import_assertions()->cbegin();
auto rhsIt = rhs->import_assertions()->cbegin();
for (; lhsIt != lhs->import_assertions()->cend(); ++lhsIt, ++rhsIt) {
if (int assertion_key_comparison =
AstRawStringComparer::ThreeWayCompare(lhsIt->first, rhsIt->first))
return assertion_key_comparison < 0;
if (int assertion_value_comparison = AstRawStringComparer::ThreeWayCompare(
lhsIt->second.first, rhsIt->second.first))
return assertion_value_comparison < 0;
}
return false;
}
void SourceTextModuleDescriptor::AddImport(
const AstRawString* import_name, const AstRawString* local_name,
const AstRawString* module_request,
const ImportAssertions* import_assertions, const Scanner::Location loc,
const Scanner::Location specifier_loc, Zone* zone) {
Entry* entry = zone->New<Entry>(loc);
entry->local_name = local_name;
entry->import_name = import_name;
entry->module_request =
AddModuleRequest(module_request, import_assertions, specifier_loc, zone);
AddRegularImport(entry);
}
void SourceTextModuleDescriptor::AddStarImport(
const AstRawString* local_name, const AstRawString* module_request,
const ImportAssertions* import_assertions, const Scanner::Location loc,
const Scanner::Location specifier_loc, Zone* zone) {
Entry* entry = zone->New<Entry>(loc);
entry->local_name = local_name;
entry->module_request =
AddModuleRequest(module_request, import_assertions, specifier_loc, zone);
AddNamespaceImport(entry, zone);
}
void SourceTextModuleDescriptor::AddEmptyImport(
const AstRawString* module_request,
const ImportAssertions* import_assertions,
const Scanner::Location specifier_loc, Zone* zone) {
AddModuleRequest(module_request, import_assertions, specifier_loc, zone);
}
void SourceTextModuleDescriptor::AddExport(const AstRawString* local_name,
const AstRawString* export_name,
Scanner::Location loc, Zone* zone) {
Entry* entry = zone->New<Entry>(loc);
entry->export_name = export_name;
entry->local_name = local_name;
AddRegularExport(entry);
}
void SourceTextModuleDescriptor::AddExport(
const AstRawString* import_name, const AstRawString* export_name,
const AstRawString* module_request,
const ImportAssertions* import_assertions, const Scanner::Location loc,
const Scanner::Location specifier_loc, Zone* zone) {
DCHECK_NOT_NULL(import_name);
DCHECK_NOT_NULL(export_name);
Entry* entry = zone->New<Entry>(loc);
entry->export_name = export_name;
entry->import_name = import_name;
entry->module_request =
AddModuleRequest(module_request, import_assertions, specifier_loc, zone);
AddSpecialExport(entry, zone);
}
void SourceTextModuleDescriptor::AddStarExport(
const AstRawString* module_request,
const ImportAssertions* import_assertions, const Scanner::Location loc,
const Scanner::Location specifier_loc, Zone* zone) {
Entry* entry = zone->New<Entry>(loc);
entry->module_request =
AddModuleRequest(module_request, import_assertions, specifier_loc, zone);
AddSpecialExport(entry, zone);
}
namespace {
template <typename LocalIsolate>
Handle<PrimitiveHeapObject> ToStringOrUndefined(LocalIsolate* isolate,
const AstRawString* s) {
if (s == nullptr) return isolate->factory()->undefined_value();
return s->string();
}
} // namespace
template <typename LocalIsolate>
Handle<ModuleRequest> SourceTextModuleDescriptor::AstModuleRequest::Serialize(
LocalIsolate* isolate) const {
// The import assertions will be stored in this array in the form:
// [key1, value1, location1, key2, value2, location2, ...]
Handle<FixedArray> import_assertions_array =
isolate->factory()->NewFixedArray(
static_cast<int>(import_assertions()->size() * 3));
int i = 0;
for (auto iter = import_assertions()->cbegin();
iter != import_assertions()->cend(); ++iter, i += 3) {
import_assertions_array->set(i, *iter->first->string());
import_assertions_array->set(i + 1, *iter->second.first->string());
import_assertions_array->set(i + 2,
Smi::FromInt(iter->second.second.beg_pos));
}
return v8::internal::ModuleRequest::New(isolate, specifier()->string(),
import_assertions_array);
}
template Handle<ModuleRequest>
SourceTextModuleDescriptor::AstModuleRequest::Serialize(Isolate* isolate) const;
template Handle<ModuleRequest>
SourceTextModuleDescriptor::AstModuleRequest::Serialize(
LocalIsolate* isolate) const;
template <typename LocalIsolate>
Handle<SourceTextModuleInfoEntry> SourceTextModuleDescriptor::Entry::Serialize(
LocalIsolate* isolate) const {
CHECK(Smi::IsValid(module_request)); // TODO(neis): Check earlier?
return SourceTextModuleInfoEntry::New(
isolate, ToStringOrUndefined(isolate, export_name),
ToStringOrUndefined(isolate, local_name),
ToStringOrUndefined(isolate, import_name), module_request, cell_index,
location.beg_pos, location.end_pos);
}
template Handle<SourceTextModuleInfoEntry>
SourceTextModuleDescriptor::Entry::Serialize(Isolate* isolate) const;
template Handle<SourceTextModuleInfoEntry>
SourceTextModuleDescriptor::Entry::Serialize(LocalIsolate* isolate) const;
template <typename LocalIsolate>
Handle<FixedArray> SourceTextModuleDescriptor::SerializeRegularExports(
LocalIsolate* 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(
SourceTextModuleInfo::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 + SourceTextModuleInfo::kRegularExportLocalNameOffset] =
it->second->local_name->string();
data[index + SourceTextModuleInfo::kRegularExportCellIndexOffset] =
handle(Smi::FromInt(it->second->cell_index), isolate);
data[index + SourceTextModuleInfo::kRegularExportExportNamesOffset] =
export_names;
index += SourceTextModuleInfo::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;
}
template Handle<FixedArray> SourceTextModuleDescriptor::SerializeRegularExports(
Isolate* isolate, Zone* zone) const;
template Handle<FixedArray> SourceTextModuleDescriptor::SerializeRegularExports(
LocalIsolate* isolate, Zone* zone) const;
void SourceTextModuleDescriptor::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++;
}
}
}
SourceTextModuleDescriptor::CellIndexKind
SourceTextModuleDescriptor::GetCellIndexKind(int cell_index) {
if (cell_index > 0) return kExport;
if (cell_index < 0) return kImport;
return kInvalid;
}
void SourceTextModuleDescriptor::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 SourceTextModuleDescriptor::Entry* BetterDuplicate(
const SourceTextModuleDescriptor::Entry* candidate,
ZoneMap<const AstRawString*, const SourceTextModuleDescriptor::Entry*>&
export_names,
const SourceTextModuleDescriptor::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 SourceTextModuleDescriptor::Entry*
SourceTextModuleDescriptor::FindDuplicateExport(Zone* zone) const {
const SourceTextModuleDescriptor::Entry* duplicate = nullptr;
ZoneMap<const AstRawString*, const SourceTextModuleDescriptor::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 SourceTextModuleDescriptor::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