diff --git a/clang/include/clang/APINotes/Types.h b/clang/include/clang/APINotes/Types.h index b81d273e9521..725711ea2e11 100644 --- a/clang/include/clang/APINotes/Types.h +++ b/clang/include/clang/APINotes/Types.h @@ -1,667 +1,689 @@ //===--- Types.h - API Notes Data Types --------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file defines data types used in the representation of API notes data. // //===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_API_NOTES_TYPES_H #define LLVM_CLANG_API_NOTES_TYPES_H #include "clang/Basic/Specifiers.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/StringRef.h" #include #include namespace llvm { class raw_ostream; } namespace clang { namespace api_notes { /// The file extension used for the source representation of API notes. static const char SOURCE_APINOTES_EXTENSION[] = "apinotes"; /// The file extension used for the binary representation of API notes. static const char BINARY_APINOTES_EXTENSION[] = "apinotesc"; using llvm::ArrayRef; using llvm::StringRef; using llvm::Optional; using llvm::None; /// Opaque context ID used to refer to an Objective-C class or protocol. class ContextID { public: unsigned Value; explicit ContextID(unsigned value) : Value(value) { } }; /// Describes API notes data for any entity. /// /// This is used as the base of all API notes. class CommonEntityInfo { public: /// Message to use when this entity is unavailable. std::string UnavailableMsg; /// Whether this entity is marked unavailable. unsigned Unavailable : 1; /// Whether this entity is marked unavailable in Swift. unsigned UnavailableInSwift : 1; private: /// Whether SwiftPrivate was specified. unsigned SwiftPrivateSpecified : 1; /// Whether this entity is considered "private" to a Swift overlay. unsigned SwiftPrivate : 1; public: /// Swift name of this entity. std::string SwiftName; CommonEntityInfo() : Unavailable(0), UnavailableInSwift(0), SwiftPrivateSpecified(0), SwiftPrivate(0) { } Optional isSwiftPrivate() const { if (!SwiftPrivateSpecified) return None; return SwiftPrivate; } void setSwiftPrivate(Optional swiftPrivate) { if (swiftPrivate) { SwiftPrivateSpecified = 1; SwiftPrivate = *swiftPrivate; } else { SwiftPrivateSpecified = 0; SwiftPrivate = 0; } } friend bool operator==(const CommonEntityInfo &lhs, const CommonEntityInfo &rhs) { return lhs.UnavailableMsg == rhs.UnavailableMsg && lhs.Unavailable == rhs.Unavailable && lhs.UnavailableInSwift == rhs.UnavailableInSwift && lhs.SwiftPrivateSpecified == rhs.SwiftPrivateSpecified && lhs.SwiftPrivate == rhs.SwiftPrivate && lhs.SwiftName == rhs.SwiftName; } friend bool operator!=(const CommonEntityInfo &lhs, const CommonEntityInfo &rhs) { return !(lhs == rhs); } friend CommonEntityInfo &operator|=(CommonEntityInfo &lhs, const CommonEntityInfo &rhs) { // Merge unavailability. if (rhs.Unavailable) { lhs.Unavailable = true; if (rhs.UnavailableMsg.length() != 0 && lhs.UnavailableMsg.length() == 0) { lhs.UnavailableMsg = rhs.UnavailableMsg; } } if (rhs.UnavailableInSwift) { lhs.UnavailableInSwift = true; if (rhs.UnavailableMsg.length() != 0 && lhs.UnavailableMsg.length() == 0) { lhs.UnavailableMsg = rhs.UnavailableMsg; } } if (rhs.SwiftPrivateSpecified && !lhs.SwiftPrivateSpecified) { lhs.SwiftPrivateSpecified = 1; lhs.SwiftPrivate = rhs.SwiftPrivate; } if (rhs.SwiftName.length() != 0 && lhs.SwiftName.length() == 0) lhs.SwiftName = rhs.SwiftName; return lhs; } }; /// Describes API notes for types. class CommonTypeInfo : public CommonEntityInfo { /// The Swift type to which a given type is bridged. /// /// Reflects the swift_bridge attribute. Optional SwiftBridge; /// The NS error domain for this type. Optional NSErrorDomain; public: CommonTypeInfo() : CommonEntityInfo() { } const Optional &getSwiftBridge() const { return SwiftBridge; } void setSwiftBridge(const Optional &swiftType) { SwiftBridge = swiftType; } void setSwiftBridge(const Optional &swiftType) { if (swiftType) SwiftBridge = *swiftType; else SwiftBridge = None; } const Optional &getNSErrorDomain() const { return NSErrorDomain; } void setNSErrorDomain(const Optional &domain) { NSErrorDomain = domain; } void setNSErrorDomain(const Optional &domain) { if (domain) NSErrorDomain = *domain; else NSErrorDomain = None; } friend CommonTypeInfo &operator|=(CommonTypeInfo &lhs, const CommonTypeInfo &rhs) { static_cast(lhs) |= rhs; if (!lhs.SwiftBridge && rhs.SwiftBridge) lhs.SwiftBridge = rhs.SwiftBridge; if (!lhs.NSErrorDomain && rhs.NSErrorDomain) lhs.NSErrorDomain = rhs.NSErrorDomain; return lhs; } friend bool operator==(const CommonTypeInfo &lhs, const CommonTypeInfo &rhs) { return static_cast(lhs) == rhs && lhs.SwiftBridge == rhs.SwiftBridge && lhs.NSErrorDomain == rhs.NSErrorDomain; } friend bool operator!=(const CommonTypeInfo &lhs, const CommonTypeInfo &rhs) { return !(lhs == rhs); } }; /// Describes API notes data for an Objective-C class or protocol. class ObjCContextInfo : public CommonTypeInfo { /// Whether this class has a default nullability. unsigned HasDefaultNullability : 1; /// The default nullability. unsigned DefaultNullability : 2; /// Whether this class has designated initializers recorded. unsigned HasDesignatedInits : 1; unsigned SwiftImportAsNonGenericSpecified : 1; unsigned SwiftImportAsNonGeneric : 1; + unsigned SwiftObjCMembersSpecified : 1; + unsigned SwiftObjCMembers : 1; + public: ObjCContextInfo() : CommonTypeInfo(), HasDefaultNullability(0), DefaultNullability(0), HasDesignatedInits(0), SwiftImportAsNonGenericSpecified(false), - SwiftImportAsNonGeneric(false) + SwiftImportAsNonGeneric(false), + SwiftObjCMembersSpecified(false), + SwiftObjCMembers(false) { } /// Determine the default nullability for properties and methods of this /// class. /// /// \returns the default nullability, if implied, or None if there is no Optional getDefaultNullability() const { if (HasDefaultNullability) return static_cast(DefaultNullability); return None; } /// Set the default nullability for properties and methods of this class. void setDefaultNullability(NullabilityKind kind) { HasDefaultNullability = true; DefaultNullability = static_cast(kind); } bool hasDesignatedInits() const { return HasDesignatedInits; } void setHasDesignatedInits(bool value) { HasDesignatedInits = value; } Optional getSwiftImportAsNonGeneric() const { if (SwiftImportAsNonGenericSpecified) return SwiftImportAsNonGeneric; return None; } void setSwiftImportAsNonGeneric(Optional value) { if (value.hasValue()) { SwiftImportAsNonGenericSpecified = true; SwiftImportAsNonGeneric = value.getValue(); } else { SwiftImportAsNonGenericSpecified = false; SwiftImportAsNonGeneric = false; } } + Optional getSwiftObjCMembers() const { + if (SwiftObjCMembersSpecified) + return SwiftObjCMembers; + return None; + } + void setSwiftObjCMembers(Optional value) { + SwiftObjCMembersSpecified = value.hasValue(); + SwiftObjCMembers = value.hasValue() ? *value : false; + } + /// Strip off any information within the class information structure that is /// module-local, such as 'audited' flags. void stripModuleLocalInfo() { HasDefaultNullability = false; DefaultNullability = 0; } friend bool operator==(const ObjCContextInfo &lhs, const ObjCContextInfo &rhs) { return static_cast(lhs) == rhs && lhs.getDefaultNullability() == rhs.getDefaultNullability() && lhs.HasDesignatedInits == rhs.HasDesignatedInits && - lhs.getSwiftImportAsNonGeneric() == rhs.getSwiftImportAsNonGeneric(); + lhs.getSwiftImportAsNonGeneric() == + rhs.getSwiftImportAsNonGeneric() && + lhs.getSwiftObjCMembers() == rhs.getSwiftObjCMembers(); } friend bool operator!=(const ObjCContextInfo &lhs, const ObjCContextInfo &rhs) { return !(lhs == rhs); } friend ObjCContextInfo &operator|=(ObjCContextInfo &lhs, const ObjCContextInfo &rhs) { // Merge inherited info. static_cast(lhs) |= rhs; // Merge nullability. if (!lhs.getDefaultNullability()) { if (auto nullable = rhs.getDefaultNullability()) { lhs.setDefaultNullability(*nullable); } } if (!lhs.SwiftImportAsNonGenericSpecified && rhs.SwiftImportAsNonGenericSpecified) { lhs.SwiftImportAsNonGenericSpecified = true; lhs.SwiftImportAsNonGeneric = rhs.SwiftImportAsNonGeneric; } + if (!lhs.SwiftObjCMembersSpecified && rhs.SwiftObjCMembersSpecified) { + lhs.SwiftObjCMembersSpecified = true; + lhs.SwiftObjCMembers = rhs.SwiftObjCMembers; + } + lhs.HasDesignatedInits |= rhs.HasDesignatedInits; return lhs; } void dump(llvm::raw_ostream &os); }; /// API notes for a variable/property. class VariableInfo : public CommonEntityInfo { /// Whether this property has been audited for nullability. unsigned NullabilityAudited : 1; /// The kind of nullability for this property. Only valid if the nullability /// has been audited. unsigned Nullable : 2; /// The C type of the variable, as a string. std::string Type; public: VariableInfo() : CommonEntityInfo(), NullabilityAudited(false), Nullable(0) { } Optional getNullability() const { if (NullabilityAudited) return static_cast(Nullable); return None; } void setNullabilityAudited(NullabilityKind kind) { NullabilityAudited = true; Nullable = static_cast(kind); } const std::string &getType() const { return Type; } void setType(const std::string &type) { Type = type; } friend bool operator==(const VariableInfo &lhs, const VariableInfo &rhs) { return static_cast(lhs) == rhs && lhs.NullabilityAudited == rhs.NullabilityAudited && lhs.Nullable == rhs.Nullable && lhs.Type == rhs.Type; } friend bool operator!=(const VariableInfo &lhs, const VariableInfo &rhs) { return !(lhs == rhs); } friend VariableInfo &operator|=(VariableInfo &lhs, const VariableInfo &rhs) { static_cast(lhs) |= rhs; if (!lhs.NullabilityAudited && rhs.NullabilityAudited) lhs.setNullabilityAudited(*rhs.getNullability()); if (lhs.Type.empty() && !rhs.Type.empty()) lhs.Type = rhs.Type; return lhs; } }; /// Describes API notes data for an Objective-C property. class ObjCPropertyInfo : public VariableInfo { unsigned SwiftImportAsAccessorsSpecified : 1; unsigned SwiftImportAsAccessors : 1; public: ObjCPropertyInfo() : VariableInfo(), SwiftImportAsAccessorsSpecified(false), SwiftImportAsAccessors(false) {} /// Merge class-wide information into the given property. friend ObjCPropertyInfo &operator|=(ObjCPropertyInfo &lhs, const ObjCContextInfo &rhs) { static_cast(lhs) |= rhs; // Merge nullability. if (!lhs.getNullability()) { if (auto nullable = rhs.getDefaultNullability()) { lhs.setNullabilityAudited(*nullable); } } return lhs; } Optional getSwiftImportAsAccessors() const { if (SwiftImportAsAccessorsSpecified) return SwiftImportAsAccessors; return None; } void setSwiftImportAsAccessors(Optional value) { if (value.hasValue()) { SwiftImportAsAccessorsSpecified = true; SwiftImportAsAccessors = value.getValue(); } else { SwiftImportAsAccessorsSpecified = false; SwiftImportAsAccessors = false; } } friend ObjCPropertyInfo &operator|=(ObjCPropertyInfo &lhs, const ObjCPropertyInfo &rhs) { lhs |= static_cast(rhs); if (!lhs.SwiftImportAsAccessorsSpecified && rhs.SwiftImportAsAccessorsSpecified) { lhs.SwiftImportAsAccessorsSpecified = true; lhs.SwiftImportAsAccessors = rhs.SwiftImportAsAccessors; } return lhs; } friend bool operator==(const ObjCPropertyInfo &lhs, const ObjCPropertyInfo &rhs) { return static_cast(lhs) == rhs && lhs.getSwiftImportAsAccessors() == rhs.getSwiftImportAsAccessors(); } }; /// Describes a function or method parameter. class ParamInfo : public VariableInfo { /// Whether noescape was specified. unsigned NoEscapeSpecified : 1; /// Whether the this parameter has the 'noescape' attribute. unsigned NoEscape : 1; public: ParamInfo() : VariableInfo(), NoEscapeSpecified(false), NoEscape(false) { } Optional isNoEscape() const { if (!NoEscapeSpecified) return None; return NoEscape; } void setNoEscape(Optional noescape) { if (noescape) { NoEscapeSpecified = true; NoEscape = *noescape; } else { NoEscapeSpecified = false; NoEscape = false; } } friend ParamInfo &operator|=(ParamInfo &lhs, const ParamInfo &rhs) { static_cast(lhs) |= rhs; if (!lhs.NoEscapeSpecified && rhs.NoEscapeSpecified) { lhs.NoEscapeSpecified = true; lhs.NoEscape = rhs.NoEscape; } return lhs; } friend bool operator==(const ParamInfo &lhs, const ParamInfo &rhs) { return static_cast(lhs) == rhs && lhs.NoEscapeSpecified == rhs.NoEscapeSpecified && lhs.NoEscape == rhs.NoEscape; } friend bool operator!=(const ParamInfo &lhs, const ParamInfo &rhs) { return !(lhs == rhs); } }; /// A temporary reference to an Objective-C selector, suitable for /// referencing selector data on the stack. /// /// Instances of this struct do not store references to any of the /// data they contain; it is up to the user to ensure that the data /// referenced by the identifier list persists. struct ObjCSelectorRef { unsigned NumPieces; ArrayRef Identifiers; }; /// API notes for a function or method. class FunctionInfo : public CommonEntityInfo { private: static unsigned const NullabilityKindMask = 0x3; static unsigned const NullabilityKindSize = 2; public: /// Whether the signature has been audited with respect to nullability. /// If yes, we consider all types to be non-nullable unless otherwise noted. /// If this flag is not set, the pointer types are considered to have /// unknown nullability. unsigned NullabilityAudited : 1; /// Number of types whose nullability is encoded with the NullabilityPayload. unsigned NumAdjustedNullable : 8; /// Stores the nullability of the return type and the parameters. // NullabilityKindSize bits are used to encode the nullability. The info // about the return type is stored at position 0, followed by the nullability // of the parameters. uint64_t NullabilityPayload = 0; /// The result type of this function, as a C type. std::string ResultType; /// The function parameters. std::vector Params; FunctionInfo() : CommonEntityInfo(), NullabilityAudited(false), NumAdjustedNullable(0) { } static unsigned getMaxNullabilityIndex() { return ((sizeof(NullabilityPayload) * CHAR_BIT)/NullabilityKindSize); } void addTypeInfo(unsigned index, NullabilityKind kind) { assert(index <= getMaxNullabilityIndex()); assert(static_cast(kind) < NullabilityKindMask); NullabilityAudited = true; if (NumAdjustedNullable < index + 1) NumAdjustedNullable = index + 1; // Mask the bits. NullabilityPayload &= ~(NullabilityKindMask << (index * NullabilityKindSize)); // Set the value. unsigned kindValue = (static_cast(kind)) << (index * NullabilityKindSize); NullabilityPayload |= kindValue; } /// Adds the return type info. void addReturnTypeInfo(NullabilityKind kind) { addTypeInfo(0, kind); } /// Adds the parameter type info. void addParamTypeInfo(unsigned index, NullabilityKind kind) { addTypeInfo(index + 1, kind); } private: NullabilityKind getTypeInfo(unsigned index) const { assert(NullabilityAudited && "Checking the type adjustment on non-audited method."); // If we don't have info about this parameter, return the default. if (index > NumAdjustedNullable) return NullabilityKind::NonNull; return static_cast(( NullabilityPayload >> (index * NullabilityKindSize) ) & NullabilityKindMask); } public: NullabilityKind getParamTypeInfo(unsigned index) const { return getTypeInfo(index + 1); } NullabilityKind getReturnTypeInfo() const { return getTypeInfo(0); } friend bool operator==(const FunctionInfo &lhs, const FunctionInfo &rhs) { return static_cast(lhs) == rhs && lhs.NullabilityAudited == rhs.NullabilityAudited && lhs.NumAdjustedNullable == rhs.NumAdjustedNullable && lhs.NullabilityPayload == rhs.NullabilityPayload && lhs.ResultType == rhs.ResultType && lhs.Params == rhs.Params; } friend bool operator!=(const FunctionInfo &lhs, const FunctionInfo &rhs) { return !(lhs == rhs); } }; /// Describes API notes data for an Objective-C method. class ObjCMethodInfo : public FunctionInfo { public: /// Whether this is a designated initializer of its class. unsigned DesignatedInit : 1; /// Whether this is a required initializer. unsigned Required : 1; ObjCMethodInfo() : FunctionInfo(), DesignatedInit(false), Required(false) { } friend bool operator==(const ObjCMethodInfo &lhs, const ObjCMethodInfo &rhs) { return static_cast(lhs) == rhs && lhs.DesignatedInit == rhs.DesignatedInit && lhs.Required == rhs.Required; } friend bool operator!=(const ObjCMethodInfo &lhs, const ObjCMethodInfo &rhs) { return !(lhs == rhs); } void mergePropInfoIntoSetter(const ObjCPropertyInfo &pInfo); void mergePropInfoIntoGetter(const ObjCPropertyInfo &pInfo); /// Merge class-wide information into the given method. friend ObjCMethodInfo &operator|=(ObjCMethodInfo &lhs, const ObjCContextInfo &rhs) { // Merge nullability. if (!lhs.NullabilityAudited) { if (auto nullable = rhs.getDefaultNullability()) { lhs.NullabilityAudited = true; lhs.addTypeInfo(0, *nullable); } } return lhs; } void dump(llvm::raw_ostream &os); }; /// Describes API notes data for a global variable. class GlobalVariableInfo : public VariableInfo { public: GlobalVariableInfo() : VariableInfo() { } }; /// Describes API notes data for a global function. class GlobalFunctionInfo : public FunctionInfo { public: GlobalFunctionInfo() : FunctionInfo() { } }; /// Describes API notes data for an enumerator. class EnumConstantInfo : public CommonEntityInfo { public: EnumConstantInfo() : CommonEntityInfo() { } }; /// Describes API notes data for a tag. class TagInfo : public CommonTypeInfo { public: TagInfo() : CommonTypeInfo() { } }; /// The kind of a swift_wrapper/swift_newtype. enum class SwiftWrapperKind { None, Struct, Enum }; /// Describes API notes data for a typedef. class TypedefInfo : public CommonTypeInfo { public: Optional SwiftWrapper; TypedefInfo() : CommonTypeInfo() { } }; /// Descripts a series of options for a module struct ModuleOptions { bool SwiftInferImportAsMember = false; }; } // end namespace api_notes } // end namespace clang #endif // LLVM_CLANG_API_NOTES_TYPES_H diff --git a/clang/lib/APINotes/APINotesFormat.h b/clang/lib/APINotes/APINotesFormat.h index 1e257f3ada3a..5a0cdbdd050b 100644 --- a/clang/lib/APINotes/APINotesFormat.h +++ b/clang/lib/APINotes/APINotesFormat.h @@ -1,308 +1,308 @@ //===--- APINotesFormat.h - The internals of API notes files ----*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// /// /// \file /// \brief Contains various constants and helper types to deal with API notes /// files. /// //===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_API_NOTES_FORMAT_H #define LLVM_CLANG_API_NOTES_FORMAT_H #include "llvm/ADT/DenseMapInfo.h" #include "llvm/ADT/Hashing.h" #include "llvm/ADT/PointerEmbeddedInt.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Bitcode/RecordLayout.h" namespace clang { namespace api_notes { using namespace llvm; /// Magic number for API notes files. const unsigned char API_NOTES_SIGNATURE[] = { 0xE2, 0x9C, 0xA8, 0x01 }; /// API notes file major version number. /// 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 = 22; // SwiftImportAsNonGeneric +const uint16_t VERSION_MINOR = 23; // SwiftObjCMembers using IdentifierID = PointerEmbeddedInt; using IdentifierIDField = BCVBR<16>; using SelectorID = PointerEmbeddedInt; using SelectorIDField = BCVBR<16>; using StoredContextID = PointerEmbeddedInt; /// The various types of blocks that can occur within a API notes file. /// /// These IDs must \em not be renumbered or reordered without incrementing /// VERSION_MAJOR. enum BlockID { /// The control block, which contains all of the information that needs to /// be validated prior to committing to loading the API notes file. /// /// \sa control_block CONTROL_BLOCK_ID = llvm::bitc::FIRST_APPLICATION_BLOCKID, /// The identifier data block, which maps identifier strings to IDs. IDENTIFIER_BLOCK_ID, /// 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 /// (class name, property name) pairs to information about the /// property. OBJC_PROPERTY_BLOCK_ID, /// The Objective-C property data block, which maps Objective-C /// (class name, selector, is_instance_method) tuples to information /// about the method. OBJC_METHOD_BLOCK_ID, /// The Objective-C selector data block, which maps Objective-C /// selector names (# of pieces, identifier IDs) to the selector ID /// used in other tables. OBJC_SELECTOR_BLOCK_ID, /// The global variables data block, which maps global variable names to /// information about the global variable. GLOBAL_VARIABLE_BLOCK_ID, /// The (global) functions data block, which maps global function names to /// information about the global function. GLOBAL_FUNCTION_BLOCK_ID, /// The tag data block, which maps tag names to information about /// the tags. TAG_BLOCK_ID, /// The typedef data block, which maps typedef names to information about /// the typedefs. TYPEDEF_BLOCK_ID, /// The enum constant data block, which maps enumerator names to /// information about the enumerators. ENUM_CONSTANT_BLOCK_ID, }; namespace control_block { // These IDs must \em not be renumbered or reordered without incrementing // VERSION_MAJOR. enum { METADATA = 1, MODULE_NAME = 2, MODULE_OPTIONS = 3, SOURCE_FILE = 4, }; using MetadataLayout = BCRecordLayout< METADATA, // ID BCFixed<16>, // Module format major version BCFixed<16> // Module format minor version >; using ModuleNameLayout = BCRecordLayout< MODULE_NAME, BCBlob // Module name >; using ModuleOptionsLayout = BCRecordLayout< MODULE_OPTIONS, BCFixed<1> // SwiftInferImportAsMember >; using SourceFileLayout = BCRecordLayout< SOURCE_FILE, BCVBR<16>, // file size BCVBR<16> // creation time >; } namespace identifier_block { enum { IDENTIFIER_DATA = 1, }; using IdentifierDataLayout = BCRecordLayout< IDENTIFIER_DATA, // record ID BCVBR<16>, // table offset within the blob (see below) BCBlob // map from identifier strings to decl kinds / decl IDs >; } namespace objc_context_block { enum { OBJC_CONTEXT_ID_DATA = 1, OBJC_CONTEXT_INFO_DATA = 2, }; using ObjCContextIDLayout = BCRecordLayout< OBJC_CONTEXT_ID_DATA, // record ID BCVBR<16>, // table offset within the blob (see below) 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. >; } namespace objc_property_block { enum { OBJC_PROPERTY_DATA = 1, }; using ObjCPropertyDataLayout = BCRecordLayout< OBJC_PROPERTY_DATA, // record ID BCVBR<16>, // table offset within the blob (see below) BCBlob // map from ObjC (class name, property name) pairs to ObjC // property information >; } namespace objc_method_block { enum { OBJC_METHOD_DATA = 1, }; using ObjCMethodDataLayout = BCRecordLayout< OBJC_METHOD_DATA, // record ID BCVBR<16>, // table offset within the blob (see below) BCBlob // map from ObjC (class names, selector, // is-instance-method) tuples to ObjC method information >; } namespace objc_selector_block { enum { OBJC_SELECTOR_DATA = 1, }; using ObjCSelectorDataLayout = BCRecordLayout< OBJC_SELECTOR_DATA, // record ID BCVBR<16>, // table offset within the blob (see below) BCBlob // map from (# pieces, identifier IDs) to Objective-C selector ID. >; } namespace global_variable_block { enum { GLOBAL_VARIABLE_DATA = 1 }; using GlobalVariableDataLayout = BCRecordLayout< GLOBAL_VARIABLE_DATA, // record ID BCVBR<16>, // table offset within the blob (see below) BCBlob // map from name to global variable information >; } namespace global_function_block { enum { GLOBAL_FUNCTION_DATA = 1 }; using GlobalFunctionDataLayout = BCRecordLayout< GLOBAL_FUNCTION_DATA, // record ID BCVBR<16>, // table offset within the blob (see below) BCBlob // map from name to global function information >; } namespace tag_block { enum { TAG_DATA = 1 }; using TagDataLayout = BCRecordLayout< TAG_DATA, // record ID BCVBR<16>, // table offset within the blob (see below) BCBlob // map from name to tag information >; }; namespace typedef_block { enum { TYPEDEF_DATA = 1 }; using TypedefDataLayout = BCRecordLayout< TYPEDEF_DATA, // record ID BCVBR<16>, // table offset within the blob (see below) BCBlob // map from name to typedef information >; }; namespace enum_constant_block { enum { ENUM_CONSTANT_DATA = 1 }; using EnumConstantDataLayout = BCRecordLayout< ENUM_CONSTANT_DATA, // record ID BCVBR<16>, // table offset within the blob (see below) BCBlob // map from name to enumerator information >; } /// A stored Objective-C selector. struct StoredObjCSelector { unsigned NumPieces; llvm::SmallVector Identifiers; }; } // end namespace api_notes } // end namespace clang namespace llvm { template<> struct DenseMapInfo { typedef DenseMapInfo UnsignedInfo; static inline clang::api_notes::StoredObjCSelector getEmptyKey() { return clang::api_notes::StoredObjCSelector{ UnsignedInfo::getEmptyKey(), { } }; } static inline clang::api_notes::StoredObjCSelector getTombstoneKey() { return clang::api_notes::StoredObjCSelector{ UnsignedInfo::getTombstoneKey(), { } }; } static unsigned getHashValue( const clang::api_notes::StoredObjCSelector& value) { auto hash = llvm::hash_value(value.NumPieces); hash = hash_combine(hash, value.Identifiers.size()); for (auto piece : value.Identifiers) hash = hash_combine(hash, static_cast(piece)); // FIXME: Mix upper/lower 32-bit values together to produce // unsigned rather than truncating. return hash; } static bool isEqual(const clang::api_notes::StoredObjCSelector &lhs, const clang::api_notes::StoredObjCSelector &rhs) { return lhs.NumPieces == rhs.NumPieces && lhs.Identifiers == rhs.Identifiers; } }; } #endif // LLVM_CLANG_API_NOTES_FORMAT_H diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp index 587903fccfb7..d9a4297d9139 100644 --- a/clang/lib/APINotes/APINotesReader.cpp +++ b/clang/lib/APINotes/APINotesReader.cpp @@ -1,1861 +1,1865 @@ //===--- APINotesReader.cpp - Side Car Reader --------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file implements the \c APINotesReader class that reads source // API notes data providing additional information about source code as // a separate input, such as the non-nil/nilable annotations for // method parameters. // //===----------------------------------------------------------------------===// #include "clang/APINotes/APINotesReader.h" #include "APINotesFormat.h" #include "llvm/Bitcode/BitstreamReader.h" #include "llvm/Support/EndianStream.h" #include "llvm/Support/OnDiskHashTable.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/Hashing.h" #include "llvm/ADT/StringExtras.h" using namespace clang; using namespace api_notes; 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)dataBefore; 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++; info.Unavailable = (unavailableBits >> 1) & 0x01; info.UnavailableInSwift = unavailableBits & 0x01; if ((unavailableBits >> 2) & 0x01) info.setSwiftPrivate(static_cast((unavailableBits >> 3) & 0x01)); unsigned msgLength = endian::readNext(data); info.UnavailableMsg = std::string(reinterpret_cast(data), reinterpret_cast(data) + msgLength); data += msgLength; unsigned swiftNameLength = endian::readNext(data); info.SwiftName = std::string(reinterpret_cast(data), reinterpret_cast(data) + swiftNameLength); data += swiftNameLength; } /// Read serialized CommonTypeInfo. void readCommonTypeInfo(const uint8_t *&data, CommonTypeInfo &info) { readCommonEntityInfo(data, info); unsigned swiftBridgeLength = endian::readNext(data); if (swiftBridgeLength > 0) { info.setSwiftBridge( std::string(reinterpret_cast(data), swiftBridgeLength-1)); data += swiftBridgeLength-1; } unsigned errorDomainLength = endian::readNext(data); if (errorDomainLength > 0) { info.setNSErrorDomain( std::string(reinterpret_cast(data), errorDomainLength-1)); data += errorDomainLength-1; } } /// Used to deserialize the on-disk identifier table. class IdentifierTableInfo { public: using internal_key_type = StringRef; using external_key_type = StringRef; using data_type = IdentifierID; using hash_value_type = uint32_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::HashString(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) { return StringRef(reinterpret_cast(data), length); } static data_type ReadData(internal_key_type key, const uint8_t *data, unsigned length) { return endian::readNext(data); } }; /// Used to deserialize the on-disk Objective-C class table. class ObjCContextIDTableInfo { public: // identifier ID, is-protocol using internal_key_type = std::pair; using external_key_type = internal_key_type; using data_type = unsigned; 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); auto isProtocol = endian::readNext(data); return { nameID, isProtocol }; } static data_type ReadData(internal_key_type key, const uint8_t *data, unsigned length) { 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)); payload >>= 3; + if (payload & (1 << 1)) + info.setSwiftObjCMembers(payload & 1); + payload >>= 2; + if (payload & (1 << 1)) info.setSwiftImportAsNonGeneric(payload & 1); return info; } }; /// Read serialized VariableInfo. void readVariableInfo(const uint8_t *&data, VariableInfo &info) { readCommonEntityInfo(data, info); if (*data++) { info.setNullabilityAudited(static_cast(*data)); } ++data; auto typeLen = endian::readNext(data); info.setType(std::string(data, data + typeLen)); data += typeLen; } /// Used to deserialize the on-disk Objective-C property table. class ObjCPropertyTableInfo : public VersionedTableInfo, ObjCPropertyInfo> { public: static internal_key_type ReadKey(const uint8_t *data, unsigned length) { auto classID = endian::readNext(data); auto nameID = endian::readNext(data); char isInstance = endian::readNext(data); return std::make_tuple(classID, nameID, isInstance); } static ObjCPropertyInfo readUnversioned(internal_key_type key, const uint8_t *&data) { ObjCPropertyInfo info; readVariableInfo(data, info); uint8_t flags = *data++; if (flags & (1 << 0)) info.setSwiftImportAsAccessors(flags & (1 << 1)); return info; } }; /// Read serialized ParamInfo. void readParamInfo(const uint8_t *&data, ParamInfo &info) { readVariableInfo(data, info); uint8_t payload = endian::readNext(data); if (payload & 0x01) { info.setNoEscape(payload & 0x02); } payload >>= 2; assert(payload == 0 && "Bad API notes"); } /// Read serialized FunctionInfo. void readFunctionInfo(const uint8_t *&data, FunctionInfo &info) { readCommonEntityInfo(data, info); info.NullabilityAudited = endian::readNext(data); info.NumAdjustedNullable = endian::readNext(data); info.NullabilityPayload = endian::readNext(data); unsigned numParams = endian::readNext(data); while (numParams > 0) { ParamInfo pi; readParamInfo(data, pi); info.Params.push_back(pi); --numParams; } unsigned resultTypeLen = endian::readNext(data); info.ResultType = std::string(data, data + resultTypeLen); data += resultTypeLen; } /// Used to deserialize the on-disk Objective-C method table. class ObjCMethodTableInfo : public VersionedTableInfo, ObjCMethodInfo> { public: static internal_key_type ReadKey(const uint8_t *data, unsigned length) { auto classID = endian::readNext(data); auto selectorID = endian::readNext(data); auto isInstance = endian::readNext(data); return internal_key_type{ classID, selectorID, isInstance }; } 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; readFunctionInfo(data, info); return info; } }; /// Used to deserialize the on-disk Objective-C selector table. class ObjCSelectorTableInfo { public: using internal_key_type = StoredObjCSelector; using external_key_type = internal_key_type; using data_type = SelectorID; using hash_value_type = unsigned; 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::DenseMapInfo::getHashValue(key); } static bool EqualKey(internal_key_type lhs, internal_key_type rhs) { return llvm::DenseMapInfo::isEqual(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) { internal_key_type key; key.NumPieces = endian::readNext(data); unsigned numIdents = (length - sizeof(uint16_t)) / sizeof(uint32_t); for (unsigned i = 0; i != numIdents; ++i) { key.Identifiers.push_back( endian::readNext(data)); } return key; } static data_type ReadData(internal_key_type key, const uint8_t *data, unsigned length) { return endian::readNext(data); } }; /// Used to deserialize the on-disk global variable table. class GlobalVariableTableInfo : public VersionedTableInfo { public: static internal_key_type ReadKey(const uint8_t *data, unsigned length) { auto nameID = endian::readNext(data); return nameID; } static GlobalVariableInfo readUnversioned(internal_key_type key, const uint8_t *&data) { GlobalVariableInfo info; readVariableInfo(data, info); return info; } }; /// Used to deserialize the on-disk global function table. class GlobalFunctionTableInfo : public VersionedTableInfo { public: static internal_key_type ReadKey(const uint8_t *data, unsigned length) { auto nameID = endian::readNext(data); return nameID; } static GlobalFunctionInfo readUnversioned(internal_key_type key, const uint8_t *&data) { GlobalFunctionInfo info; readFunctionInfo(data, info); return info; } }; /// Used to deserialize the on-disk enumerator table. class EnumConstantTableInfo : public VersionedTableInfo { public: static internal_key_type ReadKey(const uint8_t *data, unsigned length) { auto nameID = endian::readNext(data); return nameID; } static EnumConstantInfo readUnversioned(internal_key_type key, const uint8_t *&data) { EnumConstantInfo info; readCommonEntityInfo(data, info); return info; } }; /// Used to deserialize the on-disk tag table. class TagTableInfo : public VersionedTableInfo { public: static internal_key_type ReadKey(const uint8_t *data, unsigned length) { auto nameID = endian::readNext(data); return nameID; } static TagInfo readUnversioned(internal_key_type key, const uint8_t *&data) { TagInfo info; readCommonTypeInfo(data, info); return info; } }; /// Used to deserialize the on-disk typedef table. class TypedefTableInfo : public VersionedTableInfo { public: static internal_key_type ReadKey(const uint8_t *data, unsigned length) { auto nameID = endian::readNext(data); return nameID; } static TypedefInfo readUnversioned(internal_key_type key, const uint8_t *&data) { TypedefInfo info; uint8_t payload = *data++; if (payload > 0) { info.SwiftWrapper = static_cast((payload & 0x3) - 1); } readCommonTypeInfo(data, info); return info; } }; } // end anonymous namespace class APINotesReader::Implementation { public: /// The input buffer for the API notes data. llvm::MemoryBuffer *InputBuffer; /// Whether we own the input buffer. bool OwnsInputBuffer; /// The Swift version to use for filtering. VersionTuple SwiftVersion; /// The name of the module that we read from the control block. std::string ModuleName; // The size and modification time of the source file from // which this API notes file was created, if known. Optional> SourceFileSizeAndModTime; /// Various options and attributes for the module ModuleOptions ModuleOpts; using SerializedIdentifierTable = llvm::OnDiskIterableChainedHashTable; /// The identifier table. std::unique_ptr IdentifierTable; using SerializedObjCContextIDTable = llvm::OnDiskIterableChainedHashTable; /// The Objective-C context ID table. std::unique_ptr ObjCContextIDTable; using SerializedObjCContextInfoTable = llvm::OnDiskIterableChainedHashTable; /// The Objective-C context info table. std::unique_ptr ObjCContextInfoTable; using SerializedObjCPropertyTable = llvm::OnDiskIterableChainedHashTable; /// The Objective-C property table. std::unique_ptr ObjCPropertyTable; using SerializedObjCMethodTable = llvm::OnDiskIterableChainedHashTable; /// The Objective-C method table. std::unique_ptr ObjCMethodTable; using SerializedObjCSelectorTable = llvm::OnDiskIterableChainedHashTable; /// The Objective-C selector table. std::unique_ptr ObjCSelectorTable; using SerializedGlobalVariableTable = llvm::OnDiskIterableChainedHashTable; /// The global variable table. std::unique_ptr GlobalVariableTable; using SerializedGlobalFunctionTable = llvm::OnDiskIterableChainedHashTable; /// The global function table. std::unique_ptr GlobalFunctionTable; using SerializedEnumConstantTable = llvm::OnDiskIterableChainedHashTable; /// The enumerator table. std::unique_ptr EnumConstantTable; using SerializedTagTable = llvm::OnDiskIterableChainedHashTable; /// The tag table. std::unique_ptr TagTable; using SerializedTypedefTable = llvm::OnDiskIterableChainedHashTable; /// The typedef table. std::unique_ptr TypedefTable; /// Retrieve the identifier ID for the given string, or an empty /// optional if the string is unknown. Optional getIdentifier(StringRef str); /// Retrieve the selector ID for the given selector, or an empty /// optional if the string is unknown. Optional getSelector(ObjCSelectorRef selector); bool readControlBlock(llvm::BitstreamCursor &cursor, SmallVectorImpl &scratch); bool readIdentifierBlock(llvm::BitstreamCursor &cursor, SmallVectorImpl &scratch); bool readObjCContextBlock(llvm::BitstreamCursor &cursor, SmallVectorImpl &scratch); bool readObjCPropertyBlock(llvm::BitstreamCursor &cursor, SmallVectorImpl &scratch); bool readObjCMethodBlock(llvm::BitstreamCursor &cursor, SmallVectorImpl &scratch); bool readObjCSelectorBlock(llvm::BitstreamCursor &cursor, SmallVectorImpl &scratch); bool readGlobalVariableBlock(llvm::BitstreamCursor &cursor, SmallVectorImpl &scratch); bool readGlobalFunctionBlock(llvm::BitstreamCursor &cursor, SmallVectorImpl &scratch); bool readEnumConstantBlock(llvm::BitstreamCursor &cursor, SmallVectorImpl &scratch); bool readTagBlock(llvm::BitstreamCursor &cursor, SmallVectorImpl &scratch); bool readTypedefBlock(llvm::BitstreamCursor &cursor, SmallVectorImpl &scratch); }; Optional APINotesReader::Implementation::getIdentifier( StringRef str) { if (!IdentifierTable) return None; if (str.empty()) return IdentifierID(0); auto known = IdentifierTable->find(str); if (known == IdentifierTable->end()) return None; return *known; } Optional APINotesReader::Implementation::getSelector( ObjCSelectorRef selector) { if (!ObjCSelectorTable || !IdentifierTable) return None; // Translate the identifiers. StoredObjCSelector key; key.NumPieces = selector.NumPieces; for (auto ident : selector.Identifiers) { if (auto identID = getIdentifier(ident)) { key.Identifiers.push_back(*identID); } else { return None; } } auto known = ObjCSelectorTable->find(key); if (known == ObjCSelectorTable->end()) return None; return *known; } bool APINotesReader::Implementation::readControlBlock( llvm::BitstreamCursor &cursor, SmallVectorImpl &scratch) { if (cursor.EnterSubBlock(CONTROL_BLOCK_ID)) return true; bool sawMetadata = false; auto next = cursor.advance(); while (next.Kind != llvm::BitstreamEntry::EndBlock) { if (next.Kind == llvm::BitstreamEntry::Error) return true; if (next.Kind == llvm::BitstreamEntry::SubBlock) { // Unknown metadata sub-block, possibly for use by a future version of the // API notes format. if (cursor.SkipBlock()) return true; next = cursor.advance(); continue; } scratch.clear(); StringRef blobData; unsigned kind = cursor.readRecord(next.ID, scratch, &blobData); switch (kind) { case control_block::METADATA: // Already saw metadata. if (sawMetadata) return true; if (scratch[0] != VERSION_MAJOR || scratch[1] != VERSION_MINOR) return true; sawMetadata = true; break; case control_block::MODULE_NAME: ModuleName = blobData.str(); break; case control_block::MODULE_OPTIONS: ModuleOpts.SwiftInferImportAsMember = (scratch.front() & 1) != 0; break; case control_block::SOURCE_FILE: SourceFileSizeAndModTime = { scratch[0], scratch[1] }; break; default: // Unknown metadata record, possibly for use by a future version of the // module format. break; } next = cursor.advance(); } return !sawMetadata; } bool APINotesReader::Implementation::readIdentifierBlock( llvm::BitstreamCursor &cursor, SmallVectorImpl &scratch) { if (cursor.EnterSubBlock(IDENTIFIER_BLOCK_ID)) return true; auto next = cursor.advance(); while (next.Kind != llvm::BitstreamEntry::EndBlock) { if (next.Kind == llvm::BitstreamEntry::Error) return true; if (next.Kind == llvm::BitstreamEntry::SubBlock) { // Unknown sub-block, possibly for use by a future version of the // API notes format. if (cursor.SkipBlock()) return true; next = cursor.advance(); continue; } scratch.clear(); StringRef blobData; unsigned kind = cursor.readRecord(next.ID, scratch, &blobData); switch (kind) { case identifier_block::IDENTIFIER_DATA: { // Already saw identifier table. if (IdentifierTable) return true; uint32_t tableOffset; identifier_block::IdentifierDataLayout::readRecord(scratch, tableOffset); auto base = reinterpret_cast(blobData.data()); IdentifierTable.reset( SerializedIdentifierTable::Create(base + tableOffset, base + sizeof(uint32_t), base)); break; } default: // Unknown record, possibly for use by a future version of the // module format. break; } next = cursor.advance(); } return false; } bool APINotesReader::Implementation::readObjCContextBlock( llvm::BitstreamCursor &cursor, SmallVectorImpl &scratch) { if (cursor.EnterSubBlock(OBJC_CONTEXT_BLOCK_ID)) return true; auto next = cursor.advance(); while (next.Kind != llvm::BitstreamEntry::EndBlock) { if (next.Kind == llvm::BitstreamEntry::Error) return true; if (next.Kind == llvm::BitstreamEntry::SubBlock) { // Unknown sub-block, possibly for use by a future version of the // API notes format. if (cursor.SkipBlock()) return true; next = cursor.advance(); continue; } scratch.clear(); StringRef blobData; unsigned kind = cursor.readRecord(next.ID, scratch, &blobData); switch (kind) { 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::ObjCContextInfoLayout::readRecord(scratch, tableOffset); auto base = reinterpret_cast(blobData.data()); ObjCContextInfoTable.reset( SerializedObjCContextInfoTable::Create(base + tableOffset, base + sizeof(uint32_t), base)); break; } default: // Unknown record, possibly for use by a future version of the // module format. break; } next = cursor.advance(); } return false; } bool APINotesReader::Implementation::readObjCPropertyBlock( llvm::BitstreamCursor &cursor, SmallVectorImpl &scratch) { if (cursor.EnterSubBlock(OBJC_PROPERTY_BLOCK_ID)) return true; auto next = cursor.advance(); while (next.Kind != llvm::BitstreamEntry::EndBlock) { if (next.Kind == llvm::BitstreamEntry::Error) return true; if (next.Kind == llvm::BitstreamEntry::SubBlock) { // Unknown sub-block, possibly for use by a future version of the // API notes format. if (cursor.SkipBlock()) return true; next = cursor.advance(); continue; } scratch.clear(); StringRef blobData; unsigned kind = cursor.readRecord(next.ID, scratch, &blobData); switch (kind) { case objc_property_block::OBJC_PROPERTY_DATA: { // Already saw Objective-C property table. if (ObjCPropertyTable) return true; uint32_t tableOffset; objc_property_block::ObjCPropertyDataLayout::readRecord(scratch, tableOffset); auto base = reinterpret_cast(blobData.data()); ObjCPropertyTable.reset( SerializedObjCPropertyTable::Create(base + tableOffset, base + sizeof(uint32_t), base)); break; } default: // Unknown record, possibly for use by a future version of the // module format. break; } next = cursor.advance(); } return false; } bool APINotesReader::Implementation::readObjCMethodBlock( llvm::BitstreamCursor &cursor, SmallVectorImpl &scratch) { if (cursor.EnterSubBlock(OBJC_METHOD_BLOCK_ID)) return true; auto next = cursor.advance(); while (next.Kind != llvm::BitstreamEntry::EndBlock) { if (next.Kind == llvm::BitstreamEntry::Error) return true; if (next.Kind == llvm::BitstreamEntry::SubBlock) { // Unknown sub-block, possibly for use by a future version of the // API notes format. if (cursor.SkipBlock()) return true; next = cursor.advance(); continue; } scratch.clear(); StringRef blobData; unsigned kind = cursor.readRecord(next.ID, scratch, &blobData); switch (kind) { case objc_method_block::OBJC_METHOD_DATA: { // Already saw Objective-C method table. if (ObjCMethodTable) return true; uint32_t tableOffset; objc_method_block::ObjCMethodDataLayout::readRecord(scratch, tableOffset); auto base = reinterpret_cast(blobData.data()); ObjCMethodTable.reset( SerializedObjCMethodTable::Create(base + tableOffset, base + sizeof(uint32_t), base)); break; } default: // Unknown record, possibly for use by a future version of the // module format. break; } next = cursor.advance(); } return false; } bool APINotesReader::Implementation::readObjCSelectorBlock( llvm::BitstreamCursor &cursor, SmallVectorImpl &scratch) { if (cursor.EnterSubBlock(OBJC_SELECTOR_BLOCK_ID)) return true; auto next = cursor.advance(); while (next.Kind != llvm::BitstreamEntry::EndBlock) { if (next.Kind == llvm::BitstreamEntry::Error) return true; if (next.Kind == llvm::BitstreamEntry::SubBlock) { // Unknown sub-block, possibly for use by a future version of the // API notes format. if (cursor.SkipBlock()) return true; next = cursor.advance(); continue; } scratch.clear(); StringRef blobData; unsigned kind = cursor.readRecord(next.ID, scratch, &blobData); switch (kind) { case objc_selector_block::OBJC_SELECTOR_DATA: { // Already saw Objective-C selector table. if (ObjCSelectorTable) return true; uint32_t tableOffset; objc_selector_block::ObjCSelectorDataLayout::readRecord(scratch, tableOffset); auto base = reinterpret_cast(blobData.data()); ObjCSelectorTable.reset( SerializedObjCSelectorTable::Create(base + tableOffset, base + sizeof(uint32_t), base)); break; } default: // Unknown record, possibly for use by a future version of the // module format. break; } next = cursor.advance(); } return false; } bool APINotesReader::Implementation::readGlobalVariableBlock( llvm::BitstreamCursor &cursor, SmallVectorImpl &scratch) { if (cursor.EnterSubBlock(GLOBAL_VARIABLE_BLOCK_ID)) return true; auto next = cursor.advance(); while (next.Kind != llvm::BitstreamEntry::EndBlock) { if (next.Kind == llvm::BitstreamEntry::Error) return true; if (next.Kind == llvm::BitstreamEntry::SubBlock) { // Unknown sub-block, possibly for use by a future version of the // API notes format. if (cursor.SkipBlock()) return true; next = cursor.advance(); continue; } scratch.clear(); StringRef blobData; unsigned kind = cursor.readRecord(next.ID, scratch, &blobData); switch (kind) { case global_variable_block::GLOBAL_VARIABLE_DATA: { // Already saw global variable table. if (GlobalVariableTable) return true; uint32_t tableOffset; global_variable_block::GlobalVariableDataLayout::readRecord(scratch, tableOffset); auto base = reinterpret_cast(blobData.data()); GlobalVariableTable.reset( SerializedGlobalVariableTable::Create(base + tableOffset, base + sizeof(uint32_t), base)); break; } default: // Unknown record, possibly for use by a future version of the // module format. break; } next = cursor.advance(); } return false; } bool APINotesReader::Implementation::readGlobalFunctionBlock( llvm::BitstreamCursor &cursor, SmallVectorImpl &scratch) { if (cursor.EnterSubBlock(GLOBAL_FUNCTION_BLOCK_ID)) return true; auto next = cursor.advance(); while (next.Kind != llvm::BitstreamEntry::EndBlock) { if (next.Kind == llvm::BitstreamEntry::Error) return true; if (next.Kind == llvm::BitstreamEntry::SubBlock) { // Unknown sub-block, possibly for use by a future version of the // API notes format. if (cursor.SkipBlock()) return true; next = cursor.advance(); continue; } scratch.clear(); StringRef blobData; unsigned kind = cursor.readRecord(next.ID, scratch, &blobData); switch (kind) { case global_function_block::GLOBAL_FUNCTION_DATA: { // Already saw global function table. if (GlobalFunctionTable) return true; uint32_t tableOffset; global_function_block::GlobalFunctionDataLayout::readRecord(scratch, tableOffset); auto base = reinterpret_cast(blobData.data()); GlobalFunctionTable.reset( SerializedGlobalFunctionTable::Create(base + tableOffset, base + sizeof(uint32_t), base)); break; } default: // Unknown record, possibly for use by a future version of the // module format. break; } next = cursor.advance(); } return false; } bool APINotesReader::Implementation::readEnumConstantBlock( llvm::BitstreamCursor &cursor, SmallVectorImpl &scratch) { if (cursor.EnterSubBlock(ENUM_CONSTANT_BLOCK_ID)) return true; auto next = cursor.advance(); while (next.Kind != llvm::BitstreamEntry::EndBlock) { if (next.Kind == llvm::BitstreamEntry::Error) return true; if (next.Kind == llvm::BitstreamEntry::SubBlock) { // Unknown sub-block, possibly for use by a future version of the // API notes format. if (cursor.SkipBlock()) return true; next = cursor.advance(); continue; } scratch.clear(); StringRef blobData; unsigned kind = cursor.readRecord(next.ID, scratch, &blobData); switch (kind) { case enum_constant_block::ENUM_CONSTANT_DATA: { // Already saw enumerator table. if (EnumConstantTable) return true; uint32_t tableOffset; enum_constant_block::EnumConstantDataLayout::readRecord(scratch, tableOffset); auto base = reinterpret_cast(blobData.data()); EnumConstantTable.reset( SerializedEnumConstantTable::Create(base + tableOffset, base + sizeof(uint32_t), base)); break; } default: // Unknown record, possibly for use by a future version of the // module format. break; } next = cursor.advance(); } return false; } bool APINotesReader::Implementation::readTagBlock( llvm::BitstreamCursor &cursor, SmallVectorImpl &scratch) { if (cursor.EnterSubBlock(TAG_BLOCK_ID)) return true; auto next = cursor.advance(); while (next.Kind != llvm::BitstreamEntry::EndBlock) { if (next.Kind == llvm::BitstreamEntry::Error) return true; if (next.Kind == llvm::BitstreamEntry::SubBlock) { // Unknown sub-block, possibly for use by a future version of the // API notes format. if (cursor.SkipBlock()) return true; next = cursor.advance(); continue; } scratch.clear(); StringRef blobData; unsigned kind = cursor.readRecord(next.ID, scratch, &blobData); switch (kind) { case tag_block::TAG_DATA: { // Already saw tag table. if (TagTable) return true; uint32_t tableOffset; tag_block::TagDataLayout::readRecord(scratch, tableOffset); auto base = reinterpret_cast(blobData.data()); TagTable.reset( SerializedTagTable::Create(base + tableOffset, base + sizeof(uint32_t), base)); break; } default: // Unknown record, possibly for use by a future version of the // module format. break; } next = cursor.advance(); } return false; } bool APINotesReader::Implementation::readTypedefBlock( llvm::BitstreamCursor &cursor, SmallVectorImpl &scratch) { if (cursor.EnterSubBlock(TYPEDEF_BLOCK_ID)) return true; auto next = cursor.advance(); while (next.Kind != llvm::BitstreamEntry::EndBlock) { if (next.Kind == llvm::BitstreamEntry::Error) return true; if (next.Kind == llvm::BitstreamEntry::SubBlock) { // Unknown sub-block, possibly for use by a future version of the // API notes format. if (cursor.SkipBlock()) return true; next = cursor.advance(); continue; } scratch.clear(); StringRef blobData; unsigned kind = cursor.readRecord(next.ID, scratch, &blobData); switch (kind) { case typedef_block::TYPEDEF_DATA: { // Already saw typedef table. if (TypedefTable) return true; uint32_t tableOffset; typedef_block::TypedefDataLayout::readRecord(scratch, tableOffset); auto base = reinterpret_cast(blobData.data()); TypedefTable.reset( SerializedTypedefTable::Create(base + tableOffset, base + sizeof(uint32_t), base)); break; } default: // Unknown record, possibly for use by a future version of the // module format. break; } next = cursor.advance(); } return false; } APINotesReader::APINotesReader(llvm::MemoryBuffer *inputBuffer, bool ownsInputBuffer, VersionTuple swiftVersion, bool &failed) : Impl(*new Implementation) { failed = false; // Initialize the input buffer. Impl.InputBuffer = inputBuffer; Impl.OwnsInputBuffer = ownsInputBuffer; Impl.SwiftVersion = swiftVersion; llvm::BitstreamCursor cursor(*Impl.InputBuffer); // Validate signature. for (auto byte : API_NOTES_SIGNATURE) { if (cursor.AtEndOfStream() || cursor.Read(8) != byte) { failed = true; return; } } // Look at all of the blocks. bool hasValidControlBlock = false; SmallVector scratch; while (!cursor.AtEndOfStream()) { auto topLevelEntry = cursor.advance(); if (topLevelEntry.Kind != llvm::BitstreamEntry::SubBlock) break; switch (topLevelEntry.ID) { case llvm::bitc::BLOCKINFO_BLOCK_ID: if (!cursor.ReadBlockInfoBlock()) { failed = true; break; } break; case CONTROL_BLOCK_ID: // Only allow a single control block. if (hasValidControlBlock || Impl.readControlBlock(cursor, scratch)) { failed = true; return; } hasValidControlBlock = true; break; case IDENTIFIER_BLOCK_ID: if (!hasValidControlBlock || Impl.readIdentifierBlock(cursor, scratch)) { failed = true; return; } break; case OBJC_CONTEXT_BLOCK_ID: if (!hasValidControlBlock || Impl.readObjCContextBlock(cursor, scratch)) { failed = true; return; } break; case OBJC_PROPERTY_BLOCK_ID: if (!hasValidControlBlock || Impl.readObjCPropertyBlock(cursor, scratch)) { failed = true; return; } break; case OBJC_METHOD_BLOCK_ID: if (!hasValidControlBlock || Impl.readObjCMethodBlock(cursor, scratch)) { failed = true; return; } break; case OBJC_SELECTOR_BLOCK_ID: if (!hasValidControlBlock || Impl.readObjCSelectorBlock(cursor, scratch)) { failed = true; return; } break; case GLOBAL_VARIABLE_BLOCK_ID: if (!hasValidControlBlock || Impl.readGlobalVariableBlock(cursor, scratch)) { failed = true; return; } break; case GLOBAL_FUNCTION_BLOCK_ID: if (!hasValidControlBlock || Impl.readGlobalFunctionBlock(cursor, scratch)) { failed = true; return; } break; case ENUM_CONSTANT_BLOCK_ID: if (!hasValidControlBlock || Impl.readEnumConstantBlock(cursor, scratch)) { failed = true; return; } break; case TAG_BLOCK_ID: if (!hasValidControlBlock || Impl.readTagBlock(cursor, scratch)) { failed = true; return; } break; case TYPEDEF_BLOCK_ID: if (!hasValidControlBlock || Impl.readTypedefBlock(cursor, scratch)) { failed = true; return; } break; default: // Unknown top-level block, possibly for use by a future version of the // module format. if (cursor.SkipBlock()) { failed = true; return; } break; } } if (!cursor.AtEndOfStream()) { failed = true; return; } } APINotesReader::~APINotesReader() { if (Impl.OwnsInputBuffer) delete Impl.InputBuffer; delete &Impl; } std::unique_ptr APINotesReader::get(std::unique_ptr inputBuffer, VersionTuple swiftVersion) { bool failed = false; std::unique_ptr reader(new APINotesReader(inputBuffer.release(), /*ownsInputBuffer=*/true, swiftVersion, failed)); if (failed) return nullptr; return reader; } std::unique_ptr APINotesReader::getUnmanaged(llvm::MemoryBuffer *inputBuffer, VersionTuple swiftVersion) { bool failed = false; std::unique_ptr reader(new APINotesReader(inputBuffer, /*ownsInputBuffer=*/false, swiftVersion, failed)); if (failed) return nullptr; return reader; } StringRef APINotesReader::getModuleName() const { return Impl.ModuleName; } Optional> APINotesReader::getSourceFileSizeAndModTime() const { return Impl.SourceFileSizeAndModTime; } ModuleOptions APINotesReader::getModuleOptions() const { return Impl.ModuleOpts; } 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 knownID = Impl.ObjCContextIDTable->find({*classID, '\0'}); if (knownID == Impl.ObjCContextIDTable->end()) return None; return ContextID(*knownID); } auto APINotesReader::lookupObjCClassInfo(StringRef name) -> VersionedInfo { if (!Impl.ObjCContextInfoTable) return None; Optional contextID = lookupObjCClassID(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::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); } 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; Optional propertyID = Impl.getIdentifier(name); if (!propertyID) return None; auto known = Impl.ObjCPropertyTable->find(std::make_tuple(contextID.Value, *propertyID, (char)isInstance)); if (known == Impl.ObjCPropertyTable->end()) return None; return { Impl.SwiftVersion, *known }; } auto APINotesReader::lookupObjCMethod( ContextID contextID, ObjCSelectorRef selector, bool isInstanceMethod) -> VersionedInfo { if (!Impl.ObjCMethodTable) return None; Optional selectorID = Impl.getSelector(selector); if (!selectorID) return None; auto known = Impl.ObjCMethodTable->find( ObjCMethodTableInfo::internal_key_type{ contextID.Value, *selectorID, isInstanceMethod}); if (known == Impl.ObjCMethodTable->end()) return None; return { Impl.SwiftVersion, *known }; } auto APINotesReader::lookupGlobalVariable( StringRef name) -> VersionedInfo { if (!Impl.GlobalVariableTable) return None; Optional nameID = Impl.getIdentifier(name); if (!nameID) return None; auto known = Impl.GlobalVariableTable->find(*nameID); if (known == Impl.GlobalVariableTable->end()) return None; return { Impl.SwiftVersion, *known }; } auto APINotesReader::lookupGlobalFunction(StringRef name) -> VersionedInfo { if (!Impl.GlobalFunctionTable) return None; Optional nameID = Impl.getIdentifier(name); if (!nameID) return None; auto known = Impl.GlobalFunctionTable->find(*nameID); if (known == Impl.GlobalFunctionTable->end()) return None; return { Impl.SwiftVersion, *known }; } auto APINotesReader::lookupEnumConstant(StringRef name) -> VersionedInfo { if (!Impl.EnumConstantTable) return None; Optional nameID = Impl.getIdentifier(name); if (!nameID) return None; auto known = Impl.EnumConstantTable->find(*nameID); if (known == Impl.EnumConstantTable->end()) return None; return { Impl.SwiftVersion, *known }; } auto APINotesReader::lookupTag(StringRef name) -> VersionedInfo { if (!Impl.TagTable) return None; Optional nameID = Impl.getIdentifier(name); if (!nameID) return None; auto known = Impl.TagTable->find(*nameID); if (known == Impl.TagTable->end()) return None; return { Impl.SwiftVersion, *known }; } auto APINotesReader::lookupTypedef(StringRef name) -> VersionedInfo { if (!Impl.TypedefTable) return None; Optional nameID = Impl.getIdentifier(name); if (!nameID) return None; auto known = Impl.TypedefTable->find(*nameID); if (known == Impl.TypedefTable->end()) return None; return { Impl.SwiftVersion, *known }; } APINotesReader::Visitor::~Visitor() { } void APINotesReader::Visitor::visitObjCClass( ContextID contextID, StringRef name, const ObjCContextInfo &info, VersionTuple swiftVersion) { } 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, VersionTuple swiftVersion) { } void APINotesReader::Visitor::visitGlobalFunction( StringRef name, const GlobalFunctionInfo &info, VersionTuple swiftVersion) { } void APINotesReader::Visitor::visitEnumConstant( StringRef name, const EnumConstantInfo &info, VersionTuple swiftVersion) { } void APINotesReader::Visitor::visitTag( StringRef name, const TagInfo &info, VersionTuple swiftVersion) { } void APINotesReader::Visitor::visitTypedef( StringRef name, const TypedefInfo &info, VersionTuple swiftVersion) { } void APINotesReader::visit(Visitor &visitor) { // FIXME: All of these iterations would be significantly more efficient if we // could get the keys and data together, but OnDiskIterableHashTable doesn't // support that. // Build an identifier ID -> string mapping, which we'll need when visiting // any of the tables. llvm::DenseMap identifiers; if (Impl.IdentifierTable) { for (auto key : Impl.IdentifierTable->keys()) { unsigned ID = *Impl.IdentifierTable->find(key); assert(identifiers.count(ID) == 0); identifiers[ID] = key; } } // Visit classes and protocols. if (Impl.ObjCContextIDTable && Impl.ObjCContextInfoTable) { for (auto key : Impl.ObjCContextIDTable->keys()) { auto name = identifiers[key.first]; 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); } } } // Build a selector ID -> stored Objective-C selector mapping, which we need // when visiting the method tables. llvm::DenseMap selectors; if (Impl.ObjCSelectorTable) { for (auto key : Impl.ObjCSelectorTable->keys()) { std::string selector; if (key.NumPieces == 0) selector = identifiers[key.Identifiers[0]]; else { for (auto identID : key.Identifiers) { selector += identifiers[identID]; selector += ':'; } } unsigned selectorID = *Impl.ObjCSelectorTable->find(key); selectors[selectorID] = selector; } } // Visit methods. if (Impl.ObjCMethodTable) { for (auto key : Impl.ObjCMethodTable->keys()) { ContextID contextID(std::get<0>(key)); const auto &selector = selectors[std::get<1>(key)]; for (const auto &versioned : *Impl.ObjCMethodTable->find(key)) visitor.visitObjCMethod(contextID, selector, std::get<2>(key), versioned.second, versioned.first); } } // Visit properties. if (Impl.ObjCPropertyTable) { for (auto key : Impl.ObjCPropertyTable->keys()) { ContextID contextID(std::get<0>(key)); auto name = identifiers[std::get<1>(key)]; char isInstance = std::get<2>(key); for (const auto &versioned : *Impl.ObjCPropertyTable->find(key)) { visitor.visitObjCProperty(contextID, name, isInstance, versioned.second, versioned.first); } } } // Visit global functions. if (Impl.GlobalFunctionTable) { for (auto key : Impl.GlobalFunctionTable->keys()) { auto name = identifiers[key]; for (const auto &versioned : *Impl.GlobalFunctionTable->find(key)) visitor.visitGlobalFunction(name, versioned.second, versioned.first); } } // Visit global variables. if (Impl.GlobalVariableTable) { for (auto key : Impl.GlobalVariableTable->keys()) { auto name = identifiers[key]; for (const auto &versioned : *Impl.GlobalVariableTable->find(key)) visitor.visitGlobalVariable(name, versioned.second, versioned.first); } } // Visit global variables. if (Impl.EnumConstantTable) { for (auto key : Impl.EnumConstantTable->keys()) { auto name = identifiers[key]; for (const auto &versioned : *Impl.EnumConstantTable->find(key)) visitor.visitEnumConstant(name, versioned.second, versioned.first); } } // Visit tags. if (Impl.TagTable) { for (auto key : Impl.TagTable->keys()) { auto name = identifiers[key]; for (const auto &versioned : *Impl.TagTable->find(key)) visitor.visitTag(name, versioned.second, versioned.first); } } // Visit typedefs. if (Impl.TypedefTable) { for (auto key : Impl.TypedefTable->keys()) { auto name = identifiers[key]; for (const auto &versioned : *Impl.TypedefTable->find(key)) visitor.visitTypedef(name, versioned.second, versioned.first); } } } diff --git a/clang/lib/APINotes/APINotesWriter.cpp b/clang/lib/APINotes/APINotesWriter.cpp index 65edaf9a5946..2331cd305f12 100644 --- a/clang/lib/APINotes/APINotesWriter.cpp +++ b/clang/lib/APINotes/APINotesWriter.cpp @@ -1,1293 +1,1297 @@ //===--- APINotesWriter.cpp - API Notes Writer --------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file implements the \c APINotesWriter class that writes out // source API notes data providing additional information about source // code as a separate input, such as the non-nil/nilable annotations // for method parameters. // //===----------------------------------------------------------------------===// #include "clang/APINotes/APINotesWriter.h" #include "APINotesFormat.h" #include "clang/Basic/FileManager.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/Hashing.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringMap.h" #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; /// Mapping from selectors to selector ID. llvm::DenseMap SelectorIDs; /// Scratch space for bitstream writing. SmallVector ScratchRecord; public: /// The name of the module std::string ModuleName; /// The source file from which this binary representation was /// created, if known. const FileEntry *SourceFile; bool SwiftInferImportAsMember = false; /// Information about Objective-C contexts (classes or protocols). /// /// Indexed by the identifier ID and a bit indication whether we're looking /// 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; /// Mapping from context IDs to the identifier ID holding the name. llvm::DenseMap ObjCContextNames; /// Information about Objective-C properties. /// /// Indexed by the context ID, property name, and whether this is an /// instance property. 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, llvm::SmallVector, 1>> ObjCMethods; /// Information about global variables. /// /// Indexed by the identifier ID. llvm::DenseMap, 1>> GlobalVariables; /// Information about global functions. /// /// Indexed by the identifier ID. llvm::DenseMap, 1>> GlobalFunctions; /// Information about enumerators. /// /// Indexed by the identifier ID. llvm::DenseMap, 1>> EnumConstants; /// Information about tags. /// /// Indexed by the identifier ID. llvm::DenseMap, 1>> Tags; /// Information about typedefs. /// /// Indexed by the identifier ID. llvm::DenseMap, 1>> Typedefs; /// Retrieve the ID for the given identifier. IdentifierID getIdentifier(StringRef identifier) { if (identifier.empty()) return 0; auto known = IdentifierIDs.find(identifier); if (known != IdentifierIDs.end()) return known->second; // Add to the identifier table. known = IdentifierIDs.insert({identifier, IdentifierIDs.size() + 1}).first; return known->second; } /// Retrieve the ID for the given selector. SelectorID getSelector(ObjCSelectorRef selectorRef) { // Translate the selector reference into a stored selector. StoredObjCSelector selector; selector.NumPieces = selectorRef.NumPieces; selector.Identifiers.reserve(selectorRef.Identifiers.size()); for (auto piece : selectorRef.Identifiers) { selector.Identifiers.push_back(getIdentifier(piece)); } // Look for the stored selector. auto known = SelectorIDs.find(selector); if (known != SelectorIDs.end()) return known->second; // Add to the selector table. known = SelectorIDs.insert({selector, SelectorIDs.size()}).first; return known->second; } void writeToStream(llvm::raw_ostream &os); private: void writeBlockInfoBlock(llvm::BitstreamWriter &writer); void writeControlBlock(llvm::BitstreamWriter &writer); void writeIdentifierBlock(llvm::BitstreamWriter &writer); void writeObjCContextBlock(llvm::BitstreamWriter &writer); void writeObjCPropertyBlock(llvm::BitstreamWriter &writer); void writeObjCMethodBlock(llvm::BitstreamWriter &writer); void writeObjCSelectorBlock(llvm::BitstreamWriter &writer); void writeGlobalVariableBlock(llvm::BitstreamWriter &writer); void writeGlobalFunctionBlock(llvm::BitstreamWriter &writer); void writeEnumConstantBlock(llvm::BitstreamWriter &writer); void writeTagBlock(llvm::BitstreamWriter &writer); void writeTypedefBlock(llvm::BitstreamWriter &writer); }; /// Record the name of a block. static void emitBlockID(llvm::BitstreamWriter &out, unsigned ID, StringRef name, SmallVectorImpl &nameBuffer) { SmallVector idBuffer; idBuffer.push_back(ID); out.EmitRecord(llvm::bitc::BLOCKINFO_CODE_SETBID, idBuffer); // Emit the block name if present. if (name.empty()) return; nameBuffer.resize(name.size()); memcpy(nameBuffer.data(), name.data(), name.size()); out.EmitRecord(llvm::bitc::BLOCKINFO_CODE_BLOCKNAME, nameBuffer); } /// Record the name of a record within a block. static void emitRecordID(llvm::BitstreamWriter &out, unsigned ID, StringRef name, SmallVectorImpl &nameBuffer) { assert(ID < 256 && "can't fit record ID in next to name"); nameBuffer.resize(name.size()+1); nameBuffer[0] = ID; memcpy(nameBuffer.data()+1, name.data(), name.size()); out.EmitRecord(llvm::bitc::BLOCKINFO_CODE_SETRECORDNAME, nameBuffer); } void APINotesWriter::Implementation::writeBlockInfoBlock( llvm::BitstreamWriter &writer) { BCBlockRAII restoreBlock(writer, llvm::bitc::BLOCKINFO_BLOCK_ID, 2); SmallVector nameBuffer; #define BLOCK(X) emitBlockID(writer, X ## _ID, #X, nameBuffer) #define BLOCK_RECORD(K, X) emitRecordID(writer, K::X, #X, nameBuffer) BLOCK(CONTROL_BLOCK); BLOCK_RECORD(control_block, METADATA); BLOCK_RECORD(control_block, MODULE_NAME); BLOCK(IDENTIFIER_BLOCK); BLOCK_RECORD(identifier_block, IDENTIFIER_DATA); BLOCK(OBJC_CONTEXT_BLOCK); BLOCK_RECORD(objc_context_block, OBJC_CONTEXT_ID_DATA); BLOCK(OBJC_PROPERTY_BLOCK); BLOCK_RECORD(objc_property_block, OBJC_PROPERTY_DATA); BLOCK(OBJC_METHOD_BLOCK); BLOCK_RECORD(objc_method_block, OBJC_METHOD_DATA); BLOCK(OBJC_SELECTOR_BLOCK); BLOCK_RECORD(objc_selector_block, OBJC_SELECTOR_DATA); BLOCK(GLOBAL_VARIABLE_BLOCK); BLOCK_RECORD(global_variable_block, GLOBAL_VARIABLE_DATA); BLOCK(GLOBAL_FUNCTION_BLOCK); BLOCK_RECORD(global_function_block, GLOBAL_FUNCTION_DATA); #undef BLOCK #undef BLOCK_RECORD } void APINotesWriter::Implementation::writeControlBlock( llvm::BitstreamWriter &writer) { BCBlockRAII restoreBlock(writer, CONTROL_BLOCK_ID, 3); control_block::MetadataLayout metadata(writer); metadata.emit(ScratchRecord, VERSION_MAJOR, VERSION_MINOR); control_block::ModuleNameLayout moduleName(writer); moduleName.emit(ScratchRecord, ModuleName); if (SwiftInferImportAsMember) { control_block::ModuleOptionsLayout moduleOptions(writer); moduleOptions.emit(ScratchRecord, SwiftInferImportAsMember); } if (SourceFile) { control_block::SourceFileLayout sourceFile(writer); sourceFile.emit(ScratchRecord, SourceFile->getSize(), SourceFile->getModificationTime()); } } namespace { /// Used to serialize the on-disk identifier table. class IdentifierTableInfo { public: using key_type = StringRef; using key_type_ref = key_type; using data_type = IdentifierID; using data_type_ref = const data_type &; using hash_value_type = uint32_t; using offset_type = unsigned; hash_value_type ComputeHash(key_type_ref key) { return llvm::HashString(key); } std::pair EmitKeyDataLength(raw_ostream &out, key_type_ref key, data_type_ref data) { uint32_t keyLength = key.size(); uint32_t dataLength = sizeof(uint32_t); endian::Writer writer(out); writer.write(keyLength); writer.write(dataLength); return { keyLength, dataLength }; } void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { out << key; } void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data, unsigned len) { endian::Writer writer(out); writer.write(data); } }; } // end anonymous namespace void APINotesWriter::Implementation::writeIdentifierBlock( llvm::BitstreamWriter &writer) { BCBlockRAII restoreBlock(writer, IDENTIFIER_BLOCK_ID, 3); if (IdentifierIDs.empty()) return; llvm::SmallString<4096> hashTableBlob; uint32_t tableOffset; { llvm::OnDiskChainedHashTableGenerator generator; for (auto &entry : IdentifierIDs) generator.insert(entry.first(), entry.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); } identifier_block::IdentifierDataLayout layout(writer); layout.emit(ScratchRecord, tableOffset, hashTableBlob); } namespace { /// Retrieve the serialized size of the given CommonEntityInfo, for use in /// on-disk hash tables. static unsigned getCommonEntityInfoSize(const CommonEntityInfo &info) { return 5 + info.UnavailableMsg.size() + info.SwiftName.size(); } /// Emit a serialized representation of the common entity information. static void emitCommonEntityInfo(raw_ostream &out, const CommonEntityInfo &info) { endian::Writer writer(out); uint8_t payload = 0; if (auto swiftPrivate = info.isSwiftPrivate()) { payload |= 0x01; if (*swiftPrivate) payload |= 0x02; } payload <<= 1; payload |= info.Unavailable; payload <<= 1; payload |= info.UnavailableInSwift; writer.write(payload); writer.write(info.UnavailableMsg.size()); out.write(info.UnavailableMsg.c_str(), info.UnavailableMsg.size()); writer.write(info.SwiftName.size()); out.write(info.SwiftName.c_str(), info.SwiftName.size()); } // Retrieve the serialized size of the given CommonTypeInfo, for use // in on-disk hash tables. static unsigned getCommonTypeInfoSize(const CommonTypeInfo &info) { return 2 + (info.getSwiftBridge() ? info.getSwiftBridge()->size() : 0) + 2 + (info.getNSErrorDomain() ? info.getNSErrorDomain()->size() : 0) + getCommonEntityInfoSize(info); } /// Emit a serialized representation of the common type information. static void emitCommonTypeInfo(raw_ostream &out, const CommonTypeInfo &info) { emitCommonEntityInfo(out, info); endian::Writer writer(out); if (auto swiftBridge = info.getSwiftBridge()) { writer.write(swiftBridge->size() + 1); out.write(swiftBridge->c_str(), swiftBridge->size()); } else { writer.write(0); } if (auto nsErrorDomain = info.getNSErrorDomain()) { writer.write(nsErrorDomain->size() + 1); out.write(nsErrorDomain->c_str(), info.getNSErrorDomain()->size()); } else { writer.write(0); } } /// Used to serialize the on-disk Objective-C context table. class ObjCContextIDTableInfo { public: using key_type = std::pair; // identifier ID, is-protocol using key_type_ref = key_type; using data_type = unsigned; 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) + 1; uint32_t dataLength = sizeof(uint32_t); 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.first); writer.write(key.second); } void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data, unsigned len) { endian::Writer writer(out); writer.write(data); } }; } // end anonymous namespace 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; } /// Emit a serialized representation of a version tuple. void emitVersionTuple(raw_ostream &out, const VersionTuple &version) { endian::Writer writer(out); // 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); } /// 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; } /// 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); } } /// Retrieve the serialized size of the given VariableInfo, for use in /// on-disk hash tables. unsigned getVariableInfoSize(const VariableInfo &info) { return 2 + getCommonEntityInfoSize(info) + 2 + info.getType().size(); } /// Emit a serialized representation of the variable information. void emitVariableInfo(raw_ostream &out, const VariableInfo &info) { emitCommonEntityInfo(out, info); uint8_t bytes[2] = { 0, 0 }; if (auto nullable = info.getNullability()) { bytes[0] = 1; bytes[1] = static_cast(*nullable); } else { // Nothing to do. } out.write(reinterpret_cast(bytes), 2); endian::Writer writer(out); writer.write(info.getType().size()); out.write(info.getType().data(), info.getType().size()); } /// 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: using key_type = KeyType; using key_type_ref = key_type; 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 llvm::hash_value(key); } std::pair EmitKeyDataLength(raw_ostream &out, key_type_ref key, data_type_ref 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 swiftImportAsNonGeneric = info.getSwiftImportAsNonGeneric()) { payload |= (0x01 << 1) | swiftImportAsNonGeneric.getValue(); } + payload <<= 2; + if (auto swiftObjCMembers = info.getSwiftObjCMembers()) { + payload |= (0x01 << 1) | swiftObjCMembers.getValue(); + } payload <<= 3; 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)); writer.write(std::get<1>(key)); writer.write(std::get<2>(key)); } unsigned getUnversionedInfoSize(const ObjCPropertyInfo &info) { return getVariableInfoSize(info) + 1; } void emitUnversionedInfo(raw_ostream &out, const ObjCPropertyInfo &info) { emitVariableInfo(out, info); uint8_t flags = 0; if (Optional value = info.getSwiftImportAsAccessors()) { flags |= 1 << 0; flags |= value.getValue() << 1; } out << flags; } }; } // 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); if (ObjCProperties.empty()) return; llvm::SmallString<4096> hashTableBlob; uint32_t tableOffset; { llvm::OnDiskChainedHashTableGenerator generator; for (auto &entry : ObjCProperties) generator.insert(entry.first, entry.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_property_block::ObjCPropertyDataLayout layout(writer); layout.emit(ScratchRecord, tableOffset, hashTableBlob); } namespace { static unsigned getParamInfoSize(const ParamInfo &info) { return getVariableInfoSize(info) + 1; } static void emitParamInfo(raw_ostream &out, const ParamInfo &info) { emitVariableInfo(out, info); endian::Writer writer(out); uint8_t payload = 0; if (auto noescape = info.isNoEscape()) { payload |= 0x01; if (*noescape) payload |= 0x02; } writer.write(payload); } /// Retrieve the serialized size of the given FunctionInfo, for use in /// on-disk hash tables. static unsigned getFunctionInfoSize(const FunctionInfo &info) { unsigned size = 2 + sizeof(uint64_t) + getCommonEntityInfoSize(info) + 2; for (const auto ¶m : info.Params) size += getParamInfoSize(param); size += 2 + info.ResultType.size(); return size; } /// Emit a serialized representation of the function information. static void emitFunctionInfo(raw_ostream &out, const FunctionInfo &info) { emitCommonEntityInfo(out, info); endian::Writer writer(out); writer.write(info.NullabilityAudited); writer.write(info.NumAdjustedNullable); writer.write(info.NullabilityPayload); // Parameters. writer.write(info.Params.size()); for (const auto &pi : info.Params) emitParamInfo(out, pi); // Result type. writer.write(info.ResultType.size()); out.write(info.ResultType.data(), info.ResultType.size()); } /// Used to serialize the on-disk Objective-C method table. class ObjCMethodTableInfo : public VersionedTableInfo, ObjCMethodInfo> { public: unsigned getKeyLength(key_type_ref) { return sizeof(uint32_t) + sizeof(uint32_t) + 1; } void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { endian::Writer writer(out); writer.write(std::get<0>(key)); writer.write(std::get<1>(key)); writer.write(std::get<2>(key)); } unsigned getUnversionedInfoSize(const ObjCMethodInfo &info) { return 1 + getFunctionInfoSize(info); } void emitUnversionedInfo(raw_ostream &out, const ObjCMethodInfo &info) { uint8_t payload = 0; payload = (payload << 1) | info.DesignatedInit; payload = (payload << 1) | info.Required; endian::Writer writer(out); writer.write(payload); emitFunctionInfo(out, info); } }; } // end anonymous namespace void APINotesWriter::Implementation::writeObjCMethodBlock( llvm::BitstreamWriter &writer) { BCBlockRAII restoreBlock(writer, OBJC_METHOD_BLOCK_ID, 3); if (ObjCMethods.empty()) return; llvm::SmallString<4096> hashTableBlob; uint32_t tableOffset; { llvm::OnDiskChainedHashTableGenerator generator; for (auto &entry : ObjCMethods) { generator.insert(entry.first, entry.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_method_block::ObjCMethodDataLayout layout(writer); layout.emit(ScratchRecord, tableOffset, hashTableBlob); } namespace { /// Used to serialize the on-disk Objective-C selector table. class ObjCSelectorTableInfo { public: using key_type = StoredObjCSelector; using key_type_ref = const key_type &; using data_type = SelectorID; using data_type_ref = data_type; using hash_value_type = unsigned; using offset_type = unsigned; hash_value_type ComputeHash(key_type_ref key) { return llvm::DenseMapInfo::getHashValue(key); } std::pair EmitKeyDataLength(raw_ostream &out, key_type_ref key, data_type_ref data) { uint32_t keyLength = sizeof(uint16_t) + sizeof(uint32_t) * key.Identifiers.size(); uint32_t dataLength = sizeof(uint32_t); 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.NumPieces); for (auto piece : key.Identifiers) { writer.write(piece); } } void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data, unsigned len) { endian::Writer writer(out); writer.write(data); } }; } // end anonymous namespace void APINotesWriter::Implementation::writeObjCSelectorBlock( llvm::BitstreamWriter &writer) { BCBlockRAII restoreBlock(writer, OBJC_SELECTOR_BLOCK_ID, 3); if (SelectorIDs.empty()) return; llvm::SmallString<4096> hashTableBlob; uint32_t tableOffset; { llvm::OnDiskChainedHashTableGenerator generator; for (auto &entry : SelectorIDs) generator.insert(entry.first, entry.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_selector_block::ObjCSelectorDataLayout layout(writer); layout.emit(ScratchRecord, tableOffset, hashTableBlob); } namespace { /// Used to serialize the on-disk global variable table. class GlobalVariableTableInfo : public VersionedTableInfo { public: unsigned getKeyLength(key_type_ref key) { 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 GlobalVariableInfo &info) { return getVariableInfoSize(info); } void emitUnversionedInfo(raw_ostream &out, const GlobalVariableInfo &info) { emitVariableInfo(out, info); } }; } // end anonymous namespace void APINotesWriter::Implementation::writeGlobalVariableBlock( llvm::BitstreamWriter &writer) { BCBlockRAII restoreBlock(writer, GLOBAL_VARIABLE_BLOCK_ID, 3); if (GlobalVariables.empty()) return; llvm::SmallString<4096> hashTableBlob; uint32_t tableOffset; { llvm::OnDiskChainedHashTableGenerator generator; for (auto &entry : GlobalVariables) generator.insert(entry.first, entry.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); } global_variable_block::GlobalVariableDataLayout layout(writer); layout.emit(ScratchRecord, tableOffset, hashTableBlob); } namespace { /// Used to serialize the on-disk global function table. class GlobalFunctionTableInfo : 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 GlobalFunctionInfo &info) { return getFunctionInfoSize(info); } void emitUnversionedInfo(raw_ostream &out, const GlobalFunctionInfo &info) { emitFunctionInfo(out, info); } }; } // end anonymous namespace void APINotesWriter::Implementation::writeGlobalFunctionBlock( llvm::BitstreamWriter &writer) { BCBlockRAII restoreBlock(writer, GLOBAL_FUNCTION_BLOCK_ID, 3); if (GlobalFunctions.empty()) return; llvm::SmallString<4096> hashTableBlob; uint32_t tableOffset; { llvm::OnDiskChainedHashTableGenerator generator; for (auto &entry : GlobalFunctions) { generator.insert(entry.first, entry.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); } global_function_block::GlobalFunctionDataLayout layout(writer); layout.emit(ScratchRecord, tableOffset, hashTableBlob); } namespace { /// Used to serialize the on-disk global enum constant. class EnumConstantTableInfo : 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 EnumConstantInfo &info) { return getCommonEntityInfoSize(info); } void emitUnversionedInfo(raw_ostream &out, const EnumConstantInfo &info) { emitCommonEntityInfo(out, info); } }; } // end anonymous namespace void APINotesWriter::Implementation::writeEnumConstantBlock( llvm::BitstreamWriter &writer) { BCBlockRAII restoreBlock(writer, ENUM_CONSTANT_BLOCK_ID, 3); if (EnumConstants.empty()) return; llvm::SmallString<4096> hashTableBlob; uint32_t tableOffset; { llvm::OnDiskChainedHashTableGenerator generator; for (auto &entry : EnumConstants) generator.insert(entry.first, entry.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); } enum_constant_block::EnumConstantDataLayout layout(writer); layout.emit(ScratchRecord, tableOffset, hashTableBlob); } namespace { template class CommonTypeTableInfo : public VersionedTableInfo { public: using key_type_ref = typename CommonTypeTableInfo::key_type_ref; 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); } 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( llvm::BitstreamWriter &writer) { BCBlockRAII restoreBlock(writer, TAG_BLOCK_ID, 3); if (Tags.empty()) return; llvm::SmallString<4096> hashTableBlob; uint32_t tableOffset; { llvm::OnDiskChainedHashTableGenerator generator; for (auto &entry : Tags) generator.insert(entry.first, entry.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); } tag_block::TagDataLayout layout(writer); layout.emit(ScratchRecord, tableOffset, hashTableBlob); } namespace { /// Used to serialize the on-disk typedef table. class TypedefTableInfo : public CommonTypeTableInfo { public: unsigned getUnversionedInfoSize(const TypedefInfo &info) { return 1 + getCommonTypeInfoSize(info); } void emitUnversionedInfo(raw_ostream &out, const TypedefInfo &info) { endian::Writer writer(out); uint8_t payload = 0; if (auto swiftWrapper = info.SwiftWrapper) { payload |= static_cast(*swiftWrapper) + 1; } writer.write(payload); emitCommonTypeInfo(out, info); } }; } // end anonymous namespace void APINotesWriter::Implementation::writeTypedefBlock( llvm::BitstreamWriter &writer) { BCBlockRAII restoreBlock(writer, TYPEDEF_BLOCK_ID, 3); if (Typedefs.empty()) return; llvm::SmallString<4096> hashTableBlob; uint32_t tableOffset; { llvm::OnDiskChainedHashTableGenerator generator; for (auto &entry : Typedefs) generator.insert(entry.first, entry.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); } typedef_block::TypedefDataLayout layout(writer); layout.emit(ScratchRecord, tableOffset, hashTableBlob); } void APINotesWriter::Implementation::writeToStream(llvm::raw_ostream &os) { // Write the API notes file into a buffer. SmallVector buffer; { llvm::BitstreamWriter writer(buffer); // Emit the signature. for (unsigned char byte : API_NOTES_SIGNATURE) writer.Emit(byte, 8); // Emit the blocks. writeBlockInfoBlock(writer); writeControlBlock(writer); writeIdentifierBlock(writer); writeObjCContextBlock(writer); writeObjCPropertyBlock(writer); writeObjCMethodBlock(writer); writeObjCSelectorBlock(writer); writeGlobalVariableBlock(writer); writeGlobalFunctionBlock(writer); writeEnumConstantBlock(writer); writeTagBlock(writer); writeTypedefBlock(writer); } // Write the buffer to the stream. os.write(buffer.data(), buffer.size()); os.flush(); } APINotesWriter::APINotesWriter(StringRef moduleName, const FileEntry *sourceFile) : Impl(*new Implementation) { Impl.ModuleName = moduleName; Impl.SourceFile = sourceFile; } APINotesWriter::~APINotesWriter() { delete &Impl; } void APINotesWriter::writeToStream(raw_ostream &os) { Impl.writeToStream(os); } ContextID APINotesWriter::addObjCContext(StringRef name, bool isClass, const ObjCContextInfo &info, VersionTuple swiftVersion) { IdentifierID nameID = Impl.getIdentifier(name); std::pair key(nameID, isClass ? 0 : 1); auto known = Impl.ObjCContexts.find(key); 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, emptyVersionedInfo))) .first; Impl.ObjCContextNames[nextID] = nameID; } // 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, VersionTuple swiftVersion) { IdentifierID nameID = Impl.getIdentifier(name); 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, VersionTuple swiftVersion) { SelectorID selectorID = Impl.getSelector(selector); auto key = std::tuple{ contextID.Value, selectorID, isInstanceMethod}; 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})); 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, VersionTuple swiftVersion) { IdentifierID variableID = Impl.getIdentifier(name); Impl.GlobalVariables[variableID].push_back({swiftVersion, info}); } void APINotesWriter::addGlobalFunction(llvm::StringRef name, const GlobalFunctionInfo &info, VersionTuple swiftVersion) { IdentifierID nameID = Impl.getIdentifier(name); Impl.GlobalFunctions[nameID].push_back({swiftVersion, info}); } void APINotesWriter::addEnumConstant(llvm::StringRef name, const EnumConstantInfo &info, VersionTuple swiftVersion) { IdentifierID enumConstantID = Impl.getIdentifier(name); Impl.EnumConstants[enumConstantID].push_back({swiftVersion, info}); } void APINotesWriter::addTag(llvm::StringRef name, const TagInfo &info, VersionTuple swiftVersion) { IdentifierID tagID = Impl.getIdentifier(name); Impl.Tags[tagID].push_back({swiftVersion, info}); } void APINotesWriter::addTypedef(llvm::StringRef name, const TypedefInfo &info, VersionTuple swiftVersion) { IdentifierID typedefID = Impl.getIdentifier(name); Impl.Typedefs[typedefID].push_back({swiftVersion, info}); } void APINotesWriter::addModuleOptions(ModuleOptions opts) { Impl.SwiftInferImportAsMember = opts.SwiftInferImportAsMember; } diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp b/clang/lib/APINotes/APINotesYAMLCompiler.cpp index c96764617062..02b2ba1e8669 100644 --- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp @@ -1,1419 +1,1424 @@ //===--- APINotesYAMLCompiler.cpp - API Notes YAML format reader *- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file reads API notes specified in YAML format. // //===----------------------------------------------------------------------===// #include "clang/APINotes/APINotesYAMLCompiler.h" #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" #include "llvm/Support/YAMLParser.h" #include "llvm/Support/YAMLTraits.h" #include /* YAML Format specification. Nullability should be expressed using one of the following values: O - Optional (or Nullable) N - Not Optional S - Scalar U - Unknown Note, the API is considered 'audited' when at least the return value or a parameter has a nullability value. For 'audited' APIs, we assume the default nullability for any underspecified type. --- Name: AppKit # The name of the framework Availability: OSX # Optional: Specifies which platform the API is # available on. [OSX / iOS / none/ # available / nonswift] AvailabilityMsg: "" # Optional: Custom availability message to display to # the user, when API is not available. Classes: # List of classes ... Protocols: # List of protocols ... Functions: # List of functions ... Globals: # List of globals ... Enumerators: # List of enumerators ... Tags: # List of tags (struct/union/enum/C++ class) ... Typedefs: # List of typedef-names and C++11 type aliases ... Each class and protocol is defined as following: - Name: NSView # The name of the class AuditedForNullability: false # Optional: Specifies if the whole class # has been audited for nullability. # If yes, we assume all the methods and # properties of the class have default # nullability unless it is overwritten by # a method/property specific info below. # This applies to all classes, extensions, # and categories of the class defined in # the current framework/module. # (false/true) Availability: OSX AvailabilityMsg: "" Methods: - Selector: "setSubviews:" # Full name MethodKind: Instance # [Class/Instance] Nullability: [N, N, O, S] # The nullability of parameters in # the signature. NullabilityOfRet: O # The nullability of the return value. Availability: OSX AvailabilityMsg: "" DesignatedInit: false # Optional: Specifies if this method is a # designated initializer (false/true) Required: false # Optional: Specifies if this method is a # required initializer (false/true) Properties: - Name: window Nullability: O Availability: OSX AvailabilityMsg: "" The protocol definition format is the same as the class definition. Each function definition is of the following form: - Name: "myGlobalFunction" # Full name Nullability: [N, N, O, S] # The nullability of parameters in # the signature. NullabilityOfRet: O # The nullability of the return value. Availability: OSX AvailabilityMsg: "" Each global variable definition is of the following form: - Name: MyGlobalVar Nullability: O Availability: OSX AvailabilityMsg: "" */ using llvm::StringRef; using namespace clang; namespace { enum class APIAvailability { Available = 0, OSX, IOS, None, NonSwift, }; enum class MethodKind { Class, Instance, }; /// Old attribute deprecated in favor of SwiftName. enum class FactoryAsInitKind { /// Infer based on name and type (the default). Infer, /// Treat as a class method. AsClassMethod, /// Treat as an initializer. AsInitializer }; struct AvailabilityItem { APIAvailability Mode = APIAvailability::Available; StringRef Msg; AvailabilityItem() : Mode(APIAvailability::Available), Msg("") {} }; static llvm::Optional AbsentNullability = llvm::None; static llvm::Optional DefaultNullability = NullabilityKind::NonNull; typedef std::vector NullabilitySeq; struct Param { unsigned Position; Optional NoEscape = false; llvm::Optional Nullability; StringRef Type; }; typedef std::vector ParamsSeq; struct Method { StringRef Selector; MethodKind Kind; ParamsSeq Params; NullabilitySeq Nullability; llvm::Optional NullabilityOfRet; AvailabilityItem Availability; Optional SwiftPrivate; StringRef SwiftName; FactoryAsInitKind FactoryAsInit = FactoryAsInitKind::Infer; bool DesignatedInit = false; bool Required = false; StringRef ResultType; }; typedef std::vector MethodsSeq; struct Property { StringRef Name; llvm::Optional Kind; llvm::Optional Nullability; AvailabilityItem Availability; Optional SwiftPrivate; StringRef SwiftName; Optional SwiftImportAsAccessors; StringRef Type; }; typedef std::vector PropertiesSeq; struct Class { StringRef Name; bool AuditedForNullability = false; AvailabilityItem Availability; Optional SwiftPrivate; StringRef SwiftName; Optional SwiftBridge; Optional NSErrorDomain; Optional SwiftImportAsNonGeneric; + Optional SwiftObjCMembers; MethodsSeq Methods; PropertiesSeq Properties; }; typedef std::vector ClassesSeq; struct Function { StringRef Name; ParamsSeq Params; NullabilitySeq Nullability; llvm::Optional NullabilityOfRet; AvailabilityItem Availability; Optional SwiftPrivate; StringRef SwiftName; StringRef Type; StringRef ResultType; }; typedef std::vector FunctionsSeq; struct GlobalVariable { StringRef Name; llvm::Optional Nullability; AvailabilityItem Availability; Optional SwiftPrivate; StringRef SwiftName; StringRef Type; }; typedef std::vector GlobalVariablesSeq; struct EnumConstant { StringRef Name; AvailabilityItem Availability; Optional SwiftPrivate; StringRef SwiftName; }; typedef std::vector EnumConstantsSeq; struct Tag { StringRef Name; AvailabilityItem Availability; StringRef SwiftName; Optional SwiftPrivate; Optional SwiftBridge; Optional NSErrorDomain; }; typedef std::vector TagsSeq; struct Typedef { StringRef Name; AvailabilityItem Availability; StringRef SwiftName; Optional SwiftPrivate; Optional SwiftBridge; Optional NSErrorDomain; Optional SwiftWrapper; }; typedef std::vector TypedefsSeq; struct TopLevelItems { ClassesSeq Classes; ClassesSeq Protocols; FunctionsSeq Functions; GlobalVariablesSeq Globals; 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}; LLVM_ATTRIBUTE_DEPRECATED( void dump() LLVM_ATTRIBUTE_USED, "only for use within the debugger"); }; } LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(clang::NullabilityKind) LLVM_YAML_IS_SEQUENCE_VECTOR(Method) LLVM_YAML_IS_SEQUENCE_VECTOR(Property) LLVM_YAML_IS_SEQUENCE_VECTOR(Param) LLVM_YAML_IS_SEQUENCE_VECTOR(Class) LLVM_YAML_IS_SEQUENCE_VECTOR(Function) 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 { template <> struct ScalarEnumerationTraits { static void enumeration(IO &io, NullabilityKind &value) { io.enumCase(value, "N", NullabilityKind::NonNull); io.enumCase(value, "O", NullabilityKind::Nullable); io.enumCase(value, "U", NullabilityKind::Unspecified); // TODO: Mapping this to it's own value would allow for better cross // checking. Also the default should be Unknown. io.enumCase(value, "S", NullabilityKind::Unspecified); } }; template <> struct ScalarEnumerationTraits { static void enumeration(IO &io, FactoryAsInitKind &value) { io.enumCase(value, "A", FactoryAsInitKind::Infer); io.enumCase(value, "C", FactoryAsInitKind::AsClassMethod); io.enumCase(value, "I", FactoryAsInitKind::AsInitializer); } }; template <> struct ScalarEnumerationTraits { static void enumeration(IO &io, MethodKind &value) { io.enumCase(value, "Class", MethodKind::Class); io.enumCase(value, "Instance", MethodKind::Instance); } }; template <> struct ScalarEnumerationTraits { static void enumeration(IO &io, APIAvailability &value) { io.enumCase(value, "OSX", APIAvailability::OSX); io.enumCase(value, "iOS", APIAvailability::IOS); io.enumCase(value, "none", APIAvailability::None); io.enumCase(value, "nonswift", APIAvailability::NonSwift); io.enumCase(value, "available", APIAvailability::Available); } }; template<> struct ScalarEnumerationTraits { static void enumeration(IO &io, api_notes::SwiftWrapperKind &value) { io.enumCase(value, "none", api_notes::SwiftWrapperKind::None); io.enumCase(value, "struct", api_notes::SwiftWrapperKind::Struct); io.enumCase(value, "enum", api_notes::SwiftWrapperKind::Enum); } }; 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) { io.mapRequired("Position", p.Position); io.mapOptional("Nullability", p.Nullability, AbsentNullability); io.mapOptional("NoEscape", p.NoEscape); io.mapOptional("Type", p.Type, StringRef("")); } }; template <> struct MappingTraits { static void mapping(IO &io, Property& p) { io.mapRequired("Name", p.Name); io.mapOptional("PropertyKind", p.Kind); io.mapOptional("Nullability", p.Nullability, AbsentNullability); io.mapOptional("Availability", p.Availability.Mode); io.mapOptional("AvailabilityMsg", p.Availability.Msg); io.mapOptional("SwiftPrivate", p.SwiftPrivate); io.mapOptional("SwiftName", p.SwiftName); io.mapOptional("SwiftImportAsAccessors", p.SwiftImportAsAccessors); io.mapOptional("Type", p.Type, StringRef("")); } }; template <> struct MappingTraits { static void mapping(IO &io, Method& m) { io.mapRequired("Selector", m.Selector); io.mapRequired("MethodKind", m.Kind); io.mapOptional("Parameters", m.Params); io.mapOptional("Nullability", m.Nullability); io.mapOptional("NullabilityOfRet", m.NullabilityOfRet, AbsentNullability); io.mapOptional("Availability", m.Availability.Mode); io.mapOptional("AvailabilityMsg", m.Availability.Msg); io.mapOptional("SwiftPrivate", m.SwiftPrivate); io.mapOptional("SwiftName", m.SwiftName); io.mapOptional("FactoryAsInit", m.FactoryAsInit, FactoryAsInitKind::Infer); io.mapOptional("DesignatedInit", m.DesignatedInit, false); io.mapOptional("Required", m.Required, false); io.mapOptional("ResultType", m.ResultType, StringRef("")); } }; template <> struct MappingTraits { static void mapping(IO &io, Class& c) { io.mapRequired("Name", c.Name); io.mapOptional("AuditedForNullability", c.AuditedForNullability, false); io.mapOptional("Availability", c.Availability.Mode); io.mapOptional("AvailabilityMsg", c.Availability.Msg); io.mapOptional("SwiftPrivate", c.SwiftPrivate); io.mapOptional("SwiftName", c.SwiftName); io.mapOptional("SwiftBridge", c.SwiftBridge); io.mapOptional("NSErrorDomain", c.NSErrorDomain); io.mapOptional("SwiftImportAsNonGeneric", c.SwiftImportAsNonGeneric); + io.mapOptional("SwiftObjCMembers", c.SwiftObjCMembers); io.mapOptional("Methods", c.Methods); io.mapOptional("Properties", c.Properties); } }; template <> struct MappingTraits { static void mapping(IO &io, Function& f) { io.mapRequired("Name", f.Name); io.mapOptional("Parameters", f.Params); io.mapOptional("Nullability", f.Nullability); io.mapOptional("NullabilityOfRet", f.NullabilityOfRet, AbsentNullability); io.mapOptional("Availability", f.Availability.Mode); io.mapOptional("AvailabilityMsg", f.Availability.Msg); io.mapOptional("SwiftPrivate", f.SwiftPrivate); io.mapOptional("SwiftName", f.SwiftName); io.mapOptional("ResultType", f.ResultType, StringRef("")); } }; template <> struct MappingTraits { static void mapping(IO &io, GlobalVariable& v) { io.mapRequired("Name", v.Name); io.mapOptional("Nullability", v.Nullability, AbsentNullability); io.mapOptional("Availability", v.Availability.Mode); io.mapOptional("AvailabilityMsg", v.Availability.Msg); io.mapOptional("SwiftPrivate", v.SwiftPrivate); io.mapOptional("SwiftName", v.SwiftName); io.mapOptional("Type", v.Type, StringRef("")); } }; template <> struct MappingTraits { static void mapping(IO &io, EnumConstant& v) { io.mapRequired("Name", v.Name); io.mapOptional("Availability", v.Availability.Mode); io.mapOptional("AvailabilityMsg", v.Availability.Msg); io.mapOptional("SwiftPrivate", v.SwiftPrivate); io.mapOptional("SwiftName", v.SwiftName); } }; template <> struct MappingTraits { static void mapping(IO &io, Tag& t) { io.mapRequired("Name", t.Name); io.mapOptional("Availability", t.Availability.Mode); io.mapOptional("AvailabilityMsg", t.Availability.Msg); io.mapOptional("SwiftPrivate", t.SwiftPrivate); io.mapOptional("SwiftName", t.SwiftName); io.mapOptional("SwiftBridge", t.SwiftBridge); io.mapOptional("NSErrorDomain", t.NSErrorDomain); } }; template <> struct MappingTraits { static void mapping(IO &io, Typedef& t) { io.mapRequired("Name", t.Name); io.mapOptional("Availability", t.Availability.Mode); io.mapOptional("AvailabilityMsg", t.Availability.Msg); io.mapOptional("SwiftPrivate", t.SwiftPrivate); io.mapOptional("SwiftName", t.SwiftName); io.mapOptional("SwiftBridge", t.SwiftBridge); io.mapOptional("NSErrorDomain", t.NSErrorDomain); io.mapOptional("SwiftWrapper", t.SwiftWrapper); } }; 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) { io.mapRequired("Name", m.Name); io.mapOptional("Availability", m.Availability.Mode); io.mapOptional("AvailabilityMsg", m.Availability.Msg); io.mapOptional("SwiftInferImportAsMember", m.SwiftInferImportAsMember); mapTopLevelItems(io, m.TopLevel); io.mapOptional("SwiftVersions", m.SwiftVersions); } }; } } using llvm::yaml::Input; using llvm::yaml::Output; void Module::dump() { Output yout(llvm::errs()); yout << *this; } static bool parseAPINotes(StringRef yamlInput, Module &module, llvm::SourceMgr::DiagHandlerTy diagHandler, void *diagHandlerCtxt) { Input yin(yamlInput, nullptr, diagHandler, diagHandlerCtxt); yin >> module; return static_cast(yin.error()); } namespace { using namespace api_notes; class YAMLConverter { const Module &TheModule; const FileEntry *SourceFile; APINotesWriter *Writer; OSType TargetOS; llvm::raw_ostream &OS; llvm::SourceMgr::DiagHandlerTy DiagHandler; void *DiagHandlerCtxt; bool ErrorOccured; /// Emit a diagnostic bool emitError(llvm::Twine message) { DiagHandler(llvm::SMDiagnostic("", llvm::SourceMgr::DK_Error, message.str()), DiagHandlerCtxt); ErrorOccured = true; return true; } public: YAMLConverter(const Module &module, const FileEntry *sourceFile, OSType targetOS, llvm::raw_ostream &os, llvm::SourceMgr::DiagHandlerTy diagHandler, void *diagHandlerCtxt) : TheModule(module), SourceFile(sourceFile), Writer(0), TargetOS(targetOS), OS(os), DiagHandler(diagHandler), DiagHandlerCtxt(diagHandlerCtxt), ErrorOccured(false) {} bool isAvailable(const AvailabilityItem &in) { // Check if the API is available on the OS for which we are building. if (in.Mode == APIAvailability::OSX && TargetOS != OSType::OSX) return false; if (in.Mode == APIAvailability::IOS && TargetOS != OSType::IOS) return false; return true; } bool convertAvailability(const AvailabilityItem &in, CommonEntityInfo &outInfo, llvm::StringRef apiName) { // Populate the unavailability information. outInfo.Unavailable = (in.Mode == APIAvailability::None); outInfo.UnavailableInSwift = (in.Mode == APIAvailability::NonSwift); if (outInfo.Unavailable || outInfo.UnavailableInSwift) { outInfo.UnavailableMsg = in.Msg; } else { if (!in.Msg.empty()) { emitError("availability message for available API '" + apiName + "' will not be used"); } } return false; } void convertParams(const ParamsSeq ¶ms, FunctionInfo &outInfo) { for (const auto &p : params) { ParamInfo pi; if (p.Nullability) pi.setNullabilityAudited(*p.Nullability); pi.setNoEscape(p.NoEscape); pi.setType(p.Type); while (outInfo.Params.size() <= p.Position) { outInfo.Params.push_back(ParamInfo()); } outInfo.Params[p.Position] |= pi; } } void convertNullability(const NullabilitySeq &nullability, Optional nullabilityOfRet, FunctionInfo &outInfo, llvm::StringRef apiName) { if (nullability.size() > FunctionInfo::getMaxNullabilityIndex()) { emitError("nullability info for " + apiName + " does not fit"); return; } bool audited = false; unsigned int idx = 1; for (auto i = nullability.begin(), e = nullability.end(); i != e; ++i, ++idx){ outInfo.addTypeInfo(idx, *i); audited = true; } if (nullabilityOfRet) { outInfo.addTypeInfo(0, *nullabilityOfRet); audited = true; } else if (audited) { outInfo.addTypeInfo(0, *DefaultNullability); } if (audited) { outInfo.NullabilityAudited = audited; outInfo.NumAdjustedNullable = idx; } } /// Convert the common parts of an entity from YAML. template bool convertCommon(const T& common, CommonEntityInfo &info, StringRef apiName) { if (!isAvailable(common.Availability)) return true; convertAvailability(common.Availability, info, apiName); info.setSwiftPrivate(common.SwiftPrivate); info.SwiftName = common.SwiftName; return false; } /// Convert the common parts of a type entity from YAML. template bool convertCommonType(const T& common, CommonTypeInfo &info, StringRef apiName) { if (convertCommon(common, info, apiName)) return true; info.setSwiftBridge(common.SwiftBridge); info.setNSErrorDomain(common.NSErrorDomain); return false; } // Translate from Method into ObjCMethodInfo and write it out. void convertMethod(const Method &meth, ContextID classID, StringRef className, VersionTuple swiftVersion) { ObjCMethodInfo mInfo; if (convertCommon(meth, mInfo, meth.Selector)) return; // Check if the selector ends with ':' to determine if it takes arguments. bool takesArguments = meth.Selector.endswith(":"); // Split the selector into pieces. llvm::SmallVector a; meth.Selector.split(a, ":", /*MaxSplit*/ -1, /*KeepEmpty*/ false); if (!takesArguments && a.size() > 1 ) { emitError("selector " + meth.Selector + "is missing a ':' at the end"); return; } // Construct ObjCSelectorRef. api_notes::ObjCSelectorRef selectorRef; selectorRef.NumPieces = !takesArguments ? 0 : a.size(); selectorRef.Identifiers = a; // Translate the initializer info. mInfo.DesignatedInit = meth.DesignatedInit; mInfo.Required = meth.Required; if (meth.FactoryAsInit != FactoryAsInitKind::Infer) { emitError("'FactoryAsInit' is no longer valid; " "use 'SwiftName' instead"); } mInfo.ResultType = meth.ResultType; // Translate parameter information. convertParams(meth.Params, mInfo); // Translate nullability info. convertNullability(meth.Nullability, meth.NullabilityOfRet, mInfo, meth.Selector); // Write it. Writer->addObjCMethod(classID, selectorRef, meth.Kind == MethodKind::Instance, mInfo, swiftVersion); } void convertContext(const Class &cl, bool isClass, VersionTuple swiftVersion) { // Write the class. ObjCContextInfo cInfo; if (convertCommonType(cl, cInfo, cl.Name)) return; if (cl.AuditedForNullability) cInfo.setDefaultNullability(*DefaultNullability); if (cl.SwiftImportAsNonGeneric) cInfo.setSwiftImportAsNonGeneric(*cl.SwiftImportAsNonGeneric); + if (cl.SwiftObjCMembers) + cInfo.setSwiftObjCMembers(*cl.SwiftObjCMembers); ContextID clID = Writer->addObjCContext(cl.Name, isClass, cInfo, swiftVersion); // Write all methods. llvm::StringMap> knownMethods; for (const auto &method : cl.Methods) { // Check for duplicate method definitions. bool isInstanceMethod = method.Kind == MethodKind::Instance; bool &known = isInstanceMethod ? knownMethods[method.Selector].first : knownMethods[method.Selector].second; if (known) { emitError(llvm::Twine("duplicate definition of method '") + (isInstanceMethod? "-" : "+") + "[" + cl.Name + " " + method.Selector + "]'"); continue; } known = true; convertMethod(method, clID, cl.Name, swiftVersion); } // Write all properties. llvm::StringSet<> knownInstanceProperties; llvm::StringSet<> knownClassProperties; for (const auto &prop : cl.Properties) { // Check for duplicate property definitions. if ((!prop.Kind || *prop.Kind == MethodKind::Instance) && !knownInstanceProperties.insert(prop.Name).second) { emitError("duplicate definition of instance property '" + cl.Name + "." + prop.Name + "'"); continue; } if ((!prop.Kind || *prop.Kind == MethodKind::Class) && !knownClassProperties.insert(prop.Name).second) { emitError("duplicate definition of class property '" + cl.Name + "." + prop.Name + "'"); continue; } // Translate from Property into ObjCPropertyInfo. ObjCPropertyInfo pInfo; if (!isAvailable(prop.Availability)) continue; convertAvailability(prop.Availability, pInfo, prop.Name); pInfo.setSwiftPrivate(prop.SwiftPrivate); pInfo.SwiftName = prop.SwiftName; if (prop.Nullability) pInfo.setNullabilityAudited(*prop.Nullability); if (prop.SwiftImportAsAccessors) pInfo.setSwiftImportAsAccessors(*prop.SwiftImportAsAccessors); pInfo.setType(prop.Type); if (prop.Kind) { Writer->addObjCProperty(clID, prop.Name, *prop.Kind == MethodKind::Instance, pInfo, swiftVersion); } else { // Add both instance and class properties with this name. Writer->addObjCProperty(clID, prop.Name, true, pInfo, swiftVersion); Writer->addObjCProperty(clID, prop.Name, false, pInfo, swiftVersion); } } } void convertTopLevelItems(const TopLevelItems &items, VersionTuple swiftVersion) { // Write all classes. llvm::StringSet<> knownClasses; 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, swiftVersion); } // Write all protocols. llvm::StringSet<> knownProtocols; 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, swiftVersion); } // Write all global variables. llvm::StringSet<> knownGlobals; for (const auto &global : items.Globals) { // Check for duplicate global variables. if (!knownGlobals.insert(global.Name).second) { emitError("multiple definitions of global variable '" + global.Name + "'"); continue; } GlobalVariableInfo info; if (!isAvailable(global.Availability)) continue; convertAvailability(global.Availability, info, global.Name); info.setSwiftPrivate(global.SwiftPrivate); info.SwiftName = global.SwiftName; if (global.Nullability) info.setNullabilityAudited(*global.Nullability); info.setType(global.Type); Writer->addGlobalVariable(global.Name, info, swiftVersion); } // Write all global functions. llvm::StringSet<> knownFunctions; for (const auto &function : items.Functions) { // Check for duplicate global functions. if (!knownFunctions.insert(function.Name).second) { emitError("multiple definitions of global function '" + function.Name + "'"); continue; } GlobalFunctionInfo info; if (!isAvailable(function.Availability)) continue; convertAvailability(function.Availability, info, function.Name); info.setSwiftPrivate(function.SwiftPrivate); info.SwiftName = function.SwiftName; convertParams(function.Params, info); convertNullability(function.Nullability, function.NullabilityOfRet, info, function.Name); info.ResultType = function.ResultType; Writer->addGlobalFunction(function.Name, info, swiftVersion); } // Write all enumerators. llvm::StringSet<> knownEnumConstants; for (const auto &enumConstant : items.EnumConstants) { // Check for duplicate enumerators if (!knownEnumConstants.insert(enumConstant.Name).second) { emitError("multiple definitions of enumerator '" + enumConstant.Name + "'"); continue; } EnumConstantInfo info; if (!isAvailable(enumConstant.Availability)) continue; convertAvailability(enumConstant.Availability, info, enumConstant.Name); info.setSwiftPrivate(enumConstant.SwiftPrivate); info.SwiftName = enumConstant.SwiftName; Writer->addEnumConstant(enumConstant.Name, info, swiftVersion); } // Write all tags. llvm::StringSet<> knownTags; for (const auto &t : items.Tags) { // Check for duplicate tag definitions. if (!knownTags.insert(t.Name).second) { emitError("multiple definitions Of tag '" + t.Name + "'"); continue; } TagInfo tagInfo; if (convertCommonType(t, tagInfo, t.Name)) continue; Writer->addTag(t.Name, tagInfo, swiftVersion); } // Write all typedefs. llvm::StringSet<> knownTypedefs; for (const auto &t : items.Typedefs) { // Check for duplicate typedef definitions. if (!knownTypedefs.insert(t.Name).second) { emitError("multiple definitions of typedef '" + t.Name + "'"); continue; } TypedefInfo typedefInfo; if (convertCommonType(t, typedefInfo, t.Name)) continue; typedefInfo.SwiftWrapper = t.SwiftWrapper; 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; opts.SwiftInferImportAsMember = true; Writer->addModuleOptions(opts); } // Convert the versioned information. for (const auto &versioned : TheModule.SwiftVersions) { convertTopLevelItems(versioned.Items, versioned.Version); } if (!ErrorOccured) Writer->writeToStream(OS); return ErrorOccured; } }; } static bool compile(const Module &module, const FileEntry *sourceFile, llvm::raw_ostream &os, api_notes::OSType targetOS, llvm::SourceMgr::DiagHandlerTy diagHandler, void *diagHandlerCtxt){ using namespace api_notes; YAMLConverter c(module, sourceFile, targetOS, os, diagHandler, diagHandlerCtxt); return c.convertModule(); } bool api_notes::parseAndDumpAPINotes(StringRef yamlInput) { Module module; if (parseAPINotes(yamlInput, module, nullptr, nullptr)) return true; Output yout(llvm::outs()); yout << module; return false; } /// Simple diagnostic handler that prints diagnostics to standard error. static void printDiagnostic(const llvm::SMDiagnostic &diag, void *context) { diag.print(nullptr, llvm::errs()); } bool api_notes::compileAPINotes(StringRef yamlInput, const FileEntry *sourceFile, llvm::raw_ostream &os, OSType targetOS, llvm::SourceMgr::DiagHandlerTy diagHandler, void *diagHandlerCtxt) { Module module; if (!diagHandler) { diagHandler = &printDiagnostic; } if (parseAPINotes(yamlInput, module, diagHandler, diagHandlerCtxt)) return true; return compile(module, sourceFile, os, targetOS, diagHandler, diagHandlerCtxt); } namespace { // Deserialize the API notes file into a module. class DecompileVisitor : public APINotesReader::Visitor { /// Allocator used to clone those strings that need it. llvm::BumpPtrAllocator Allocator; /// 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; /// Copy a string into allocated memory so it does disappear on us. StringRef copyString(StringRef string) { if (string.empty()) return StringRef(); void *ptr = Allocator.Allocate(string.size(), 1); memcpy(ptr, string.data(), string.size()); return StringRef(reinterpret_cast(ptr), string.size()); } /// Copy an optional string into allocated memory so it does disappear on us. Optional maybeCopyString(Optional string) { if (!string) return None; return copyString(*string); } /// Copy an optional string into allocated memory so it does disappear on us. Optional maybeCopyString(Optional string) { if (!string) return None; return copyString(*string); } template void handleCommon(T &record, const CommonEntityInfo &info) { handleAvailability(record.Availability, info); record.SwiftPrivate = info.isSwiftPrivate(); record.SwiftName = copyString(info.SwiftName); } template void handleCommonType(T &record, const CommonTypeInfo &info) { handleCommon(record, info); record.SwiftBridge = maybeCopyString(info.getSwiftBridge()); record.NSErrorDomain = maybeCopyString(info.getNSErrorDomain()); } /// Map Objective-C context info. void handleObjCContext(Class &record, StringRef name, const ObjCContextInfo &info) { record.Name = name; handleCommonType(record, info); record.SwiftImportAsNonGeneric = info.getSwiftImportAsNonGeneric(); + record.SwiftObjCMembers = info.getSwiftObjCMembers(); if (info.getDefaultNullability()) { record.AuditedForNullability = true; } } /// Map availability information, if present. void handleAvailability(AvailabilityItem &availability, const CommonEntityInfo &info) { if (info.Unavailable) { availability.Mode = APIAvailability::None; availability.Msg = copyString(info.UnavailableMsg); } if (info.UnavailableInSwift) { availability.Mode = APIAvailability::NonSwift; availability.Msg = copyString(info.UnavailableMsg); } } /// Map parameter information for a function. void handleParameters(ParamsSeq ¶ms, const FunctionInfo &info) { unsigned position = 0; for (const auto &pi: info.Params) { Param p; p.Position = position++; p.Nullability = pi.getNullability(); p.NoEscape = pi.isNoEscape(); p.Type = copyString(pi.getType()); params.push_back(p); } } /// Map nullability information for a function. void handleNullability(NullabilitySeq &nullability, llvm::Optional &nullabilityOfRet, const FunctionInfo &info, unsigned numParams) { if (info.NullabilityAudited) { nullabilityOfRet = info.getReturnTypeInfo(); // Figure out the number of parameters from the selector. for (unsigned i = 0; i != numParams; ++i) nullability.push_back(info.getParamTypeInfo(i)); } } 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, VersionTuple swiftVersion) { // Record this known context. auto &items = getTopLevelItems(swiftVersion); auto &known = knownContexts[contextID.Value]; known.isProtocol = false; handleObjCContext(known.getContext(swiftVersion, items), name, info); } virtual void visitObjCProtocol(ContextID contextID, StringRef name, const ObjCContextInfo &info, VersionTuple swiftVersion) { // Record this known context. auto &items = getTopLevelItems(swiftVersion); auto &known = knownContexts[contextID.Value]; known.isProtocol = true; handleObjCContext(known.getContext(swiftVersion, items), name, info); } virtual void visitObjCMethod(ContextID contextID, StringRef selector, bool isInstanceMethod, const ObjCMethodInfo &info, VersionTuple swiftVersion) { Method method; method.Selector = copyString(selector); method.Kind = isInstanceMethod ? MethodKind::Instance : MethodKind::Class; handleCommon(method, info); handleParameters(method.Params, info); handleNullability(method.Nullability, method.NullabilityOfRet, info, selector.count(':')); method.DesignatedInit = info.DesignatedInit; method.Required = info.Required; method.ResultType = copyString(info.ResultType); 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, VersionTuple swiftVersion) { Property property; property.Name = name; property.Kind = isInstance ? MethodKind::Instance : MethodKind::Class; handleCommon(property, info); // FIXME: No way to represent "not audited for nullability". if (auto nullability = info.getNullability()) { property.Nullability = *nullability; } property.SwiftImportAsAccessors = info.getSwiftImportAsAccessors(); property.Type = copyString(info.getType()); auto &items = getTopLevelItems(swiftVersion); knownContexts[contextID.Value].getContext(swiftVersion, items) .Properties.push_back(property); } virtual void visitGlobalFunction(StringRef name, const GlobalFunctionInfo &info, VersionTuple swiftVersion) { Function function; function.Name = name; handleCommon(function, info); handleParameters(function.Params, info); if (info.NumAdjustedNullable > 0) handleNullability(function.Nullability, function.NullabilityOfRet, info, info.NumAdjustedNullable-1); function.ResultType = copyString(info.ResultType); auto &items = getTopLevelItems(swiftVersion); items.Functions.push_back(function); } virtual void visitGlobalVariable(StringRef name, const GlobalVariableInfo &info, VersionTuple swiftVersion) { GlobalVariable global; global.Name = name; handleCommon(global, info); // FIXME: No way to represent "not audited for nullability". if (auto nullability = info.getNullability()) { global.Nullability = *nullability; } global.Type = copyString(info.getType()); auto &items = getTopLevelItems(swiftVersion); items.Globals.push_back(global); } virtual void visitEnumConstant(StringRef name, const EnumConstantInfo &info, VersionTuple swiftVersion) { EnumConstant enumConstant; enumConstant.Name = name; handleCommon(enumConstant, info); auto &items = getTopLevelItems(swiftVersion); items.EnumConstants.push_back(enumConstant); } virtual void visitTag(StringRef name, const TagInfo &info, VersionTuple swiftVersion) { Tag tag; tag.Name = name; handleCommonType(tag, info); auto &items = getTopLevelItems(swiftVersion); items.Tags.push_back(tag); } virtual void visitTypedef(StringRef name, const TypedefInfo &info, VersionTuple swiftVersion) { Typedef td; td.Name = name; handleCommonType(td, info); td.SwiftWrapper = info.SwiftWrapper; auto &items = getTopLevelItems(swiftVersion); items.Typedefs.push_back(td); } /// Retrieve the module. Module &getModule() { return TheModule; } }; } /// Produce a flattened, numeric value for optional method/property kinds. static unsigned flattenPropertyKind(llvm::Optional kind) { return kind ? (*kind == MethodKind::Instance ? 2 : 1) : 0; } /// Sort the items in the given block of "top-level" items. static void sortTopLevelItems(TopLevelItems &items) { // Sort classes. std::sort(items.Classes.begin(), items.Classes.end(), [](const Class &lhs, const Class &rhs) -> bool { return lhs.Name < rhs.Name; }); // Sort protocols. std::sort(items.Protocols.begin(), items.Protocols.end(), [](const Class &lhs, const Class &rhs) -> bool { return lhs.Name < rhs.Name; }); // Sort methods and properties within each class and protocol. auto sortMembers = [](Class &record) { // Sort properties. 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)); }); // 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)); }); }; std::for_each(items.Classes.begin(), items.Classes.end(), sortMembers); std::for_each(items.Protocols.begin(), items.Protocols.end(), sortMembers); // Sort functions. 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(items.Globals.begin(), items.Globals.end(), [](const GlobalVariable &lhs, const GlobalVariable &rhs) -> bool { return lhs.Name < rhs.Name; }); // Sort enum constants. std::sort(items.EnumConstants.begin(), items.EnumConstants.end(), [](const EnumConstant &lhs, const EnumConstant &rhs) -> bool { return lhs.Name < rhs.Name; }); // Sort tags. std::sort(items.Tags.begin(), items.Tags.end(), [](const Tag &lhs, const Tag &rhs) -> bool { return lhs.Name < rhs.Name; }); // Sort typedefs. 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); yout << module; return false; } diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp index f58691181f1c..59016284861a 100644 --- a/clang/lib/Sema/SemaAPINotes.cpp +++ b/clang/lib/Sema/SemaAPINotes.cpp @@ -1,807 +1,814 @@ //===--- SemaAPINotes.cpp - API Notes Handling ----------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file implements the mapping from API notes to declaration attributes. // //===----------------------------------------------------------------------===// #include "clang/Sema/SemaInternal.h" #include "clang/AST/DeclObjC.h" #include "clang/APINotes/APINotesReader.h" using namespace clang; namespace { enum IsActive_t : bool { IsNotActive, IsActive }; struct VersionedInfoMetadata { /// An empty version refers to unversioned metadata. VersionTuple Version; bool IsActive; VersionedInfoMetadata(VersionTuple version, IsActive_t active) : Version(version), IsActive(active == IsActive_t::IsActive) {} }; } // end anonymous namespace /// Determine whether this is a multi-level pointer type. static bool isMultiLevelPointerType(QualType type) { QualType pointee = type->getPointeeType(); if (pointee.isNull()) return false; return pointee->isAnyPointerType() || pointee->isObjCObjectPointerType() || pointee->isMemberPointerType(); } // Apply nullability to the given declaration. static void applyNullability(Sema &S, Decl *decl, NullabilityKind nullability, VersionedInfoMetadata metadata) { if (!metadata.IsActive) return; QualType type; // Nullability for a function/method appertains to the retain type. if (auto function = dyn_cast(decl)) { type = function->getReturnType(); } else if (auto method = dyn_cast(decl)) { type = method->getReturnType(); } else if (auto value = dyn_cast(decl)) { type = value->getType(); } else if (auto property = dyn_cast(decl)) { type = property->getType(); } else { return; } // Check the nullability specifier on this type. QualType origType = type; S.checkNullabilityTypeSpecifier(type, nullability, decl->getLocation(), /*isContextSensitive=*/false, isa(decl), /*implicit=*/true, /*overrideExisting=*/true); if (type.getTypePtr() == origType.getTypePtr()) return; if (auto function = dyn_cast(decl)) { const FunctionType *fnType = function->getType()->castAs(); if (const FunctionProtoType *proto = dyn_cast(fnType)) { function->setType(S.Context.getFunctionType(type, proto->getParamTypes(), proto->getExtProtoInfo())); } else { function->setType(S.Context.getFunctionNoProtoType(type, fnType->getExtInfo())); } } else if (auto method = dyn_cast(decl)) { method->setReturnType(type); // Make it a context-sensitive keyword if we can. if (!isMultiLevelPointerType(type)) { method->setObjCDeclQualifier( Decl::ObjCDeclQualifier(method->getObjCDeclQualifier() | Decl::OBJC_TQ_CSNullability)); } } else if (auto value = dyn_cast(decl)) { value->setType(type); // Make it a context-sensitive keyword if we can. if (auto parm = dyn_cast(decl)) { if (parm->isObjCMethodParameter() && !isMultiLevelPointerType(type)) { parm->setObjCDeclQualifier( Decl::ObjCDeclQualifier(parm->getObjCDeclQualifier() | Decl::OBJC_TQ_CSNullability)); } } } else if (auto property = dyn_cast(decl)) { property->setType(type, property->getTypeSourceInfo()); // Make it a property attribute if we can. if (!isMultiLevelPointerType(type)) { property->setPropertyAttributes( ObjCPropertyDecl::OBJC_PR_null_resettable); } } else { llvm_unreachable("cannot handle nullability here"); } } /// Copy a string into ASTContext-allocated memory. static StringRef CopyString(ASTContext &ctx, StringRef string) { void *mem = ctx.Allocate(string.size(), alignof(char)); memcpy(mem, string.data(), string.size()); return StringRef(static_cast(mem), string.size()); } namespace { template struct AttrKindFor {}; #define ATTR(X) \ template <> struct AttrKindFor { \ static const attr::Kind value = attr::X; \ }; #include "clang/Basic/AttrList.inc" /// Handle an attribute introduced by API notes. /// /// \param shouldAddAttribute Whether we should add a new attribute /// (otherwise, we might remove an existing attribute). /// \param createAttr Create the new attribute to be added. template void handleAPINotedAttribute( Sema &S, Decl *D, bool shouldAddAttribute, VersionedInfoMetadata metadata, llvm::function_ref createAttr, llvm::function_ref(Decl*)> getExistingAttr) { if (metadata.IsActive) { auto end = D->specific_attr_end(); auto existing = getExistingAttr(D); if (existing != end) { // Remove the existing attribute, and treat it as a superseded // non-versioned attribute. auto *versioned = SwiftVersionedAttr::CreateImplicit(S.Context, clang::VersionTuple(), *existing); D->getAttrs().erase(existing.getCurrent()); D->addAttr(versioned); } // If we're supposed to add a new attribute, do so. if (shouldAddAttribute) { if (auto attr = createAttr()) { D->addAttr(attr); } } } else { if (shouldAddAttribute) { if (auto attr = createAttr()) { auto *versioned = SwiftVersionedAttr::CreateImplicit(S.Context, metadata.Version, attr); D->addAttr(versioned); } } else { // FIXME: This isn't preserving enough information for things like // availability, where we're trying to remove a /specific/ kind of // attribute. auto *versioned = SwiftVersionedRemovalAttr::CreateImplicit(S.Context, metadata.Version, AttrKindFor::value); D->addAttr(versioned); } } } template void handleAPINotedAttribute( Sema &S, Decl *D, bool shouldAddAttribute, VersionedInfoMetadata metadata, llvm::function_ref createAttr) { handleAPINotedAttribute(S, D, shouldAddAttribute, metadata, createAttr, [](Decl *decl) { return decl->specific_attr_begin(); }); } } static void ProcessAPINotes(Sema &S, Decl *D, const api_notes::CommonEntityInfo &info, VersionedInfoMetadata metadata) { // Availability if (info.Unavailable) { handleAPINotedAttribute(S, D, true, metadata, [&] { return UnavailableAttr::CreateImplicit(S.Context, CopyString(S.Context, info.UnavailableMsg)); }); } if (info.UnavailableInSwift) { handleAPINotedAttribute(S, D, true, metadata, [&] { return AvailabilityAttr::CreateImplicit( S.Context, &S.Context.Idents.get("swift"), VersionTuple(), VersionTuple(), VersionTuple(), /*Unavailable=*/true, CopyString(S.Context, info.UnavailableMsg), /*Strict=*/false, /*Replacement=*/StringRef()); }, [](Decl *decl) { auto existing = decl->specific_attr_begin(), end = decl->specific_attr_end(); while (existing != end) { if (auto platform = (*existing)->getPlatform()) { if (platform->isStr("swift")) break; } ++existing; } return existing; }); } // swift_private if (auto swiftPrivate = info.isSwiftPrivate()) { handleAPINotedAttribute(S, D, *swiftPrivate, metadata, [&] { return SwiftPrivateAttr::CreateImplicit(S.Context); }); } // swift_name if (!info.SwiftName.empty()) { handleAPINotedAttribute(S, D, true, metadata, [&]() -> SwiftNameAttr * { auto &APINoteName = S.getASTContext().Idents.get("SwiftName API Note"); if (!S.DiagnoseSwiftName(D, info.SwiftName, D->getLocation(), &APINoteName)) { return nullptr; } return SwiftNameAttr::CreateImplicit(S.Context, CopyString(S.Context, info.SwiftName)); }); } } static void ProcessAPINotes(Sema &S, Decl *D, const api_notes::CommonTypeInfo &info, VersionedInfoMetadata metadata) { // swift_bridge if (auto swiftBridge = info.getSwiftBridge()) { handleAPINotedAttribute(S, D, !swiftBridge->empty(), metadata, [&] { return SwiftBridgeAttr::CreateImplicit(S.Context, CopyString(S.Context, *swiftBridge)); }); } // ns_error_domain if (auto nsErrorDomain = info.getNSErrorDomain()) { handleAPINotedAttribute(S, D, !nsErrorDomain->empty(), metadata, [&] { return NSErrorDomainAttr::CreateImplicit( S.Context, &S.Context.Idents.get(*nsErrorDomain)); }); } ProcessAPINotes(S, D, static_cast(info), metadata); } /// Check that the replacement type provided by API notes is reasonable. /// /// This is a very weak form of ABI check. static bool checkAPINotesReplacementType(Sema &S, SourceLocation loc, QualType origType, QualType replacementType) { if (S.Context.getTypeSize(origType) != S.Context.getTypeSize(replacementType)) { S.Diag(loc, diag::err_incompatible_replacement_type) << replacementType << origType; return true; } return false; } /// Process API notes for a variable or property. static void ProcessAPINotes(Sema &S, Decl *D, const api_notes::VariableInfo &info, VersionedInfoMetadata metadata) { // Type override. if (metadata.IsActive && !info.getType().empty() && S.ParseTypeFromStringCallback) { auto parsedType = S.ParseTypeFromStringCallback(info.getType(), "", D->getLocation()); if (parsedType.isUsable()) { QualType type = Sema::GetTypeFromParser(parsedType.get()); auto typeInfo = S.Context.getTrivialTypeSourceInfo(type, D->getLocation()); if (auto var = dyn_cast(D)) { // Make adjustments to parameter types. if (isa(var)) { type = S.adjustParameterTypeForObjCAutoRefCount(type, D->getLocation()); type = S.Context.getAdjustedParameterType(type); } if (!checkAPINotesReplacementType(S, var->getLocation(), var->getType(), type)) { var->setType(type); var->setTypeSourceInfo(typeInfo); } } else if (auto property = dyn_cast(D)) { if (!checkAPINotesReplacementType(S, property->getLocation(), property->getType(), type)) { property->setType(type, typeInfo); } } else { llvm_unreachable("API notes allowed a type on an unknown declaration"); } } } // Nullability. if (auto Nullability = info.getNullability()) { applyNullability(S, D, *Nullability, metadata); } // Handle common entity information. ProcessAPINotes(S, D, static_cast(info), metadata); } /// Process API notes for a parameter. static void ProcessAPINotes(Sema &S, ParmVarDecl *D, const api_notes::ParamInfo &info, VersionedInfoMetadata metadata) { // noescape if (auto noescape = info.isNoEscape()) { handleAPINotedAttribute(S, D, *noescape, metadata, [&] { return NoEscapeAttr::CreateImplicit(S.Context); }); } // Handle common entity information. ProcessAPINotes(S, D, static_cast(info), metadata); } /// Process API notes for a global variable. static void ProcessAPINotes(Sema &S, VarDecl *D, const api_notes::GlobalVariableInfo &info, VersionedInfoMetadata metadata) { // Handle common entity information. ProcessAPINotes(S, D, static_cast(info), metadata); } /// Process API notes for an Objective-C property. static void ProcessAPINotes(Sema &S, ObjCPropertyDecl *D, const api_notes::ObjCPropertyInfo &info, VersionedInfoMetadata metadata) { // Handle common entity information. ProcessAPINotes(S, D, static_cast(info), metadata); if (auto asAccessors = info.getSwiftImportAsAccessors()) { handleAPINotedAttribute(S, D, *asAccessors, metadata, [&] { return SwiftImportPropertyAsAccessorsAttr::CreateImplicit(S.Context); }); } } namespace { typedef llvm::PointerUnion FunctionOrMethod; } /// Process API notes for a function or method. static void ProcessAPINotes(Sema &S, FunctionOrMethod AnyFunc, const api_notes::FunctionInfo &info, VersionedInfoMetadata metadata) { // Find the declaration itself. FunctionDecl *FD = AnyFunc.dyn_cast(); Decl *D = FD; ObjCMethodDecl *MD = 0; if (!D) { MD = AnyFunc.get(); D = MD; } // Nullability of return type. if (info.NullabilityAudited) { applyNullability(S, D, info.getReturnTypeInfo(), metadata); } // Parameters. unsigned NumParams; if (FD) NumParams = FD->getNumParams(); else NumParams = MD->param_size(); bool anyTypeChanged = false; for (unsigned I = 0; I != NumParams; ++I) { ParmVarDecl *Param; if (FD) Param = FD->getParamDecl(I); else Param = MD->param_begin()[I]; QualType paramTypeBefore = Param->getType(); if (I < info.Params.size()) { ProcessAPINotes(S, Param, info.Params[I], metadata); } // Nullability. if (info.NullabilityAudited) applyNullability(S, Param, info.getParamTypeInfo(I), metadata); if (paramTypeBefore.getAsOpaquePtr() != Param->getType().getAsOpaquePtr()) anyTypeChanged = true; } // Result type override. QualType overriddenResultType; if (metadata.IsActive && !info.ResultType.empty() && S.ParseTypeFromStringCallback) { auto parsedType = S.ParseTypeFromStringCallback(info.ResultType, "", D->getLocation()); if (parsedType.isUsable()) { QualType resultType = Sema::GetTypeFromParser(parsedType.get()); if (MD) { if (!checkAPINotesReplacementType(S, D->getLocation(), MD->getReturnType(), resultType)) { auto resultTypeInfo = S.Context.getTrivialTypeSourceInfo(resultType, D->getLocation()); MD->setReturnType(resultType); MD->setReturnTypeSourceInfo(resultTypeInfo); } } else if (!checkAPINotesReplacementType(S, FD->getLocation(), FD->getReturnType(), resultType)) { overriddenResultType = resultType; anyTypeChanged = true; } } } // If the result type or any of the parameter types changed for a function // declaration, we have to rebuild the type. if (FD && anyTypeChanged) { if (const auto *fnProtoType = FD->getType()->getAs()) { if (overriddenResultType.isNull()) overriddenResultType = fnProtoType->getReturnType(); SmallVector paramTypes; for (auto param : FD->parameters()) { paramTypes.push_back(param->getType()); } FD->setType(S.Context.getFunctionType(overriddenResultType, paramTypes, fnProtoType->getExtProtoInfo())); } else if (!overriddenResultType.isNull()) { const auto *fnNoProtoType = FD->getType()->castAs(); FD->setType( S.Context.getFunctionNoProtoType(overriddenResultType, fnNoProtoType->getExtInfo())); } } // Handle common entity information. ProcessAPINotes(S, D, static_cast(info), metadata); } /// Process API notes for a global function. static void ProcessAPINotes(Sema &S, FunctionDecl *D, const api_notes::GlobalFunctionInfo &info, VersionedInfoMetadata metadata) { // Handle common function information. ProcessAPINotes(S, FunctionOrMethod(D), static_cast(info), metadata); } /// Process API notes for an enumerator. static void ProcessAPINotes(Sema &S, EnumConstantDecl *D, const api_notes::EnumConstantInfo &info, VersionedInfoMetadata metadata) { // Handle common information. ProcessAPINotes(S, D, static_cast(info), metadata); } /// Process API notes for an Objective-C method. static void ProcessAPINotes(Sema &S, ObjCMethodDecl *D, const api_notes::ObjCMethodInfo &info, VersionedInfoMetadata metadata) { // Designated initializers. if (info.DesignatedInit) { handleAPINotedAttribute(S, D, true, metadata, [&] { if (ObjCInterfaceDecl *IFace = D->getClassInterface()) { IFace->setHasDesignatedInitializers(); } return ObjCDesignatedInitializerAttr::CreateImplicit(S.Context); }); } // Handle common function information. ProcessAPINotes(S, FunctionOrMethod(D), static_cast(info), metadata); } /// Process API notes for a tag. static void ProcessAPINotes(Sema &S, TagDecl *D, const api_notes::TagInfo &info, VersionedInfoMetadata metadata) { // Handle common type information. ProcessAPINotes(S, D, static_cast(info), metadata); } /// Process API notes for a typedef. static void ProcessAPINotes(Sema &S, TypedefNameDecl *D, const api_notes::TypedefInfo &info, VersionedInfoMetadata metadata) { // swift_wrapper using SwiftWrapperKind = api_notes::SwiftWrapperKind; if (auto swiftWrapper = info.SwiftWrapper) { handleAPINotedAttribute(S, D, *swiftWrapper != SwiftWrapperKind::None, metadata, [&] { SwiftNewtypeAttr::NewtypeKind kind; switch (*swiftWrapper) { case SwiftWrapperKind::None: llvm_unreachable("Shouldn't build an attribute"); case SwiftWrapperKind::Struct: kind = SwiftNewtypeAttr::NK_Struct; break; case SwiftWrapperKind::Enum: kind = SwiftNewtypeAttr::NK_Enum; break; } return SwiftNewtypeAttr::CreateImplicit( S.Context, SwiftNewtypeAttr::GNU_swift_wrapper, kind); }); } // Handle common type information. ProcessAPINotes(S, D, static_cast(info), metadata); } /// Process API notes for an Objective-C class or protocol. static void ProcessAPINotes(Sema &S, ObjCContainerDecl *D, const api_notes::ObjCContextInfo &info, VersionedInfoMetadata metadata) { // Handle common type information. ProcessAPINotes(S, D, static_cast(info), metadata); } /// Process API notes for an Objective-C class. static void ProcessAPINotes(Sema &S, ObjCInterfaceDecl *D, const api_notes::ObjCContextInfo &info, VersionedInfoMetadata metadata) { if (auto asNonGeneric = info.getSwiftImportAsNonGeneric()) { handleAPINotedAttribute(S, D, *asNonGeneric, metadata, [&] { return SwiftImportAsNonGenericAttr::CreateImplicit(S.Context); }); } + if (auto objcMembers = info.getSwiftObjCMembers()) { + handleAPINotedAttribute(S, D, *objcMembers, + metadata, [&] { + return SwiftObjCMembersAttr::CreateImplicit(S.Context); + }); + } + // Handle information common to Objective-C classes and protocols. ProcessAPINotes(S, static_cast(D), info, metadata); } /// Processes all versions of versioned API notes. /// /// Just dispatches to the various ProcessAPINotes functions in this file. template static void ProcessVersionedAPINotes( Sema &S, SpecificDecl *D, const api_notes::APINotesReader::VersionedInfo Info) { unsigned Selected = Info.getSelected().getValueOr(Info.size()); VersionTuple Version; SpecificInfo InfoSlice; for (unsigned i = 0, e = Info.size(); i != e; ++i) { std::tie(Version, InfoSlice) = Info[i]; auto Active = (i == Selected) ? IsActive : IsNotActive; ProcessAPINotes(S, D, InfoSlice, VersionedInfoMetadata(Version, Active)); } } /// Process API notes that are associated with this declaration, mapping them /// to attributes as appropriate. void Sema::ProcessAPINotes(Decl *D) { if (!D) return; // Globals. if (D->getDeclContext()->isFileContext()) { // Global variables. if (auto VD = dyn_cast(D)) { for (auto Reader : APINotes.findAPINotes(D->getLocation())) { auto Info = Reader->lookupGlobalVariable(VD->getName()); ProcessVersionedAPINotes(*this, VD, Info); } return; } // Global functions. if (auto FD = dyn_cast(D)) { if (FD->getDeclName().isIdentifier()) { for (auto Reader : APINotes.findAPINotes(D->getLocation())) { auto Info = Reader->lookupGlobalFunction(FD->getName()); ProcessVersionedAPINotes(*this, FD, Info); } } return; } // Objective-C classes. if (auto Class = dyn_cast(D)) { for (auto Reader : APINotes.findAPINotes(D->getLocation())) { auto Info = Reader->lookupObjCClassInfo(Class->getName()); ProcessVersionedAPINotes(*this, Class, Info); } return; } // Objective-C protocols. if (auto Protocol = dyn_cast(D)) { for (auto Reader : APINotes.findAPINotes(D->getLocation())) { auto Info = Reader->lookupObjCProtocolInfo(Protocol->getName()); ProcessVersionedAPINotes(*this, Protocol, Info); } return; } // Tags if (auto Tag = dyn_cast(D)) { for (auto Reader : APINotes.findAPINotes(D->getLocation())) { auto Info = Reader->lookupTag(Tag->getName()); ProcessVersionedAPINotes(*this, Tag, Info); } return; } // Typedefs if (auto Typedef = dyn_cast(D)) { for (auto Reader : APINotes.findAPINotes(D->getLocation())) { auto Info = Reader->lookupTypedef(Typedef->getName()); ProcessVersionedAPINotes(*this, Typedef, Info); } return; } return; } // Enumerators. if (D->getDeclContext()->getRedeclContext()->isFileContext()) { if (auto EnumConstant = dyn_cast(D)) { for (auto Reader : APINotes.findAPINotes(D->getLocation())) { auto Info = Reader->lookupEnumConstant(EnumConstant->getName()); ProcessVersionedAPINotes(*this, EnumConstant, Info); } return; } } if (auto ObjCContainer = dyn_cast(D->getDeclContext())) { // Location function that looks up an Objective-C context. auto GetContext = [&](api_notes::APINotesReader *Reader) -> Optional { if (auto Protocol = dyn_cast(ObjCContainer)) { if (auto Found = Reader->lookupObjCProtocolID(Protocol->getName())) return *Found; return None; } if (auto Impl = dyn_cast(ObjCContainer)) { if (auto Cat = Impl->getCategoryDecl()) ObjCContainer = Cat; else return None; } if (auto Category = dyn_cast(ObjCContainer)) { if (Category->getClassInterface()) ObjCContainer = Category->getClassInterface(); else return None; } if (auto Impl = dyn_cast(ObjCContainer)) { if (Impl->getClassInterface()) ObjCContainer = Impl->getClassInterface(); else return None; } if (auto Class = dyn_cast(ObjCContainer)) { if (auto Found = Reader->lookupObjCClassID(Class->getName())) return *Found; return None; } return None; }; // Objective-C methods. if (auto Method = dyn_cast(D)) { for (auto Reader : APINotes.findAPINotes(D->getLocation())) { if (auto Context = GetContext(Reader)) { // Map the selector. Selector Sel = Method->getSelector(); SmallVector SelPieces; if (Sel.isUnarySelector()) SelPieces.push_back(Sel.getNameForSlot(0)); else { for (unsigned i = 0, n = Sel.getNumArgs(); i != n; ++i) SelPieces.push_back(Sel.getNameForSlot(i)); } api_notes::ObjCSelectorRef SelectorRef; SelectorRef.NumPieces = Sel.getNumArgs(); SelectorRef.Identifiers = SelPieces; auto Info = Reader->lookupObjCMethod(*Context, SelectorRef, Method->isInstanceMethod()); ProcessVersionedAPINotes(*this, Method, Info); } } } // Objective-C properties. if (auto Property = dyn_cast(D)) { for (auto Reader : APINotes.findAPINotes(D->getLocation())) { if (auto Context = GetContext(Reader)) { bool isInstanceProperty = (Property->getPropertyAttributesAsWritten() & ObjCPropertyDecl::OBJC_PR_class) == 0; auto Info = Reader->lookupObjCProperty(*Context, Property->getName(), isInstanceProperty); ProcessVersionedAPINotes(*this, Property, Info); } } return; } return; } } diff --git a/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes b/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes index bd09953dcda9..394b15689fe4 100644 --- a/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes +++ b/clang/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes @@ -1,55 +1,57 @@ Name: VersionedKit Classes: - Name: TestProperties + SwiftObjCMembers: true Properties: - Name: accessorsOnly PropertyKind: Instance SwiftImportAsAccessors: true - Name: accessorsOnlyForClass PropertyKind: Class SwiftImportAsAccessors: true - Name: accessorsOnlyExceptInVersion3 PropertyKind: Instance SwiftImportAsAccessors: true - Name: accessorsOnlyForClassExceptInVersion3 PropertyKind: Class SwiftImportAsAccessors: true SwiftVersions: - Version: 3.0 Classes: - Name: MyReferenceType SwiftBridge: '' - Name: TestGenericDUMP SwiftImportAsNonGeneric: true - Name: TestProperties + SwiftObjCMembers: false Properties: - Name: accessorsOnlyInVersion3 PropertyKind: Instance SwiftImportAsAccessors: true - Name: accessorsOnlyForClassInVersion3 PropertyKind: Class SwiftImportAsAccessors: true - Name: accessorsOnlyExceptInVersion3 PropertyKind: Instance SwiftImportAsAccessors: false - Name: accessorsOnlyForClassExceptInVersion3 PropertyKind: Class SwiftImportAsAccessors: false Functions: - Name: moveToPointDUMP SwiftName: 'moveTo(a:b:)' - Name: acceptClosure Parameters: - Position: 0 NoEscape: false - Name: privateFunc SwiftPrivate: false Tags: - Name: MyErrorCode NSErrorDomain: '' Typedefs: - Name: MyDoubleWrapper SwiftWrapper: none \ No newline at end of file diff --git a/clang/test/APINotes/Inputs/roundtrip.apinotes b/clang/test/APINotes/Inputs/roundtrip.apinotes index 489506081379..3b493e1ba046 100644 --- a/clang/test/APINotes/Inputs/roundtrip.apinotes +++ b/clang/test/APINotes/Inputs/roundtrip.apinotes @@ -1,181 +1,183 @@ --- Name: AppKit Availability: available AvailabilityMsg: '' SwiftInferImportAsMember: true Classes: - Name: NSCell Availability: available AvailabilityMsg: '' SwiftPrivate: false SwiftName: '' SwiftImportAsNonGeneric: true + SwiftObjCMembers: false Methods: - Selector: 'cellWithImage:' MethodKind: Class Availability: available AvailabilityMsg: '' SwiftPrivate: false SwiftName: '' ResultType: id - Selector: init MethodKind: Instance NullabilityOfRet: U Availability: available AvailabilityMsg: '' SwiftPrivate: true SwiftName: '' DesignatedInit: true - Selector: 'initImageCell:' MethodKind: Instance Nullability: [ N ] NullabilityOfRet: U Availability: available AvailabilityMsg: '' SwiftPrivate: false SwiftName: '' DesignatedInit: true - Selector: 'initTextCell:' MethodKind: Instance Nullability: [ N ] NullabilityOfRet: U Availability: available AvailabilityMsg: '' SwiftPrivate: false SwiftName: '' DesignatedInit: true - Selector: 'initWithCoder:' MethodKind: Instance Nullability: [ N ] NullabilityOfRet: U Availability: available AvailabilityMsg: '' SwiftPrivate: false SwiftName: '' DesignatedInit: true Required: true - Name: NSView AuditedForNullability: true Availability: available AvailabilityMsg: '' SwiftPrivate: false SwiftName: '' SwiftBridge: View + SwiftObjCMembers: true Methods: - Selector: 'addSubview:' MethodKind: Instance Nullability: [ N ] NullabilityOfRet: N Availability: available AvailabilityMsg: '' SwiftPrivate: false SwiftName: '' - Selector: 'addSubview:positioned:relativeTo:' MethodKind: Instance Parameters: - Position: 0 NoEscape: false - Position: 1 - Position: 2 NoEscape: true Type: id Nullability: [ N, N, O ] NullabilityOfRet: N Availability: available AvailabilityMsg: '' SwiftName: '' - Selector: 'beginDraggingSessionWithItems:event:source:' MethodKind: Instance Nullability: [ U, U, N ] NullabilityOfRet: N Availability: available AvailabilityMsg: '' SwiftPrivate: false SwiftName: 'beginDragginSession(_:event:source:)' Properties: - Name: enclosingScrollView PropertyKind: Instance Nullability: O Availability: available AvailabilityMsg: '' SwiftName: enclosing Type: id - Name: makeBackingLayer PropertyKind: Class Nullability: N Availability: available AvailabilityMsg: '' SwiftPrivate: false SwiftName: '' SwiftImportAsAccessors: false Functions: - Name: NSAvailableWindowDepths NullabilityOfRet: N Availability: available AvailabilityMsg: '' SwiftName: 'availableWindowDepths()' ResultType: NSInteger Globals: - Name: NSCalibratedWhiteColorSpace Nullability: N Availability: available AvailabilityMsg: '' SwiftPrivate: false SwiftName: calibratedWhite Type: id Enumerators: - Name: NSColorRed Availability: available AvailabilityMsg: '' SwiftPrivate: false SwiftName: Red Tags: - Name: NSSomeEnum Availability: available AvailabilityMsg: '' SwiftPrivate: false SwiftName: SomeEnum NSErrorDomain: some_error_domain - Name: NSSomeStruct Availability: available AvailabilityMsg: '' SwiftPrivate: false SwiftName: SomeStruct NSErrorDomain: '' Typedefs: - Name: NSTypedef Availability: available AvailabilityMsg: '' SwiftPrivate: false SwiftName: Typedef SwiftBridge: '' SwiftWrapper: struct SwiftVersions: - Version: 3.0 Classes: - Name: NSCell Availability: available AvailabilityMsg: '' SwiftPrivate: false SwiftName: NSBox SwiftBridge: '' Methods: - Selector: init MethodKind: Instance NullabilityOfRet: N Availability: available AvailabilityMsg: '' SwiftPrivate: true SwiftName: '' DesignatedInit: true - Name: NSView Availability: available AvailabilityMsg: '' SwiftName: '' Properties: - Name: makeBackingLayer PropertyKind: Class Availability: available AvailabilityMsg: '' SwiftName: '' SwiftImportAsAccessors: true diff --git a/clang/test/APINotes/versioned.m b/clang/test/APINotes/versioned.m index 29800b623c19..21a783e18023 100644 --- a/clang/test/APINotes/versioned.m +++ b/clang/test/APINotes/versioned.m @@ -1,56 +1,61 @@ // RUN: rm -rf %t && mkdir -p %t // Build and check the unversioned module file. // RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Unversioned -fdisable-module-hash -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s // RUN: %clang_cc1 -ast-print %t/ModulesCache/Unversioned/VersionedKit.pcm | FileCheck -check-prefix=CHECK-UNVERSIONED %s // RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Unversioned -fdisable-module-hash -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter 'DUMP' | FileCheck -check-prefix=CHECK-DUMP -check-prefix=CHECK-UNVERSIONED-DUMP %s // Build and check the versioned module file. // RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Versioned -fdisable-module-hash -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fapinotes-swift-version=3 -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s // RUN: %clang_cc1 -ast-print %t/ModulesCache/Versioned/VersionedKit.pcm | FileCheck -check-prefix=CHECK-VERSIONED %s // RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Versioned -fdisable-module-hash -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fapinotes-swift-version=3 -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter 'DUMP' | FileCheck -check-prefix=CHECK-DUMP -check-prefix=CHECK-VERSIONED-DUMP %s #import // CHECK-UNVERSIONED: void moveToPointDUMP(double x, double y) __attribute__((swift_name("moveTo(x:y:)"))); // CHECK-VERSIONED: void moveToPointDUMP(double x, double y) __attribute__((swift_name("moveTo(a:b:)"))); // CHECK-DUMP-LABEL: Dumping moveToPointDUMP // CHECK-VERSIONED-DUMP: SwiftVersionedAttr {{.+}} Implicit 0 // CHECK-VERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} "moveTo(x:y:)" // CHECK-VERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} Implicit "moveTo(a:b:)" // CHECK-UNVERSIONED-DUMP: SwiftNameAttr {{.+}} "moveTo(x:y:)" // CHECK-UNVERSIONED-DUMP-NEXT: SwiftVersionedAttr {{.+}} Implicit 3.0 // CHECK-UNVERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} Implicit "moveTo(a:b:)" // CHECK-DUMP-LABEL: Dumping TestGenericDUMP // CHECK-VERSIONED-DUMP: SwiftImportAsNonGenericAttr {{.+}} Implicit // CHECK-UNVERSIONED-DUMP: SwiftVersionedAttr {{.+}} Implicit 3.0 // CHECK-UNVERSIONED-DUMP-NEXT: SwiftImportAsNonGenericAttr {{.+}} Implicit // CHECK-DUMP-NOT: Dumping // CHECK-UNVERSIONED: void acceptClosure(void (^block)(void) __attribute__((noescape))); // CHECK-VERSIONED: void acceptClosure(void (^block)(void)); // CHECK-UNVERSIONED: enum MyErrorCode { // CHECK-UNVERSIONED-NEXT: MyErrorCodeFailed = 1 // CHECK-UNVERSIONED-NEXT: } __attribute__((ns_error_domain(MyErrorDomain))); // CHECK-UNVERSIONED: __attribute__((swift_bridge("MyValueType"))) // CHECK-UNVERSIONED: @interface MyReferenceType // CHECK-UNVERSIONED: void privateFunc() __attribute__((swift_private)); // CHECK-UNVERSIONED: typedef double MyDoubleWrapper __attribute__((swift_wrapper("struct"))); // CHECK-VERSIONED: enum MyErrorCode { // CHECK-VERSIONED-NEXT: MyErrorCodeFailed = 1 // CHECK-VERSIONED-NEXT: }; // CHECK-VERSIONED-NOT: __attribute__((swift_bridge("MyValueType"))) // CHECK-VERSIONED: @interface MyReferenceType // CHECK-VERSIONED: void privateFunc(); // CHECK-VERSIONED: typedef double MyDoubleWrapper; + +// CHECK-UNVERSIONED: __attribute__((swift_objc_members) +// CHECK-UNVERSIONED-NEXT: @interface TestProperties +// CHECK-VERSIONED-NOT: __attribute__((swift_objc_members) +// CHECK-VERSIONED: @interface TestProperties