blob: f156d7a411ac79409e13695ea78ee1fe7b8a98a1 [file] [log] [blame]
// 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_