| //===--- ModuleFile.cpp - Loading a serialized module ---------------------===// | |
| // | |
| // This source file is part of the Swift.org open source project | |
| // | |
| // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors | |
| // Licensed under Apache License v2.0 with Runtime Library Exception | |
| // | |
| // See https://swift.org/LICENSE.txt for license information | |
| // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "ModuleFile.h" | |
| #include "ModuleFileCoreTableInfo.h" | |
| #include "BCReadingExtras.h" | |
| #include "DeserializationErrors.h" | |
| #include "ModuleFormat.h" | |
| #include "swift/Serialization/SerializationOptions.h" | |
| #include "swift/Subsystems.h" | |
| #include "swift/AST/ASTContext.h" | |
| #include "swift/AST/ASTMangler.h" | |
| #include "swift/AST/GenericSignature.h" | |
| #include "swift/AST/ModuleLoader.h" | |
| #include "swift/AST/NameLookup.h" | |
| #include "swift/AST/PrettyStackTrace.h" | |
| #include "swift/AST/USRGeneration.h" | |
| #include "swift/Basic/Range.h" | |
| #include "swift/ClangImporter/ClangImporter.h" | |
| #include "swift/Serialization/SerializedModuleLoader.h" | |
| #include "llvm/ADT/StringExtras.h" | |
| #include "llvm/Support/Chrono.h" | |
| #include "llvm/Support/MemoryBuffer.h" | |
| #include "llvm/Support/OnDiskHashTable.h" | |
| using namespace swift; | |
| using namespace swift::serialization; | |
| using namespace llvm::support; | |
| using llvm::Expected; | |
| static_assert(IsTriviallyDestructible<SerializedASTFile>::value, | |
| "SerializedASTFiles are BumpPtrAllocated; d'tors are not called"); | |
| static bool areCompatibleArchitectures(const llvm::Triple &moduleTarget, | |
| const llvm::Triple &ctxTarget) { | |
| if (moduleTarget.getArch() == ctxTarget.getArch()) | |
| return true; | |
| // Special case: ARM and Thumb are compatible. | |
| const llvm::Triple::ArchType moduleArch = moduleTarget.getArch(); | |
| const llvm::Triple::ArchType ctxArch = ctxTarget.getArch(); | |
| if ((moduleArch == llvm::Triple::arm && ctxArch == llvm::Triple::thumb) || | |
| (moduleArch == llvm::Triple::thumb && ctxArch == llvm::Triple::arm)) | |
| return true; | |
| if ((moduleArch == llvm::Triple::armeb && ctxArch == llvm::Triple::thumbeb) || | |
| (moduleArch == llvm::Triple::thumbeb && ctxArch == llvm::Triple::armeb)) | |
| return true; | |
| return false; | |
| } | |
| static bool areCompatibleOSs(const llvm::Triple &moduleTarget, | |
| const llvm::Triple &ctxTarget) { | |
| if ((!moduleTarget.hasEnvironment() && ctxTarget.isSimulatorEnvironment()) || | |
| (!ctxTarget.hasEnvironment() && moduleTarget.isSimulatorEnvironment())) | |
| return false; | |
| if (moduleTarget.getOS() == ctxTarget.getOS()) | |
| return true; | |
| // Special case: macOS and Darwin are compatible. | |
| const llvm::Triple::OSType moduleOS = moduleTarget.getOS(); | |
| const llvm::Triple::OSType ctxOS = ctxTarget.getOS(); | |
| if ((moduleOS == llvm::Triple::Darwin && ctxOS == llvm::Triple::MacOSX) || | |
| (moduleOS == llvm::Triple::MacOSX && ctxOS == llvm::Triple::Darwin)) | |
| return true; | |
| return false; | |
| } | |
| static bool isTargetTooNew(const llvm::Triple &moduleTarget, | |
| const llvm::Triple &ctxTarget) { | |
| unsigned major, minor, micro; | |
| if (moduleTarget.isMacOSX()) { | |
| moduleTarget.getMacOSXVersion(major, minor, micro); | |
| return ctxTarget.isMacOSXVersionLT(major, minor, micro); | |
| } | |
| moduleTarget.getOSVersion(major, minor, micro); | |
| return ctxTarget.isOSVersionLT(major, minor, micro); | |
| } | |
| ModuleFile::ModuleFile(std::shared_ptr<const ModuleFileSharedCore> core) | |
| : Core(core), | |
| DeserializedTypeCallback([](Type ty) {}) { | |
| assert(!core->hasError()); | |
| DeclTypeCursor = core->DeclTypeCursor; | |
| SILCursor = core->SILCursor; | |
| SILIndexCursor = core->SILIndexCursor; | |
| DeclMemberTablesCursor = core->DeclMemberTablesCursor; | |
| for (const auto &coreDep : core->Dependencies) { | |
| Dependencies.emplace_back(coreDep); | |
| } | |
| // `ModuleFileSharedCore` has immutable data, we copy these into `ModuleFile` | |
| // so we can mutate the arrays and replace the offsets with AST object | |
| // pointers as we lazily deserialize them. | |
| allocateBuffer(Decls, core->Decls); | |
| allocateBuffer(LocalDeclContexts, core->LocalDeclContexts); | |
| allocateBuffer(NormalConformances, core->NormalConformances); | |
| allocateBuffer(SILLayouts, core->SILLayouts); | |
| allocateBuffer(Types, core->Types); | |
| allocateBuffer(ClangTypes, core->ClangTypes); | |
| allocateBuffer(GenericSignatures, core->GenericSignatures); | |
| allocateBuffer(SubstitutionMaps, core->SubstitutionMaps); | |
| allocateBuffer(Identifiers, core->Identifiers); | |
| } | |
| Status ModuleFile::associateWithFileContext(FileUnit *file, SourceLoc diagLoc) { | |
| PrettyStackTraceModuleFile stackEntry(*this); | |
| assert(!hasError() && "error already detected; should not call this"); | |
| assert(!FileContext && "already associated with an AST module"); | |
| FileContext = file; | |
| ModuleDecl *M = file->getParentModule(); | |
| if (M->getName().str() != Core->Name) | |
| return error(Status::NameMismatch); | |
| ASTContext &ctx = getContext(); | |
| llvm::Triple moduleTarget(llvm::Triple::normalize(Core->TargetTriple)); | |
| if (!areCompatibleArchitectures(moduleTarget, ctx.LangOpts.Target) || | |
| !areCompatibleOSs(moduleTarget, ctx.LangOpts.Target)) { | |
| return error(Status::TargetIncompatible); | |
| } | |
| if (ctx.LangOpts.EnableTargetOSChecking && | |
| !M->isResilient() && | |
| isTargetTooNew(moduleTarget, ctx.LangOpts.Target)) { | |
| return error(Status::TargetTooNew); | |
| } | |
| for (const auto &searchPath : Core->SearchPaths) | |
| ctx.addSearchPath(searchPath.Path, searchPath.IsFramework, | |
| searchPath.IsSystem); | |
| auto clangImporter = static_cast<ClangImporter *>(ctx.getClangModuleLoader()); | |
| bool missingDependency = false; | |
| for (auto &dependency : Dependencies) { | |
| assert(!dependency.isLoaded() && "already loaded?"); | |
| if (dependency.isHeader()) { | |
| // The path may be empty if the file being loaded is a partial AST, | |
| // and the current compiler invocation is a merge-modules step. | |
| if (!dependency.Core.RawPath.empty()) { | |
| bool hadError = | |
| clangImporter->importHeader(dependency.Core.RawPath, | |
| file->getParentModule(), | |
| Core->importedHeaderInfo.fileSize, | |
| Core->importedHeaderInfo.fileModTime, | |
| Core->importedHeaderInfo.contents, | |
| diagLoc); | |
| if (hadError) | |
| return error(Status::FailedToLoadBridgingHeader); | |
| } | |
| ModuleDecl *importedHeaderModule = clangImporter->getImportedHeaderModule(); | |
| dependency.Import = ImportedModule{ImportPath::Access(), | |
| importedHeaderModule}; | |
| continue; | |
| } | |
| // If this module file is being installed into the main module, it's treated | |
| // as a partial module. | |
| auto isPartialModule = M->isMainModule(); | |
| if (dependency.isImplementationOnly() && | |
| !(isPartialModule || ctx.LangOpts.DebuggerSupport)) { | |
| // When building normally (and not merging partial modules), we don't | |
| // want to bring in the implementation-only module, because that might | |
| // change the set of visible declarations. However, when debugging we | |
| // want to allow getting at the internals of this module when possible, | |
| // and so we'll try to reference the implementation-only module if it's | |
| // available. | |
| continue; | |
| } | |
| ImportPath::Builder builder(ctx, dependency.Core.RawPath, | |
| /*separator=*/'\0'); | |
| for (const auto &elem : builder) { | |
| assert(!elem.Item.empty() && "invalid import path name"); | |
| } | |
| auto importPath = builder.copyTo(ctx); | |
| auto modulePath = importPath.getModulePath(dependency.isScoped()); | |
| auto accessPath = importPath.getAccessPath(dependency.isScoped()); | |
| auto module = getModule(modulePath, /*allowLoading*/true); | |
| if (!module || module->failedToLoad()) { | |
| // If we're missing the module we're an overlay for, treat that specially. | |
| if (modulePath.size() == 1 && | |
| modulePath.front().Item == file->getParentModule()->getName()) { | |
| return error(Status::MissingUnderlyingModule); | |
| } | |
| // Otherwise, continue trying to load dependencies, so that we can list | |
| // everything that's missing. | |
| if (!(dependency.isImplementationOnly() && ctx.LangOpts.DebuggerSupport)) | |
| missingDependency = true; | |
| continue; | |
| } | |
| dependency.Import = ImportedModule{accessPath, module}; | |
| // SPI | |
| StringRef spisStr = dependency.Core.RawSPIs; | |
| while (!spisStr.empty()) { | |
| StringRef nextComponent; | |
| std::tie(nextComponent, spisStr) = spisStr.split('\0'); | |
| dependency.spiGroups.push_back(ctx.getIdentifier(nextComponent)); | |
| } | |
| if (!module->hasResolvedImports()) { | |
| // Notice that we check this condition /after/ recording the module that | |
| // caused the problem. Clients need to be able to track down what the | |
| // cycle was. | |
| return error(Status::CircularDependency); | |
| } | |
| } | |
| if (missingDependency) { | |
| return error(Status::MissingDependency); | |
| } | |
| if (Core->Bits.HasEntryPoint) { | |
| FileContext->getParentModule()->registerEntryPointFile(FileContext, | |
| SourceLoc(), | |
| None); | |
| } | |
| return Status::Valid; | |
| } | |
| bool ModuleFile::mayHaveDiagnosticsPointingAtBuffer() const { | |
| if (!hasError()) | |
| return false; | |
| // Today, the only buffer that might have diagnostics in them is the input | |
| // buffer, and even then only if it has imported module contents. | |
| return !Core->importedHeaderInfo.contents.empty(); | |
| } | |
| ModuleFile::~ModuleFile() { } | |
| void ModuleFile::lookupValue(DeclName name, | |
| SmallVectorImpl<ValueDecl*> &results) { | |
| PrettyStackTraceModuleFile stackEntry(*this); | |
| if (Core->TopLevelDecls) { | |
| // Find top-level declarations with the given name. | |
| // FIXME: As a bit of a hack, do lookup by the simple name, then filter | |
| // compound decls, to avoid having to completely redo how modules are | |
| // serialized. | |
| auto iter = Core->TopLevelDecls->find(name.getBaseName()); | |
| if (iter != Core->TopLevelDecls->end()) { | |
| for (auto item : *iter) { | |
| Expected<Decl *> declOrError = getDeclChecked(item.second); | |
| if (!declOrError) { | |
| if (!getContext().LangOpts.EnableDeserializationRecovery) | |
| fatal(declOrError.takeError()); | |
| llvm::consumeError(declOrError.takeError()); | |
| continue; | |
| } | |
| auto VD = cast<ValueDecl>(declOrError.get()); | |
| if (name.isSimpleName() || VD->getName().matchesRef(name)) | |
| results.push_back(VD); | |
| } | |
| } | |
| } | |
| // If the name is an operator name, also look for operator methods. | |
| if (name.isOperator() && Core->OperatorMethodDecls) { | |
| auto iter = Core->OperatorMethodDecls->find(name.getBaseName()); | |
| if (iter != Core->OperatorMethodDecls->end()) { | |
| for (auto item : *iter) { | |
| Expected<Decl *> declOrError = getDeclChecked(item.second); | |
| if (!declOrError) { | |
| if (!getContext().LangOpts.EnableDeserializationRecovery) | |
| fatal(declOrError.takeError()); | |
| llvm::consumeError(declOrError.takeError()); | |
| continue; | |
| } | |
| auto VD = cast<ValueDecl>(declOrError.get()); | |
| results.push_back(VD); | |
| } | |
| } | |
| } | |
| } | |
| TypeDecl *ModuleFile::lookupLocalType(StringRef MangledName) { | |
| PrettyStackTraceModuleFile stackEntry(*this); | |
| if (!Core->LocalTypeDecls) | |
| return nullptr; | |
| auto iter = Core->LocalTypeDecls->find(MangledName); | |
| if (iter == Core->LocalTypeDecls->end()) | |
| return nullptr; | |
| return cast<TypeDecl>(getDecl(*iter)); | |
| } | |
| std::unique_ptr<llvm::MemoryBuffer> | |
| ModuleFile::getModuleName(ASTContext &Ctx, StringRef modulePath, | |
| std::string &Name) { | |
| // Open the module file | |
| auto &fs = *Ctx.SourceMgr.getFileSystem(); | |
| auto moduleBuf = fs.getBufferForFile(modulePath); | |
| if (!moduleBuf) | |
| return nullptr; | |
| // FIXME: This goes through the full cost of creating a ModuleFile object | |
| // and then it keeps just the name and discards the whole object. | |
| // The user of this API is `ExplicitSwiftModuleLoader`, this API should | |
| // change to return a `ModuleFileSharedCore` object that | |
| // `ExplicitSwiftModuleLoader` caches. | |
| // Load the module file without validation. | |
| std::unique_ptr<llvm::MemoryBuffer> newBuf = | |
| llvm::MemoryBuffer::getMemBuffer(llvm::MemoryBufferRef(*moduleBuf.get()), | |
| /*RequiresNullTerminator=*/false); | |
| std::shared_ptr<const ModuleFileSharedCore> loadedModuleFile; | |
| bool isFramework = false; | |
| serialization::ValidationInfo loadInfo = | |
| ModuleFileSharedCore::load(modulePath.str(), | |
| std::move(newBuf), | |
| nullptr, | |
| nullptr, | |
| /*isFramework*/isFramework, loadedModuleFile); | |
| Name = loadedModuleFile->Name.str(); | |
| return std::move(moduleBuf.get()); | |
| } | |
| OpaqueTypeDecl *ModuleFile::lookupOpaqueResultType(StringRef MangledName) { | |
| PrettyStackTraceModuleFile stackEntry(*this); | |
| if (!Core->OpaqueReturnTypeDecls) | |
| return nullptr; | |
| auto iter = Core->OpaqueReturnTypeDecls->find(MangledName); | |
| if (iter == Core->OpaqueReturnTypeDecls->end()) | |
| return nullptr; | |
| return cast<OpaqueTypeDecl>(getDecl(*iter)); | |
| } | |
| TypeDecl *ModuleFile::lookupNestedType(Identifier name, | |
| const NominalTypeDecl *parent) { | |
| PrettyStackTraceModuleFile stackEntry(*this); | |
| if (Core->NestedTypeDecls) { | |
| auto iter = Core->NestedTypeDecls->find(name); | |
| if (iter != Core->NestedTypeDecls->end()) { | |
| for (std::pair<DeclID, DeclID> entry : *iter) { | |
| assert(entry.first); | |
| auto declOrOffset = Decls[entry.first - 1]; | |
| if (!declOrOffset.isComplete()) | |
| continue; | |
| Decl *decl = declOrOffset; | |
| if (decl != parent) | |
| continue; | |
| return cast<TypeDecl>(getDecl(entry.second)); | |
| } | |
| } | |
| } | |
| if (!UnderlyingModule) | |
| return nullptr; | |
| for (FileUnit *file : UnderlyingModule->getFiles()) | |
| if (auto *nestedType = file->lookupNestedType(name, parent)) | |
| return nestedType; | |
| return nullptr; | |
| } | |
| OperatorDecl *ModuleFile::lookupOperator(Identifier name, | |
| OperatorFixity fixity) { | |
| PrettyStackTraceModuleFile stackEntry(*this); | |
| if (!Core->OperatorDecls) | |
| return nullptr; | |
| auto iter = Core->OperatorDecls->find(name); | |
| if (iter == Core->OperatorDecls->end()) | |
| return nullptr; | |
| for (auto item : *iter) { | |
| if (getStableFixity(fixity) == item.first) | |
| return cast<OperatorDecl>(getDecl(item.second)); | |
| } | |
| return nullptr; | |
| } | |
| PrecedenceGroupDecl *ModuleFile::lookupPrecedenceGroup(Identifier name) { | |
| PrettyStackTraceModuleFile stackEntry(*this); | |
| if (!Core->PrecedenceGroupDecls) | |
| return nullptr; | |
| auto iter = Core->PrecedenceGroupDecls->find(name); | |
| if (iter == Core->PrecedenceGroupDecls->end()) | |
| return nullptr; | |
| auto data = *iter; | |
| assert(data.size() == 1); | |
| return cast<PrecedenceGroupDecl>(getDecl(data[0].second)); | |
| } | |
| void ModuleFile::getImportedModules(SmallVectorImpl<ImportedModule> &results, | |
| ModuleDecl::ImportFilter filter) { | |
| PrettyStackTraceModuleFile stackEntry(*this); | |
| for (auto &dep : Dependencies) { | |
| if (dep.isExported()) { | |
| if (!filter.contains(ModuleDecl::ImportFilterKind::Exported)) | |
| continue; | |
| } else if (dep.isImplementationOnly()) { | |
| if (!filter.contains(ModuleDecl::ImportFilterKind::ImplementationOnly)) | |
| continue; | |
| if (!dep.isLoaded()) { | |
| // Pretend we didn't have this import if we weren't originally asked to | |
| // load it. | |
| continue; | |
| } | |
| } else { | |
| if (!filter.contains(ModuleDecl::ImportFilterKind::Default)) | |
| continue; | |
| } | |
| assert(dep.isLoaded()); | |
| results.push_back(*(dep.Import)); | |
| } | |
| } | |
| void ModuleFile::getImportDecls(SmallVectorImpl<Decl *> &Results) { | |
| if (!Bits.ComputedImportDecls) { | |
| ASTContext &Ctx = getContext(); | |
| for (auto &Dep : Dependencies) { | |
| // FIXME: We need a better way to show headers, since they usually /are/ | |
| // re-exported. This isn't likely to come up much, though. | |
| if (Dep.isHeader()) | |
| continue; | |
| ImportPath::Builder importPath(Ctx, Dep.Core.RawPath, /*separator=*/'\0'); | |
| if (importPath.size() == 1 | |
| && importPath.front().Item == Ctx.StdlibModuleName) | |
| continue; | |
| auto modulePath = importPath.get().getModulePath(Dep.isScoped()); | |
| ModuleDecl *M = Ctx.getLoadedModule(modulePath); | |
| auto Kind = ImportKind::Module; | |
| if (Dep.isScoped()) { | |
| auto ScopeID = importPath.get().getAccessPath(true).front().Item; | |
| assert(!ScopeID.empty() && | |
| "invalid decl name (non-top-level decls not supported)"); | |
| if (!M) { | |
| // The dependency module could not be loaded. Just make a guess | |
| // about the import kind, we cannot do better. | |
| Kind = ImportKind::Func; | |
| } else { | |
| // Lookup the decl in the top-level module. | |
| ModuleDecl *TopLevelModule = M; | |
| if (importPath.size() > 1) | |
| TopLevelModule = Ctx.getLoadedModule(modulePath.getTopLevelPath()); | |
| SmallVector<ValueDecl *, 8> Decls; | |
| TopLevelModule->lookupQualified( | |
| TopLevelModule, DeclNameRef(ScopeID), | |
| NL_QualifiedDefault, Decls); | |
| Optional<ImportKind> FoundKind = ImportDecl::findBestImportKind(Decls); | |
| assert(FoundKind.hasValue() && | |
| "deserialized imports should not be ambiguous"); | |
| Kind = *FoundKind; | |
| } | |
| } | |
| auto *ID = ImportDecl::create(Ctx, FileContext, SourceLoc(), Kind, | |
| SourceLoc(), importPath.get()); | |
| ID->setModule(M); | |
| if (Dep.isExported()) | |
| ID->getAttrs().add( | |
| new (Ctx) ExportedAttr(/*IsImplicit=*/false)); | |
| ImportDecls.push_back(ID); | |
| } | |
| Bits.ComputedImportDecls = true; | |
| } | |
| Results.append(ImportDecls.begin(), ImportDecls.end()); | |
| } | |
| void ModuleFile::lookupVisibleDecls(ImportPath::Access accessPath, | |
| VisibleDeclConsumer &consumer, | |
| NLKind lookupKind) { | |
| PrettyStackTraceModuleFile stackEntry(*this); | |
| assert(accessPath.size() <= 1 && "can only refer to top-level decls"); | |
| if (!Core->TopLevelDecls) | |
| return; | |
| auto tryImport = [this, &consumer](DeclID ID) { | |
| Expected<Decl *> declOrError = getDeclChecked(ID); | |
| if (!declOrError) { | |
| if (!getContext().LangOpts.EnableDeserializationRecovery) | |
| fatal(declOrError.takeError()); | |
| llvm::consumeError(declOrError.takeError()); | |
| return; | |
| } | |
| consumer.foundDecl(cast<ValueDecl>(declOrError.get()), | |
| DeclVisibilityKind::VisibleAtTopLevel); | |
| }; | |
| if (!accessPath.empty()) { | |
| auto iter = Core->TopLevelDecls->find(accessPath.front().Item); | |
| if (iter == Core->TopLevelDecls->end()) | |
| return; | |
| for (auto item : *iter) | |
| tryImport(item.second); | |
| return; | |
| } | |
| for (auto entry : Core->TopLevelDecls->data()) { | |
| for (auto item : entry) | |
| tryImport(item.second); | |
| } | |
| } | |
| void ModuleFile::loadExtensions(NominalTypeDecl *nominal) { | |
| PrettyStackTraceModuleFile stackEntry(*this); | |
| if (!Core->ExtensionDecls) | |
| return; | |
| auto iter = Core->ExtensionDecls->find(nominal->getName()); | |
| if (iter == Core->ExtensionDecls->end()) | |
| return; | |
| if (nominal->getEffectiveAccess() < AccessLevel::Internal) { | |
| if (nominal->getModuleScopeContext() != getFile()) | |
| return; | |
| } | |
| if (nominal->getParent()->isModuleScopeContext()) { | |
| auto parentFile = cast<FileUnit>(nominal->getParent()); | |
| StringRef moduleName = parentFile->getExportedModuleName(); | |
| for (auto item : *iter) { | |
| if (item.first != moduleName) | |
| continue; | |
| Expected<Decl *> declOrError = getDeclChecked(item.second); | |
| if (!declOrError) { | |
| if (!getContext().LangOpts.EnableDeserializationRecovery) | |
| fatal(declOrError.takeError()); | |
| llvm::consumeError(declOrError.takeError()); | |
| } | |
| } | |
| } else { | |
| std::string mangledName = | |
| Mangle::ASTMangler().mangleNominalType(nominal); | |
| for (auto item : *iter) { | |
| if (item.first != mangledName) | |
| continue; | |
| Expected<Decl *> declOrError = getDeclChecked(item.second); | |
| if (!declOrError) { | |
| if (!getContext().LangOpts.EnableDeserializationRecovery) | |
| fatal(declOrError.takeError()); | |
| llvm::consumeError(declOrError.takeError()); | |
| } | |
| } | |
| } | |
| } | |
| void ModuleFile::loadObjCMethods( | |
| ClassDecl *classDecl, | |
| ObjCSelector selector, | |
| bool isInstanceMethod, | |
| llvm::TinyPtrVector<AbstractFunctionDecl *> &methods) { | |
| // If we don't have an Objective-C method table, there's nothing to do. | |
| if (!Core->ObjCMethods) | |
| return; | |
| // Look for all methods in the module file with this selector. | |
| auto known = Core->ObjCMethods->find(selector); | |
| if (known == Core->ObjCMethods->end()) { | |
| return; | |
| } | |
| std::string ownerName = Mangle::ASTMangler().mangleNominalType(classDecl); | |
| auto results = *known; | |
| for (const auto &result : results) { | |
| // If the method is the wrong kind (instance vs. class), skip it. | |
| if (isInstanceMethod != std::get<1>(result)) | |
| continue; | |
| // If the method isn't defined in the requested class, skip it. | |
| if (std::get<0>(result) != ownerName) | |
| continue; | |
| // Deserialize the method and add it to the list. | |
| if (auto func = dyn_cast_or_null<AbstractFunctionDecl>( | |
| getDecl(std::get<2>(result)))) { | |
| methods.push_back(func); | |
| } | |
| } | |
| } | |
| void ModuleFile::loadDerivativeFunctionConfigurations( | |
| AbstractFunctionDecl *originalAFD, | |
| llvm::SetVector<AutoDiffConfig> &results) { | |
| if (!Core->DerivativeFunctionConfigurations) | |
| return; | |
| auto &ctx = originalAFD->getASTContext(); | |
| Mangle::ASTMangler Mangler; | |
| auto mangledName = Mangler.mangleDeclAsUSR(originalAFD, ""); | |
| auto configs = Core->DerivativeFunctionConfigurations->find(mangledName); | |
| if (configs == Core->DerivativeFunctionConfigurations->end()) | |
| return; | |
| for (auto entry : *configs) { | |
| auto *parameterIndices = IndexSubset::getFromString(ctx, entry.first); | |
| auto derivativeGenSigOrError = getGenericSignatureChecked(entry.second); | |
| if (!derivativeGenSigOrError) { | |
| if (!getContext().LangOpts.EnableDeserializationRecovery) | |
| fatal(derivativeGenSigOrError.takeError()); | |
| llvm::consumeError(derivativeGenSigOrError.takeError()); | |
| } | |
| auto derivativeGenSig = derivativeGenSigOrError.get(); | |
| // NOTE(TF-1038): Result indices are currently unsupported in derivative | |
| // registration attributes. In the meantime, always use `{0}` (wrt the | |
| // first and only result). | |
| auto resultIndices = IndexSubset::get(ctx, 1, {0}); | |
| results.insert({parameterIndices, resultIndices, derivativeGenSig}); | |
| } | |
| } | |
| Optional<Fingerprint> | |
| ModuleFile::loadFingerprint(const IterableDeclContext *IDC) const { | |
| PrettyStackTraceDecl trace("loading fingerprints for", IDC->getDecl()); | |
| assert(IDC->wasDeserialized()); | |
| assert(IDC->getDeclID() != 0); | |
| if (!Core->DeclFingerprints) { | |
| return None; | |
| } | |
| auto it = Core->DeclFingerprints->find(IDC->getDeclID()); | |
| if (it == Core->DeclFingerprints->end()) { | |
| return None; | |
| } | |
| return *it; | |
| } | |
| TinyPtrVector<ValueDecl *> | |
| ModuleFile::loadNamedMembers(const IterableDeclContext *IDC, DeclBaseName N, | |
| uint64_t contextData) { | |
| PrettyStackTraceDecl trace("loading members for", IDC->getDecl()); | |
| assert(IDC->wasDeserialized()); | |
| assert(Core->DeclMemberNames); | |
| TinyPtrVector<ValueDecl *> results; | |
| auto i = Core->DeclMemberNames->find(N); | |
| if (i == Core->DeclMemberNames->end()) | |
| return results; | |
| BitOffset subTableOffset = *i; | |
| std::unique_ptr<SerializedDeclMembersTable> &subTable = | |
| DeclMembersTables[subTableOffset]; | |
| if (!subTable) { | |
| BCOffsetRAII restoreOffset(DeclMemberTablesCursor); | |
| fatalIfNotSuccess(DeclMemberTablesCursor.JumpToBit(subTableOffset)); | |
| llvm::BitstreamEntry entry = | |
| fatalIfUnexpected(DeclMemberTablesCursor.advance()); | |
| if (entry.Kind != llvm::BitstreamEntry::Record) { | |
| fatal(); | |
| return results; | |
| } | |
| SmallVector<uint64_t, 64> scratch; | |
| StringRef blobData; | |
| unsigned kind = fatalIfUnexpected( | |
| DeclMemberTablesCursor.readRecord(entry.ID, scratch, &blobData)); | |
| assert(kind == decl_member_tables_block::DECL_MEMBERS); | |
| (void)kind; | |
| subTable = Core->readDeclMembersTable(scratch, blobData); | |
| } | |
| assert(subTable); | |
| auto j = subTable->find(IDC->getDeclID()); | |
| if (j != subTable->end()) { | |
| for (DeclID d : *j) { | |
| Expected<Decl *> mem = getDeclChecked(d); | |
| if (mem) { | |
| assert(mem.get() && "unchecked error deserializing named member"); | |
| if (auto MVD = dyn_cast<ValueDecl>(mem.get())) { | |
| results.push_back(MVD); | |
| } | |
| } else { | |
| if (!getContext().LangOpts.EnableDeserializationRecovery) | |
| fatal(mem.takeError()); | |
| consumeError(mem.takeError()); | |
| } | |
| } | |
| } | |
| return results; | |
| } | |
| void ModuleFile::lookupClassMember(ImportPath::Access accessPath, | |
| DeclName name, | |
| SmallVectorImpl<ValueDecl*> &results) { | |
| PrettyStackTraceModuleFile stackEntry(*this); | |
| assert(accessPath.size() <= 1 && "can only refer to top-level decls"); | |
| if (!Core->ClassMembersForDynamicLookup) | |
| return; | |
| auto iter = Core->ClassMembersForDynamicLookup->find(name.getBaseName()); | |
| if (iter == Core->ClassMembersForDynamicLookup->end()) | |
| return; | |
| if (!accessPath.empty()) { | |
| // As a hack to avoid completely redoing how the module is indexed, we take | |
| // the simple-name-based lookup then filter by the compound name if we have | |
| // one. | |
| if (name.isSimpleName()) { | |
| for (auto item : *iter) { | |
| auto vd = cast<ValueDecl>(getDecl(item.second)); | |
| auto dc = vd->getDeclContext(); | |
| while (!dc->getParent()->isModuleScopeContext()) | |
| dc = dc->getParent(); | |
| if (auto nominal = dc->getSelfNominalTypeDecl()) | |
| if (nominal->getName() == accessPath.front().Item) | |
| results.push_back(vd); | |
| } | |
| } else { | |
| for (auto item : *iter) { | |
| auto vd = cast<ValueDecl>(getDecl(item.second)); | |
| if (!vd->getName().matchesRef(name)) | |
| continue; | |
| auto dc = vd->getDeclContext(); | |
| while (!dc->getParent()->isModuleScopeContext()) | |
| dc = dc->getParent(); | |
| if (auto nominal = dc->getSelfNominalTypeDecl()) | |
| if (nominal->getName() == accessPath.front().Item) | |
| results.push_back(vd); | |
| } | |
| } | |
| return; | |
| } | |
| for (auto item : *iter) { | |
| auto vd = cast<ValueDecl>(getDecl(item.second)); | |
| results.push_back(vd); | |
| } | |
| } | |
| void ModuleFile::lookupClassMembers(ImportPath::Access accessPath, | |
| VisibleDeclConsumer &consumer) { | |
| PrettyStackTraceModuleFile stackEntry(*this); | |
| assert(accessPath.size() <= 1 && "can only refer to top-level decls"); | |
| if (!Core->ClassMembersForDynamicLookup) | |
| return; | |
| if (!accessPath.empty()) { | |
| for (const auto &list : Core->ClassMembersForDynamicLookup->data()) { | |
| for (auto item : list) { | |
| auto vd = cast<ValueDecl>(getDecl(item.second)); | |
| auto dc = vd->getDeclContext(); | |
| while (!dc->getParent()->isModuleScopeContext()) | |
| dc = dc->getParent(); | |
| if (auto nominal = dc->getSelfNominalTypeDecl()) | |
| if (nominal->getName() == accessPath.front().Item) | |
| consumer.foundDecl(vd, DeclVisibilityKind::DynamicLookup, | |
| DynamicLookupInfo::AnyObject); | |
| } | |
| } | |
| return; | |
| } | |
| for (const auto &list : Core->ClassMembersForDynamicLookup->data()) { | |
| for (auto item : list) | |
| consumer.foundDecl(cast<ValueDecl>(getDecl(item.second)), | |
| DeclVisibilityKind::DynamicLookup, | |
| DynamicLookupInfo::AnyObject); | |
| } | |
| } | |
| void ModuleFile::lookupObjCMethods( | |
| ObjCSelector selector, | |
| SmallVectorImpl<AbstractFunctionDecl *> &results) { | |
| // If we don't have an Objective-C method table, there's nothing to do. | |
| if (!Core->ObjCMethods) return; | |
| // Look for all methods in the module file with this selector. | |
| auto known = Core->ObjCMethods->find(selector); | |
| if (known == Core->ObjCMethods->end()) return; | |
| auto found = *known; | |
| for (const auto &result : found) { | |
| // Deserialize the method and add it to the list. | |
| if (auto func = dyn_cast_or_null<AbstractFunctionDecl>( | |
| getDecl(std::get<2>(result)))) | |
| results.push_back(func); | |
| } | |
| } | |
| void ModuleFile::lookupImportedSPIGroups( | |
| const ModuleDecl *importedModule, | |
| llvm::SmallSetVector<Identifier, 4> &spiGroups) const { | |
| for (auto &dep : Dependencies) { | |
| auto depSpis = dep.spiGroups; | |
| if (dep.Import.hasValue() && dep.Import->importedModule == importedModule && | |
| !depSpis.empty()) { | |
| spiGroups.insert(depSpis.begin(), depSpis.end()); | |
| } | |
| } | |
| } | |
| void | |
| ModuleFile::collectLinkLibraries(ModuleDecl::LinkLibraryCallback callback) const { | |
| for (const auto &lib : Core->LinkLibraries) | |
| callback(lib); | |
| if (Core->Bits.IsFramework) | |
| callback(LinkLibrary(Core->Name, LibraryKind::Framework)); | |
| } | |
| void ModuleFile::getTopLevelDecls( | |
| SmallVectorImpl<Decl *> &results, | |
| llvm::function_ref<bool(DeclAttributes)> matchAttributes) { | |
| PrettyStackTraceModuleFile stackEntry(*this); | |
| for (DeclID entry : Core->OrderedTopLevelDecls) { | |
| Expected<Decl *> declOrError = getDeclChecked(entry, matchAttributes); | |
| if (!declOrError) { | |
| if (declOrError.errorIsA<DeclAttributesDidNotMatch>()) { | |
| // Decl rejected by matchAttributes, ignore it. | |
| assert(matchAttributes); | |
| consumeError(declOrError.takeError()); | |
| continue; | |
| } | |
| if (!getContext().LangOpts.EnableDeserializationRecovery) | |
| fatal(declOrError.takeError()); | |
| consumeError(declOrError.takeError()); | |
| continue; | |
| } | |
| results.push_back(declOrError.get()); | |
| } | |
| } | |
| void ModuleFile::getExportedPrespecializations( | |
| SmallVectorImpl<Decl *> &results) { | |
| for (DeclID entry : Core->ExportedPrespecializationDecls) { | |
| Expected<Decl *> declOrError = getDeclChecked(entry); | |
| if (!declOrError) { | |
| if (!getContext().LangOpts.EnableDeserializationRecovery) | |
| fatal(declOrError.takeError()); | |
| consumeError(declOrError.takeError()); | |
| continue; | |
| } | |
| results.push_back(declOrError.get()); | |
| } | |
| } | |
| void ModuleFile::getOperatorDecls(SmallVectorImpl<OperatorDecl *> &results) { | |
| PrettyStackTraceModuleFile stackEntry(*this); | |
| if (!Core->OperatorDecls) | |
| return; | |
| for (auto entry : Core->OperatorDecls->data()) { | |
| for (auto item : entry) | |
| results.push_back(cast<OperatorDecl>(getDecl(item.second))); | |
| } | |
| } | |
| void ModuleFile::getPrecedenceGroups( | |
| SmallVectorImpl<PrecedenceGroupDecl*> &results) { | |
| PrettyStackTraceModuleFile stackEntry(*this); | |
| if (Core->PrecedenceGroupDecls) { | |
| for (auto entry : Core->PrecedenceGroupDecls->data()) { | |
| for (auto item : entry) | |
| results.push_back(cast<PrecedenceGroupDecl>(getDecl(item.second))); | |
| } | |
| } | |
| } | |
| void | |
| ModuleFile::getLocalTypeDecls(SmallVectorImpl<TypeDecl *> &results) { | |
| PrettyStackTraceModuleFile stackEntry(*this); | |
| if (!Core->LocalTypeDecls) | |
| return; | |
| for (auto DeclID : Core->LocalTypeDecls->data()) { | |
| auto TD = cast<TypeDecl>(getDecl(DeclID)); | |
| results.push_back(TD); | |
| } | |
| } | |
| void | |
| ModuleFile::getOpaqueReturnTypeDecls(SmallVectorImpl<OpaqueTypeDecl *> &results) | |
| { | |
| PrettyStackTraceModuleFile stackEntry(*this); | |
| if (!Core->OpaqueReturnTypeDecls) | |
| return; | |
| for (auto DeclID : Core->OpaqueReturnTypeDecls->data()) { | |
| auto TD = cast<OpaqueTypeDecl>(getDecl(DeclID)); | |
| results.push_back(TD); | |
| } | |
| } | |
| void ModuleFile::getDisplayDecls(SmallVectorImpl<Decl *> &results) { | |
| if (UnderlyingModule) | |
| UnderlyingModule->getDisplayDecls(results); | |
| PrettyStackTraceModuleFile stackEntry(*this); | |
| getImportDecls(results); | |
| getTopLevelDecls(results); | |
| } | |
| Optional<CommentInfo> ModuleFile::getCommentForDecl(const Decl *D) const { | |
| assert(D); | |
| // Keep these as assertions instead of early exits to ensure that we are not | |
| // doing extra work. These cases should be handled by clients of this API. | |
| assert(!D->hasClangNode() && | |
| "cannot find comments for Clang decls in Swift modules"); | |
| assert(D->getDeclContext()->getModuleScopeContext() == FileContext && | |
| "Decl is from a different serialized file"); | |
| if (!Core->DeclCommentTable) | |
| return None; | |
| if (D->isImplicit()) | |
| return None; | |
| // Compute the USR. | |
| llvm::SmallString<128> USRBuffer; | |
| llvm::raw_svector_ostream OS(USRBuffer); | |
| if (ide::printDeclUSR(D, OS)) | |
| return None; | |
| return getCommentForDeclByUSR(USRBuffer.str()); | |
| } | |
| Optional<BasicDeclLocs> | |
| ModuleFile::getBasicDeclLocsForDecl(const Decl *D) const { | |
| assert(D); | |
| // Keep these as assertions instead of early exits to ensure that we are not | |
| // doing extra work. These cases should be handled by clients of this API. | |
| assert(!D->hasClangNode() && | |
| "cannot find comments for Clang decls in Swift modules"); | |
| assert(D->getDeclContext()->getModuleScopeContext() == FileContext && | |
| "Decl is from a different serialized file"); | |
| if (!Core->DeclUSRsTable) | |
| return None; | |
| // Future compilers may not provide BasicDeclLocsData anymore. | |
| if (Core->BasicDeclLocsData.empty()) | |
| return None; | |
| if (D->isImplicit()) | |
| return None; | |
| // Compute the USR. | |
| llvm::SmallString<128> USRBuffer; | |
| llvm::raw_svector_ostream OS(USRBuffer); | |
| if (ide::printDeclUSR(D, OS)) | |
| return None; | |
| auto It = Core->DeclUSRsTable->find(OS.str()); | |
| if (It == Core->DeclUSRsTable->end()) | |
| return None; | |
| auto UsrId = *It; | |
| uint32_t NumSize = 4; | |
| // Size of BasicDeclLocs in the buffer. | |
| // FilePathOffset + LocNum * LineColumn | |
| uint32_t LineColumnCount = 3; | |
| uint32_t RecordSize = | |
| NumSize + // Offset into source filename blob | |
| NumSize + // Offset into doc ranges blob | |
| NumSize * 2 * LineColumnCount; // Line/column of: Loc, StartLoc, EndLoc | |
| uint32_t RecordOffset = RecordSize * UsrId; | |
| assert(RecordOffset < Core->BasicDeclLocsData.size()); | |
| assert(Core->BasicDeclLocsData.size() % RecordSize == 0); | |
| BasicDeclLocs Result; | |
| auto *Record = Core->BasicDeclLocsData.data() + RecordOffset; | |
| auto ReadNext = [&Record]() { | |
| return endian::readNext<uint32_t, little, unaligned>(Record); | |
| }; | |
| auto FilePath = Core->SourceLocsTextData.substr(ReadNext()); | |
| size_t TerminatorOffset = FilePath.find('\0'); | |
| assert(TerminatorOffset != StringRef::npos && "unterminated string data"); | |
| Result.SourceFilePath = FilePath.slice(0, TerminatorOffset); | |
| const auto DocRangesOffset = ReadNext(); | |
| if (DocRangesOffset) { | |
| assert(!Core->DocRangesData.empty()); | |
| const auto *Data = Core->DocRangesData.data() + DocRangesOffset; | |
| const auto NumLocs = endian::readNext<uint32_t, little, unaligned>(Data); | |
| assert(NumLocs); | |
| for (uint32_t i = 0; i < NumLocs; ++i) { | |
| LineColumn LC; | |
| LC.Line = endian::readNext<uint32_t, little, unaligned>(Data); | |
| LC.Column = endian::readNext<uint32_t, little, unaligned>(Data); | |
| auto Length = endian::readNext<uint32_t, little, unaligned>(Data); | |
| Result.DocRanges.push_back(std::make_pair(LC, Length)); | |
| } | |
| } | |
| #define READ_FIELD(X) \ | |
| Result.X.Line = ReadNext(); \ | |
| Result.X.Column = ReadNext(); | |
| READ_FIELD(Loc) | |
| READ_FIELD(StartLoc) | |
| READ_FIELD(EndLoc) | |
| #undef READ_FIELD | |
| return Result; | |
| } | |
| const static StringRef Separator = "/"; | |
| Optional<StringRef> ModuleFile::getGroupNameById(unsigned Id) const { | |
| if (!Core->GroupNamesMap) | |
| return None; | |
| const auto &GroupNamesMap = *Core->GroupNamesMap; | |
| auto it = GroupNamesMap.find(Id); | |
| if (it == GroupNamesMap.end()) | |
| return None; | |
| StringRef Original = it->second; | |
| if (Original.empty()) | |
| return None; | |
| auto SepPos = Original.find_last_of(Separator); | |
| assert(SepPos != StringRef::npos && "Cannot find Separator."); | |
| return StringRef(Original.data(), SepPos); | |
| } | |
| Optional<StringRef> ModuleFile::getSourceFileNameById(unsigned Id) const { | |
| if (!Core->GroupNamesMap) | |
| return None; | |
| const auto &GroupNamesMap = *Core->GroupNamesMap; | |
| auto it = GroupNamesMap.find(Id); | |
| if (it == GroupNamesMap.end()) | |
| return None; | |
| StringRef Original = it->second; | |
| if (Original.empty()) | |
| return None; | |
| auto SepPos = Original.find_last_of(Separator); | |
| assert(SepPos != StringRef::npos && "Cannot find Separator."); | |
| auto Start = Original.data() + SepPos + 1; | |
| auto Len = Original.size() - SepPos - 1; | |
| return StringRef(Start, Len); | |
| } | |
| Optional<StringRef> ModuleFile::getGroupNameForDecl(const Decl *D) const { | |
| auto Triple = getCommentForDecl(D); | |
| if (!Triple.hasValue()) { | |
| return None; | |
| } | |
| return getGroupNameById(Triple.getValue().Group); | |
| } | |
| Optional<StringRef> | |
| ModuleFile::getSourceFileNameForDecl(const Decl *D) const { | |
| auto Triple = getCommentForDecl(D); | |
| if (!Triple.hasValue()) { | |
| return None; | |
| } | |
| return getSourceFileNameById(Triple.getValue().Group); | |
| } | |
| Optional<unsigned> | |
| ModuleFile::getSourceOrderForDecl(const Decl *D) const { | |
| auto Triple = getCommentForDecl(D); | |
| if (!Triple.hasValue()) { | |
| return None; | |
| } | |
| return Triple.getValue().SourceOrder; | |
| } | |
| void ModuleFile::collectAllGroups(std::vector<StringRef> &Names) const { | |
| if (!Core->GroupNamesMap) | |
| return; | |
| for (auto It = Core->GroupNamesMap->begin(); It != Core->GroupNamesMap->end(); | |
| ++It) { | |
| StringRef FullGroupName = It->getSecond(); | |
| if (FullGroupName.empty()) | |
| continue; | |
| auto Sep = FullGroupName.find_last_of(Separator); | |
| assert(Sep != StringRef::npos); | |
| auto Group = FullGroupName.substr(0, Sep); | |
| auto Found = std::find(Names.begin(), Names.end(), Group); | |
| if (Found != Names.end()) | |
| continue; | |
| Names.push_back(Group); | |
| } | |
| } | |
| Optional<CommentInfo> | |
| ModuleFile::getCommentForDeclByUSR(StringRef USR) const { | |
| if (!Core->DeclCommentTable) | |
| return None; | |
| // Use the comment cache to preserve the memory that the array of | |
| // `SingleRawComment`s, inside `CommentInfo`, points to, and generally avoid | |
| // allocating memory every time we query `Core->DeclCommentTable`. | |
| auto it = CommentsCache.find(USR); | |
| if (it != CommentsCache.end()) { | |
| const auto &cachePtr = it->second; | |
| if (!cachePtr) | |
| return None; | |
| return cachePtr->Info; | |
| } | |
| auto I = Core->DeclCommentTable->find(USR); | |
| if (I == Core->DeclCommentTable->end()) | |
| return None; | |
| auto &cachePtr = CommentsCache[USR]; | |
| cachePtr = *I; | |
| return cachePtr->Info; | |
| } | |
| Optional<StringRef> | |
| ModuleFile::getGroupNameByUSR(StringRef USR) const { | |
| if (auto Comment = getCommentForDeclByUSR(USR)) { | |
| return getGroupNameById(Comment.getValue().Group); | |
| } | |
| return None; | |
| } | |
| Identifier ModuleFile::getDiscriminatorForPrivateValue(const ValueDecl *D) { | |
| Identifier discriminator = PrivateDiscriminatorsByValue.lookup(D); | |
| assert(!discriminator.empty() && "no discriminator found for decl"); | |
| return discriminator; | |
| } | |
| void ModuleFile::verify() const { | |
| #ifndef NDEBUG | |
| const auto &Context = getContext(); | |
| for (const Serialized<Decl*> &next : Decls) | |
| if (next.isComplete() && swift::shouldVerify(next, Context)) | |
| swift::verify(next); | |
| #endif | |
| } | |
| bool SerializedASTFile::hasEntryPoint() const { | |
| return File.hasEntryPoint(); | |
| } | |
| bool SerializedASTFile::getAllGenericSignatures( | |
| SmallVectorImpl<GenericSignature> &genericSignatures) { | |
| genericSignatures.clear(); | |
| for (unsigned index : indices(File.GenericSignatures)) { | |
| if (auto genericSig = File.getGenericSignature(index + 1)) | |
| genericSignatures.push_back(genericSig); | |
| } | |
| return true; | |
| } | |
| Decl *SerializedASTFile::getMainDecl() const { | |
| assert(hasEntryPoint()); | |
| return File.getDecl(File.getEntryPointDeclID()); | |
| } | |
| const version::Version &SerializedASTFile::getLanguageVersionBuiltWith() const { | |
| return File.getCompatibilityVersion(); | |
| } | |
| StringRef SerializedASTFile::getModuleDefiningPath() const { | |
| StringRef moduleFilename = getFilename(); | |
| StringRef parentDir = llvm::sys::path::parent_path(moduleFilename); | |
| if (llvm::sys::path::extension(parentDir) == ".swiftmodule") | |
| return parentDir; | |
| return moduleFilename; | |
| } |