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 | #include "src/ast/modules.h" |
6 | #include "src/ast/ast-value-factory.h" |
7 | #include "src/ast/scopes.h" |
8 | #include "src/objects-inl.h" |
9 | #include "src/objects/module-inl.h" |
10 | #include "src/pending-compilation-error-handler.h" |
11 | |
12 | namespace v8 { |
13 | namespace internal { |
14 | |
15 | bool ModuleDescriptor::AstRawStringComparer::operator()( |
16 | const AstRawString* lhs, const AstRawString* rhs) const { |
17 | // Fast path for equal pointers: a pointer is not strictly less than itself. |
18 | if (lhs == rhs) return false; |
19 | |
20 | // Order by contents (ordering by hash is unstable across runs). |
21 | if (lhs->is_one_byte() != rhs->is_one_byte()) { |
22 | return lhs->is_one_byte(); |
23 | } |
24 | if (lhs->byte_length() != rhs->byte_length()) { |
25 | return lhs->byte_length() < rhs->byte_length(); |
26 | } |
27 | return memcmp(lhs->raw_data(), rhs->raw_data(), lhs->byte_length()) < 0; |
28 | } |
29 | |
30 | void ModuleDescriptor::AddImport(const AstRawString* import_name, |
31 | const AstRawString* local_name, |
32 | const AstRawString* module_request, |
33 | const Scanner::Location loc, |
34 | const Scanner::Location specifier_loc, |
35 | Zone* zone) { |
36 | Entry* entry = new (zone) Entry(loc); |
37 | entry->local_name = local_name; |
38 | entry->import_name = import_name; |
39 | entry->module_request = AddModuleRequest(module_request, specifier_loc); |
40 | AddRegularImport(entry); |
41 | } |
42 | |
43 | void ModuleDescriptor::AddStarImport(const AstRawString* local_name, |
44 | const AstRawString* module_request, |
45 | const Scanner::Location loc, |
46 | const Scanner::Location specifier_loc, |
47 | Zone* zone) { |
48 | Entry* entry = new (zone) Entry(loc); |
49 | entry->local_name = local_name; |
50 | entry->module_request = AddModuleRequest(module_request, specifier_loc); |
51 | AddNamespaceImport(entry, zone); |
52 | } |
53 | |
54 | void ModuleDescriptor::AddEmptyImport(const AstRawString* module_request, |
55 | const Scanner::Location specifier_loc) { |
56 | AddModuleRequest(module_request, specifier_loc); |
57 | } |
58 | |
59 | |
60 | void ModuleDescriptor::AddExport( |
61 | const AstRawString* local_name, const AstRawString* export_name, |
62 | Scanner::Location loc, Zone* zone) { |
63 | Entry* entry = new (zone) Entry(loc); |
64 | entry->export_name = export_name; |
65 | entry->local_name = local_name; |
66 | AddRegularExport(entry); |
67 | } |
68 | |
69 | void ModuleDescriptor::AddExport(const AstRawString* import_name, |
70 | const AstRawString* export_name, |
71 | const AstRawString* module_request, |
72 | const Scanner::Location loc, |
73 | const Scanner::Location specifier_loc, |
74 | Zone* zone) { |
75 | DCHECK_NOT_NULL(import_name); |
76 | DCHECK_NOT_NULL(export_name); |
77 | Entry* entry = new (zone) Entry(loc); |
78 | entry->export_name = export_name; |
79 | entry->import_name = import_name; |
80 | entry->module_request = AddModuleRequest(module_request, specifier_loc); |
81 | AddSpecialExport(entry, zone); |
82 | } |
83 | |
84 | void ModuleDescriptor::AddStarExport(const AstRawString* module_request, |
85 | const Scanner::Location loc, |
86 | const Scanner::Location specifier_loc, |
87 | Zone* zone) { |
88 | Entry* entry = new (zone) Entry(loc); |
89 | entry->module_request = AddModuleRequest(module_request, specifier_loc); |
90 | AddSpecialExport(entry, zone); |
91 | } |
92 | |
93 | namespace { |
94 | Handle<Object> ToStringOrUndefined(Isolate* isolate, const AstRawString* s) { |
95 | return (s == nullptr) |
96 | ? Handle<Object>::cast(isolate->factory()->undefined_value()) |
97 | : Handle<Object>::cast(s->string()); |
98 | } |
99 | } // namespace |
100 | |
101 | Handle<ModuleInfoEntry> ModuleDescriptor::Entry::Serialize( |
102 | Isolate* isolate) const { |
103 | CHECK(Smi::IsValid(module_request)); // TODO(neis): Check earlier? |
104 | return ModuleInfoEntry::New( |
105 | isolate, ToStringOrUndefined(isolate, export_name), |
106 | ToStringOrUndefined(isolate, local_name), |
107 | ToStringOrUndefined(isolate, import_name), module_request, cell_index, |
108 | location.beg_pos, location.end_pos); |
109 | } |
110 | |
111 | Handle<FixedArray> ModuleDescriptor::SerializeRegularExports(Isolate* isolate, |
112 | Zone* zone) const { |
113 | // We serialize regular exports in a way that lets us later iterate over their |
114 | // local names and for each local name immediately access all its export |
115 | // names. (Regular exports have neither import name nor module request.) |
116 | |
117 | ZoneVector<Handle<Object>> data( |
118 | ModuleInfo::kRegularExportLength * regular_exports_.size(), zone); |
119 | int index = 0; |
120 | |
121 | for (auto it = regular_exports_.begin(); it != regular_exports_.end();) { |
122 | // Find out how many export names this local name has. |
123 | auto next = it; |
124 | int count = 0; |
125 | do { |
126 | DCHECK_EQ(it->second->local_name, next->second->local_name); |
127 | DCHECK_EQ(it->second->cell_index, next->second->cell_index); |
128 | ++next; |
129 | ++count; |
130 | } while (next != regular_exports_.end() && next->first == it->first); |
131 | |
132 | Handle<FixedArray> export_names = isolate->factory()->NewFixedArray(count); |
133 | data[index + ModuleInfo::kRegularExportLocalNameOffset] = |
134 | it->second->local_name->string(); |
135 | data[index + ModuleInfo::kRegularExportCellIndexOffset] = |
136 | handle(Smi::FromInt(it->second->cell_index), isolate); |
137 | data[index + ModuleInfo::kRegularExportExportNamesOffset] = export_names; |
138 | index += ModuleInfo::kRegularExportLength; |
139 | |
140 | // Collect the export names. |
141 | int i = 0; |
142 | for (; it != next; ++it) { |
143 | export_names->set(i++, *it->second->export_name->string()); |
144 | } |
145 | DCHECK_EQ(i, count); |
146 | |
147 | // Continue with the next distinct key. |
148 | DCHECK(it == next); |
149 | } |
150 | DCHECK_LE(index, static_cast<int>(data.size())); |
151 | data.resize(index); |
152 | |
153 | // We cannot create the FixedArray earlier because we only now know the |
154 | // precise size. |
155 | Handle<FixedArray> result = isolate->factory()->NewFixedArray(index); |
156 | for (int i = 0; i < index; ++i) { |
157 | result->set(i, *data[i]); |
158 | } |
159 | return result; |
160 | } |
161 | |
162 | void ModuleDescriptor::MakeIndirectExportsExplicit(Zone* zone) { |
163 | for (auto it = regular_exports_.begin(); it != regular_exports_.end();) { |
164 | Entry* entry = it->second; |
165 | DCHECK_NOT_NULL(entry->local_name); |
166 | auto import = regular_imports_.find(entry->local_name); |
167 | if (import != regular_imports_.end()) { |
168 | // Found an indirect export. Patch export entry and move it from regular |
169 | // to special. |
170 | DCHECK_NULL(entry->import_name); |
171 | DCHECK_LT(entry->module_request, 0); |
172 | DCHECK_NOT_NULL(import->second->import_name); |
173 | DCHECK_LE(0, import->second->module_request); |
174 | DCHECK_LT(import->second->module_request, |
175 | static_cast<int>(module_requests_.size())); |
176 | entry->import_name = import->second->import_name; |
177 | entry->module_request = import->second->module_request; |
178 | // Hack: When the indirect export cannot be resolved, we want the error |
179 | // message to point at the import statement, not at the export statement. |
180 | // Therefore we overwrite [entry]'s location here. Note that Validate() |
181 | // has already checked for duplicate exports, so it's guaranteed that we |
182 | // won't need to report any error pointing at the (now lost) export |
183 | // location. |
184 | entry->location = import->second->location; |
185 | entry->local_name = nullptr; |
186 | AddSpecialExport(entry, zone); |
187 | it = regular_exports_.erase(it); |
188 | } else { |
189 | it++; |
190 | } |
191 | } |
192 | } |
193 | |
194 | ModuleDescriptor::CellIndexKind ModuleDescriptor::GetCellIndexKind( |
195 | int cell_index) { |
196 | if (cell_index > 0) return kExport; |
197 | if (cell_index < 0) return kImport; |
198 | return kInvalid; |
199 | } |
200 | |
201 | void ModuleDescriptor::AssignCellIndices() { |
202 | int export_index = 1; |
203 | for (auto it = regular_exports_.begin(); it != regular_exports_.end();) { |
204 | auto current_key = it->first; |
205 | // This local name may be exported under multiple export names. Assign the |
206 | // same index to each such entry. |
207 | do { |
208 | Entry* entry = it->second; |
209 | DCHECK_NOT_NULL(entry->local_name); |
210 | DCHECK_NULL(entry->import_name); |
211 | DCHECK_LT(entry->module_request, 0); |
212 | DCHECK_EQ(entry->cell_index, 0); |
213 | entry->cell_index = export_index; |
214 | it++; |
215 | } while (it != regular_exports_.end() && it->first == current_key); |
216 | export_index++; |
217 | } |
218 | |
219 | int import_index = -1; |
220 | for (const auto& elem : regular_imports_) { |
221 | Entry* entry = elem.second; |
222 | DCHECK_NOT_NULL(entry->local_name); |
223 | DCHECK_NOT_NULL(entry->import_name); |
224 | DCHECK_LE(0, entry->module_request); |
225 | DCHECK_EQ(entry->cell_index, 0); |
226 | entry->cell_index = import_index; |
227 | import_index--; |
228 | } |
229 | } |
230 | |
231 | namespace { |
232 | |
233 | const ModuleDescriptor::Entry* BetterDuplicate( |
234 | const ModuleDescriptor::Entry* candidate, |
235 | ZoneMap<const AstRawString*, const ModuleDescriptor::Entry*>& export_names, |
236 | const ModuleDescriptor::Entry* current_duplicate) { |
237 | DCHECK_NOT_NULL(candidate->export_name); |
238 | DCHECK(candidate->location.IsValid()); |
239 | auto insert_result = |
240 | export_names.insert(std::make_pair(candidate->export_name, candidate)); |
241 | if (insert_result.second) return current_duplicate; |
242 | if (current_duplicate == nullptr) { |
243 | current_duplicate = insert_result.first->second; |
244 | } |
245 | return (candidate->location.beg_pos > current_duplicate->location.beg_pos) |
246 | ? candidate |
247 | : current_duplicate; |
248 | } |
249 | |
250 | } // namespace |
251 | |
252 | const ModuleDescriptor::Entry* ModuleDescriptor::FindDuplicateExport( |
253 | Zone* zone) const { |
254 | const ModuleDescriptor::Entry* duplicate = nullptr; |
255 | ZoneMap<const AstRawString*, const ModuleDescriptor::Entry*> export_names( |
256 | zone); |
257 | for (const auto& elem : regular_exports_) { |
258 | duplicate = BetterDuplicate(elem.second, export_names, duplicate); |
259 | } |
260 | for (auto entry : special_exports_) { |
261 | if (entry->export_name == nullptr) continue; // Star export. |
262 | duplicate = BetterDuplicate(entry, export_names, duplicate); |
263 | } |
264 | return duplicate; |
265 | } |
266 | |
267 | bool ModuleDescriptor::Validate(ModuleScope* module_scope, |
268 | PendingCompilationErrorHandler* error_handler, |
269 | Zone* zone) { |
270 | DCHECK_EQ(this, module_scope->module()); |
271 | DCHECK_NOT_NULL(error_handler); |
272 | |
273 | // Report error iff there are duplicate exports. |
274 | { |
275 | const Entry* entry = FindDuplicateExport(zone); |
276 | if (entry != nullptr) { |
277 | error_handler->ReportMessageAt( |
278 | entry->location.beg_pos, entry->location.end_pos, |
279 | MessageTemplate::kDuplicateExport, entry->export_name); |
280 | return false; |
281 | } |
282 | } |
283 | |
284 | // Report error iff there are exports of non-existent local names. |
285 | for (const auto& elem : regular_exports_) { |
286 | const Entry* entry = elem.second; |
287 | DCHECK_NOT_NULL(entry->local_name); |
288 | if (module_scope->LookupLocal(entry->local_name) == nullptr) { |
289 | error_handler->ReportMessageAt( |
290 | entry->location.beg_pos, entry->location.end_pos, |
291 | MessageTemplate::kModuleExportUndefined, entry->local_name); |
292 | return false; |
293 | } |
294 | } |
295 | |
296 | MakeIndirectExportsExplicit(zone); |
297 | AssignCellIndices(); |
298 | return true; |
299 | } |
300 | |
301 | } // namespace internal |
302 | } // namespace v8 |
303 | |