diff --git a/include/clang/APINotes/APINotesManager.h b/include/clang/APINotes/APINotesManager.h index a300c14ee9d..2adc29c0bf4 100644 --- a/include/clang/APINotes/APINotesManager.h +++ b/include/clang/APINotes/APINotesManager.h @@ -16,6 +16,7 @@ #include "clang/Basic/SourceLocation.h" #include "clang/Basic/Module.h" +#include "clang/Basic/VersionTuple.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/PointerUnion.h" @@ -55,6 +56,9 @@ class APINotesManager { /// source file from which an entity was declared. bool ImplicitAPINotes; + /// The Swift version to use when interpreting versioned API notes. + VersionTuple SwiftVersion; + /// API notes readers for the current module. /// /// There can be up to two of these, one for public headers and one @@ -109,6 +113,11 @@ class APINotesManager { APINotesManager(SourceManager &sourceMgr, const LangOptions &langOpts); ~APINotesManager(); + /// Set the Swift version to use when filtering API notes. + void setSwiftVersion(VersionTuple swiftVersion) { + SwiftVersion = swiftVersion; + } + /// Load the API notes for the current module. /// /// \param module The current module. diff --git a/include/clang/APINotes/APINotesOptions.h b/include/clang/APINotes/APINotesOptions.h index 01c7513a1d3..24bb9134b21 100644 --- a/include/clang/APINotes/APINotesOptions.h +++ b/include/clang/APINotes/APINotesOptions.h @@ -14,6 +14,7 @@ #ifndef LLVM_CLANG_APINOTES_APINOTESOPTIONS_H #define LLVM_CLANG_APINOTES_APINOTESOPTIONS_H +#include "clang/Basic/VersionTuple.h" #include #include @@ -23,6 +24,9 @@ namespace clang { /// notes are found and handled. class APINotesOptions { public: + /// The Swift version which should be used for API notes. + VersionTuple SwiftVersion; + /// The set of search paths where we API notes can be found for /// particular modules. /// diff --git a/include/clang/APINotes/APINotesReader.h b/include/clang/APINotes/APINotesReader.h index aa88bac0626..a09bcc76df1 100644 --- a/include/clang/APINotes/APINotesReader.h +++ b/include/clang/APINotes/APINotesReader.h @@ -17,6 +17,7 @@ #define LLVM_CLANG_API_NOTES_READER_H #include "clang/APINotes/Types.h" +#include "clang/Basic/VersionTuple.h" #include "llvm/ADT/Optional.h" #include "llvm/Support/MemoryBuffer.h" #include @@ -32,7 +33,7 @@ class APINotesReader { Implementation &Impl; APINotesReader(llvm::MemoryBuffer *inputBuffer, bool ownsInputBuffer, - bool &failed); + VersionTuple swiftVersion, bool &failed); public: /// Create a new API notes reader from the given member buffer, which @@ -40,14 +41,16 @@ class APINotesReader { /// /// \returns the new API notes reader, or null if an error occurred. static std::unique_ptr - get(std::unique_ptr inputBuffer); + get(std::unique_ptr inputBuffer, + VersionTuple swiftVersion); /// Create a new API notes reader from the given member buffer, which /// contains the contents of a binary API notes file. /// /// \returns the new API notes reader, or null if an error occurred. static std::unique_ptr - getUnmanaged(llvm::MemoryBuffer *inputBuffer); + getUnmanaged(llvm::MemoryBuffer *inputBuffer, + VersionTuple swiftVersion); ~APINotesReader(); @@ -65,21 +68,83 @@ class APINotesReader { /// Retrieve the module options ModuleOptions getModuleOptions() const; + /// Captures the completed versioned information for a particular part of + /// API notes, including both unversioned API notes and each versioned API + /// note for that particular entity. + template + class VersionedInfo { + /// The complete set of results. + SmallVector, 1> Results; + + /// The index of the result that is the "selected" set based on the desired + /// Swift version, or \c Results.size() if nothing matched. + unsigned Selected; + + public: + /// Form an empty set of versioned information. + VersionedInfo(llvm::NoneType) : Selected(0) { } + + /// Form a versioned info set given the desired version and a set of + /// results. + VersionedInfo(VersionTuple version, + SmallVector, 1> results); + + /// Determine whether there is a result that should be applied directly + /// to the AST. + explicit operator bool() const { return Selected != size(); } + + /// Retrieve the information to apply directly to the AST. + const T& operator*() const { + assert(*this && "No result to apply directly"); + return (*this)[Selected].second; + } + + /// Retrieve the selected index in the result set. + Optional getSelected() const { + if (Selected == Results.size()) return None; + return Selected; + } + + /// Return the number of versioned results we know about. + unsigned size() const { return Results.size(); } + + /// Access all versioned results. + const std::pair *begin() const { return Results.begin(); } + const std::pair *end() const { return Results.end(); } + + /// Access a specific versioned result. + const std::pair &operator[](unsigned index) const { + return Results[index]; + } + }; + + /// Look for the context ID of the given Objective-C class. + /// + /// \param name The name of the class we're looking for. + /// + /// \returns The ID, if known. + Optional lookupObjCClassID(StringRef name); + /// Look for information regarding the given Objective-C class. /// /// \param name The name of the class we're looking for. /// - /// \returns The ID and information about the class, if known. - Optional> - lookupObjCClass(StringRef name); + /// \returns The information about the class, if known. + VersionedInfo lookupObjCClassInfo(StringRef name); + + /// Look for the context ID of the given Objective-C protocol. + /// + /// \param name The name of the protocol we're looking for. + /// + /// \returns The ID of the protocol, if known. + Optional lookupObjCProtocolID(StringRef name); /// Look for information regarding the given Objective-C protocol. /// /// \param name The name of the protocol we're looking for. /// - /// \returns The ID and information about the protocol, if known. - Optional> - lookupObjCProtocol(StringRef name); + /// \returns The information about the protocol, if known. + VersionedInfo lookupObjCProtocolInfo(StringRef name); /// Look for information regarding the given Objective-C property in /// the given context. @@ -88,11 +153,12 @@ class APINotesReader { /// \param name The name of the property we're looking for. /// \param isInstance Whether we are looking for an instance property (vs. /// a class property). + /// \param swiftVersion The Swift version to filter for, if any. /// /// \returns Information about the property, if known. - Optional lookupObjCProperty(ContextID contextID, - StringRef name, - bool isInstance); + VersionedInfo lookupObjCProperty(ContextID contextID, + StringRef name, + bool isInstance); /// Look for information regarding the given Objective-C method in /// the given context. @@ -102,30 +168,30 @@ class APINotesReader { /// \param isInstanceMethod Whether we are looking for an instance method. /// /// \returns Information about the method, if known. - Optional lookupObjCMethod(ContextID contextID, - ObjCSelectorRef selector, - bool isInstanceMethod); + VersionedInfo lookupObjCMethod(ContextID contextID, + ObjCSelectorRef selector, + bool isInstanceMethod); /// Look for information regarding the given global variable. /// /// \param name The name of the global variable. /// /// \returns information about the global variable, if known. - Optional lookupGlobalVariable(StringRef name); + VersionedInfo lookupGlobalVariable(StringRef name); /// Look for information regarding the given global function. /// /// \param name The name of the global function. /// /// \returns information about the global function, if known. - Optional lookupGlobalFunction(StringRef name); + VersionedInfo lookupGlobalFunction(StringRef name); /// Look for information regarding the given enumerator. /// /// \param name The name of the enumerator. /// /// \returns information about the enumerator, if known. - Optional lookupEnumConstant(StringRef name); + VersionedInfo lookupEnumConstant(StringRef name); /// Look for information regarding the given tag /// (struct/union/enum/C++ class). @@ -133,14 +199,14 @@ class APINotesReader { /// \param name The name of the tag. /// /// \returns information about the tag, if known. - Optional lookupTag(StringRef name); + VersionedInfo lookupTag(StringRef name); /// Look for information regarding the given typedef. /// /// \param name The name of the typedef. /// /// \returns information about the typedef, if known. - Optional lookupTypedef(StringRef name); + VersionedInfo lookupTypedef(StringRef name); /// Visitor used when walking the contents of the API notes file. class Visitor { @@ -149,39 +215,48 @@ class APINotesReader { /// Visit an Objective-C class. virtual void visitObjCClass(ContextID contextID, StringRef name, - const ObjCContextInfo &info); + const ObjCContextInfo &info, + VersionTuple swiftVersion); /// Visit an Objective-C protocol. virtual void visitObjCProtocol(ContextID contextID, StringRef name, - const ObjCContextInfo &info); + const ObjCContextInfo &info, + VersionTuple swiftVersion); /// Visit an Objective-C method. virtual void visitObjCMethod(ContextID contextID, StringRef selector, bool isInstanceMethod, - const ObjCMethodInfo &info); + const ObjCMethodInfo &info, + VersionTuple swiftVersion); /// Visit an Objective-C property. virtual void visitObjCProperty(ContextID contextID, StringRef name, bool isInstance, - const ObjCPropertyInfo &info); + const ObjCPropertyInfo &info, + VersionTuple swiftVersion); /// Visit a global variable. virtual void visitGlobalVariable(StringRef name, - const GlobalVariableInfo &info); + const GlobalVariableInfo &info, + VersionTuple swiftVersion); /// Visit a global function. virtual void visitGlobalFunction(StringRef name, - const GlobalFunctionInfo &info); + const GlobalFunctionInfo &info, + VersionTuple swiftVersion); /// Visit an enumerator. virtual void visitEnumConstant(StringRef name, - const EnumConstantInfo &info); + const EnumConstantInfo &info, + VersionTuple swiftVersion); /// Visit a tag. - virtual void visitTag(StringRef name, const TagInfo &info); + virtual void visitTag(StringRef name, const TagInfo &info, + VersionTuple swiftVersion); /// Visit a typedef. - virtual void visitTypedef(StringRef name, const TypedefInfo &info); + virtual void visitTypedef(StringRef name, const TypedefInfo &info, + VersionTuple swiftVersion); }; /// Visit the contents of the API notes file, passing each entity to the diff --git a/include/clang/APINotes/APINotesWriter.h b/include/clang/APINotes/APINotesWriter.h index 4bf3ce13716..62defc1f944 100644 --- a/include/clang/APINotes/APINotesWriter.h +++ b/include/clang/APINotes/APINotesWriter.h @@ -16,6 +16,7 @@ #ifndef LLVM_CLANG_API_NOTES_WRITER_H #define LLVM_CLANG_API_NOTES_WRITER_H +#include "clang/Basic/VersionTuple.h" #include "clang/APINotes/Types.h" namespace llvm { @@ -46,23 +47,17 @@ class APINotesWriter { /// Write the API notes data to the given stream. void writeToStream(llvm::raw_ostream &os); - /// Add information about a specific Objective-C class. + /// Add information about a specific Objective-C class or protocol. /// - /// \param name The name of this class. - /// \param info Information about this class. + /// \param name The name of this class/protocol. + /// \param isClass Whether this is a class (vs. a protocol). + /// \param info Information about this class/protocol. /// - /// \returns the ID of the class, which can be used to add properties and - /// methods to the class. - ContextID addObjCClass(StringRef name, const ObjCContextInfo &info); - - /// Add information about a specific Objective-C protocol. - /// - /// \param name The name of this protocol. - /// \param info Information about this protocol. - /// - /// \returns the ID of the protocol, which can be used to add properties and - /// methods to the protocol. - ContextID addObjCProtocol(StringRef name, const ObjCContextInfo &info); + /// \returns the ID of the class or protocol, which can be used to add + /// properties and methods to the class/protocol. + ContextID addObjCContext(StringRef name, bool isClass, + const ObjCContextInfo &info, + VersionTuple swiftVersion); /// Add information about a specific Objective-C property. /// @@ -71,7 +66,8 @@ class APINotesWriter { /// \param info Information about this property. void addObjCProperty(ContextID contextID, StringRef name, bool isInstanceProperty, - const ObjCPropertyInfo &info); + const ObjCPropertyInfo &info, + VersionTuple swiftVersion); /// Add information about a specific Objective-C method. /// @@ -81,37 +77,43 @@ class APINotesWriter { /// (vs. a class method). /// \param info Information about this method. void addObjCMethod(ContextID contextID, ObjCSelectorRef selector, - bool isInstanceMethod, const ObjCMethodInfo &info); + bool isInstanceMethod, const ObjCMethodInfo &info, + VersionTuple swiftVersion); /// Add information about a global variable. /// /// \param name The name of this global variable. /// \param info Information about this global variable. - void addGlobalVariable(StringRef name, const GlobalVariableInfo &info); + void addGlobalVariable(StringRef name, const GlobalVariableInfo &info, + VersionTuple swiftVersion); /// Add information about a global function. /// /// \param name The name of this global function. /// \param info Information about this global function. - void addGlobalFunction(StringRef name, const GlobalFunctionInfo &info); + void addGlobalFunction(StringRef name, const GlobalFunctionInfo &info, + VersionTuple swiftVersion); /// Add information about an enumerator. /// /// \param name The name of this enumerator. /// \param info Information about this enumerator. - void addEnumConstant(StringRef name, const EnumConstantInfo &info); + void addEnumConstant(StringRef name, const EnumConstantInfo &info, + VersionTuple swiftVersion); /// Add information about a tag (struct/union/enum/C++ class). /// /// \param name The name of this tag. /// \param info Information about this tag. - void addTag(StringRef name, const TagInfo &info); + void addTag(StringRef name, const TagInfo &info, + VersionTuple swiftVersion); /// Add information about a typedef. /// /// \param name The name of this typedef. /// \param info Information about this typedef. - void addTypedef(StringRef name, const TypedefInfo &info); + void addTypedef(StringRef name, const TypedefInfo &info, + VersionTuple swiftVersion); /// Add module options void addModuleOptions(ModuleOptions opts); diff --git a/include/clang/Basic/VersionTuple.h b/include/clang/Basic/VersionTuple.h index da3b01903ed..07315f008cd 100644 --- a/include/clang/Basic/VersionTuple.h +++ b/include/clang/Basic/VersionTuple.h @@ -17,6 +17,7 @@ #include "clang/Basic/LLVM.h" #include "llvm/ADT/Optional.h" +#include "llvm/ADT/DenseMapInfo.h" #include #include @@ -70,6 +71,9 @@ class VersionTuple { return Major == 0 && Minor == 0 && Subminor == 0 && Build == 0; } + /// Whether this is a non-empty version tuple. + explicit operator bool () const { return !empty(); } + /// \brief Retrieve the major version number. unsigned getMajor() const { return Major; } @@ -165,4 +169,35 @@ class VersionTuple { raw_ostream& operator<<(raw_ostream &Out, const VersionTuple &V); } // end namespace clang + +namespace llvm { + // Provide DenseMapInfo for version tuples. + template<> + struct DenseMapInfo { + static inline clang::VersionTuple getEmptyKey() { + return clang::VersionTuple(0x7FFFFFFF); + } + static inline clang::VersionTuple getTombstoneKey() { + return clang::VersionTuple(0x7FFFFFFE); + } + static unsigned getHashValue(const clang::VersionTuple& value) { + unsigned result = value.getMajor(); + if (auto minor = value.getMinor()) + result = combineHashValue(result, *minor); + if (auto subminor = value.getSubminor()) + result = combineHashValue(result, *subminor); + if (auto build = value.getBuild()) + result = combineHashValue(result, *build); + + return result; + } + + static bool isEqual(const clang::VersionTuple &lhs, + const clang::VersionTuple &rhs) { + return lhs == rhs; + } + }; + +} // end namespace llvm + #endif // LLVM_CLANG_BASIC_VERSIONTUPLE_H diff --git a/include/clang/Driver/Options.td b/include/clang/Driver/Options.td index cbd2a4a7343..c22106b023d 100644 --- a/include/clang/Driver/Options.td +++ b/include/clang/Driver/Options.td @@ -551,6 +551,9 @@ def fno_apinotes_modules : Flag<["-"], "fno-apinotes-modules">, Group">, HelpText<"Specify the API notes cache path">; +def fapinotes_swift_version : Joined<["-"], "fapinotes-swift-version=">, + Group, Flags<[CC1Option]>, MetaVarName<"">, + HelpText<"Specify the Swift version to use when filtering API notes">; def fblocks : Flag<["-"], "fblocks">, Group, Flags<[CC1Option]>, HelpText<"Enable the 'blocks' language feature">; diff --git a/lib/APINotes/APINotesFormat.h b/lib/APINotes/APINotesFormat.h index 542f90831a5..9b68cf29238 100644 --- a/lib/APINotes/APINotesFormat.h +++ b/lib/APINotes/APINotesFormat.h @@ -36,7 +36,7 @@ const uint16_t VERSION_MAJOR = 0; /// API notes file minor version number. /// /// When the format changes IN ANY WAY, this number should be incremented. -const uint16_t VERSION_MINOR = 15; // source file info +const uint16_t VERSION_MINOR = 16; // versioned API notes. using IdentifierID = PointerEmbeddedInt; using IdentifierIDField = BCVBR<16>; @@ -60,8 +60,8 @@ enum BlockID { /// The identifier data block, which maps identifier strings to IDs. IDENTIFIER_BLOCK_ID, - /// The Objective-C class data block, which maps Objective-C class - /// names to information about the class. + /// The Objective-C context data block, which contains information about + /// Objective-C classes and protocols. OBJC_CONTEXT_BLOCK_ID, /// The Objective-C property data block, which maps Objective-C @@ -147,13 +147,20 @@ namespace identifier_block { namespace objc_context_block { enum { - OBJC_CONTEXT_DATA = 1, + OBJC_CONTEXT_ID_DATA = 1, + OBJC_CONTEXT_INFO_DATA = 2, }; - using ObjCContextDataLayout = BCRecordLayout< - OBJC_CONTEXT_DATA, // record ID + using ObjCContextIDLayout = BCRecordLayout< + OBJC_CONTEXT_ID_DATA, // record ID BCVBR<16>, // table offset within the blob (see below) - BCBlob // map from ObjC class names (as IDs) to ObjC class information + BCBlob // map from ObjC class names/protocol (as IDs) to context IDs + >; + + using ObjCContextInfoLayout = BCRecordLayout< + OBJC_CONTEXT_INFO_DATA, // record ID + BCVBR<16>, // table offset within the blob (see below) + BCBlob // map from ObjC context IDs to context information. >; } diff --git a/lib/APINotes/APINotesManager.cpp b/lib/APINotes/APINotesManager.cpp index 834d7e1f72f..5d50d3cd1dd 100644 --- a/lib/APINotes/APINotesManager.cpp +++ b/lib/APINotes/APINotesManager.cpp @@ -153,7 +153,7 @@ APINotesManager::loadAPINotes(const FileEntry *apiNotesFile) { if (!buffer) return nullptr; // Load the binary form. - return APINotesReader::getUnmanaged(buffer); + return APINotesReader::getUnmanaged(buffer, SwiftVersion); } // If we haven't pruned the API notes cache yet during this execution, do @@ -184,7 +184,8 @@ APINotesManager::loadAPINotes(const FileEntry *apiNotesFile) { // Load the file contents. if (auto buffer = fileMgr.getBufferForFile(compiledFile)) { // Load the file. - if (auto reader = APINotesReader::get(std::move(buffer.get()))) { + if (auto reader = APINotesReader::get(std::move(buffer.get()), + SwiftVersion)) { bool outOfDate = false; if (auto sizeAndModTime = reader->getSourceFileSizeAndModTime()) { if (sizeAndModTime->first != apiNotesFile->getSize() || @@ -272,7 +273,7 @@ APINotesManager::loadAPINotes(const FileEntry *apiNotesFile) { } // Load the binary form we just compiled. - auto reader = APINotesReader::get(std::move(compiledBuffer)); + auto reader = APINotesReader::get(std::move(compiledBuffer), SwiftVersion); assert(reader && "Could not load the API notes we just generated?"); return reader; } @@ -395,14 +396,20 @@ bool APINotesManager::loadCurrentModuleAPINotes( }; if (module->IsFramework) { - // For frameworks, we search in the "APINotes" subdirectory. + // For frameworks, we search in the "Headers" or "PrivateHeaders" + // subdirectory. llvm::SmallString<128> path; path += module->Directory->getName(); - llvm::sys::path::append(path, "APINotes"); - if (auto apinotesDir = fileMgr.getDirectory(path)) { + unsigned pathLen = path.size(); + + llvm::sys::path::append(path, "Headers"); + if (auto apinotesDir = fileMgr.getDirectory(path)) tryAPINotes(apinotesDir, /*wantPublic=*/true); - tryAPINotes(apinotesDir, /*wantPublic=*/false); - } + + path.resize(pathLen); + llvm::sys::path::append(path, "PrivateHeaders"); + if (auto privateAPINotesDir = fileMgr.getDirectory(path)) + tryAPINotes(privateAPINotesDir, /*wantPublic=*/false); } else { tryAPINotes(module->Directory, /*wantPublic=*/true); tryAPINotes(module->Directory, /*wantPublic=*/false); diff --git a/lib/APINotes/APINotesReader.cpp b/lib/APINotes/APINotesReader.cpp index 0c93756a005..dda721e4112 100644 --- a/lib/APINotes/APINotesReader.cpp +++ b/lib/APINotes/APINotesReader.cpp @@ -28,6 +28,77 @@ using namespace llvm::support; using namespace llvm; namespace { + /// Deserialize a version tuple. + VersionTuple readVersionTuple(const uint8_t *&data) { + uint8_t numVersions = (*data++) & 0x03; + + unsigned major = endian::readNext(data); + if (numVersions == 0) + return VersionTuple(major); + + unsigned minor = endian::readNext(data); + if (numVersions == 1) + return VersionTuple(major, minor); + + unsigned subminor = endian::readNext(data); + if (numVersions == 2) + return VersionTuple(major, minor, subminor); + + unsigned build = endian::readNext(data); + return VersionTuple(major, minor, subminor, build); + } + + /// An on-disk hash table whose data is versioned based on the Swift version. + template + class VersionedTableInfo { + public: + using internal_key_type = KeyType; + using external_key_type = KeyType; + using data_type = SmallVector, 1>; + using hash_value_type = size_t; + using offset_type = unsigned; + + internal_key_type GetInternalKey(external_key_type key) { + return key; + } + + external_key_type GetExternalKey(internal_key_type key) { + return key; + } + + hash_value_type ComputeHash(internal_key_type key) { + return static_cast(llvm::hash_value(key)); + } + + static bool EqualKey(internal_key_type lhs, internal_key_type rhs) { + return lhs == rhs; + } + + static std::pair + ReadKeyDataLength(const uint8_t *&data) { + unsigned keyLength = endian::readNext(data); + unsigned dataLength = endian::readNext(data); + return { keyLength, dataLength }; + } + + static data_type ReadData(internal_key_type key, const uint8_t *data, + unsigned length) { + unsigned numElements = endian::readNext(data); + data_type result; + result.reserve(numElements); + for (unsigned i = 0; i != numElements; ++i) { + auto version = readVersionTuple(data); + auto dataBefore = data; (void)data; + auto unversionedData = Derived::readUnversioned(key, data); + assert(data != dataBefore + && "Unversioned data reader didn't move pointer"); + result.push_back({version, unversionedData}); + } + return result; + } + }; + + /// Read serialized CommonEntityInfo. void readCommonEntityInfo(const uint8_t *&data, CommonEntityInfo &info) { uint8_t unavailableBits = *data++; @@ -109,12 +180,12 @@ namespace { }; /// Used to deserialize the on-disk Objective-C class table. - class ObjCContextTableInfo { + class ObjCContextIDTableInfo { public: // identifier ID, is-protocol using internal_key_type = std::pair; using external_key_type = internal_key_type; - using data_type = std::pair; + using data_type = unsigned; using hash_value_type = size_t; using offset_type = unsigned; @@ -150,16 +221,35 @@ namespace { static data_type ReadData(internal_key_type key, const uint8_t *data, unsigned length) { - data_type result; - result.first = endian::readNext(data); - readCommonTypeInfo(data, result.second); - if (*data++) { - result.second.setDefaultNullability(static_cast(*data)); - } - ++data; - result.second.setHasDesignatedInits(*data++); - - return result; + return endian::readNext(data); + } + }; + + /// Used to deserialize the on-disk Objective-C property table. + class ObjCContextInfoTableInfo + : public VersionedTableInfo + { + public: + static internal_key_type ReadKey(const uint8_t *data, unsigned length) { + return endian::readNext(data); + } + + static ObjCContextInfo readUnversioned(internal_key_type key, + const uint8_t *&data) { + ObjCContextInfo info; + readCommonTypeInfo(data, info); + uint8_t payload = *data++; + + if (payload & 0x01) + info.setHasDesignatedInits(true); + payload = payload >> 1; + + if (payload & 0x4) + info.setDefaultNullability(static_cast(payload&0x03)); + + return info; } }; @@ -173,38 +263,12 @@ namespace { } /// Used to deserialize the on-disk Objective-C property table. - class ObjCPropertyTableInfo { + class ObjCPropertyTableInfo + : public VersionedTableInfo, + ObjCPropertyInfo> + { public: - // (context ID, name ID, isInstance) - using internal_key_type = std::tuple; - using external_key_type = internal_key_type; - using data_type = ObjCPropertyInfo; - using hash_value_type = size_t; - using offset_type = unsigned; - - internal_key_type GetInternalKey(external_key_type key) { - return key; - } - - external_key_type GetExternalKey(internal_key_type key) { - return key; - } - - hash_value_type ComputeHash(internal_key_type key) { - return static_cast(llvm::hash_value(key)); - } - - static bool EqualKey(internal_key_type lhs, internal_key_type rhs) { - return lhs == rhs; - } - - static std::pair - ReadKeyDataLength(const uint8_t *&data) { - unsigned keyLength = endian::readNext(data); - unsigned dataLength = endian::readNext(data); - return { keyLength, dataLength }; - } - static internal_key_type ReadKey(const uint8_t *data, unsigned length) { auto classID = endian::readNext(data); auto nameID = endian::readNext(data); @@ -212,8 +276,8 @@ namespace { return std::make_tuple(classID, nameID, isInstance); } - static data_type ReadData(internal_key_type key, const uint8_t *data, - unsigned length) { + static ObjCPropertyInfo readUnversioned(internal_key_type key, + const uint8_t *&data) { ObjCPropertyInfo info; readVariableInfo(data, info); return info; @@ -248,40 +312,11 @@ namespace { } /// Used to deserialize the on-disk Objective-C method table. - class ObjCMethodTableInfo { + class ObjCMethodTableInfo + : public VersionedTableInfo, + ObjCMethodInfo> { public: - // (class ID, selector ID, is-instance) - using internal_key_type = std::tuple; - using external_key_type = internal_key_type; - using data_type = ObjCMethodInfo; - using hash_value_type = size_t; - using offset_type = unsigned; - - internal_key_type GetInternalKey(external_key_type key) { - return key; - } - - external_key_type GetExternalKey(internal_key_type key) { - return key; - } - - hash_value_type ComputeHash(internal_key_type key) { - return llvm::hash_combine(std::get<0>(key), - std::get<1>(key), - std::get<2>(key)); - } - - static bool EqualKey(internal_key_type lhs, internal_key_type rhs) { - return lhs == rhs; - } - - static std::pair - ReadKeyDataLength(const uint8_t *&data) { - unsigned keyLength = endian::readNext(data); - unsigned dataLength = endian::readNext(data); - return { keyLength, dataLength }; - } - static internal_key_type ReadKey(const uint8_t *data, unsigned length) { auto classID = endian::readNext(data); auto selectorID = endian::readNext(data); @@ -289,13 +324,18 @@ namespace { return internal_key_type{ classID, selectorID, isInstance }; } - static data_type ReadData(internal_key_type key, const uint8_t *data, - unsigned length) { + static ObjCMethodInfo readUnversioned(internal_key_type key, + const uint8_t *&data) { ObjCMethodInfo info; + uint8_t payload = *data++; + info.Required = payload & 0x01; + payload >>= 1; + info.DesignatedInit = payload & 0x01; + payload >>= 1; + info.FactoryAsInit = payload & 0x03; + payload >>= 2; + readFunctionInfo(data, info); - info.DesignatedInit = endian::readNext(data); - info.FactoryAsInit = endian::readNext(data); - info.Required = endian::readNext(data); return info; } }; @@ -350,44 +390,17 @@ namespace { }; /// Used to deserialize the on-disk global variable table. - class GlobalVariableTableInfo { + class GlobalVariableTableInfo + : public VersionedTableInfo { public: - using internal_key_type = unsigned; // name ID - using external_key_type = internal_key_type; - using data_type = GlobalVariableInfo; - using hash_value_type = size_t; - using offset_type = unsigned; - - internal_key_type GetInternalKey(external_key_type key) { - return key; - } - - external_key_type GetExternalKey(internal_key_type key) { - return key; - } - - hash_value_type ComputeHash(internal_key_type key) { - return static_cast(llvm::hash_value(key)); - } - - static bool EqualKey(internal_key_type lhs, internal_key_type rhs) { - return lhs == rhs; - } - - static std::pair - ReadKeyDataLength(const uint8_t *&data) { - unsigned keyLength = endian::readNext(data); - unsigned dataLength = endian::readNext(data); - return { keyLength, dataLength }; - } - static internal_key_type ReadKey(const uint8_t *data, unsigned length) { auto nameID = endian::readNext(data); return nameID; } - - static data_type ReadData(internal_key_type key, const uint8_t *data, - unsigned length) { + + static GlobalVariableInfo readUnversioned(internal_key_type key, + const uint8_t *&data) { GlobalVariableInfo info; readVariableInfo(data, info); return info; @@ -395,44 +408,17 @@ namespace { }; /// Used to deserialize the on-disk global function table. - class GlobalFunctionTableInfo { + class GlobalFunctionTableInfo + : public VersionedTableInfo { public: - using internal_key_type = unsigned; // name ID - using external_key_type = internal_key_type; - using data_type = GlobalFunctionInfo; - using hash_value_type = size_t; - using offset_type = unsigned; - - internal_key_type GetInternalKey(external_key_type key) { - return key; - } - - external_key_type GetExternalKey(internal_key_type key) { - return key; - } - - hash_value_type ComputeHash(internal_key_type key) { - return static_cast(llvm::hash_value(key)); - } - - static bool EqualKey(internal_key_type lhs, internal_key_type rhs) { - return lhs == rhs; - } - - static std::pair - ReadKeyDataLength(const uint8_t *&data) { - unsigned keyLength = endian::readNext(data); - unsigned dataLength = endian::readNext(data); - return { keyLength, dataLength }; - } - static internal_key_type ReadKey(const uint8_t *data, unsigned length) { auto nameID = endian::readNext(data); return nameID; } - static data_type ReadData(internal_key_type key, const uint8_t *data, - unsigned length) { + static GlobalFunctionInfo readUnversioned(internal_key_type key, + const uint8_t *&data) { GlobalFunctionInfo info; readFunctionInfo(data, info); return info; @@ -440,44 +426,17 @@ namespace { }; /// Used to deserialize the on-disk enumerator table. - class EnumConstantTableInfo { + class EnumConstantTableInfo + : public VersionedTableInfo { public: - using internal_key_type = unsigned; // name ID - using external_key_type = internal_key_type; - using data_type = EnumConstantInfo; - using hash_value_type = size_t; - using offset_type = unsigned; - - internal_key_type GetInternalKey(external_key_type key) { - return key; - } - - external_key_type GetExternalKey(internal_key_type key) { - return key; - } - - hash_value_type ComputeHash(internal_key_type key) { - return static_cast(llvm::hash_value(key)); - } - - static bool EqualKey(internal_key_type lhs, internal_key_type rhs) { - return lhs == rhs; - } - - static std::pair - ReadKeyDataLength(const uint8_t *&data) { - unsigned keyLength = endian::readNext(data); - unsigned dataLength = endian::readNext(data); - return { keyLength, dataLength }; - } - static internal_key_type ReadKey(const uint8_t *data, unsigned length) { auto nameID = endian::readNext(data); return nameID; } - static data_type ReadData(internal_key_type key, const uint8_t *data, - unsigned length) { + static EnumConstantInfo readUnversioned(internal_key_type key, + const uint8_t *&data) { EnumConstantInfo info; readCommonEntityInfo(data, info); return info; @@ -485,44 +444,16 @@ namespace { }; /// Used to deserialize the on-disk tag table. - class TagTableInfo { + class TagTableInfo + : public VersionedTableInfo { public: - using internal_key_type = unsigned; // name ID - using external_key_type = internal_key_type; - using data_type = TagInfo; - using hash_value_type = size_t; - using offset_type = unsigned; - - internal_key_type GetInternalKey(external_key_type key) { - return key; - } - - external_key_type GetExternalKey(internal_key_type key) { - return key; - } - - hash_value_type ComputeHash(internal_key_type key) { - return static_cast(llvm::hash_value(key)); - } - - static bool EqualKey(internal_key_type lhs, internal_key_type rhs) { - return lhs == rhs; - } - - static std::pair - ReadKeyDataLength(const uint8_t *&data) { - unsigned keyLength = endian::readNext(data); - unsigned dataLength = endian::readNext(data); - return { keyLength, dataLength }; - } - static internal_key_type ReadKey(const uint8_t *data, unsigned length) { auto nameID = endian::readNext(data); return nameID; } - static data_type ReadData(internal_key_type key, const uint8_t *data, - unsigned length) { + static TagInfo readUnversioned(internal_key_type key, + const uint8_t *&data) { TagInfo info; readCommonTypeInfo(data, info); return info; @@ -530,44 +461,16 @@ namespace { }; /// Used to deserialize the on-disk typedef table. - class TypedefTableInfo { + class TypedefTableInfo + : public VersionedTableInfo { public: - using internal_key_type = unsigned; // name ID - using external_key_type = internal_key_type; - using data_type = TypedefInfo; - using hash_value_type = size_t; - using offset_type = unsigned; - - internal_key_type GetInternalKey(external_key_type key) { - return key; - } - - external_key_type GetExternalKey(internal_key_type key) { - return key; - } - - hash_value_type ComputeHash(internal_key_type key) { - return static_cast(llvm::hash_value(key)); - } - - static bool EqualKey(internal_key_type lhs, internal_key_type rhs) { - return lhs == rhs; - } - - static std::pair - ReadKeyDataLength(const uint8_t *&data) { - unsigned keyLength = endian::readNext(data); - unsigned dataLength = endian::readNext(data); - return { keyLength, dataLength }; - } - static internal_key_type ReadKey(const uint8_t *data, unsigned length) { auto nameID = endian::readNext(data); return nameID; } - - static data_type ReadData(internal_key_type key, const uint8_t *data, - unsigned length) { + + static TypedefInfo readUnversioned(internal_key_type key, + const uint8_t *&data) { TypedefInfo info; readCommonTypeInfo(data, info); return info; @@ -583,6 +486,9 @@ class APINotesReader::Implementation { /// Whether we own the input buffer. bool OwnsInputBuffer; + /// The Swift version to use for filtering. + VersionTuple SwiftVersion; + /// The reader attached to \c InputBuffer. llvm::BitstreamReader InputReader; @@ -602,11 +508,17 @@ class APINotesReader::Implementation { /// The identifier table. std::unique_ptr IdentifierTable; - using SerializedObjCContextTable = - llvm::OnDiskIterableChainedHashTable; + using SerializedObjCContextIDTable = + llvm::OnDiskIterableChainedHashTable; + + /// The Objective-C context ID table. + std::unique_ptr ObjCContextIDTable; + + using SerializedObjCContextInfoTable = + llvm::OnDiskIterableChainedHashTable; - /// The Objective-C context table. - std::unique_ptr ObjCContextTable; + /// The Objective-C context info table. + std::unique_ptr ObjCContextInfoTable; using SerializedObjCPropertyTable = llvm::OnDiskIterableChainedHashTable; @@ -867,19 +779,36 @@ bool APINotesReader::Implementation::readObjCContextBlock( StringRef blobData; unsigned kind = cursor.readRecord(next.ID, scratch, &blobData); switch (kind) { - case objc_context_block::OBJC_CONTEXT_DATA: { - // Already saw Objective-C class table. - if (ObjCContextTable) + case objc_context_block::OBJC_CONTEXT_ID_DATA: { + // Already saw Objective-C context ID table. + if (ObjCContextIDTable) + return true; + + uint32_t tableOffset; + objc_context_block::ObjCContextIDLayout::readRecord(scratch, tableOffset); + auto base = reinterpret_cast(blobData.data()); + + ObjCContextIDTable.reset( + SerializedObjCContextIDTable::Create(base + tableOffset, + base + sizeof(uint32_t), + base)); + break; + } + + case objc_context_block::OBJC_CONTEXT_INFO_DATA: { + // Already saw Objective-C context info table. + if (ObjCContextInfoTable) return true; uint32_t tableOffset; - objc_context_block::ObjCContextDataLayout::readRecord(scratch, tableOffset); + objc_context_block::ObjCContextInfoLayout::readRecord(scratch, + tableOffset); auto base = reinterpret_cast(blobData.data()); - ObjCContextTable.reset( - SerializedObjCContextTable::Create(base + tableOffset, - base + sizeof(uint32_t), - base)); + ObjCContextInfoTable.reset( + SerializedObjCContextInfoTable::Create(base + tableOffset, + base + sizeof(uint32_t), + base)); break; } @@ -1326,6 +1255,7 @@ bool APINotesReader::Implementation::readTypedefBlock( APINotesReader::APINotesReader(llvm::MemoryBuffer *inputBuffer, bool ownsInputBuffer, + VersionTuple swiftVersion, bool &failed) : Impl(*new Implementation) { @@ -1334,6 +1264,7 @@ APINotesReader::APINotesReader(llvm::MemoryBuffer *inputBuffer, // Initialize the input buffer. Impl.InputBuffer = inputBuffer; Impl.OwnsInputBuffer = ownsInputBuffer; + Impl.SwiftVersion = swiftVersion; Impl.InputReader.init( reinterpret_cast(Impl.InputBuffer->getBufferStart()), reinterpret_cast(Impl.InputBuffer->getBufferEnd())); @@ -1473,11 +1404,12 @@ APINotesReader::~APINotesReader() { } std::unique_ptr -APINotesReader::get(std::unique_ptr inputBuffer) { +APINotesReader::get(std::unique_ptr inputBuffer, + VersionTuple swiftVersion) { bool failed = false; std::unique_ptr reader(new APINotesReader(inputBuffer.release(), /*ownsInputBuffer=*/true, - failed)); + swiftVersion, failed)); if (failed) return nullptr; @@ -1485,11 +1417,12 @@ APINotesReader::get(std::unique_ptr inputBuffer) { } std::unique_ptr -APINotesReader::getUnmanaged(llvm::MemoryBuffer *inputBuffer) { +APINotesReader::getUnmanaged(llvm::MemoryBuffer *inputBuffer, + VersionTuple swiftVersion) { bool failed = false; std::unique_ptr reader(new APINotesReader(inputBuffer, /*ownsInputBuffer=*/false, - failed)); + swiftVersion, failed)); if (failed) return nullptr; @@ -1509,44 +1442,101 @@ ModuleOptions APINotesReader::getModuleOptions() const { return Impl.ModuleOpts; } -auto APINotesReader::lookupObjCClass(StringRef name) - -> Optional> { - if (!Impl.ObjCContextTable) +template +APINotesReader::VersionedInfo::VersionedInfo( + VersionTuple version, + SmallVector, 1> results) + : Results(std::move(results)) { + + // Look for an exact version match. + Optional unversioned; + Selected = Results.size(); + for (unsigned i = 0, n = Results.size(); i != n; ++i) { + if (Results[i].first == version) { + Selected = i; + break; + } + + if (!Results[i].first) { + assert(!unversioned && "Two unversioned entries?"); + unversioned = i; + } + } + + // If we didn't find a match but we have an unversioned result, use the + // unversioned result. + if (Selected == Results.size() && unversioned) + Selected = *unversioned; +} + +auto APINotesReader::lookupObjCClassID(StringRef name) -> Optional { + if (!Impl.ObjCContextIDTable) return None; Optional classID = Impl.getIdentifier(name); if (!classID) return None; - auto known = Impl.ObjCContextTable->find({*classID, '\0'}); - if (known == Impl.ObjCContextTable->end()) + auto knownID = Impl.ObjCContextIDTable->find({*classID, '\0'}); + if (knownID == Impl.ObjCContextIDTable->end()) return None; - auto result = *known; - return std::make_pair(ContextID(result.first), result.second); + return ContextID(*knownID); } -auto APINotesReader::lookupObjCProtocol(StringRef name) - -> Optional> { - if (!Impl.ObjCContextTable) +auto APINotesReader::lookupObjCClassInfo(StringRef name) + -> VersionedInfo { + if (!Impl.ObjCContextInfoTable) return None; - Optional classID = Impl.getIdentifier(name); - if (!classID) + Optional contextID = lookupObjCClassID(name); + if (!contextID) return None; - auto known = Impl.ObjCContextTable->find({*classID, '\1'}); - if (known == Impl.ObjCContextTable->end()) + auto knownInfo = Impl.ObjCContextInfoTable->find(contextID->Value); + if (knownInfo == Impl.ObjCContextInfoTable->end()) return None; - auto result = *known; - return std::make_pair(ContextID(result.first), result.second); + return { Impl.SwiftVersion, *knownInfo }; +} + +auto APINotesReader::lookupObjCProtocolID(StringRef name) + -> Optional { + if (!Impl.ObjCContextIDTable) + return None; + + Optional classID = Impl.getIdentifier(name); + if (!classID) + return None; + + auto knownID = Impl.ObjCContextIDTable->find({*classID, '\1'}); + if (knownID == Impl.ObjCContextIDTable->end()) + return None; + + return ContextID(*knownID); } -Optional APINotesReader::lookupObjCProperty( - ContextID contextID, - StringRef name, - bool isInstance) { +auto APINotesReader::lookupObjCProtocolInfo(StringRef name) + -> VersionedInfo { + if (!Impl.ObjCContextInfoTable) + return None; + + Optional contextID = lookupObjCProtocolID(name); + if (!contextID) + return None; + + auto knownInfo = Impl.ObjCContextInfoTable->find(contextID->Value); + if (knownInfo == Impl.ObjCContextInfoTable->end()) + return None; + + return { Impl.SwiftVersion, *knownInfo }; +} + + +auto APINotesReader::lookupObjCProperty(ContextID contextID, + StringRef name, + bool isInstance) + -> VersionedInfo { if (!Impl.ObjCPropertyTable) return None; @@ -1560,13 +1550,14 @@ Optional APINotesReader::lookupObjCProperty( if (known == Impl.ObjCPropertyTable->end()) return None; - return *known; + return { Impl.SwiftVersion, *known }; } -Optional APINotesReader::lookupObjCMethod( - ContextID contextID, - ObjCSelectorRef selector, - bool isInstanceMethod) { +auto APINotesReader::lookupObjCMethod( + ContextID contextID, + ObjCSelectorRef selector, + bool isInstanceMethod) + -> VersionedInfo { if (!Impl.ObjCMethodTable) return None; @@ -1580,11 +1571,12 @@ Optional APINotesReader::lookupObjCMethod( if (known == Impl.ObjCMethodTable->end()) return None; - return *known; + return { Impl.SwiftVersion, *known }; } -Optional APINotesReader::lookupGlobalVariable( - StringRef name) { +auto APINotesReader::lookupGlobalVariable( + StringRef name) + -> VersionedInfo { if (!Impl.GlobalVariableTable) return None; @@ -1596,11 +1588,11 @@ Optional APINotesReader::lookupGlobalVariable( if (known == Impl.GlobalVariableTable->end()) return None; - return *known; + return { Impl.SwiftVersion, *known }; } -Optional APINotesReader::lookupGlobalFunction( - StringRef name) { +auto APINotesReader::lookupGlobalFunction(StringRef name) + -> VersionedInfo { if (!Impl.GlobalFunctionTable) return None; @@ -1612,10 +1604,11 @@ Optional APINotesReader::lookupGlobalFunction( if (known == Impl.GlobalFunctionTable->end()) return None; - return *known; + return { Impl.SwiftVersion, *known }; } -Optional APINotesReader::lookupEnumConstant(StringRef name) { +auto APINotesReader::lookupEnumConstant(StringRef name) + -> VersionedInfo { if (!Impl.EnumConstantTable) return None; @@ -1627,10 +1620,10 @@ Optional APINotesReader::lookupEnumConstant(StringRef name) { if (known == Impl.EnumConstantTable->end()) return None; - return *known; + return { Impl.SwiftVersion, *known }; } -Optional APINotesReader::lookupTag(StringRef name) { +auto APINotesReader::lookupTag(StringRef name) -> VersionedInfo { if (!Impl.TagTable) return None; @@ -1642,10 +1635,11 @@ Optional APINotesReader::lookupTag(StringRef name) { if (known == Impl.TagTable->end()) return None; - return *known; + return { Impl.SwiftVersion, *known }; } -Optional APINotesReader::lookupTypedef(StringRef name) { +auto APINotesReader::lookupTypedef(StringRef name) + -> VersionedInfo { if (!Impl.TypedefTable) return None; @@ -1657,48 +1651,61 @@ Optional APINotesReader::lookupTypedef(StringRef name) { if (known == Impl.TypedefTable->end()) return None; - return *known; + return { Impl.SwiftVersion, *known }; } APINotesReader::Visitor::~Visitor() { } -void APINotesReader::Visitor::visitObjCClass(ContextID contextID, - StringRef name, - const ObjCContextInfo &info) { } - -void APINotesReader::Visitor::visitObjCProtocol(ContextID contextID, - StringRef name, - const ObjCContextInfo &info) { } - -void APINotesReader::Visitor::visitObjCMethod(ContextID contextID, - StringRef selector, - bool isInstanceMethod, - const ObjCMethodInfo &info) { } +void APINotesReader::Visitor::visitObjCClass( + ContextID contextID, + StringRef name, + const ObjCContextInfo &info, + VersionTuple swiftVersion) { } -void APINotesReader::Visitor::visitObjCProperty(ContextID contextID, - StringRef name, - bool isInstance, - const ObjCPropertyInfo &info) { } +void APINotesReader::Visitor::visitObjCProtocol( + ContextID contextID, + StringRef name, + const ObjCContextInfo &info, + VersionTuple swiftVersion) { } + +void APINotesReader::Visitor::visitObjCMethod( + ContextID contextID, + StringRef selector, + bool isInstanceMethod, + const ObjCMethodInfo &info, + VersionTuple swiftVersion) { } + +void APINotesReader::Visitor::visitObjCProperty( + ContextID contextID, + StringRef name, + bool isInstance, + const ObjCPropertyInfo &info, + VersionTuple swiftVersion) { } void APINotesReader::Visitor::visitGlobalVariable( StringRef name, - const GlobalVariableInfo &info) { } + const GlobalVariableInfo &info, + VersionTuple swiftVersion) { } void APINotesReader::Visitor::visitGlobalFunction( StringRef name, - const GlobalFunctionInfo &info) { } + const GlobalFunctionInfo &info, + VersionTuple swiftVersion) { } void APINotesReader::Visitor::visitEnumConstant( StringRef name, - const EnumConstantInfo &info) { } + const EnumConstantInfo &info, + VersionTuple swiftVersion) { } void APINotesReader::Visitor::visitTag( StringRef name, - const TagInfo &info) { } + const TagInfo &info, + VersionTuple swiftVersion) { } void APINotesReader::Visitor::visitTypedef( StringRef name, - const TypedefInfo &info) { } + const TypedefInfo &info, + VersionTuple swiftVersion) { } void APINotesReader::visit(Visitor &visitor) { // FIXME: All of these iterations would be significantly more efficient if we @@ -1717,15 +1724,22 @@ void APINotesReader::visit(Visitor &visitor) { } // Visit classes and protocols. - if (Impl.ObjCContextTable) { - for (auto key : Impl.ObjCContextTable->keys()) { + if (Impl.ObjCContextIDTable && Impl.ObjCContextInfoTable) { + for (auto key : Impl.ObjCContextIDTable->keys()) { auto name = identifiers[key.first]; - auto info = *Impl.ObjCContextTable->find(key); - - if (key.second) - visitor.visitObjCProtocol(ContextID(info.first), name, info.second); - else - visitor.visitObjCClass(ContextID(info.first), name, info.second); + auto contextID = *Impl.ObjCContextIDTable->find(key); + + auto knownInfo = Impl.ObjCContextInfoTable->find(contextID); + if (knownInfo == Impl.ObjCContextInfoTable->end()) continue; + + for (const auto &versioned : *knownInfo) { + if (key.second) + visitor.visitObjCProtocol(ContextID(contextID), name, + versioned.second, versioned.first); + else + visitor.visitObjCClass(ContextID(contextID), name, versioned.second, + versioned.first); + } } } @@ -1754,8 +1768,9 @@ void APINotesReader::visit(Visitor &visitor) { for (auto key : Impl.ObjCMethodTable->keys()) { ContextID contextID(std::get<0>(key)); const auto &selector = selectors[std::get<1>(key)]; - auto info = *Impl.ObjCMethodTable->find(key); - visitor.visitObjCMethod(contextID, selector, std::get<2>(key), info); + for (const auto &versioned : *Impl.ObjCMethodTable->find(key)) + visitor.visitObjCMethod(contextID, selector, std::get<2>(key), + versioned.second, versioned.first); } } @@ -1765,8 +1780,10 @@ void APINotesReader::visit(Visitor &visitor) { ContextID contextID(std::get<0>(key)); auto name = identifiers[std::get<1>(key)]; char isInstance = std::get<2>(key); - auto info = *Impl.ObjCPropertyTable->find(key); - visitor.visitObjCProperty(contextID, name, isInstance, info); + for (const auto &versioned : *Impl.ObjCPropertyTable->find(key)) { + visitor.visitObjCProperty(contextID, name, isInstance, versioned.second, + versioned.first); + } } } @@ -1774,8 +1791,8 @@ void APINotesReader::visit(Visitor &visitor) { if (Impl.GlobalFunctionTable) { for (auto key : Impl.GlobalFunctionTable->keys()) { auto name = identifiers[key]; - auto info = *Impl.GlobalFunctionTable->find(key); - visitor.visitGlobalFunction(name, info); + for (const auto &versioned : *Impl.GlobalFunctionTable->find(key)) + visitor.visitGlobalFunction(name, versioned.second, versioned.first); } } @@ -1783,8 +1800,8 @@ void APINotesReader::visit(Visitor &visitor) { if (Impl.GlobalVariableTable) { for (auto key : Impl.GlobalVariableTable->keys()) { auto name = identifiers[key]; - auto info = *Impl.GlobalVariableTable->find(key); - visitor.visitGlobalVariable(name, info); + for (const auto &versioned : *Impl.GlobalVariableTable->find(key)) + visitor.visitGlobalVariable(name, versioned.second, versioned.first); } } @@ -1792,8 +1809,8 @@ void APINotesReader::visit(Visitor &visitor) { if (Impl.EnumConstantTable) { for (auto key : Impl.EnumConstantTable->keys()) { auto name = identifiers[key]; - auto info = *Impl.EnumConstantTable->find(key); - visitor.visitEnumConstant(name, info); + for (const auto &versioned : *Impl.EnumConstantTable->find(key)) + visitor.visitEnumConstant(name, versioned.second, versioned.first); } } @@ -1801,8 +1818,8 @@ void APINotesReader::visit(Visitor &visitor) { if (Impl.TagTable) { for (auto key : Impl.TagTable->keys()) { auto name = identifiers[key]; - auto info = *Impl.TagTable->find(key); - visitor.visitTag(name, info); + for (const auto &versioned : *Impl.TagTable->find(key)) + visitor.visitTag(name, versioned.second, versioned.first); } } @@ -1810,8 +1827,8 @@ void APINotesReader::visit(Visitor &visitor) { if (Impl.TypedefTable) { for (auto key : Impl.TypedefTable->keys()) { auto name = identifiers[key]; - auto info = *Impl.TypedefTable->find(key); - visitor.visitTypedef(name, info); + for (const auto &versioned : *Impl.TypedefTable->find(key)) + visitor.visitTypedef(name, versioned.second, versioned.first); } } } diff --git a/lib/APINotes/APINotesWriter.cpp b/lib/APINotes/APINotesWriter.cpp index 2df5e76f7c8..36c504afc34 100644 --- a/lib/APINotes/APINotesWriter.cpp +++ b/lib/APINotes/APINotesWriter.cpp @@ -24,12 +24,18 @@ #include "llvm/Support/EndianStream.h" #include "llvm/Support/OnDiskHashTable.h" #include "llvm/Support/raw_ostream.h" +#include "llvm/Support/DataTypes.h" #include #include using namespace clang; using namespace api_notes; using namespace llvm::support; +namespace { + template using VersionedSmallVector = + SmallVector, 1>; +} + class APINotesWriter::Implementation { /// Mapping from strings to identifier IDs. llvm::StringMap IdentifierIDs; @@ -56,7 +62,8 @@ class APINotesWriter::Implementation { /// for a class (0) or protocol (1) and provides both the context ID and /// information describing the context within that module. llvm::DenseMap, - std::pair> ObjCContexts; + std::pair>> + ObjCContexts; /// Mapping from context IDs to the identifier ID holding the name. llvm::DenseMap ObjCContextNames; @@ -65,40 +72,56 @@ class APINotesWriter::Implementation { /// /// Indexed by the context ID, property name, and whether this is an /// instance property. - llvm::DenseMap, ObjCPropertyInfo> + llvm::DenseMap, + llvm::SmallVector, + 1>> ObjCProperties; /// Information about Objective-C methods. /// /// Indexed by the context ID, selector ID, and Boolean (stored as a /// char) indicating whether this is a class or instance method. - llvm::DenseMap, ObjCMethodInfo> + llvm::DenseMap, + llvm::SmallVector, 1>> ObjCMethods; /// Information about global variables. /// /// Indexed by the identifier ID. - llvm::DenseMap GlobalVariables; + llvm::DenseMap, + 1>> + GlobalVariables; /// Information about global functions. /// /// Indexed by the identifier ID. - llvm::DenseMap GlobalFunctions; + llvm::DenseMap, + 1>> + GlobalFunctions; /// Information about enumerators. /// /// Indexed by the identifier ID. - llvm::DenseMap EnumConstants; + llvm::DenseMap, + 1>> + EnumConstants; /// Information about tags. /// /// Indexed by the identifier ID. - llvm::DenseMap Tags; + llvm::DenseMap, 1>> + Tags; /// Information about typedefs. /// /// Indexed by the identifier ID. - llvm::DenseMap Typedefs; + llvm::DenseMap, 1>> + Typedefs; /// Retrieve the ID for the given identifier. IdentifierID getIdentifier(StringRef identifier) { @@ -194,7 +217,7 @@ void APINotesWriter::Implementation::writeBlockInfoBlock( BLOCK_RECORD(identifier_block, IDENTIFIER_DATA); BLOCK(OBJC_CONTEXT_BLOCK); - BLOCK_RECORD(objc_context_block, OBJC_CONTEXT_DATA); + BLOCK_RECORD(objc_context_block, OBJC_CONTEXT_ID_DATA); BLOCK(OBJC_PROPERTY_BLOCK); BLOCK_RECORD(objc_property_block, OBJC_PROPERTY_DATA); @@ -336,18 +359,15 @@ namespace { } /// Used to serialize the on-disk Objective-C context table. - class ObjCContextTableInfo { + class ObjCContextIDTableInfo { public: using key_type = std::pair; // identifier ID, is-protocol using key_type_ref = key_type; - using data_type = std::pair; + using data_type = unsigned; using data_type_ref = const data_type &; using hash_value_type = size_t; using offset_type = unsigned; - /// The number of bytes in a data entry. - static const unsigned dataBytes = 3; - hash_value_type ComputeHash(key_type_ref key) { return static_cast(llvm::hash_value(key)); } @@ -356,9 +376,7 @@ namespace { key_type_ref key, data_type_ref data) { uint32_t keyLength = sizeof(uint32_t) + 1; - uint32_t dataLength = sizeof(uint32_t) - + getCommonTypeInfoSize(data.second) - + dataBytes; + uint32_t dataLength = sizeof(uint32_t); endian::Writer writer(out); writer.write(keyLength); writer.write(dataLength); @@ -374,58 +392,92 @@ namespace { void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data, unsigned len) { endian::Writer writer(out); - writer.write(data.first); - - emitCommonTypeInfo(out, data.second); - - // FIXME: Inefficient representation. - uint8_t bytes[dataBytes] = { 0, 0, 0 }; - if (auto nullable = data.second.getDefaultNullability()) { - bytes[0] = 1; - bytes[1] = static_cast(*nullable); - } else { - // Nothing to do. - } - bytes[2] = data.second.hasDesignatedInits(); - - out.write(reinterpret_cast(bytes), dataBytes); + writer.write(data); } }; } // end anonymous namespace -void APINotesWriter::Implementation::writeObjCContextBlock( - llvm::BitstreamWriter &writer) { - BCBlockRAII restoreBlock(writer, OBJC_CONTEXT_BLOCK_ID, 3); +namespace { + /// Retrieve the serialized size of the given VersionTuple, for use in + /// on-disk hash tables. + unsigned getVersionTupleSize(const VersionTuple &version) { + unsigned size = sizeof(uint8_t) + /*major*/sizeof(uint32_t); + if (version.getMinor()) size += sizeof(uint32_t); + if (version.getSubminor()) size += sizeof(uint32_t); + if (version.getBuild()) size += sizeof(uint32_t); + return size; + } - if (ObjCContexts.empty()) - return; + /// Emit a serialized representation of a version tuple. + void emitVersionTuple(raw_ostream &out, const VersionTuple &version) { + endian::Writer writer(out); - llvm::SmallString<4096> hashTableBlob; - uint32_t tableOffset; - { - llvm::OnDiskChainedHashTableGenerator generator; - for (auto &entry : ObjCContexts) - generator.insert(entry.first, entry.second); + // First byte contains the number of components beyond the 'major' + // component. + uint8_t descriptor; + if (version.getBuild()) descriptor = 3; + else if (version.getSubminor()) descriptor = 2; + else if (version.getMinor()) descriptor = 1; + else descriptor = 0; + assert(!version.usesUnderscores() && "Not a serializable version"); + writer.write(descriptor); + + // Write the components. + writer.write(version.getMajor()); + if (auto minor = version.getMinor()) + writer.write(*minor); + if (auto subminor = version.getSubminor()) + writer.write(*subminor); + if (auto build = version.getBuild()) + writer.write(*build); + } - llvm::raw_svector_ostream blobStream(hashTableBlob); - // Make sure that no bucket is at offset 0 - endian::Writer(blobStream).write(0); - tableOffset = generator.Emit(blobStream); + /// Localized helper to make a type dependent, thwarting template argument + /// deduction. + template + struct MakeDependent { + typedef T Type; + }; + + /// Determine the size of an array of versioned information, + template + unsigned getVersionedInfoSize( + const SmallVectorImpl> &infoArray, + llvm::function_ref::Type&)> + getInfoSize) { + unsigned result = sizeof(uint16_t); // # of elements + for (const auto &element : infoArray) { + result += getVersionTupleSize(element.first); + result += getInfoSize(element.second); + } + + return result; } - objc_context_block::ObjCContextDataLayout layout(writer); - layout.emit(ScratchRecord, tableOffset, hashTableBlob); -} + /// Emit versioned information. + template + void emitVersionedInfo( + raw_ostream &out, + const SmallVectorImpl> &infoArray, + llvm::function_ref::Type& info)> + emitInfo) { + endian::Writer writer(out); + writer.write(infoArray.size()); + for (const auto &element : infoArray) { + emitVersionTuple(out, element.first); + emitInfo(out, element.second); + } + } -namespace { /// Retrieve the serialized size of the given VariableInfo, for use in /// on-disk hash tables. - static unsigned getVariableInfoSize(const VariableInfo &info) { + unsigned getVariableInfoSize(const VariableInfo &info) { return 2 + getCommonEntityInfoSize(info); } /// Emit a serialized representation of the variable information. - static void emitVariableInfo(raw_ostream &out, const VariableInfo &info) { + void emitVariableInfo(raw_ostream &out, const VariableInfo &info) { emitCommonEntityInfo(out, info); uint8_t bytes[2] = { 0, 0 }; @@ -439,32 +491,96 @@ namespace { out.write(reinterpret_cast(bytes), 2); } - /// Used to serialize the on-disk Objective-C property table. - class ObjCPropertyTableInfo { + /// On-dish hash table info key base for handling versioned data. + template + class VersionedTableInfo { + Derived &asDerived() { + return *static_cast(this); + } + + const Derived &asDerived() const { + return *static_cast(this); + } + public: - // (class ID, name ID, isInstance) - using key_type = std::tuple; + using key_type = KeyType; using key_type_ref = key_type; - using data_type = ObjCPropertyInfo; + using data_type = + SmallVector, 1>; using data_type_ref = const data_type &; using hash_value_type = size_t; using offset_type = unsigned; hash_value_type ComputeHash(key_type_ref key) { - return static_cast(llvm::hash_value(key)); + return llvm::hash_value(key); } std::pair EmitKeyDataLength(raw_ostream &out, key_type_ref key, data_type_ref data) { - uint32_t keyLength = sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint8_t); - uint32_t dataLength = getVariableInfoSize(data); + uint32_t keyLength = asDerived().getKeyLength(key); + uint32_t dataLength = getVersionedInfoSize(data, + [this](const UnversionedDataType &unversionedInfo) { + return asDerived().getUnversionedInfoSize(unversionedInfo); + }); + endian::Writer writer(out); writer.write(keyLength); writer.write(dataLength); return { keyLength, dataLength }; } + void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data, + unsigned len) { + emitVersionedInfo(out, data, + [this](llvm::raw_ostream &out, + const UnversionedDataType &unversionedInfo) { + asDerived().emitUnversionedInfo(out, unversionedInfo); + }); + } + }; + + /// Used to serialize the on-disk Objective-C property table. + class ObjCContextInfoTableInfo + : public VersionedTableInfo { + public: + unsigned getKeyLength(key_type_ref) { + return sizeof(uint32_t); + } + + void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { + endian::Writer writer(out); + writer.write(key); + } + + unsigned getUnversionedInfoSize(const ObjCContextInfo &info) { + return getCommonTypeInfoSize(info) + 1; + } + + void emitUnversionedInfo(raw_ostream &out, const ObjCContextInfo &info) { + emitCommonTypeInfo(out, info); + + uint8_t payload = 0; + if (auto nullable = info.getDefaultNullability()) { + payload = (0x01 << 2) | static_cast(*nullable); + } + payload = (payload << 1) | (info.hasDesignatedInits() ? 1 : 0); + out << payload; + } + }; + + /// Used to serialize the on-disk Objective-C property table. + class ObjCPropertyTableInfo + : public VersionedTableInfo, + ObjCPropertyInfo> { + public: + unsigned getKeyLength(key_type_ref) { + return sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint8_t); + } + void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { endian::Writer writer(out); writer.write(std::get<0>(key)); @@ -472,13 +588,61 @@ namespace { writer.write(std::get<2>(key)); } - void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data, - unsigned len) { - emitVariableInfo(out, data); + unsigned getUnversionedInfoSize(const ObjCPropertyInfo &info) { + return getVariableInfoSize(info); + } + + void emitUnversionedInfo(raw_ostream &out, const ObjCPropertyInfo &info) { + emitVariableInfo(out, info); } }; } // end anonymous namespace +void APINotesWriter::Implementation::writeObjCContextBlock( + llvm::BitstreamWriter &writer) { + BCBlockRAII restoreBlock(writer, OBJC_CONTEXT_BLOCK_ID, 3); + + if (ObjCContexts.empty()) + return; + + { + llvm::SmallString<4096> hashTableBlob; + uint32_t tableOffset; + { + llvm::OnDiskChainedHashTableGenerator generator; + for (auto &entry : ObjCContexts) + generator.insert(entry.first, entry.second.first); + + llvm::raw_svector_ostream blobStream(hashTableBlob); + // Make sure that no bucket is at offset 0 + endian::Writer(blobStream).write(0); + tableOffset = generator.Emit(blobStream); + } + + objc_context_block::ObjCContextIDLayout layout(writer); + layout.emit(ScratchRecord, tableOffset, hashTableBlob); + } + + { + llvm::SmallString<4096> hashTableBlob; + uint32_t tableOffset; + { + llvm::OnDiskChainedHashTableGenerator + generator; + for (auto &entry : ObjCContexts) + generator.insert(entry.second.first, entry.second.second); + + llvm::raw_svector_ostream blobStream(hashTableBlob); + // Make sure that no bucket is at offset 0 + endian::Writer(blobStream).write(0); + tableOffset = generator.Emit(blobStream); + } + + objc_context_block::ObjCContextInfoLayout layout(writer); + layout.emit(ScratchRecord, tableOffset, hashTableBlob); + } +} + void APINotesWriter::Implementation::writeObjCPropertyBlock( llvm::BitstreamWriter &writer) { BCBlockRAII restoreBlock(writer, OBJC_PROPERTY_BLOCK_ID, 3); @@ -536,31 +700,13 @@ namespace { } /// Used to serialize the on-disk Objective-C method table. - class ObjCMethodTableInfo { + class ObjCMethodTableInfo + : public VersionedTableInfo, + ObjCMethodInfo> { public: - // (class ID, selector ID, is-instance) - using key_type = std::tuple; - using key_type_ref = key_type; - using data_type = ObjCMethodInfo; - using data_type_ref = const data_type &; - using hash_value_type = size_t; - using offset_type = unsigned; - - hash_value_type ComputeHash(key_type_ref key) { - return llvm::hash_combine(std::get<0>(key), - std::get<1>(key), - std::get<2>(key)); - } - - std::pair EmitKeyDataLength(raw_ostream &out, - key_type_ref key, - data_type_ref data) { - uint32_t keyLength = sizeof(uint32_t) + sizeof(uint32_t) + 1; - uint32_t dataLength = getFunctionInfoSize(data) + 3; - endian::Writer writer(out); - writer.write(keyLength); - writer.write(dataLength); - return { keyLength, dataLength }; + unsigned getKeyLength(key_type_ref) { + return sizeof(uint32_t) + sizeof(uint32_t) + 1; } void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { @@ -570,16 +716,18 @@ namespace { writer.write(std::get<2>(key)); } - void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data, - unsigned len) { - emitFunctionInfo(out, data); + unsigned getUnversionedInfoSize(const ObjCMethodInfo &info) { + return 1 + getFunctionInfoSize(info); + } + void emitUnversionedInfo(raw_ostream &out, const ObjCMethodInfo &info) { + uint8_t payload = info.FactoryAsInit << 2; + payload = (payload | info.DesignatedInit) << 1; + payload = (payload | info.Required); endian::Writer writer(out); + writer.write(payload); - // FIXME: Inefficient representation - writer.write(data.DesignatedInit); - writer.write(data.FactoryAsInit); - writer.write(data.Required); + emitFunctionInfo(out, info); } }; } // end anonymous namespace @@ -678,28 +826,13 @@ void APINotesWriter::Implementation::writeObjCSelectorBlock( namespace { /// Used to serialize the on-disk global variable table. - class GlobalVariableTableInfo { + class GlobalVariableTableInfo + : public VersionedTableInfo { public: - using key_type = unsigned; // name ID - using key_type_ref = key_type; - using data_type = GlobalVariableInfo; - using data_type_ref = const data_type &; - using hash_value_type = size_t; - using offset_type = unsigned; - - hash_value_type ComputeHash(key_type_ref key) { - return static_cast(llvm::hash_value(key)); - } - - std::pair EmitKeyDataLength(raw_ostream &out, - key_type_ref key, - data_type_ref data) { - uint32_t keyLength = sizeof(uint32_t); - uint32_t dataLength = getVariableInfoSize(data); - endian::Writer writer(out); - writer.write(keyLength); - writer.write(dataLength); - return { keyLength, dataLength }; + unsigned getKeyLength(key_type_ref key) { + return sizeof(uint32_t); } void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { @@ -707,9 +840,13 @@ namespace { writer.write(key); } - void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data, - unsigned len) { - emitVariableInfo(out, data); + unsigned getUnversionedInfoSize(const GlobalVariableInfo &info) { + return getVariableInfoSize(info); + } + + void emitUnversionedInfo(raw_ostream &out, + const GlobalVariableInfo &info) { + emitVariableInfo(out, info); } }; } // end anonymous namespace @@ -740,28 +877,13 @@ void APINotesWriter::Implementation::writeGlobalVariableBlock( namespace { /// Used to serialize the on-disk global function table. - class GlobalFunctionTableInfo { + class GlobalFunctionTableInfo + : public VersionedTableInfo { public: - using key_type = unsigned; // name ID - using key_type_ref = key_type; - using data_type = GlobalFunctionInfo; - using data_type_ref = const data_type &; - using hash_value_type = size_t; - using offset_type = unsigned; - - hash_value_type ComputeHash(key_type_ref key) { - return llvm::hash_value(key); - } - - std::pair EmitKeyDataLength(raw_ostream &out, - key_type_ref key, - data_type_ref data) { - uint32_t keyLength = sizeof(uint32_t); - uint32_t dataLength = getFunctionInfoSize(data); - endian::Writer writer(out); - writer.write(keyLength); - writer.write(dataLength); - return { keyLength, dataLength }; + unsigned getKeyLength(key_type_ref) { + return sizeof(uint32_t); } void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { @@ -769,9 +891,13 @@ namespace { writer.write(key); } - void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data, - unsigned len) { - emitFunctionInfo(out, data); + unsigned getUnversionedInfoSize(const GlobalFunctionInfo &info) { + return getFunctionInfoSize(info); + } + + void emitUnversionedInfo(raw_ostream &out, + const GlobalFunctionInfo &info) { + emitFunctionInfo(out, info); } }; } // end anonymous namespace @@ -803,28 +929,13 @@ void APINotesWriter::Implementation::writeGlobalFunctionBlock( namespace { /// Used to serialize the on-disk global enum constant. - class EnumConstantTableInfo { + class EnumConstantTableInfo + : public VersionedTableInfo { public: - using key_type = unsigned; // name ID - using key_type_ref = key_type; - using data_type = EnumConstantInfo; - using data_type_ref = const data_type &; - using hash_value_type = size_t; - using offset_type = unsigned; - - hash_value_type ComputeHash(key_type_ref key) { - return static_cast(llvm::hash_value(key)); - } - - std::pair EmitKeyDataLength(raw_ostream &out, - key_type_ref key, - data_type_ref data) { - uint32_t keyLength = sizeof(uint32_t); - uint32_t dataLength = getCommonEntityInfoSize(data); - endian::Writer writer(out); - writer.write(keyLength); - writer.write(dataLength); - return { keyLength, dataLength }; + unsigned getKeyLength(key_type_ref) { + return sizeof(uint32_t); } void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { @@ -832,9 +943,12 @@ namespace { writer.write(key); } - void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data, - unsigned len) { - emitCommonEntityInfo(out, data); + unsigned getUnversionedInfoSize(const EnumConstantInfo &info) { + return getCommonEntityInfoSize(info); + } + + void emitUnversionedInfo(raw_ostream &out, const EnumConstantInfo &info) { + emitCommonEntityInfo(out, info); } }; } // end anonymous namespace @@ -864,41 +978,32 @@ void APINotesWriter::Implementation::writeEnumConstantBlock( } namespace { - /// Used to serialize the on-disk tag table. - class TagTableInfo { + template + class CommonTypeTableInfo + : public VersionedTableInfo { public: - using key_type = unsigned; // name ID - using key_type_ref = key_type; - using data_type = TagInfo; - using data_type_ref = const data_type &; - using hash_value_type = size_t; - using offset_type = unsigned; - - hash_value_type ComputeHash(key_type_ref key) { - return static_cast(llvm::hash_value(key)); - } + using key_type_ref = typename CommonTypeTableInfo::key_type_ref; - std::pair EmitKeyDataLength(raw_ostream &out, - key_type_ref key, - data_type_ref data) { - uint32_t keyLength = sizeof(IdentifierID); - uint32_t dataLength = getCommonTypeInfoSize(data); - endian::Writer writer(out); - writer.write(keyLength); - writer.write(dataLength); - return { keyLength, dataLength }; + unsigned getKeyLength(key_type_ref) { + return sizeof(IdentifierID); } - void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { endian::Writer writer(out); writer.write(key); } - void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data, - unsigned len) { - emitCommonTypeInfo(out, data); + unsigned getUnversionedInfoSize(const UnversionedDataType &info) { + return getCommonTypeInfoSize(info); + } + + void emitUnversionedInfo(raw_ostream &out, + const UnversionedDataType &info) { + emitCommonTypeInfo(out, info); } }; + + /// Used to serialize the on-disk tag table. + class TagTableInfo : public CommonTypeTableInfo { }; } // end anonymous namespace void APINotesWriter::Implementation::writeTagBlock( @@ -927,40 +1032,8 @@ void APINotesWriter::Implementation::writeTagBlock( namespace { /// Used to serialize the on-disk typedef table. - class TypedefTableInfo { - public: - using key_type = unsigned; // name ID - using key_type_ref = key_type; - using data_type = TypedefInfo; - using data_type_ref = const data_type &; - using hash_value_type = size_t; - using offset_type = unsigned; - - hash_value_type ComputeHash(key_type_ref key) { - return static_cast(llvm::hash_value(key)); - } - - std::pair EmitKeyDataLength(raw_ostream &out, - key_type_ref key, - data_type_ref data) { - uint32_t keyLength = sizeof(IdentifierID); - uint32_t dataLength = getCommonTypeInfoSize(data); - endian::Writer writer(out); - writer.write(keyLength); - writer.write(dataLength); - return { keyLength, dataLength }; - } - - void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { - endian::Writer writer(out); - writer.write(key); - } - - void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data, - unsigned len) { - emitCommonTypeInfo(out, data); - } - }; + class TypedefTableInfo + : public CommonTypeTableInfo { }; } // end anonymous namespace void APINotesWriter::Implementation::writeTypedefBlock( @@ -1033,106 +1106,115 @@ void APINotesWriter::writeToStream(raw_ostream &os) { Impl.writeToStream(os); } -ContextID APINotesWriter::addObjCClass(StringRef name, - const ObjCContextInfo &info) { - IdentifierID classID = Impl.getIdentifier(name); +ContextID APINotesWriter::addObjCContext(StringRef name, bool isClass, + const ObjCContextInfo &info, + VersionTuple swiftVersion) { + IdentifierID nameID = Impl.getIdentifier(name); - std::pair key(classID, 0); + std::pair key(nameID, isClass ? 0 : 1); auto known = Impl.ObjCContexts.find(key); - if (known != Impl.ObjCContexts.end()) { - known->second.second |= info; - } else { + if (known == Impl.ObjCContexts.end()) { unsigned nextID = Impl.ObjCContexts.size() + 1; + VersionedSmallVector emptyVersionedInfo; known = Impl.ObjCContexts.insert( - std::make_pair(key, std::make_pair(nextID, info))) + std::make_pair(key, std::make_pair(nextID, emptyVersionedInfo))) .first; - Impl.ObjCContextNames[nextID] = classID; + Impl.ObjCContextNames[nextID] = nameID; } - return ContextID(known->second.first); -} - -ContextID APINotesWriter::addObjCProtocol(StringRef name, - const ObjCContextInfo &info) { - IdentifierID protocolID = Impl.getIdentifier(name); - - std::pair key(protocolID, 1); - auto known = Impl.ObjCContexts.find(key); - if (known != Impl.ObjCContexts.end()) { - known->second.second |= info; - } else { - unsigned nextID = Impl.ObjCContexts.size() + 1; - - known = Impl.ObjCContexts.insert( - std::make_pair(key, std::make_pair(nextID, info))) - .first; - - Impl.ObjCContextNames[nextID] = protocolID; + // Add this version information. + auto &versionedVec = known->second.second; + bool found = false; + for (auto &versioned : versionedVec){ + if (versioned.first == swiftVersion) { + versioned.second |= info; + found = true; + break; + } } + if (!found) + versionedVec.push_back({swiftVersion, info}); + return ContextID(known->second.first); } + void APINotesWriter::addObjCProperty(ContextID contextID, StringRef name, bool isInstance, - const ObjCPropertyInfo &info) { + const ObjCPropertyInfo &info, + VersionTuple swiftVersion) { IdentifierID nameID = Impl.getIdentifier(name); - assert(!Impl.ObjCProperties.count(std::make_tuple(contextID.Value, nameID, isInstance))); - Impl.ObjCProperties[std::make_tuple(contextID.Value, nameID, isInstance)] = info; + Impl.ObjCProperties[std::make_tuple(contextID.Value, nameID, isInstance)] + .push_back({swiftVersion, info}); } void APINotesWriter::addObjCMethod(ContextID contextID, ObjCSelectorRef selector, bool isInstanceMethod, - const ObjCMethodInfo &info) { + const ObjCMethodInfo &info, + VersionTuple swiftVersion) { SelectorID selectorID = Impl.getSelector(selector); auto key = std::tuple{ contextID.Value, selectorID, isInstanceMethod}; - assert(!Impl.ObjCMethods.count(key)); - Impl.ObjCMethods[key] = info; + Impl.ObjCMethods[key].push_back({swiftVersion, info}); // If this method is a designated initializer, update the class to note that // it has designated initializers. if (info.DesignatedInit) { assert(Impl.ObjCContexts.count({Impl.ObjCContextNames[contextID.Value], (char)0})); - Impl.ObjCContexts[{Impl.ObjCContextNames[contextID.Value], (char)0}] - .second.setHasDesignatedInits(true); + auto &versionedVec = + Impl.ObjCContexts[{Impl.ObjCContextNames[contextID.Value], (char)0}] + .second; + bool found = false; + for (auto &versioned : versionedVec) { + if (versioned.first == swiftVersion) { + versioned.second.setHasDesignatedInits(true); + found = true; + break; + } + } + + if (!found) { + versionedVec.push_back({swiftVersion, ObjCContextInfo()}); + versionedVec.back().second.setHasDesignatedInits(true); + } } } void APINotesWriter::addGlobalVariable(llvm::StringRef name, - const GlobalVariableInfo &info) { + const GlobalVariableInfo &info, + VersionTuple swiftVersion) { IdentifierID variableID = Impl.getIdentifier(name); - assert(!Impl.GlobalVariables.count(variableID)); - Impl.GlobalVariables[variableID] = info; + Impl.GlobalVariables[variableID].push_back({swiftVersion, info}); } void APINotesWriter::addGlobalFunction(llvm::StringRef name, - const GlobalFunctionInfo &info) { + const GlobalFunctionInfo &info, + VersionTuple swiftVersion) { IdentifierID nameID = Impl.getIdentifier(name); - assert(!Impl.GlobalFunctions.count(nameID)); - Impl.GlobalFunctions[nameID] = info; + Impl.GlobalFunctions[nameID].push_back({swiftVersion, info}); } void APINotesWriter::addEnumConstant(llvm::StringRef name, - const EnumConstantInfo &info) { + const EnumConstantInfo &info, + VersionTuple swiftVersion) { IdentifierID enumConstantID = Impl.getIdentifier(name); - assert(!Impl.EnumConstants.count(enumConstantID)); - Impl.EnumConstants[enumConstantID] = info; + Impl.EnumConstants[enumConstantID].push_back({swiftVersion, info}); } -void APINotesWriter::addTag(llvm::StringRef name, const TagInfo &info) { +void APINotesWriter::addTag(llvm::StringRef name, const TagInfo &info, + VersionTuple swiftVersion) { IdentifierID tagID = Impl.getIdentifier(name); - assert(!Impl.Tags.count(tagID)); - Impl.Tags[tagID] = info; + Impl.Tags[tagID].push_back({swiftVersion, info}); } -void APINotesWriter::addTypedef(llvm::StringRef name, const TypedefInfo &info) { +void APINotesWriter::addTypedef(llvm::StringRef name, const TypedefInfo &info, + VersionTuple swiftVersion) { IdentifierID typedefID = Impl.getIdentifier(name); - assert(!Impl.Typedefs.count(typedefID)); - Impl.Typedefs[typedefID] = info; + Impl.Typedefs[typedefID].push_back({swiftVersion, info}); } void APINotesWriter::addModuleOptions(ModuleOptions opts) { diff --git a/lib/APINotes/APINotesYAMLCompiler.cpp b/lib/APINotes/APINotesYAMLCompiler.cpp index 0d30731194c..8f1e5a29d4e 100644 --- a/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/lib/APINotes/APINotesYAMLCompiler.cpp @@ -14,6 +14,7 @@ #include "clang/APINotes/APINotesReader.h" #include "clang/APINotes/Types.h" #include "clang/APINotes/APINotesWriter.h" +#include "clang/Basic/VersionTuple.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/StringSet.h" #include "llvm/Support/SourceMgr.h" @@ -262,9 +263,7 @@ namespace { }; typedef std::vector TypedefsSeq; - struct Module { - StringRef Name; - AvailabilityItem Availability; + struct TopLevelItems { ClassesSeq Classes; ClassesSeq Protocols; FunctionsSeq Functions; @@ -272,6 +271,20 @@ namespace { EnumConstantsSeq EnumConstants; TagsSeq Tags; TypedefsSeq Typedefs; + }; + + struct Versioned { + VersionTuple Version; + TopLevelItems Items; + }; + + typedef std::vector VersionedSeq; + + struct Module { + StringRef Name; + AvailabilityItem Availability; + TopLevelItems TopLevel; + VersionedSeq SwiftVersions; llvm::Optional SwiftInferImportAsMember = {llvm::None}; @@ -291,6 +304,7 @@ LLVM_YAML_IS_SEQUENCE_VECTOR(GlobalVariable) LLVM_YAML_IS_SEQUENCE_VECTOR(EnumConstant) LLVM_YAML_IS_SEQUENCE_VECTOR(Tag) LLVM_YAML_IS_SEQUENCE_VECTOR(Typedef) +LLVM_YAML_IS_SEQUENCE_VECTOR(Versioned) namespace llvm { namespace yaml { @@ -335,6 +349,24 @@ namespace llvm { } }; + template <> + struct ScalarTraits { + static void output(const VersionTuple &value, void*, + llvm::raw_ostream &out) { + out << value; + } + static StringRef input(StringRef scalar, void*, VersionTuple &value) { + if (value.tryParse(scalar)) + return "not a version number in the form XX.YY"; + + // Canonicalize on '.' as a separator. + value.UseDotAsSeparator(); + return StringRef(); + } + + static bool mustQuote(StringRef) { return false; } + }; + template <> struct MappingTraits { static void mapping(IO &io, Param& p) { @@ -460,6 +492,24 @@ namespace llvm { } }; + static void mapTopLevelItems(IO &io, TopLevelItems &i) { + io.mapOptional("Classes", i.Classes); + io.mapOptional("Protocols", i.Protocols); + io.mapOptional("Functions", i.Functions); + io.mapOptional("Globals", i.Globals); + io.mapOptional("Enumerators", i.EnumConstants); + io.mapOptional("Tags", i.Tags); + io.mapOptional("Typedefs", i.Typedefs); + } + + template <> + struct MappingTraits { + static void mapping(IO &io, Versioned& v) { + io.mapRequired("Version", v.Version); + mapTopLevelItems(io, v.Items); + } + }; + template <> struct MappingTraits { static void mapping(IO &io, Module& m) { @@ -467,13 +517,10 @@ namespace llvm { io.mapOptional("Availability", m.Availability.Mode); io.mapOptional("AvailabilityMsg", m.Availability.Msg); io.mapOptional("SwiftInferImportAsMember", m.SwiftInferImportAsMember); - io.mapOptional("Classes", m.Classes); - io.mapOptional("Protocols", m.Protocols); - io.mapOptional("Functions", m.Functions); - io.mapOptional("Globals", m.Globals); - io.mapOptional("Enumerators", m.EnumConstants); - io.mapOptional("Tags", m.Tags); - io.mapOptional("Typedefs", m.Typedefs); + + mapTopLevelItems(io, m.TopLevel); + + io.mapOptional("SwiftVersions", m.SwiftVersions); } }; } @@ -624,7 +671,8 @@ namespace { // Translate from Method into ObjCMethodInfo and write it out. void convertMethod(const Method &meth, - ContextID classID, StringRef className) { + ContextID classID, StringRef className, + VersionTuple swiftVersion) { ObjCMethodInfo mInfo; if (convertCommon(meth, mInfo, meth.Selector)) @@ -662,10 +710,11 @@ namespace { // Write it. Writer->addObjCMethod(classID, selectorRef, meth.Kind == MethodKind::Instance, - mInfo); + mInfo, swiftVersion); } - void convertContext(const Class &cl, bool isClass) { + void convertContext(const Class &cl, bool isClass, + VersionTuple swiftVersion) { // Write the class. ObjCContextInfo cInfo; @@ -675,8 +724,8 @@ namespace { if (cl.AuditedForNullability) cInfo.setDefaultNullability(*DefaultNullability); - ContextID clID = isClass ? Writer->addObjCClass(cl.Name, cInfo) : - Writer->addObjCProtocol(cl.Name, cInfo); + ContextID clID = Writer->addObjCContext(cl.Name, isClass, cInfo, + swiftVersion); // Write all methods. llvm::StringMap> knownMethods; @@ -693,7 +742,7 @@ namespace { } known = true; - convertMethod(method, clID, cl.Name); + convertMethod(method, clID, cl.Name, swiftVersion); } // Write all properties. @@ -726,51 +775,45 @@ namespace { pInfo.setNullabilityAudited(*prop.Nullability); if (prop.Kind) { Writer->addObjCProperty(clID, prop.Name, - *prop.Kind == MethodKind::Instance, pInfo); + *prop.Kind == MethodKind::Instance, pInfo, + swiftVersion); } else { // Add both instance and class properties with this name. - Writer->addObjCProperty(clID, prop.Name, true, pInfo); - Writer->addObjCProperty(clID, prop.Name, false, pInfo); + Writer->addObjCProperty(clID, prop.Name, true, pInfo, swiftVersion); + Writer->addObjCProperty(clID, prop.Name, false, pInfo, swiftVersion); } } } - bool convertModule() { - if (!isAvailable(TheModule.Availability)) - return false; - - // Set up the writer. - // FIXME: This is kindof ugly. - APINotesWriter writer(TheModule.Name, SourceFile); - Writer = &writer; - + void convertTopLevelItems(const TopLevelItems &items, + VersionTuple swiftVersion) { // Write all classes. llvm::StringSet<> knownClasses; - for (const auto &cl : TheModule.Classes) { + for (const auto &cl : items.Classes) { // Check for duplicate class definitions. if (!knownClasses.insert(cl.Name).second) { emitError("multiple definitions of class '" + cl.Name + "'"); continue; } - convertContext(cl, /*isClass*/ true); + convertContext(cl, /*isClass*/ true, swiftVersion); } // Write all protocols. llvm::StringSet<> knownProtocols; - for (const auto &pr : TheModule.Protocols) { + for (const auto &pr : items.Protocols) { // Check for duplicate protocol definitions. if (!knownProtocols.insert(pr.Name).second) { emitError("multiple definitions of protocol '" + pr.Name + "'"); continue; } - convertContext(pr, /*isClass*/ false); + convertContext(pr, /*isClass*/ false, swiftVersion); } // Write all global variables. llvm::StringSet<> knownGlobals; - for (const auto &global : TheModule.Globals) { + for (const auto &global : items.Globals) { // Check for duplicate global variables. if (!knownGlobals.insert(global.Name).second) { emitError("multiple definitions of global variable '" + @@ -786,12 +829,12 @@ namespace { info.SwiftName = global.SwiftName; if (global.Nullability) info.setNullabilityAudited(*global.Nullability); - Writer->addGlobalVariable(global.Name, info); + Writer->addGlobalVariable(global.Name, info, swiftVersion); } // Write all global functions. llvm::StringSet<> knownFunctions; - for (const auto &function : TheModule.Functions) { + for (const auto &function : items.Functions) { // Check for duplicate global functions. if (!knownFunctions.insert(function.Name).second) { emitError("multiple definitions of global function '" + @@ -810,12 +853,12 @@ namespace { function.NullabilityOfRet, info, function.Name); - Writer->addGlobalFunction(function.Name, info); + Writer->addGlobalFunction(function.Name, info, swiftVersion); } // Write all enumerators. llvm::StringSet<> knownEnumConstants; - for (const auto &enumConstant : TheModule.EnumConstants) { + for (const auto &enumConstant : items.EnumConstants) { // Check for duplicate enumerators if (!knownEnumConstants.insert(enumConstant.Name).second) { emitError("multiple definitions of enumerator '" + @@ -829,12 +872,12 @@ namespace { convertAvailability(enumConstant.Availability, info, enumConstant.Name); info.SwiftPrivate = enumConstant.SwiftPrivate; info.SwiftName = enumConstant.SwiftName; - Writer->addEnumConstant(enumConstant.Name, info); + Writer->addEnumConstant(enumConstant.Name, info, swiftVersion); } // Write all tags. llvm::StringSet<> knownTags; - for (const auto &t : TheModule.Tags) { + for (const auto &t : items.Tags) { // Check for duplicate tag definitions. if (!knownTags.insert(t.Name).second) { emitError("multiple definitions Of tag '" + t.Name + "'"); @@ -845,12 +888,12 @@ namespace { if (convertCommonType(t, tagInfo, t.Name)) continue; - Writer->addTag(t.Name, tagInfo); + Writer->addTag(t.Name, tagInfo, swiftVersion); } // Write all typedefs. llvm::StringSet<> knownTypedefs; - for (const auto &t : TheModule.Typedefs) { + for (const auto &t : items.Typedefs) { // Check for duplicate typedef definitions. if (!knownTypedefs.insert(t.Name).second) { emitError("multiple definitions of typedef '" + t.Name + "'"); @@ -860,9 +903,22 @@ namespace { TypedefInfo typedefInfo; if (convertCommonType(t, typedefInfo, t.Name)) continue; - - Writer->addTypedef(t.Name, typedefInfo); + + Writer->addTypedef(t.Name, typedefInfo, swiftVersion); } + } + + bool convertModule() { + if (!isAvailable(TheModule.Availability)) + return false; + + // Set up the writer. + // FIXME: This is kindof ugly. + APINotesWriter writer(TheModule.Name, SourceFile); + Writer = &writer; + + // Write the top-level items. + convertTopLevelItems(TheModule.TopLevel, VersionTuple()); if (TheModule.SwiftInferImportAsMember) { ModuleOptions opts; @@ -870,6 +926,12 @@ namespace { Writer->addModuleOptions(opts); } + // Convert the versioned information. + for (const auto &versioned : TheModule.SwiftVersions) { + convertTopLevelItems(versioned.Items, versioned.Version); + + } + if (!ErrorOccured) Writer->writeToStream(OS); @@ -934,10 +996,34 @@ namespace { /// The module we're building. Module TheModule; + /// A known context, which tracks what we know about a context ID. + struct KnownContext { + /// Whether this is a protocol (vs. a class). + bool isProtocol; + + /// The indices into the top-level items for this context at each + /// Swift version. + SmallVector, 1> indices; + + Class &getContext(const VersionTuple &swiftVersion, + TopLevelItems &items) { + ClassesSeq &seq = isProtocol ? items.Protocols : items.Classes; + + for (auto &index : indices) { + if (index.first == swiftVersion) + return seq[index.second]; + } + + indices.push_back({swiftVersion, seq.size()}); + seq.push_back(Class()); + return seq.back(); + } + }; + /// A mapping from context ID to a pair (index, is-protocol) that indicates /// the index of that class or protocol in the global "classes" or /// "protocols" list. - llvm::DenseMap> knownContexts; + llvm::DenseMap knownContexts; /// Copy a string into allocated memory so it does disappear on us. StringRef copyString(StringRef string) { @@ -1015,30 +1101,46 @@ namespace { } } + TopLevelItems &getTopLevelItems(VersionTuple swiftVersion) { + if (!swiftVersion) return TheModule.TopLevel; + + for (auto &versioned : TheModule.SwiftVersions) { + if (versioned.Version == swiftVersion) + return versioned.Items; + } + + TheModule.SwiftVersions.push_back(Versioned()); + TheModule.SwiftVersions.back().Version = swiftVersion; + return TheModule.SwiftVersions.back().Items; + } + public: virtual void visitObjCClass(ContextID contextID, StringRef name, - const ObjCContextInfo &info) { + const ObjCContextInfo &info, + VersionTuple swiftVersion) { // Record this known context. - knownContexts[contextID.Value] = { TheModule.Classes.size(), false }; + auto &items = getTopLevelItems(swiftVersion); + auto &known = knownContexts[contextID.Value]; + known.isProtocol = false; - // Add the class. - TheModule.Classes.push_back(Class()); - handleObjCContext(TheModule.Classes.back(), name, info); + handleObjCContext(known.getContext(swiftVersion, items), name, info); } virtual void visitObjCProtocol(ContextID contextID, StringRef name, - const ObjCContextInfo &info) { + const ObjCContextInfo &info, + VersionTuple swiftVersion) { // Record this known context. - knownContexts[contextID.Value] = { TheModule.Protocols.size(), true }; + auto &items = getTopLevelItems(swiftVersion); + auto &known = knownContexts[contextID.Value]; + known.isProtocol = true; - // Add the protocol. - TheModule.Protocols.push_back(Class()); - handleObjCContext(TheModule.Protocols.back(), name, info); + handleObjCContext(known.getContext(swiftVersion, items), name, info); } virtual void visitObjCMethod(ContextID contextID, StringRef selector, bool isInstanceMethod, - const ObjCMethodInfo &info) { + const ObjCMethodInfo &info, + VersionTuple swiftVersion) { Method method; method.Selector = copyString(selector); method.Kind = isInstanceMethod ? MethodKind::Instance : MethodKind::Class; @@ -1051,16 +1153,15 @@ namespace { method.DesignatedInit = info.DesignatedInit; method.Required = info.Required; - auto known = knownContexts[contextID.Value]; - if (known.second) - TheModule.Protocols[known.first].Methods.push_back(method); - else - TheModule.Classes[known.first].Methods.push_back(method); + auto &items = getTopLevelItems(swiftVersion); + knownContexts[contextID.Value].getContext(swiftVersion, items) + .Methods.push_back(method); } virtual void visitObjCProperty(ContextID contextID, StringRef name, bool isInstance, - const ObjCPropertyInfo &info) { + const ObjCPropertyInfo &info, + VersionTuple swiftVersion) { Property property; property.Name = name; property.Kind = isInstance ? MethodKind::Instance : MethodKind::Class; @@ -1071,15 +1172,14 @@ namespace { property.Nullability = *nullability; } - auto known = knownContexts[contextID.Value]; - if (known.second) - TheModule.Protocols[known.first].Properties.push_back(property); - else - TheModule.Classes[known.first].Properties.push_back(property); + auto &items = getTopLevelItems(swiftVersion); + knownContexts[contextID.Value].getContext(swiftVersion, items) + .Properties.push_back(property); } virtual void visitGlobalFunction(StringRef name, - const GlobalFunctionInfo &info) { + const GlobalFunctionInfo &info, + VersionTuple swiftVersion) { Function function; function.Name = name; handleCommon(function, info); @@ -1088,11 +1188,13 @@ namespace { handleNullability(function.Nullability, function.NullabilityOfRet, info, info.NumAdjustedNullable-1); - TheModule.Functions.push_back(function); + auto &items = getTopLevelItems(swiftVersion); + items.Functions.push_back(function); } virtual void visitGlobalVariable(StringRef name, - const GlobalVariableInfo &info) { + const GlobalVariableInfo &info, + VersionTuple swiftVersion) { GlobalVariable global; global.Name = name; handleCommon(global, info); @@ -1102,30 +1204,37 @@ namespace { global.Nullability = *nullability; } - TheModule.Globals.push_back(global); + auto &items = getTopLevelItems(swiftVersion); + items.Globals.push_back(global); } virtual void visitEnumConstant(StringRef name, - const EnumConstantInfo &info) { + const EnumConstantInfo &info, + VersionTuple swiftVersion) { EnumConstant enumConstant; enumConstant.Name = name; handleCommon(enumConstant, info); - TheModule.EnumConstants.push_back(enumConstant); + auto &items = getTopLevelItems(swiftVersion); + items.EnumConstants.push_back(enumConstant); } - virtual void visitTag(StringRef name, const TagInfo &info) { + virtual void visitTag(StringRef name, const TagInfo &info, + VersionTuple swiftVersion) { Tag tag; tag.Name = name; handleCommonType(tag, info); - TheModule.Tags.push_back(tag); + auto &items = getTopLevelItems(swiftVersion); + items.Tags.push_back(tag); } - virtual void visitTypedef(StringRef name, const TypedefInfo &info) { + virtual void visitTypedef(StringRef name, const TypedefInfo &info, + VersionTuple swiftVersion) { Typedef td; td.Name = name; handleCommonType(td, info); - TheModule.Typedefs.push_back(td); + auto &items = getTopLevelItems(swiftVersion); + items.Typedefs.push_back(td); } /// Retrieve the module. @@ -1138,38 +1247,16 @@ static unsigned flattenPropertyKind(llvm::Optional kind) { return kind ? (*kind == MethodKind::Instance ? 2 : 1) : 0; } -bool api_notes::decompileAPINotes(std::unique_ptr input, - llvm::raw_ostream &os) { - // Try to read the file. - auto reader = APINotesReader::get(std::move(input)); - if (!reader) { - llvm::errs() << "not a well-formed API notes binary file\n"; - return true; - } - - DecompileVisitor decompileVisitor; - reader->visit(decompileVisitor); - - // Sort the data in the module, because the API notes reader doesn't preserve - // order. - auto &module = decompileVisitor.getModule(); - - // Set module name. - module.Name = reader->getModuleName(); - - // Set module options - auto opts = reader->getModuleOptions(); - if (opts.SwiftInferImportAsMember) - module.SwiftInferImportAsMember = true; - +/// Sort the items in the given block of "top-level" items. +static void sortTopLevelItems(TopLevelItems &items) { // Sort classes. - std::sort(module.Classes.begin(), module.Classes.end(), + std::sort(items.Classes.begin(), items.Classes.end(), [](const Class &lhs, const Class &rhs) -> bool { return lhs.Name < rhs.Name; }); // Sort protocols. - std::sort(module.Protocols.begin(), module.Protocols.end(), + std::sort(items.Protocols.begin(), items.Protocols.end(), [](const Class &lhs, const Class &rhs) -> bool { return lhs.Name < rhs.Name; }); @@ -1180,52 +1267,90 @@ bool api_notes::decompileAPINotes(std::unique_ptr input, std::sort(record.Properties.begin(), record.Properties.end(), [](const Property &lhs, const Property &rhs) -> bool { return lhs.Name < rhs.Name || - (lhs.Name == rhs.Name && - flattenPropertyKind(lhs.Kind) < - flattenPropertyKind(rhs.Kind)); + (lhs.Name == rhs.Name && + flattenPropertyKind(lhs.Kind) < + flattenPropertyKind(rhs.Kind)); }); // Sort methods. std::sort(record.Methods.begin(), record.Methods.end(), [](const Method &lhs, const Method &rhs) -> bool { return lhs.Selector < rhs.Selector || - (lhs.Selector == rhs.Selector && - static_cast(lhs.Kind) - < static_cast(rhs.Kind)); + (lhs.Selector == rhs.Selector && + static_cast(lhs.Kind) + < static_cast(rhs.Kind)); }); }; - std::for_each(module.Classes.begin(), module.Classes.end(), sortMembers); - std::for_each(module.Protocols.begin(), module.Protocols.end(), sortMembers); + std::for_each(items.Classes.begin(), items.Classes.end(), sortMembers); + std::for_each(items.Protocols.begin(), items.Protocols.end(), sortMembers); // Sort functions. - std::sort(module.Functions.begin(), module.Functions.end(), + std::sort(items.Functions.begin(), items.Functions.end(), [](const Function &lhs, const Function &rhs) -> bool { return lhs.Name < rhs.Name; }); // Sort global variables. - std::sort(module.Globals.begin(), module.Globals.end(), + std::sort(items.Globals.begin(), items.Globals.end(), [](const GlobalVariable &lhs, const GlobalVariable &rhs) -> bool { return lhs.Name < rhs.Name; }); // Sort enum constants. - std::sort(module.EnumConstants.begin(), module.EnumConstants.end(), + std::sort(items.EnumConstants.begin(), items.EnumConstants.end(), [](const EnumConstant &lhs, const EnumConstant &rhs) -> bool { return lhs.Name < rhs.Name; }); // Sort tags. - std::sort(module.Tags.begin(), module.Tags.end(), + std::sort(items.Tags.begin(), items.Tags.end(), [](const Tag &lhs, const Tag &rhs) -> bool { return lhs.Name < rhs.Name; }); // Sort typedefs. - std::sort(module.Typedefs.begin(), module.Typedefs.end(), + std::sort(items.Typedefs.begin(), items.Typedefs.end(), [](const Typedef &lhs, const Typedef &rhs) -> bool { return lhs.Name < rhs.Name; }); +} + +bool api_notes::decompileAPINotes(std::unique_ptr input, + llvm::raw_ostream &os) { + // Try to read the file. + auto reader = APINotesReader::get(std::move(input), VersionTuple()); + if (!reader) { + llvm::errs() << "not a well-formed API notes binary file\n"; + return true; + } + + DecompileVisitor decompileVisitor; + reader->visit(decompileVisitor); + + // Sort the data in the module, because the API notes reader doesn't preserve + // order. + auto &module = decompileVisitor.getModule(); + + // Set module name. + module.Name = reader->getModuleName(); + + // Set module options + auto opts = reader->getModuleOptions(); + if (opts.SwiftInferImportAsMember) + module.SwiftInferImportAsMember = true; + + // Sort the top-level items. + sortTopLevelItems(module.TopLevel); + + // Sort the Swift versions. + std::sort(module.SwiftVersions.begin(), module.SwiftVersions.end(), + [](const Versioned &lhs, const Versioned &rhs) -> bool { + return lhs.Version < rhs.Version; + }); + + // Sort the top-level items within each Swift version. + for (auto &versioned : module.SwiftVersions) + sortTopLevelItems(versioned.Items); // Output the YAML representation. Output yout(os); diff --git a/lib/Frontend/CompilerInstance.cpp b/lib/Frontend/CompilerInstance.cpp index a68d87b4186..d3b1df0faef 100644 --- a/lib/Frontend/CompilerInstance.cpp +++ b/lib/Frontend/CompilerInstance.cpp @@ -540,6 +540,9 @@ void CompilerInstance::createSema(TranslationUnitKind TUKind, TheSema.reset(new Sema(getPreprocessor(), getASTContext(), getASTConsumer(), TUKind, CompletionConsumer)); + // Set up API notes. + TheSema->APINotes.setSwiftVersion(getAPINotesOpts().SwiftVersion); + // If we're building a module and are supposed to load API notes, // notify the API notes manager. if (auto currentModule = getPreprocessor().getCurrentModule()) { diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index bf657706beb..c1635d55dcb 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -1473,8 +1473,14 @@ static void ParseHeaderSearchArgs(HeaderSearchOptions &Opts, ArgList &Args) { Opts.AddVFSOverlayFile(A->getValue()); } -static void ParseAPINotesArgs(APINotesOptions &Opts, ArgList &Args) { +static void ParseAPINotesArgs(APINotesOptions &Opts, ArgList &Args, + DiagnosticsEngine &diags) { using namespace options; + if (const Arg *A = Args.getLastArg(OPT_fapinotes_swift_version)) { + if (Opts.SwiftVersion.tryParse(A->getValue())) + diags.Report(diag::err_drv_invalid_value) + << A->getAsString(Args) << A->getValue(); + } for (const Arg *A : Args.filtered(OPT_iapinotes_modules)) Opts.ModuleSearchPaths.push_back(A->getValue()); } @@ -2369,7 +2375,7 @@ bool CompilerInvocation::CreateFromArgs(CompilerInvocation &Res, Success &= ParseCodeGenArgs(Res.getCodeGenOpts(), Args, DashX, Diags, Res.getTargetOpts()); ParseHeaderSearchArgs(Res.getHeaderSearchOpts(), Args); - ParseAPINotesArgs(Res.getAPINotesOpts(), Args); + ParseAPINotesArgs(Res.getAPINotesOpts(), Args, Diags); if (DashX == IK_AST || DashX == IK_LLVM_IR) { // ObjCAAutoRefCount and Sanitize LangOpts are used to setup the @@ -2502,7 +2508,19 @@ std::string CompilerInvocation::getModuleHash() const { // Extend the signature with the module file extensions. const FrontendOptions &frontendOpts = getFrontendOpts(); for (const auto &ext : frontendOpts.ModuleFileExtensions) { - code = ext->hashExtension(code); + code = hash_combine(code, ext->hashExtension(code)); + } + + // Extend the signature with the SWift version for API notes. + const APINotesOptions &apiNotesOpts = getAPINotesOpts(); + if (apiNotesOpts.SwiftVersion) { + code = hash_combine(code, apiNotesOpts.SwiftVersion.getMajor()); + if (auto minor = apiNotesOpts.SwiftVersion.getMinor()) + code = hash_combine(code, *minor); + if (auto subminor = apiNotesOpts.SwiftVersion.getSubminor()) + code = hash_combine(code, *subminor); + if (auto build = apiNotesOpts.SwiftVersion.getBuild()) + code = hash_combine(code, *build); } // Darwin-specific hack: if we have a sysroot, use the contents and diff --git a/lib/Sema/SemaAPINotes.cpp b/lib/Sema/SemaAPINotes.cpp index 29861d140f2..4a666ccbd94 100644 --- a/lib/Sema/SemaAPINotes.cpp +++ b/lib/Sema/SemaAPINotes.cpp @@ -352,8 +352,8 @@ void Sema::ProcessAPINotes(Decl *D) { // Objective-C classes. if (auto Class = dyn_cast(D)) { for (auto Reader : APINotes.findAPINotes(D->getLocation())) { - if (auto Info = Reader->lookupObjCClass(Class->getName())) { - ::ProcessAPINotes(*this, Class, Info->second); + if (auto Info = Reader->lookupObjCClassInfo(Class->getName())) { + ::ProcessAPINotes(*this, Class, *Info); } } @@ -363,8 +363,8 @@ void Sema::ProcessAPINotes(Decl *D) { // Objective-C protocols. if (auto Protocol = dyn_cast(D)) { for (auto Reader : APINotes.findAPINotes(D->getLocation())) { - if (auto Info = Reader->lookupObjCProtocol(Protocol->getName())) { - ::ProcessAPINotes(*this, Protocol, Info->second); + if (auto Info = Reader->lookupObjCProtocolInfo(Protocol->getName())) { + ::ProcessAPINotes(*this, Protocol, *Info); } } @@ -414,8 +414,8 @@ void Sema::ProcessAPINotes(Decl *D) { auto GetContext = [&](api_notes::APINotesReader *Reader) -> Optional { if (auto Protocol = dyn_cast(ObjCContainer)) { - if (auto Found = Reader->lookupObjCProtocol(Protocol->getName())) - return Found->first; + if (auto Found = Reader->lookupObjCProtocolID(Protocol->getName())) + return *Found; return None; } @@ -442,8 +442,8 @@ void Sema::ProcessAPINotes(Decl *D) { } if (auto Class = dyn_cast(ObjCContainer)) { - if (auto Found = Reader->lookupObjCClass(Class->getName())) - return Found->first; + if (auto Found = Reader->lookupObjCClassID(Class->getName())) + return *Found; return None; diff --git a/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit.apinotes b/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit.apinotes index e79a210e39a..9c855c6dc6a 100644 --- a/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit.apinotes +++ b/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit.apinotes @@ -31,3 +31,12 @@ Classes: - Selector: "initWithA:" MethodKind: Instance DesignatedInit: true +SwiftVersions: + - Version: 3.0 + Classes: + - Name: A + Methods: + - Selector: "transform:integer:" + MethodKind: Instance + NullabilityOfRet: O + Nullability: [ O, S ] diff --git a/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.apinotes b/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.apinotes new file mode 100644 index 00000000000..9c855c6dc6a --- /dev/null +++ b/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.apinotes @@ -0,0 +1,42 @@ +Name: SomeKit +Classes: + - Name: A + Methods: + - Selector: "transform:" + MethodKind: Instance + Availability: none + AvailabilityMsg: "anything but this" + - Selector: "transform:integer:" + MethodKind: Instance + NullabilityOfRet: N + Nullability: [ N, S ] + Properties: + - Name: intValue + PropertyKind: Instance + Availability: none + AvailabilityMsg: "wouldn't work anyway" + - Name: nonnullAInstance + PropertyKind: Instance + Nullability: N + - Name: nonnullAClass + PropertyKind: Class + Nullability: N + - Name: nonnullABoth + Nullability: N + - Name: B + Availability: none + AvailabilityMsg: "just don't" + - Name: C + Methods: + - Selector: "initWithA:" + MethodKind: Instance + DesignatedInit: true +SwiftVersions: + - Version: 3.0 + Classes: + - Name: A + Methods: + - Selector: "transform:integer:" + MethodKind: Instance + NullabilityOfRet: O + Nullability: [ O, S ] diff --git a/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKitExplicitNullability.h b/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKitExplicitNullability.h new file mode 100644 index 00000000000..40be241eb93 --- /dev/null +++ b/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKitExplicitNullability.h @@ -0,0 +1,5 @@ +@interface A(ExplicitNullabilityProperties) +@property (nonatomic, readwrite, retain, nonnull) A *explicitNonnullInstance; +@property (nonatomic, readwrite, retain, nullable) A *explicitNullableInstance; +@end + diff --git a/test/APINotes/Inputs/Frameworks/SomeKit.framework/PrivateHeaders/SomeKit_private.apinotes b/test/APINotes/Inputs/Frameworks/SomeKit.framework/PrivateHeaders/SomeKit_private.apinotes new file mode 100644 index 00000000000..28ede9dfa25 --- /dev/null +++ b/test/APINotes/Inputs/Frameworks/SomeKit.framework/PrivateHeaders/SomeKit_private.apinotes @@ -0,0 +1,15 @@ +Name: SomeKit +Classes: + - Name: A + Methods: + - Selector: "privateTransform:input:" + MethodKind: Instance + NullabilityOfRet: N + Nullability: [ N, S ] + Properties: + - Name: internalProperty + Nullability: N +Protocols: + - Name: InternalProtocol + Availability: none + AvailabilityMsg: "not for you" diff --git a/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/Headers/SomeOtherKit.apinotes b/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/Headers/SomeOtherKit.apinotes new file mode 100644 index 00000000000..2ad546b8f8b --- /dev/null +++ b/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/Headers/SomeOtherKit.apinotes @@ -0,0 +1,8 @@ +Name: SomeOtherKit +Classes: + - Name: A + Methods: + - Selector: "methodA" + MethodKind: Instance + Availability: none + AvailabilityMsg: "anything but this" diff --git a/test/APINotes/Inputs/roundtrip.apinotes b/test/APINotes/Inputs/roundtrip.apinotes index 10ac249817d..c0e91f93fde 100644 --- a/test/APINotes/Inputs/roundtrip.apinotes +++ b/test/APINotes/Inputs/roundtrip.apinotes @@ -146,3 +146,22 @@ Typedefs: SwiftName: Typedef SwiftBridge: '' NSErrorDomain: '' +SwiftVersions: + - Version: 3.0 + Classes: + - Name: NSCell + Availability: available + AvailabilityMsg: '' + SwiftPrivate: false + SwiftName: NSBox + SwiftBridge: '' + NSErrorDomain: '' + Methods: + - Selector: init + MethodKind: Instance + NullabilityOfRet: N + Availability: available + AvailabilityMsg: '' + SwiftPrivate: true + SwiftName: '' + DesignatedInit: true diff --git a/test/APINotes/nullability.m b/test/APINotes/nullability.m index fc149b465ea..c11cea5bd52 100644 --- a/test/APINotes/nullability.m +++ b/test/APINotes/nullability.m @@ -1,12 +1,22 @@ // RUN: rm -rf %t && mkdir -p %t // RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify +// Test with Swift version 3.0. This should only affect the few APIs that have an entry in the 3.0 tables. + +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fapinotes-swift-version=3.0 -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify -DSWIFT_VERSION_3_0 + #import int main() { A *a; - [a transform: 0 integer: 0]; // expected-warning{{null passed to a callee that requires a non-null argument}} +#if SWIFT_VERSION_3_0 + float *fp = // expected-warning{{incompatible pointer types initializing 'float *' with an expression of type 'A * _Nullable'}} + [a transform: 0 integer: 0]; +#else + float *fp = // expected-warning{{incompatible pointer types initializing 'float *' with an expression of type 'A *'}} + [a transform: 0 integer: 0]; // expected-warning{{null passed to a callee that requires a non-null argument}} +#endif [a setNonnullAInstance: 0]; // expected-warning{{null passed to a callee that requires a non-null argument}} [A setNonnullAInstance: 0]; // no warning