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
12namespace v8 {
13namespace internal {
14
15bool 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
30void 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
43void 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
54void ModuleDescriptor::AddEmptyImport(const AstRawString* module_request,
55 const Scanner::Location specifier_loc) {
56 AddModuleRequest(module_request, specifier_loc);
57}
58
59
60void 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
69void 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
84void 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
93namespace {
94Handle<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
101Handle<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
111Handle<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
162void 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
194ModuleDescriptor::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
201void 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
231namespace {
232
233const 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
252const 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
267bool 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