xref: /third_party/node/deps/v8/src/ast/modules.cc (revision 1cb0ef41)
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
7#include "src/ast/ast-value-factory.h"
8#include "src/ast/scopes.h"
9#include "src/common/globals.h"
10#include "src/heap/local-factory-inl.h"
11#include "src/objects/module-inl.h"
12#include "src/objects/objects-inl.h"
13#include "src/parsing/pending-compilation-error-handler.h"
14
15namespace v8 {
16namespace internal {
17
18bool SourceTextModuleDescriptor::AstRawStringComparer::operator()(
19    const AstRawString* lhs, const AstRawString* rhs) const {
20  return AstRawString::Compare(lhs, rhs) < 0;
21}
22
23bool SourceTextModuleDescriptor::ModuleRequestComparer::operator()(
24    const AstModuleRequest* lhs, const AstModuleRequest* rhs) const {
25  if (int specifier_comparison =
26          AstRawString::Compare(lhs->specifier(), rhs->specifier())) {
27    return specifier_comparison < 0;
28  }
29
30  auto lhsIt = lhs->import_assertions()->cbegin();
31  auto rhsIt = rhs->import_assertions()->cbegin();
32  for (; lhsIt != lhs->import_assertions()->cend() &&
33         rhsIt != rhs->import_assertions()->cend();
34       ++lhsIt, ++rhsIt) {
35    if (int assertion_key_comparison =
36            AstRawString::Compare(lhsIt->first, rhsIt->first)) {
37      return assertion_key_comparison < 0;
38    }
39
40    if (int assertion_value_comparison =
41            AstRawString::Compare(lhsIt->second.first, rhsIt->second.first)) {
42      return assertion_value_comparison < 0;
43    }
44  }
45
46  if (lhs->import_assertions()->size() != rhs->import_assertions()->size()) {
47    return (lhs->import_assertions()->size() <
48            rhs->import_assertions()->size());
49  }
50
51  return false;
52}
53
54void SourceTextModuleDescriptor::AddImport(
55    const AstRawString* import_name, const AstRawString* local_name,
56    const AstRawString* module_request,
57    const ImportAssertions* import_assertions, const Scanner::Location loc,
58    const Scanner::Location specifier_loc, Zone* zone) {
59  Entry* entry = zone->New<Entry>(loc);
60  entry->local_name = local_name;
61  entry->import_name = import_name;
62  entry->module_request =
63      AddModuleRequest(module_request, import_assertions, specifier_loc, zone);
64  AddRegularImport(entry);
65}
66
67void SourceTextModuleDescriptor::AddStarImport(
68    const AstRawString* local_name, const AstRawString* module_request,
69    const ImportAssertions* import_assertions, const Scanner::Location loc,
70    const Scanner::Location specifier_loc, Zone* zone) {
71  Entry* entry = zone->New<Entry>(loc);
72  entry->local_name = local_name;
73  entry->module_request =
74      AddModuleRequest(module_request, import_assertions, specifier_loc, zone);
75  AddNamespaceImport(entry, zone);
76}
77
78void SourceTextModuleDescriptor::AddEmptyImport(
79    const AstRawString* module_request,
80    const ImportAssertions* import_assertions,
81    const Scanner::Location specifier_loc, Zone* zone) {
82  AddModuleRequest(module_request, import_assertions, specifier_loc, zone);
83}
84
85void SourceTextModuleDescriptor::AddExport(const AstRawString* local_name,
86                                           const AstRawString* export_name,
87                                           Scanner::Location loc, Zone* zone) {
88  Entry* entry = zone->New<Entry>(loc);
89  entry->export_name = export_name;
90  entry->local_name = local_name;
91  AddRegularExport(entry);
92}
93
94void SourceTextModuleDescriptor::AddExport(
95    const AstRawString* import_name, const AstRawString* export_name,
96    const AstRawString* module_request,
97    const ImportAssertions* import_assertions, const Scanner::Location loc,
98    const Scanner::Location specifier_loc, Zone* zone) {
99  DCHECK_NOT_NULL(import_name);
100  DCHECK_NOT_NULL(export_name);
101  Entry* entry = zone->New<Entry>(loc);
102  entry->export_name = export_name;
103  entry->import_name = import_name;
104  entry->module_request =
105      AddModuleRequest(module_request, import_assertions, specifier_loc, zone);
106  AddSpecialExport(entry, zone);
107}
108
109void SourceTextModuleDescriptor::AddStarExport(
110    const AstRawString* module_request,
111    const ImportAssertions* import_assertions, const Scanner::Location loc,
112    const Scanner::Location specifier_loc, Zone* zone) {
113  Entry* entry = zone->New<Entry>(loc);
114  entry->module_request =
115      AddModuleRequest(module_request, import_assertions, specifier_loc, zone);
116  AddSpecialExport(entry, zone);
117}
118
119namespace {
120template <typename IsolateT>
121Handle<PrimitiveHeapObject> ToStringOrUndefined(IsolateT* isolate,
122                                                const AstRawString* s) {
123  if (s == nullptr) return isolate->factory()->undefined_value();
124  return s->string();
125}
126}  // namespace
127
128template <typename IsolateT>
129Handle<ModuleRequest> SourceTextModuleDescriptor::AstModuleRequest::Serialize(
130    IsolateT* isolate) const {
131  // The import assertions will be stored in this array in the form:
132  // [key1, value1, location1, key2, value2, location2, ...]
133  Handle<FixedArray> import_assertions_array =
134      isolate->factory()->NewFixedArray(
135          static_cast<int>(import_assertions()->size() *
136                           ModuleRequest::kAssertionEntrySize),
137          AllocationType::kOld);
138
139  int i = 0;
140  for (auto iter = import_assertions()->cbegin();
141       iter != import_assertions()->cend();
142       ++iter, i += ModuleRequest::kAssertionEntrySize) {
143    import_assertions_array->set(i, *iter->first->string());
144    import_assertions_array->set(i + 1, *iter->second.first->string());
145    import_assertions_array->set(i + 2,
146                                 Smi::FromInt(iter->second.second.beg_pos));
147  }
148  return v8::internal::ModuleRequest::New(isolate, specifier()->string(),
149                                          import_assertions_array, position());
150}
151template Handle<ModuleRequest>
152SourceTextModuleDescriptor::AstModuleRequest::Serialize(Isolate* isolate) const;
153template Handle<ModuleRequest>
154SourceTextModuleDescriptor::AstModuleRequest::Serialize(
155    LocalIsolate* isolate) const;
156
157template <typename IsolateT>
158Handle<SourceTextModuleInfoEntry> SourceTextModuleDescriptor::Entry::Serialize(
159    IsolateT* isolate) const {
160  CHECK(Smi::IsValid(module_request));  // TODO(neis): Check earlier?
161  return SourceTextModuleInfoEntry::New(
162      isolate, ToStringOrUndefined(isolate, export_name),
163      ToStringOrUndefined(isolate, local_name),
164      ToStringOrUndefined(isolate, import_name), module_request, cell_index,
165      location.beg_pos, location.end_pos);
166}
167template Handle<SourceTextModuleInfoEntry>
168SourceTextModuleDescriptor::Entry::Serialize(Isolate* isolate) const;
169template Handle<SourceTextModuleInfoEntry>
170SourceTextModuleDescriptor::Entry::Serialize(LocalIsolate* isolate) const;
171
172template <typename IsolateT>
173Handle<FixedArray> SourceTextModuleDescriptor::SerializeRegularExports(
174    IsolateT* isolate, Zone* zone) const {
175  // We serialize regular exports in a way that lets us later iterate over their
176  // local names and for each local name immediately access all its export
177  // names.  (Regular exports have neither import name nor module request.)
178
179  ZoneVector<Handle<Object>> data(
180      SourceTextModuleInfo::kRegularExportLength * regular_exports_.size(),
181      zone);
182  int index = 0;
183
184  for (auto it = regular_exports_.begin(); it != regular_exports_.end();) {
185    // Find out how many export names this local name has.
186    auto next = it;
187    int count = 0;
188    do {
189      DCHECK_EQ(it->second->local_name, next->second->local_name);
190      DCHECK_EQ(it->second->cell_index, next->second->cell_index);
191      ++next;
192      ++count;
193    } while (next != regular_exports_.end() && next->first == it->first);
194
195    Handle<FixedArray> export_names =
196        isolate->factory()->NewFixedArray(count, AllocationType::kOld);
197    data[index + SourceTextModuleInfo::kRegularExportLocalNameOffset] =
198        it->second->local_name->string();
199    data[index + SourceTextModuleInfo::kRegularExportCellIndexOffset] =
200        handle(Smi::FromInt(it->second->cell_index), isolate);
201    data[index + SourceTextModuleInfo::kRegularExportExportNamesOffset] =
202        export_names;
203    index += SourceTextModuleInfo::kRegularExportLength;
204
205    // Collect the export names.
206    int i = 0;
207    for (; it != next; ++it) {
208      export_names->set(i++, *it->second->export_name->string());
209    }
210    DCHECK_EQ(i, count);
211
212    // Continue with the next distinct key.
213    DCHECK(it == next);
214  }
215  DCHECK_LE(index, static_cast<int>(data.size()));
216  data.resize(index);
217
218  // We cannot create the FixedArray earlier because we only now know the
219  // precise size.
220  Handle<FixedArray> result =
221      isolate->factory()->NewFixedArray(index, AllocationType::kOld);
222  for (int i = 0; i < index; ++i) {
223    result->set(i, *data[i]);
224  }
225  return result;
226}
227template Handle<FixedArray> SourceTextModuleDescriptor::SerializeRegularExports(
228    Isolate* isolate, Zone* zone) const;
229template Handle<FixedArray> SourceTextModuleDescriptor::SerializeRegularExports(
230    LocalIsolate* isolate, Zone* zone) const;
231
232void SourceTextModuleDescriptor::MakeIndirectExportsExplicit(Zone* zone) {
233  for (auto it = regular_exports_.begin(); it != regular_exports_.end();) {
234    Entry* entry = it->second;
235    DCHECK_NOT_NULL(entry->local_name);
236    auto import = regular_imports_.find(entry->local_name);
237    if (import != regular_imports_.end()) {
238      // Found an indirect export.  Patch export entry and move it from regular
239      // to special.
240      DCHECK_NULL(entry->import_name);
241      DCHECK_LT(entry->module_request, 0);
242      DCHECK_NOT_NULL(import->second->import_name);
243      DCHECK_LE(0, import->second->module_request);
244      DCHECK_LT(import->second->module_request,
245                static_cast<int>(module_requests_.size()));
246      entry->import_name = import->second->import_name;
247      entry->module_request = import->second->module_request;
248      // Hack: When the indirect export cannot be resolved, we want the error
249      // message to point at the import statement, not at the export statement.
250      // Therefore we overwrite [entry]'s location here.  Note that Validate()
251      // has already checked for duplicate exports, so it's guaranteed that we
252      // won't need to report any error pointing at the (now lost) export
253      // location.
254      entry->location = import->second->location;
255      entry->local_name = nullptr;
256      AddSpecialExport(entry, zone);
257      it = regular_exports_.erase(it);
258    } else {
259      it++;
260    }
261  }
262}
263
264SourceTextModuleDescriptor::CellIndexKind
265SourceTextModuleDescriptor::GetCellIndexKind(int cell_index) {
266  if (cell_index > 0) return kExport;
267  if (cell_index < 0) return kImport;
268  return kInvalid;
269}
270
271void SourceTextModuleDescriptor::AssignCellIndices() {
272  int export_index = 1;
273  for (auto it = regular_exports_.begin(); it != regular_exports_.end();) {
274    auto current_key = it->first;
275    // This local name may be exported under multiple export names.  Assign the
276    // same index to each such entry.
277    do {
278      Entry* entry = it->second;
279      DCHECK_NOT_NULL(entry->local_name);
280      DCHECK_NULL(entry->import_name);
281      DCHECK_LT(entry->module_request, 0);
282      DCHECK_EQ(entry->cell_index, 0);
283      entry->cell_index = export_index;
284      it++;
285    } while (it != regular_exports_.end() && it->first == current_key);
286    export_index++;
287  }
288
289  int import_index = -1;
290  for (const auto& elem : regular_imports_) {
291    Entry* entry = elem.second;
292    DCHECK_NOT_NULL(entry->local_name);
293    DCHECK_NOT_NULL(entry->import_name);
294    DCHECK_LE(0, entry->module_request);
295    DCHECK_EQ(entry->cell_index, 0);
296    entry->cell_index = import_index;
297    import_index--;
298  }
299}
300
301namespace {
302
303const SourceTextModuleDescriptor::Entry* BetterDuplicate(
304    const SourceTextModuleDescriptor::Entry* candidate,
305    ZoneMap<const AstRawString*, const SourceTextModuleDescriptor::Entry*>&
306        export_names,
307    const SourceTextModuleDescriptor::Entry* current_duplicate) {
308  DCHECK_NOT_NULL(candidate->export_name);
309  DCHECK(candidate->location.IsValid());
310  auto insert_result =
311      export_names.insert(std::make_pair(candidate->export_name, candidate));
312  if (insert_result.second) return current_duplicate;
313  if (current_duplicate == nullptr) {
314    current_duplicate = insert_result.first->second;
315  }
316  return (candidate->location.beg_pos > current_duplicate->location.beg_pos)
317             ? candidate
318             : current_duplicate;
319}
320
321}  // namespace
322
323const SourceTextModuleDescriptor::Entry*
324SourceTextModuleDescriptor::FindDuplicateExport(Zone* zone) const {
325  const SourceTextModuleDescriptor::Entry* duplicate = nullptr;
326  ZoneMap<const AstRawString*, const SourceTextModuleDescriptor::Entry*>
327      export_names(zone);
328  for (const auto& elem : regular_exports_) {
329    duplicate = BetterDuplicate(elem.second, export_names, duplicate);
330  }
331  for (auto entry : special_exports_) {
332    if (entry->export_name == nullptr) continue;  // Star export.
333    duplicate = BetterDuplicate(entry, export_names, duplicate);
334  }
335  return duplicate;
336}
337
338bool SourceTextModuleDescriptor::Validate(
339    ModuleScope* module_scope, PendingCompilationErrorHandler* error_handler,
340    Zone* zone) {
341  DCHECK_EQ(this, module_scope->module());
342  DCHECK_NOT_NULL(error_handler);
343
344  // Report error iff there are duplicate exports.
345  {
346    const Entry* entry = FindDuplicateExport(zone);
347    if (entry != nullptr) {
348      error_handler->ReportMessageAt(
349          entry->location.beg_pos, entry->location.end_pos,
350          MessageTemplate::kDuplicateExport, entry->export_name);
351      return false;
352    }
353  }
354
355  // Report error iff there are exports of non-existent local names.
356  for (const auto& elem : regular_exports_) {
357    const Entry* entry = elem.second;
358    DCHECK_NOT_NULL(entry->local_name);
359    if (module_scope->LookupLocal(entry->local_name) == nullptr) {
360      error_handler->ReportMessageAt(
361          entry->location.beg_pos, entry->location.end_pos,
362          MessageTemplate::kModuleExportUndefined, entry->local_name);
363      return false;
364    }
365  }
366
367  MakeIndirectExportsExplicit(zone);
368  AssignCellIndices();
369  return true;
370}
371
372}  // namespace internal
373}  // namespace v8
374