| // 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. |
| |
| #ifndef V8_AST_MODULES_H_ |
| #define V8_AST_MODULES_H_ |
| |
| #include "src/parsing/scanner.h" // Only for Scanner::Location. |
| #include "src/zone/zone-containers.h" |
| |
| namespace v8 { |
| namespace internal { |
| |
| |
| class AstRawString; |
| class ModuleRequest; |
| class SourceTextModuleInfo; |
| class SourceTextModuleInfoEntry; |
| class PendingCompilationErrorHandler; |
| |
| class SourceTextModuleDescriptor : public ZoneObject { |
| public: |
| explicit SourceTextModuleDescriptor(Zone* zone) |
| : module_requests_(zone), |
| special_exports_(zone), |
| namespace_imports_(zone), |
| regular_exports_(zone), |
| regular_imports_(zone) {} |
| |
| using ImportAssertions = |
| ZoneMap<const AstRawString*, |
| std::pair<const AstRawString*, Scanner::Location>>; |
| |
| // The following Add* methods are high-level convenience functions for use by |
| // the parser. |
| |
| // import x from "foo.js"; |
| // import {x} from "foo.js"; |
| // import {x as y} from "foo.js"; |
| void 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); |
| |
| // import * as x from "foo.js"; |
| void AddStarImport(const AstRawString* local_name, |
| const AstRawString* module_request, |
| const ImportAssertions* import_assertions, |
| const Scanner::Location loc, |
| const Scanner::Location specifier_loc, Zone* zone); |
| |
| // import "foo.js"; |
| // import {} from "foo.js"; |
| // export {} from "foo.js"; (sic!) |
| void AddEmptyImport(const AstRawString* module_request, |
| const ImportAssertions* import_assertions, |
| const Scanner::Location specifier_loc, Zone* zone); |
| |
| // export {x}; |
| // export {x as y}; |
| // export VariableStatement |
| // export Declaration |
| // export default ... |
| void AddExport( |
| const AstRawString* local_name, const AstRawString* export_name, |
| const Scanner::Location loc, Zone* zone); |
| |
| // export {x} from "foo.js"; |
| // export {x as y} from "foo.js"; |
| void AddExport(const AstRawString* export_name, |
| const AstRawString* import_name, |
| const AstRawString* module_request, |
| const ImportAssertions* import_assertions, |
| const Scanner::Location loc, |
| const Scanner::Location specifier_loc, Zone* zone); |
| |
| // export * from "foo.js"; |
| void AddStarExport(const AstRawString* module_request, |
| const ImportAssertions* import_assertions, |
| const Scanner::Location loc, |
| const Scanner::Location specifier_loc, Zone* zone); |
| |
| // Check if module is well-formed and report error if not. |
| // Also canonicalize indirect exports. |
| bool Validate(ModuleScope* module_scope, |
| PendingCompilationErrorHandler* error_handler, Zone* zone); |
| |
| struct Entry : public ZoneObject { |
| Scanner::Location location; |
| const AstRawString* export_name; |
| const AstRawString* local_name; |
| const AstRawString* import_name; |
| |
| // The module_request value records the order in which modules are |
| // requested. It also functions as an index into the SourceTextModuleInfo's |
| // array of module specifiers and into the Module's array of requested |
| // modules. A negative value means no module request. |
| int module_request; |
| |
| // Import/export entries that are associated with a MODULE-allocated |
| // variable (i.e. regular_imports and regular_exports after Validate) use |
| // the cell_index value to encode the location of their cell. During |
| // variable allocation, this will be be copied into the variable's index |
| // field. |
| // Entries that are not associated with a MODULE-allocated variable have |
| // GetCellIndexKind(cell_index) == kInvalid. |
| int cell_index; |
| |
| // TODO(neis): Remove local_name component? |
| explicit Entry(Scanner::Location loc) |
| : location(loc), |
| export_name(nullptr), |
| local_name(nullptr), |
| import_name(nullptr), |
| module_request(-1), |
| cell_index(0) {} |
| |
| template <typename LocalIsolate> |
| Handle<SourceTextModuleInfoEntry> Serialize(LocalIsolate* isolate) const; |
| }; |
| |
| enum CellIndexKind { kInvalid, kExport, kImport }; |
| static CellIndexKind GetCellIndexKind(int cell_index); |
| |
| class AstModuleRequest : public ZoneObject { |
| public: |
| // TODO(v8:10958): Consider storing module request location here |
| // instead of using separate ModuleRequestLocation struct. |
| AstModuleRequest(const AstRawString* specifier, |
| const ImportAssertions* import_assertions) |
| : specifier_(specifier), import_assertions_(import_assertions) {} |
| |
| template <typename LocalIsolate> |
| Handle<v8::internal::ModuleRequest> Serialize(LocalIsolate* isolate) const; |
| |
| const AstRawString* specifier() const { return specifier_; } |
| const ImportAssertions* import_assertions() const { |
| return import_assertions_; |
| } |
| |
| private: |
| const AstRawString* specifier_; |
| const ImportAssertions* import_assertions_; |
| }; |
| |
| struct ModuleRequestLocation { |
| // The index at which we will place the request in SourceTextModuleInfo's |
| // module_requests FixedArray. |
| int index; |
| |
| // The JS source code position of the request, used for reporting errors. |
| int position; |
| |
| ModuleRequestLocation(int index, int position) |
| : index(index), position(position) {} |
| }; |
| |
| // Custom content-based comparer for the below maps, to keep them stable |
| // across parses. |
| struct V8_EXPORT_PRIVATE AstRawStringComparer { |
| bool operator()(const AstRawString* lhs, const AstRawString* rhs) const; |
| static int ThreeWayCompare(const AstRawString* lhs, |
| const AstRawString* rhs); |
| }; |
| |
| struct V8_EXPORT_PRIVATE ModuleRequestComparer { |
| bool operator()(const AstModuleRequest* lhs, |
| const AstModuleRequest* rhs) const; |
| }; |
| |
| using ModuleRequestMap = |
| ZoneMap<const AstModuleRequest*, ModuleRequestLocation, |
| ModuleRequestComparer>; |
| using RegularExportMap = |
| ZoneMultimap<const AstRawString*, Entry*, AstRawStringComparer>; |
| using RegularImportMap = |
| ZoneMap<const AstRawString*, Entry*, AstRawStringComparer>; |
| |
| // Module requests. |
| const ModuleRequestMap& module_requests() const { return module_requests_; } |
| |
| // Namespace imports. |
| const ZoneVector<const Entry*>& namespace_imports() const { |
| return namespace_imports_; |
| } |
| |
| // All the remaining imports, indexed by local name. |
| const RegularImportMap& regular_imports() const { return regular_imports_; } |
| |
| // Star exports and explicitly indirect exports. |
| const ZoneVector<const Entry*>& special_exports() const { |
| return special_exports_; |
| } |
| |
| // All the remaining exports, indexed by local name. |
| // After canonicalization (see Validate), these are exactly the local exports. |
| const RegularExportMap& regular_exports() const { return regular_exports_; } |
| |
| void AddRegularExport(Entry* entry) { |
| DCHECK_NOT_NULL(entry->export_name); |
| DCHECK_NOT_NULL(entry->local_name); |
| DCHECK_NULL(entry->import_name); |
| DCHECK_LT(entry->module_request, 0); |
| regular_exports_.insert(std::make_pair(entry->local_name, entry)); |
| } |
| |
| void AddSpecialExport(const Entry* entry, Zone* zone) { |
| DCHECK_NULL(entry->local_name); |
| DCHECK_LE(0, entry->module_request); |
| special_exports_.push_back(entry); |
| } |
| |
| void AddRegularImport(Entry* entry) { |
| DCHECK_NOT_NULL(entry->import_name); |
| DCHECK_NOT_NULL(entry->local_name); |
| DCHECK_NULL(entry->export_name); |
| DCHECK_LE(0, entry->module_request); |
| regular_imports_.insert(std::make_pair(entry->local_name, entry)); |
| // We don't care if there's already an entry for this local name, as in that |
| // case we will report an error when declaring the variable. |
| } |
| |
| void AddNamespaceImport(const Entry* entry, Zone* zone) { |
| DCHECK_NULL(entry->import_name); |
| DCHECK_NULL(entry->export_name); |
| DCHECK_NOT_NULL(entry->local_name); |
| DCHECK_LE(0, entry->module_request); |
| namespace_imports_.push_back(entry); |
| } |
| |
| template <typename LocalIsolate> |
| Handle<FixedArray> SerializeRegularExports(LocalIsolate* isolate, |
| Zone* zone) const; |
| |
| private: |
| ModuleRequestMap module_requests_; |
| ZoneVector<const Entry*> special_exports_; |
| ZoneVector<const Entry*> namespace_imports_; |
| RegularExportMap regular_exports_; |
| RegularImportMap regular_imports_; |
| |
| // If there are multiple export entries with the same export name, return the |
| // last of them (in source order). Otherwise return nullptr. |
| const Entry* FindDuplicateExport(Zone* zone) const; |
| |
| // Find any implicitly indirect exports and make them explicit. |
| // |
| // An explicitly indirect export is an export entry arising from an export |
| // statement of the following form: |
| // export {a as c} from "X"; |
| // An implicitly indirect export corresponds to |
| // export {b as c}; |
| // in the presence of an import statement of the form |
| // import {a as b} from "X"; |
| // This function finds such implicitly indirect export entries and rewrites |
| // them by filling in the import name and module request, as well as nulling |
| // out the local name. Effectively, it turns |
| // import {a as b} from "X"; export {b as c}; |
| // into: |
| // import {a as b} from "X"; export {a as c} from "X"; |
| // (The import entry is never deleted.) |
| void MakeIndirectExportsExplicit(Zone* zone); |
| |
| // Assign a cell_index of -1,-2,... to regular imports. |
| // Assign a cell_index of +1,+2,... to regular (local) exports. |
| // Assign a cell_index of 0 to anything else. |
| void AssignCellIndices(); |
| |
| int AddModuleRequest(const AstRawString* specifier, |
| const ImportAssertions* import_assertions, |
| Scanner::Location specifier_loc, Zone* zone) { |
| DCHECK_NOT_NULL(specifier); |
| int module_requests_count = static_cast<int>(module_requests_.size()); |
| auto it = module_requests_ |
| .insert(std::make_pair( |
| zone->New<AstModuleRequest>(specifier, import_assertions), |
| ModuleRequestLocation(module_requests_count, |
| specifier_loc.beg_pos))) |
| .first; |
| return it->second.index; |
| } |
| }; |
| |
| } // namespace internal |
| } // namespace v8 |
| |
| #endif // V8_AST_MODULES_H_ |