// Copyright 2017 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 #include #include "src/objects/module.h" #include "src/accessors.h" #include "src/api-inl.h" #include "src/ast/modules.h" #include "src/objects-inl.h" #include "src/objects/hash-table-inl.h" #include "src/objects/js-generator-inl.h" #include "src/objects/module-inl.h" namespace v8 { namespace internal { namespace { struct ModuleHandleHash { V8_INLINE size_t operator()(Handle module) const { return module->hash(); } }; struct ModuleHandleEqual { V8_INLINE bool operator()(Handle lhs, Handle rhs) const { return *lhs == *rhs; } }; 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 UnorderedModuleSet : public std::unordered_set, ModuleHandleHash, ModuleHandleEqual, ZoneAllocator>> { public: explicit UnorderedModuleSet(Zone* zone) : std::unordered_set, ModuleHandleHash, ModuleHandleEqual, ZoneAllocator>>( 2 /* bucket count */, ModuleHandleHash(), ModuleHandleEqual(), 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)) {} }; } // anonymous namespace 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_; }; namespace { int ExportIndex(int cell_index) { DCHECK_EQ(ModuleDescriptor::GetCellIndexKind(cell_index), ModuleDescriptor::kExport); return cell_index - 1; } int ImportIndex(int cell_index) { DCHECK_EQ(ModuleDescriptor::GetCellIndexKind(cell_index), ModuleDescriptor::kImport); return -cell_index - 1; } } // anonymous namespace void Module::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 Module::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* Module::GetCell(int cell_index) { DisallowHeapAllocation no_gc; Object* cell; switch (ModuleDescriptor::GetCellIndexKind(cell_index)) { case ModuleDescriptor::kImport: cell = regular_imports()->get(ImportIndex(cell_index)); break; case ModuleDescriptor::kExport: cell = regular_exports()->get(ExportIndex(cell_index)); break; case ModuleDescriptor::kInvalid: UNREACHABLE(); break; } return Cell::cast(cell); } Handle Module::LoadVariable(Isolate* isolate, Handle module, int cell_index) { return handle(module->GetCell(cell_index)->value(), isolate); } void Module::StoreVariable(Handle module, int cell_index, Handle value) { DCHECK_EQ(ModuleDescriptor::GetCellIndexKind(cell_index), ModuleDescriptor::kExport); module->GetCell(cell_index)->set_value(*value); } #ifdef DEBUG void Module::PrintStatusTransition(Status new_status) { if (FLAG_trace_module_status) { StdoutStream os; os << "Changing module status from " << status() << " to " << new_status << " for "; script()->GetNameOrSourceURL()->Print(os); #ifndef OBJECT_PRINT os << "\n"; #endif // OBJECT_PRINT } } #endif // DEBUG void Module::SetStatus(Status new_status) { DisallowHeapAllocation no_alloc; DCHECK_LE(status(), new_status); DCHECK_NE(new_status, Module::kErrored); #ifdef DEBUG PrintStatusTransition(new_status); #endif // DEBUG set_status(new_status); } void Module::ResetGraph(Isolate* isolate, Handle module) { DCHECK_NE(module->status(), kInstantiating); DCHECK_NE(module->status(), kEvaluating); if (module->status() != kPreInstantiating) return; Handle requested_modules(module->requested_modules(), isolate); Reset(isolate, module); for (int i = 0; i < requested_modules->length(); ++i) { Handle descendant(requested_modules->get(i), isolate); if (descendant->IsModule()) { ResetGraph(isolate, Handle::cast(descendant)); } else { DCHECK(descendant->IsUndefined(isolate)); } } } void Module::Reset(Isolate* isolate, Handle module) { Factory* factory = isolate->factory(); DCHECK(module->status() == kPreInstantiating || module->status() == kInstantiating); DCHECK(module->exception()->IsTheHole(isolate)); DCHECK(module->import_meta()->IsTheHole(isolate)); // The namespace object cannot exist, because it would have been created // by RunInitializationCode, which is called only after this module's SCC // succeeds instantiation. DCHECK(!module->module_namespace()->IsJSModuleNamespace()); Handle exports = ObjectHashTable::New(isolate, module->info()->RegularExportCount()); Handle regular_exports = factory->NewFixedArray(module->regular_exports()->length()); Handle regular_imports = factory->NewFixedArray(module->regular_imports()->length()); Handle requested_modules = factory->NewFixedArray(module->requested_modules()->length()); if (module->status() == kInstantiating) { module->set_code(JSFunction::cast(module->code())->shared()); } #ifdef DEBUG module->PrintStatusTransition(kUninstantiated); #endif // DEBUG module->set_status(kUninstantiated); module->set_exports(*exports); module->set_regular_exports(*regular_exports); module->set_regular_imports(*regular_imports); module->set_requested_modules(*requested_modules); module->set_dfs_index(-1); module->set_dfs_ancestor_index(-1); } void Module::RecordError(Isolate* isolate) { DisallowHeapAllocation no_alloc; DCHECK(exception()->IsTheHole(isolate)); Object* the_exception = isolate->pending_exception(); DCHECK(!the_exception->IsTheHole(isolate)); set_code(info()); #ifdef DEBUG PrintStatusTransition(Module::kErrored); #endif // DEBUG set_status(Module::kErrored); set_exception(the_exception); } Object* Module::GetException() { DisallowHeapAllocation no_alloc; DCHECK_EQ(status(), Module::kErrored); DCHECK(!exception()->IsTheHole()); return exception(); } SharedFunctionInfo* Module::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(); } MaybeHandle Module::ResolveImport(Isolate* isolate, Handle module, Handle name, int module_request, MessageLocation loc, bool must_resolve, Module::ResolveSet* resolve_set) { Handle requested_module( Module::cast(module->requested_modules()->get(module_request)), isolate); Handle specifier( String::cast(module->info()->module_requests()->get(module_request)), isolate); MaybeHandle result = Module::ResolveExport(isolate, requested_module, specifier, name, loc, must_resolve, resolve_set); DCHECK_IMPLIES(isolate->has_pending_exception(), result.is_null()); return result; } MaybeHandle Module::ResolveExport(Isolate* isolate, Handle module, Handle module_specifier, Handle export_name, MessageLocation loc, bool must_resolve, Module::ResolveSet* resolve_set) { DCHECK_GE(module->status(), kPreInstantiating); DCHECK_NE(module->status(), kEvaluating); 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->IsModuleInfoEntry()) { // Not yet resolved indirect export. Handle entry = Handle::cast(object); Handle import_name(String::cast(entry->import_name()), isolate); Handle