1 // Copyright 2012 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef V8_AST_MODULES_H_
6 #define V8_AST_MODULES_H_
7 
8 #include "src/parsing/scanner.h"  // Only for Scanner::Location.
9 #include "src/zone/zone-containers.h"
10 
11 namespace v8 {
12 namespace internal {
13 
14 
15 class AstRawString;
16 class SourceTextModuleInfo;
17 class SourceTextModuleInfoEntry;
18 class PendingCompilationErrorHandler;
19 
20 class SourceTextModuleDescriptor : public ZoneObject {
21  public:
SourceTextModuleDescriptor(Zone * zone)22   explicit SourceTextModuleDescriptor(Zone* zone)
23       : module_requests_(zone),
24         special_exports_(zone),
25         namespace_imports_(zone),
26         regular_exports_(zone),
27         regular_imports_(zone) {}
28 
29   // The following Add* methods are high-level convenience functions for use by
30   // the parser.
31 
32   // import x from "foo.js";
33   // import {x} from "foo.js";
34   // import {x as y} from "foo.js";
35   void AddImport(const AstRawString* import_name,
36                  const AstRawString* local_name,
37                  const AstRawString* module_request,
38                  const Scanner::Location loc,
39                  const Scanner::Location specifier_loc, Zone* zone);
40 
41   // import * as x from "foo.js";
42   void AddStarImport(const AstRawString* local_name,
43                      const AstRawString* module_request,
44                      const Scanner::Location loc,
45                      const Scanner::Location specifier_loc, Zone* zone);
46 
47   // import "foo.js";
48   // import {} from "foo.js";
49   // export {} from "foo.js";  (sic!)
50   void AddEmptyImport(const AstRawString* module_request,
51                       const Scanner::Location specifier_loc);
52 
53   // export {x};
54   // export {x as y};
55   // export VariableStatement
56   // export Declaration
57   // export default ...
58   void AddExport(
59     const AstRawString* local_name, const AstRawString* export_name,
60     const Scanner::Location loc, Zone* zone);
61 
62   // export {x} from "foo.js";
63   // export {x as y} from "foo.js";
64   void AddExport(const AstRawString* export_name,
65                  const AstRawString* import_name,
66                  const AstRawString* module_request,
67                  const Scanner::Location loc,
68                  const Scanner::Location specifier_loc, Zone* zone);
69 
70   // export * from "foo.js";
71   void AddStarExport(const AstRawString* module_request,
72                      const Scanner::Location loc,
73                      const Scanner::Location specifier_loc, Zone* zone);
74 
75   // Check if module is well-formed and report error if not.
76   // Also canonicalize indirect exports.
77   bool Validate(ModuleScope* module_scope,
78                 PendingCompilationErrorHandler* error_handler, Zone* zone);
79 
80   struct Entry : public ZoneObject {
81     Scanner::Location location;
82     const AstRawString* export_name;
83     const AstRawString* local_name;
84     const AstRawString* import_name;
85 
86     // The module_request value records the order in which modules are
87     // requested. It also functions as an index into the SourceTextModuleInfo's
88     // array of module specifiers and into the Module's array of requested
89     // modules.  A negative value means no module request.
90     int module_request;
91 
92     // Import/export entries that are associated with a MODULE-allocated
93     // variable (i.e. regular_imports and regular_exports after Validate) use
94     // the cell_index value to encode the location of their cell.  During
95     // variable allocation, this will be be copied into the variable's index
96     // field.
97     // Entries that are not associated with a MODULE-allocated variable have
98     // GetCellIndexKind(cell_index) == kInvalid.
99     int cell_index;
100 
101     // TODO(neis): Remove local_name component?
EntryEntry102     explicit Entry(Scanner::Location loc)
103         : location(loc),
104           export_name(nullptr),
105           local_name(nullptr),
106           import_name(nullptr),
107           module_request(-1),
108           cell_index(0) {}
109 
110     template <typename LocalIsolate>
111     Handle<SourceTextModuleInfoEntry> Serialize(LocalIsolate* isolate) const;
112   };
113 
114   enum CellIndexKind { kInvalid, kExport, kImport };
115   static CellIndexKind GetCellIndexKind(int cell_index);
116 
117   struct ModuleRequest {
118     int index;
119     int position;
ModuleRequestModuleRequest120     ModuleRequest(int index, int position) : index(index), position(position) {}
121   };
122 
123   // Custom content-based comparer for the below maps, to keep them stable
124   // across parses.
125   struct V8_EXPORT_PRIVATE AstRawStringComparer {
126     bool operator()(const AstRawString* lhs, const AstRawString* rhs) const;
127   };
128 
129   using ModuleRequestMap =
130       ZoneMap<const AstRawString*, ModuleRequest, AstRawStringComparer>;
131   using RegularExportMap =
132       ZoneMultimap<const AstRawString*, Entry*, AstRawStringComparer>;
133   using RegularImportMap =
134       ZoneMap<const AstRawString*, Entry*, AstRawStringComparer>;
135 
136   // Module requests.
module_requests()137   const ModuleRequestMap& module_requests() const { return module_requests_; }
138 
139   // Namespace imports.
namespace_imports()140   const ZoneVector<const Entry*>& namespace_imports() const {
141     return namespace_imports_;
142   }
143 
144   // All the remaining imports, indexed by local name.
regular_imports()145   const RegularImportMap& regular_imports() const { return regular_imports_; }
146 
147   // Star exports and explicitly indirect exports.
special_exports()148   const ZoneVector<const Entry*>& special_exports() const {
149     return special_exports_;
150   }
151 
152   // All the remaining exports, indexed by local name.
153   // After canonicalization (see Validate), these are exactly the local exports.
regular_exports()154   const RegularExportMap& regular_exports() const { return regular_exports_; }
155 
AddRegularExport(Entry * entry)156   void AddRegularExport(Entry* entry) {
157     DCHECK_NOT_NULL(entry->export_name);
158     DCHECK_NOT_NULL(entry->local_name);
159     DCHECK_NULL(entry->import_name);
160     DCHECK_LT(entry->module_request, 0);
161     regular_exports_.insert(std::make_pair(entry->local_name, entry));
162   }
163 
AddSpecialExport(const Entry * entry,Zone * zone)164   void AddSpecialExport(const Entry* entry, Zone* zone) {
165     DCHECK_NULL(entry->local_name);
166     DCHECK_LE(0, entry->module_request);
167     special_exports_.push_back(entry);
168   }
169 
AddRegularImport(Entry * entry)170   void AddRegularImport(Entry* entry) {
171     DCHECK_NOT_NULL(entry->import_name);
172     DCHECK_NOT_NULL(entry->local_name);
173     DCHECK_NULL(entry->export_name);
174     DCHECK_LE(0, entry->module_request);
175     regular_imports_.insert(std::make_pair(entry->local_name, entry));
176     // We don't care if there's already an entry for this local name, as in that
177     // case we will report an error when declaring the variable.
178   }
179 
AddNamespaceImport(const Entry * entry,Zone * zone)180   void AddNamespaceImport(const Entry* entry, Zone* zone) {
181     DCHECK_NULL(entry->import_name);
182     DCHECK_NULL(entry->export_name);
183     DCHECK_NOT_NULL(entry->local_name);
184     DCHECK_LE(0, entry->module_request);
185     namespace_imports_.push_back(entry);
186   }
187 
188   template <typename LocalIsolate>
189   Handle<FixedArray> SerializeRegularExports(LocalIsolate* isolate,
190                                              Zone* zone) const;
191 
192  private:
193   ModuleRequestMap module_requests_;
194   ZoneVector<const Entry*> special_exports_;
195   ZoneVector<const Entry*> namespace_imports_;
196   RegularExportMap regular_exports_;
197   RegularImportMap regular_imports_;
198 
199   // If there are multiple export entries with the same export name, return the
200   // last of them (in source order).  Otherwise return nullptr.
201   const Entry* FindDuplicateExport(Zone* zone) const;
202 
203   // Find any implicitly indirect exports and make them explicit.
204   //
205   // An explicitly indirect export is an export entry arising from an export
206   // statement of the following form:
207   //   export {a as c} from "X";
208   // An implicitly indirect export corresponds to
209   //   export {b as c};
210   // in the presence of an import statement of the form
211   //   import {a as b} from "X";
212   // This function finds such implicitly indirect export entries and rewrites
213   // them by filling in the import name and module request, as well as nulling
214   // out the local name.  Effectively, it turns
215   //   import {a as b} from "X"; export {b as c};
216   // into:
217   //   import {a as b} from "X"; export {a as c} from "X";
218   // (The import entry is never deleted.)
219   void MakeIndirectExportsExplicit(Zone* zone);
220 
221   // Assign a cell_index of -1,-2,... to regular imports.
222   // Assign a cell_index of +1,+2,... to regular (local) exports.
223   // Assign a cell_index of 0 to anything else.
224   void AssignCellIndices();
225 
AddModuleRequest(const AstRawString * specifier,Scanner::Location specifier_loc)226   int AddModuleRequest(const AstRawString* specifier,
227                        Scanner::Location specifier_loc) {
228     DCHECK_NOT_NULL(specifier);
229     int module_requests_count = static_cast<int>(module_requests_.size());
230     auto it = module_requests_
231                   .insert(std::make_pair(specifier,
232                                          ModuleRequest(module_requests_count,
233                                                        specifier_loc.beg_pos)))
234                   .first;
235     return it->second.index;
236   }
237 };
238 
239 }  // namespace internal
240 }  // namespace v8
241 
242 #endif  // V8_AST_MODULES_H_
243