| // 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 |