// Copyright 2019 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. #include "src/objects/source-text-module.h" #include "src/api/api-inl.h" #include "src/ast/modules.h" #include "src/builtins/accessors.h" #include "src/objects/js-generator-inl.h" #include "src/objects/module-inl.h" #include "src/objects/objects-inl.h" #include "src/objects/shared-function-info.h" #include "src/utils/ostreams.h" namespace v8 { namespace internal { struct StringHandleHash { V8_INLINE size_t operator()(Handle string) const { return string->Hash(); } }; struct StringHandleEqual { V8_INLINE bool operator()(Handle lhs, Handle rhs) const { return lhs->Equals(*rhs); } }; class UnorderedStringSet : public std::unordered_set, StringHandleHash, StringHandleEqual, ZoneAllocator>> { public: explicit UnorderedStringSet(Zone* zone) : std::unordered_set, StringHandleHash, StringHandleEqual, ZoneAllocator>>( 2 /* bucket count */, StringHandleHash(), StringHandleEqual(), ZoneAllocator>(zone)) {} }; class UnorderedStringMap : public std::unordered_map< Handle, Handle, StringHandleHash, StringHandleEqual, ZoneAllocator, Handle>>> { public: explicit UnorderedStringMap(Zone* zone) : std::unordered_map< Handle, Handle, StringHandleHash, StringHandleEqual, ZoneAllocator, Handle>>>( 2 /* bucket count */, StringHandleHash(), StringHandleEqual(), ZoneAllocator, Handle>>( zone)) {} }; class Module::ResolveSet : public std::unordered_map< Handle, UnorderedStringSet*, ModuleHandleHash, ModuleHandleEqual, ZoneAllocator, UnorderedStringSet*>>> { public: explicit ResolveSet(Zone* zone) : std::unordered_map, UnorderedStringSet*, ModuleHandleHash, ModuleHandleEqual, ZoneAllocator, UnorderedStringSet*>>>( 2 /* bucket count */, ModuleHandleHash(), ModuleHandleEqual(), ZoneAllocator, UnorderedStringSet*>>( zone)), zone_(zone) {} Zone* zone() const { return zone_; } private: Zone* zone_; }; SharedFunctionInfo SourceTextModule::GetSharedFunctionInfo() const { DisallowHeapAllocation no_alloc; DCHECK_NE(status(), Module::kEvaluating); DCHECK_NE(status(), Module::kEvaluated); switch (status()) { case kUninstantiated: case kPreInstantiating: DCHECK(code().IsSharedFunctionInfo()); return SharedFunctionInfo::cast(code()); case kInstantiating: DCHECK(code().IsJSFunction()); return JSFunction::cast(code()).shared(); case kInstantiated: DCHECK(code().IsJSGeneratorObject()); return JSGeneratorObject::cast(code()).function().shared(); case kEvaluating: case kEvaluated: case kErrored: UNREACHABLE(); } UNREACHABLE(); } int SourceTextModule::ExportIndex(int cell_index) { DCHECK_EQ(SourceTextModuleDescriptor::GetCellIndexKind(cell_index), SourceTextModuleDescriptor::kExport); return cell_index - 1; } int SourceTextModule::ImportIndex(int cell_index) { DCHECK_EQ(SourceTextModuleDescriptor::GetCellIndexKind(cell_index), SourceTextModuleDescriptor::kImport); return -cell_index - 1; } void SourceTextModule::CreateIndirectExport( Isolate* isolate, Handle module, Handle name, Handle entry) { Handle exports(module->exports(), isolate); DCHECK(exports->Lookup(name).IsTheHole(isolate)); exports = ObjectHashTable::Put(exports, name, entry); module->set_exports(*exports); } void SourceTextModule::CreateExport(Isolate* isolate, Handle module, int cell_index, Handle names) { DCHECK_LT(0, names->length()); Handle cell = isolate->factory()->NewCell(isolate->factory()->undefined_value()); module->regular_exports().set(ExportIndex(cell_index), *cell); Handle exports(module->exports(), isolate); for (int i = 0, n = names->length(); i < n; ++i) { Handle name(String::cast(names->get(i)), isolate); DCHECK(exports->Lookup(name).IsTheHole(isolate)); exports = ObjectHashTable::Put(exports, name, cell); } module->set_exports(*exports); } Cell SourceTextModule::GetCell(int cell_index) { DisallowHeapAllocation no_gc; Object cell; switch (SourceTextModuleDescriptor::GetCellIndexKind(cell_index)) { case SourceTextModuleDescriptor::kImport: cell = regular_imports().get(ImportIndex(cell_index)); break; case SourceTextModuleDescriptor::kExport: cell = regular_exports().get(ExportIndex(cell_index)); break; case SourceTextModuleDescriptor::kInvalid: UNREACHABLE(); break; } return Cell::cast(cell); } Handle SourceTextModule::LoadVariable(Isolate* isolate, Handle module, int cell_index) { return handle(module->GetCell(cell_index).value(), isolate); } void SourceTextModule::StoreVariable(Handle module, int cell_index, Handle value) { DisallowHeapAllocation no_gc; DCHECK_EQ(SourceTextModuleDescriptor::GetCellIndexKind(cell_index), SourceTextModuleDescriptor::kExport); module->GetCell(cell_index).set_value(*value); } MaybeHandle SourceTextModule::ResolveExport( Isolate* isolate, Handle module, Handle module_specifier, Handle export_name, MessageLocation loc, bool must_resolve, Module::ResolveSet* resolve_set) { Handle object(module->exports().Lookup(export_name), isolate); if (object->IsCell()) { // Already resolved (e.g. because it's a local export). return Handle::cast(object); } // Check for cycle before recursing. { // Attempt insertion with a null string set. auto result = resolve_set->insert({module, nullptr}); UnorderedStringSet*& name_set = result.first->second; if (result.second) { // |module| wasn't in the map previously, so allocate a new name set. Zone* zone = resolve_set->zone(); name_set = new (zone->New(sizeof(UnorderedStringSet))) UnorderedStringSet(zone); } else if (name_set->count(export_name)) { // Cycle detected. if (must_resolve) { return isolate->Throw( isolate->factory()->NewSyntaxError( MessageTemplate::kCyclicModuleDependency, export_name, module_specifier), &loc); } return MaybeHandle(); } name_set->insert(export_name); } if (object->IsSourceTextModuleInfoEntry()) { // Not yet resolved indirect export. Handle entry = Handle::cast(object); Handle import_name(String::cast(entry->import_name()), isolate); Handle