//===--- Utils.cpp - Misc utilities ---------------------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// #include "swift/IDE/Utils.h" #include "swift/Basic/Fallthrough.h" #include "swift/Basic/SourceManager.h" #include "swift/ClangImporter/ClangModule.h" #include "swift/Frontend/Frontend.h" #include "swift/Parse/Parser.h" #include "swift/Subsystems.h" #include "clang/AST/ASTContext.h" #include "clang/AST/DeclObjC.h" #include "clang/CodeGen/ObjectFilePCHContainerOperations.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/TextDiagnosticBuffer.h" #include "clang/Serialization/ASTReader.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" using namespace swift; using namespace ide; static const char *skipStringInCode(const char *p, const char *End); static const char *skipParenExpression(const char *p, const char *End) { const char *e = p; if (*e == '(') { uint32_t ParenCount = 1; bool done = false; for (++e; e < End; ++e) { switch (*e) { case ')': done = --ParenCount == 0; break; case '(': ++ParenCount; break; case '"': e = skipStringInCode (e, End); break; default: break; } // If "done" is true make sure we don't increment "e" if (done) break; } } if (e >= End) return End; return e; } static const char *skipStringInCode(const char *p, const char *End) { const char *e = p; if (*e == '"') { bool done = false; for (++e; e < End; ++e) { switch (*e) { case '"': done = true; break; case '\\': ++e; if (e >= End) done = true; else if (*e == '(') e = skipParenExpression (e, End); break; default: break; } // If "done" is true make sure we don't increment "e" if (done) break; } } if (e >= End) return End; return e; } SourceCompleteResult ide::isSourceInputComplete(std::unique_ptr MemBuf) { SourceManager SM; auto BufferID = SM.addNewSourceBuffer(std::move(MemBuf)); ParserUnit Parse(SM, BufferID); Parser &P = Parse.getParser(); bool Done; do { P.parseTopLevel(); Done = P.Tok.is(tok::eof); } while (!Done); SourceCompleteResult SCR; SCR.IsComplete = !P.isInputIncomplete(); // Use the same code that was in the REPL code to track the indent level // for now. In the future we should get this from the Parser if possible. CharSourceRange entireRange = SM.getRangeForBuffer(BufferID); StringRef Buffer = SM.extractText(entireRange); const char *SourceStart = Buffer.data(); const char *SourceEnd = Buffer.data() + Buffer.size(); const char *LineStart = SourceStart; const char *LineSourceStart = NULL; uint32_t LineIndent = 0; struct IndentInfo { StringRef Prefix; uint32_t Indent; IndentInfo(const char *s, size_t n, uint32_t i) : Prefix(s, n), Indent(i) {} }; SmallVector IndentInfos; for (const char *p = SourceStart; p 0) --LineIndent; if (!IndentInfos.empty()) IndentInfos.pop_back(); break; default: if (LineSourceStart == NULL && !isspace(*p)) LineSourceStart = p; break; } if (*p == '\0') break; } if (!IndentInfos.empty()) { SCR.IndentPrefix = IndentInfos.back().Prefix.str(); // Trim off anything that follows a non-space character const size_t pos = SCR.IndentPrefix.find_first_not_of(" \t"); if (pos != std::string::npos) SCR.IndentPrefix.erase(pos); SCR.IndentLevel = IndentInfos.back().Indent; } return SCR; } SourceCompleteResult ide::isSourceInputComplete(StringRef Text) { return ide::isSourceInputComplete(llvm::MemoryBuffer::getMemBufferCopy(Text)); } // Adjust the cc1 triple string we got from clang, to make sure it will be // accepted when it goes throught the swift clang importer. static std::string adjustClangTriple(StringRef TripleStr) { std::string Result; llvm::raw_string_ostream OS(Result); llvm::Triple Triple(TripleStr); switch (Triple.getSubArch()) { case llvm::Triple::SubArchType::ARMSubArch_v7: OS << "armv7"; break; case llvm::Triple::SubArchType::ARMSubArch_v7s: OS << "armv7s"; break; case llvm::Triple::SubArchType::ARMSubArch_v7k: OS << "armv7k"; break; default: // Adjust i386-macosx to x86_64 because there is no Swift stdlib for i386. if ((Triple.getOS() == llvm::Triple::MacOSX || Triple.getOS() == llvm::Triple::Darwin) && Triple.getArch() == llvm::Triple::x86) { OS << "x86_64"; } else { OS << Triple.getArchName(); } break; } OS << '-' << Triple.getVendorName() << '-' << Triple.getOSName(); OS.flush(); return Result; } bool ide::initInvocationByClangArguments(ArrayRef ArgList, CompilerInvocation &Invok, std::string &Error) { llvm::IntrusiveRefCntPtr DiagOpts{ new clang::DiagnosticOptions() }; clang::TextDiagnosticBuffer DiagBuf; llvm::IntrusiveRefCntPtr ClangDiags = clang::CompilerInstance::createDiagnostics(DiagOpts.get(), &DiagBuf, /*ShouldOwnClient=*/false); // Create a new Clang compiler invocation. llvm::IntrusiveRefCntPtr ClangInvok{ clang::createInvocationFromCommandLine(ArgList, ClangDiags) }; if (!ClangInvok || ClangDiags->hasErrorOccurred()) { for (auto I = DiagBuf.err_begin(), E = DiagBuf.err_end(); I != E; ++I) { Error += I->second; Error += " "; } return true; } auto &PPOpts = ClangInvok->getPreprocessorOpts(); auto &HSOpts = ClangInvok->getHeaderSearchOpts(); Invok.setTargetTriple(adjustClangTriple(ClangInvok->getTargetOpts().Triple)); if (!HSOpts.Sysroot.empty()) Invok.setSDKPath(HSOpts.Sysroot); if (!HSOpts.ModuleCachePath.empty()) Invok.setClangModuleCachePath(HSOpts.ModuleCachePath); auto &CCArgs = Invok.getClangImporterOptions().ExtraArgs; for (auto MacroEntry : PPOpts.Macros) { std::string MacroFlag; if (MacroEntry.second) MacroFlag += "-U"; else MacroFlag += "-D"; MacroFlag += MacroEntry.first; CCArgs.push_back(MacroFlag); } for (auto &Entry : HSOpts.UserEntries) { switch (Entry.Group) { case clang::frontend::Quoted: CCArgs.push_back("-iquote"); CCArgs.push_back(Entry.Path); break; case clang::frontend::IndexHeaderMap: CCArgs.push_back("-index-header-map"); SWIFT_FALLTHROUGH; case clang::frontend::Angled: { std::string Flag; if (Entry.IsFramework) Flag += "-F"; else Flag += "-I"; Flag += Entry.Path; CCArgs.push_back(Flag); break; } case clang::frontend::System: if (Entry.IsFramework) CCArgs.push_back("-iframework"); else CCArgs.push_back("-isystem"); CCArgs.push_back(Entry.Path); break; case clang::frontend::ExternCSystem: case clang::frontend::CSystem: case clang::frontend::CXXSystem: case clang::frontend::ObjCSystem: case clang::frontend::ObjCXXSystem: case clang::frontend::After: break; } } if (!PPOpts.ImplicitPCHInclude.empty()) { clang::FileSystemOptions FileSysOpts; clang::FileManager FileMgr(FileSysOpts); auto PCHContainerOperations = std::make_shared(); std::string HeaderFile = clang::ASTReader::getOriginalSourceFile( PPOpts.ImplicitPCHInclude, FileMgr, PCHContainerOperations->getRawReader(), *ClangDiags); if (!HeaderFile.empty()) { CCArgs.push_back("-include"); CCArgs.push_back(std::move(HeaderFile)); } } for (auto &Header : PPOpts.Includes) { CCArgs.push_back("-include"); CCArgs.push_back(Header); } for (auto &Entry : HSOpts.ModulesIgnoreMacros) { std::string Flag = "-fmodules-ignore-macro="; Flag += Entry; CCArgs.push_back(Flag); } for (auto &Entry : HSOpts.VFSOverlayFiles) { CCArgs.push_back("-ivfsoverlay"); CCArgs.push_back(Entry); } if (!ClangInvok->getLangOpts()->ImplementationOfModule.empty()) { CCArgs.push_back("-Xclang"); CCArgs.push_back("-fmodule-implementation-of"); CCArgs.push_back("-Xclang"); CCArgs.push_back(ClangInvok->getLangOpts()->ImplementationOfModule); } if (PPOpts.DetailedRecord) { Invok.getClangImporterOptions().DetailedPreprocessingRecord = true; } if (!ClangInvok->getFrontendOpts().Inputs.empty()) { Invok.getFrontendOptions().ImplicitObjCHeaderPath = ClangInvok->getFrontendOpts().Inputs[0].getFile(); } return false; } template static void walkOverriddenClangDecls(const clang::NamedDecl *D, const FnTy &Fn){ SmallVector OverDecls; D->getASTContext().getOverriddenMethods(D, OverDecls); for (auto Over : OverDecls) Fn(Over); for (auto Over : OverDecls) walkOverriddenClangDecls(Over, Fn); } void ide::walkOverriddenDecls(const ValueDecl *VD, std::function)> Fn) { for (auto CurrOver = VD; CurrOver; CurrOver = CurrOver->getOverriddenDecl()) { if (CurrOver != VD) Fn(CurrOver); if (auto ClangD = dyn_cast_or_null(CurrOver->getClangDecl())) { walkOverriddenClangDecls(ClangD, Fn); return; } for (auto Conf: CurrOver->getSatisfiedProtocolRequirements()) Fn(Conf); } } /// \returns true if a placeholder was found. static bool findPlaceholder(StringRef Input, PlaceholderOccurrence &Occur) { while (true) { size_t Pos = Input.find("<#"); if (Pos == StringRef::npos) return false; const char *Begin = Input.begin() + Pos; const char *Ptr = Begin + 2; const char *End = Input.end(); for (; Ptr < End-1; ++Ptr) { if (*Ptr == '\n') break; if (Ptr[0] == '<' && Ptr[1] == '#') break; if (Ptr[0] == '#' && Ptr[1] == '>') { // Found it. Occur.FullPlaceholder = Input.substr(Pos, Ptr-Begin + 2); Occur.PlaceholderContent = Occur.FullPlaceholder.drop_front(2).drop_back(2); return true; } } // Try again. Input = Input.substr(Ptr - Input.begin()); } } std::unique_ptr ide::replacePlaceholders(std::unique_ptr InputBuf, llvm::function_ref Callback) { StringRef Input = InputBuf->getBuffer(); PlaceholderOccurrence Occur; bool Found = findPlaceholder(Input, Occur); if (!Found) return InputBuf; std::unique_ptr NewBuf = llvm::MemoryBuffer::getMemBufferCopy(InputBuf->getBuffer(), InputBuf->getBufferIdentifier()); unsigned Counter = 0; auto replacePlaceholder = [&](PlaceholderOccurrence &Occur) { llvm::SmallString<10> Id; Id = "$_"; llvm::raw_svector_ostream(Id) << (Counter++); assert(Occur.FullPlaceholder.size() >= 2); if (Id.size() > Occur.FullPlaceholder.size()) { // The identifier+counter exceeds placeholder size; replace it without // using the counter. Id = "$"; Id.append(Occur.FullPlaceholder.size()-1, '_'); } else { Id.append(Occur.FullPlaceholder.size()-Id.size(), '_'); } assert(Id.size() == Occur.FullPlaceholder.size()); unsigned Offset = Occur.FullPlaceholder.data() - InputBuf->getBufferStart(); char *Ptr = (char*)NewBuf->getBufferStart() + Offset; std::copy(Id.begin(), Id.end(), Ptr); Occur.IdentifierReplacement = Id.str(); Callback(Occur); }; while (true) { replacePlaceholder(Occur); unsigned Offset = Occur.FullPlaceholder.data() - InputBuf->getBufferStart(); Input = InputBuf->getBuffer().substr(Offset+Occur.FullPlaceholder.size()); bool Found = findPlaceholder(Input, Occur); if (!Found) break; } return NewBuf; } std::unique_ptr ide::replacePlaceholders(std::unique_ptr InputBuf, bool *HadPlaceholder) { if (HadPlaceholder) *HadPlaceholder = false; return replacePlaceholders(std::move(InputBuf), [&](const PlaceholderOccurrence &){ if (HadPlaceholder) *HadPlaceholder = true; }); } static std::string getPlistEntry(const llvm::Twine &Path, StringRef KeyName) { auto BufOrErr = llvm::MemoryBuffer::getFile(Path); if (!BufOrErr) { llvm::errs() << BufOrErr.getError().message() << '\n'; return {}; } std::string Key = ""; Key += KeyName; Key += ""; StringRef Lines = BufOrErr.get()->getBuffer(); while (!Lines.empty()) { StringRef CurLine; std::tie(CurLine, Lines) = Lines.split('\n'); if (CurLine.find(Key) != StringRef::npos) { std::tie(CurLine, Lines) = Lines.split('\n'); unsigned Begin = CurLine.find("") + strlen(""); unsigned End = CurLine.find(""); return CurLine.substr(Begin, End-Begin); } } return {}; } std::string ide::getSDKName(StringRef Path) { std::string Name = getPlistEntry(llvm::Twine(Path)+"/SDKSettings.plist", "CanonicalName"); if (Name.empty() && Path.endswith(".sdk")) { Name = llvm::sys::path::filename(Path).drop_back(strlen(".sdk")); } return Name; } std::string ide::getSDKVersion(StringRef Path) { return getPlistEntry(llvm::Twine(Path)+"/System/Library/CoreServices/" "SystemVersion.plist", "ProductBuildVersion"); } // Modules failing to load are commented-out. static const char *OSXModuleList[] = { "AGL", "AVFoundation", "AVKit", "Accelerate", "Accounts", "AddressBook", "AppKit", "AppKitScripting", "AppleScriptKit", "AppleScriptObjC", "ApplicationServices", "AudioToolbox", "AudioUnit", "AudioVideoBridging", "Automator", "CFNetwork", // "CalendarStore", "Carbon", "CloudKit", "Cocoa", "Collaboration", "Contacts", "CoreAudio", "CoreAudioKit", "CoreBluetooth", "CoreData", "CoreFoundation", "CoreGraphics", "CoreImage", "CoreLocation", "CoreMIDI", // "CoreMIDIServer", "CoreMedia", "CoreMediaIO", "CoreServices", "CoreTelephony", "CoreText", "CoreVideo", "CoreWLAN", // "CryptoTokenKit", // "DVComponentGlue", "DVDPlayback", "Darwin", "DirectoryService", "DiscRecording", "DiscRecordingUI", "DiskArbitration", "Dispatch", // "DrawSprocket", "EventKit", "ExceptionHandling", "FWAUserLib", "FinderSync", "ForceFeedback", "Foundation", "GLKit", "GLUT", "GSS", "GameController", "GameKit", "GameplayKit", "Hypervisor", // "ICADevices", "IMServicePlugIn", "IOBluetooth", "IOBluetoothUI", "IOKit", "IOSurface", "ImageCaptureCore", "ImageIO", "InputMethodKit", // "InstallerPlugins", // "InstantMessage", // "JavaFrameEmbedding", "JavaScriptCore", // "JavaVM", // "Kerberos", // "LDAP", "LatentSemanticMapping", "LocalAuthentication", "MachO", "MapKit", "MediaAccessibility", "MediaLibrary", "MediaToolbox", // "Message", "Metal", "MetalKit", "ModelIO", "MultipeerConnectivity", "NetFS", // "NetworkExtension", "NotificationCenter", "OSAKit", "ObjectiveC", "OpenAL", "OpenCL", "OpenDirectory", "OpenGL", // "PCSC", "PreferencePanes", "PubSub", "Python", // "QTKit", QTKit is unavailable on Swift. "Quartz", "QuartzCore", "QuickLook", "QuickTime", // "Ruby", "SceneKit", "ScreenSaver", "Scripting", "ScriptingBridge", "Security", "SecurityFoundation", "SecurityInterface", // "ServiceManagement", "Social", "SpriteKit", "StoreKit", // "SyncServices", "SystemConfiguration", "TWAIN", "Tcl", // "VideoDecodeAcceleration", "VideoToolbox", "WebKit", "XPC", "libkern", "os", // "vecLib", "vmnet", }; // Modules failing to load are commented-out. static const char *iOSModuleList[] = { "AVFoundation", "AVKit", "Accelerate", "Accounts", "AdSupport", "AddressBook", "AddressBookUI", "AssetsLibrary", "AudioToolbox", "AudioUnit", "CFNetwork", "CloudKit", "Contacts", "ContactsUI", "CoreAudio", "CoreAudioKit", "CoreBluetooth", "CoreData", "CoreFoundation", "CoreGraphics", "CoreImage", "CoreLocation", "CoreMIDI", "CoreMedia", "CoreMotion", "CoreSpotlight", "CoreTelephony", "CoreText", "CoreVideo", "Darwin", "Dispatch", "EventKit", "EventKitUI", "ExternalAccessory", "Foundation", "GLKit", "GSS", "GameController", "GameFoundation", "GameKit", "GameplayKit", "HealthKit", "HomeKit", "IMCommonCore", // "IOKit", "ImageIO", "JavaScriptCore", "LocalAuthentication", "MachO", "MapKit", "MediaAccessibility", "MediaPlayer", "MediaToolbox", "MessageUI", "MobileCoreServices", "ModelIO", "MultipeerConnectivity", "NetworkExtension", "NewsstandKit", "NotificationCenter", "ObjectiveC", "OpenAL", "OpenGLES", "PassKit", "Photos", "PhotosUI", "PushKit", "QuartzCore", "QuickLook", "SafariServices", "SceneKit", "ScreenRecorder", "Security", "Social", "SpriteKit", "StoreKit", "SystemConfiguration", "Twitter", "UIKit", "UIKit.UIGestureRecognizerSubclass", "VideoToolbox", "WatchConnectivity", "WatchKit", "WebKit", "iAd", "libkern", "os", }; static const char *DeviceOnlyModuleList[] = { "Metal", "MetalKit", "MetalShaders", }; static ArrayRef getOSXModuleList() { return OSXModuleList; } static ArrayRef getiOSModuleList() { return iOSModuleList; } static ArrayRef getDeviceOnlyModuleList() { return DeviceOnlyModuleList; } void ide::collectModuleNames(StringRef SDKPath, std::vector &Modules) { std::string SDKName = getSDKName(SDKPath); std::string lowerSDKName = StringRef(SDKName).lower(); bool isOSXSDK = StringRef(lowerSDKName).find("macosx") != StringRef::npos; bool isDeviceOnly = StringRef(lowerSDKName).find("iphoneos") != StringRef::npos; auto Mods = isOSXSDK ? getOSXModuleList() : getiOSModuleList(); Modules.insert(Modules.end(), Mods.begin(), Mods.end()); if (isDeviceOnly) { Mods = getDeviceOnlyModuleList(); Modules.insert(Modules.end(), Mods.begin(), Mods.end()); } }