blob: f0f6cb8f958986cdf5ab7ceca6e61a9a7767b7f2 [file] [log] [blame]
//===--- 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 <algorithm>
/*
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
};
/// Syntactic sugar for EnumExtensibility and FlagEnum
enum class EnumConvenienceAliasKind {
/// EnumExtensibility: none, FlagEnum: false
None,
/// EnumExtensibility: open, FlagEnum: false
CFEnum,
/// EnumExtensibility: open, FlagEnum: true
CFOptions,
/// EnumExtensibility: closed, FlagEnum: false
CFClosedEnum
};
struct AvailabilityItem {
APIAvailability Mode = APIAvailability::Available;
StringRef Msg;
AvailabilityItem() : Mode(APIAvailability::Available), Msg("") {}
};
static llvm::Optional<NullabilityKind> AbsentNullability = llvm::None;
static llvm::Optional<NullabilityKind> DefaultNullability =
NullabilityKind::NonNull;
typedef std::vector<clang::NullabilityKind> NullabilitySeq;
struct Param {
unsigned Position;
Optional<bool> NoEscape = false;
llvm::Optional<NullabilityKind> Nullability;
StringRef Type;
};
typedef std::vector<Param> ParamsSeq;
struct Method {
StringRef Selector;
MethodKind Kind;
ParamsSeq Params;
NullabilitySeq Nullability;
llvm::Optional<NullabilityKind> NullabilityOfRet;
AvailabilityItem Availability;
Optional<bool> SwiftPrivate;
StringRef SwiftName;
FactoryAsInitKind FactoryAsInit = FactoryAsInitKind::Infer;
bool DesignatedInit = false;
bool Required = false;
StringRef ResultType;
};
typedef std::vector<Method> MethodsSeq;
struct Property {
StringRef Name;
llvm::Optional<MethodKind> Kind;
llvm::Optional<NullabilityKind> Nullability;
AvailabilityItem Availability;
Optional<bool> SwiftPrivate;
StringRef SwiftName;
Optional<bool> SwiftImportAsAccessors;
StringRef Type;
};
typedef std::vector<Property> PropertiesSeq;
struct Class {
StringRef Name;
bool AuditedForNullability = false;
AvailabilityItem Availability;
Optional<bool> SwiftPrivate;
StringRef SwiftName;
Optional<StringRef> SwiftBridge;
Optional<StringRef> NSErrorDomain;
Optional<bool> SwiftImportAsNonGeneric;
Optional<bool> SwiftObjCMembers;
MethodsSeq Methods;
PropertiesSeq Properties;
};
typedef std::vector<Class> ClassesSeq;
struct Function {
StringRef Name;
ParamsSeq Params;
NullabilitySeq Nullability;
llvm::Optional<NullabilityKind> NullabilityOfRet;
AvailabilityItem Availability;
Optional<bool> SwiftPrivate;
StringRef SwiftName;
StringRef Type;
StringRef ResultType;
};
typedef std::vector<Function> FunctionsSeq;
struct GlobalVariable {
StringRef Name;
llvm::Optional<NullabilityKind> Nullability;
AvailabilityItem Availability;
Optional<bool> SwiftPrivate;
StringRef SwiftName;
StringRef Type;
};
typedef std::vector<GlobalVariable> GlobalVariablesSeq;
struct EnumConstant {
StringRef Name;
AvailabilityItem Availability;
Optional<bool> SwiftPrivate;
StringRef SwiftName;
};
typedef std::vector<EnumConstant> EnumConstantsSeq;
struct Tag {
StringRef Name;
AvailabilityItem Availability;
StringRef SwiftName;
Optional<bool> SwiftPrivate;
Optional<StringRef> SwiftBridge;
Optional<StringRef> NSErrorDomain;
Optional<api_notes::EnumExtensibilityKind> EnumExtensibility;
Optional<bool> FlagEnum;
Optional<EnumConvenienceAliasKind> EnumConvenienceKind;
};
typedef std::vector<Tag> TagsSeq;
struct Typedef {
StringRef Name;
AvailabilityItem Availability;
StringRef SwiftName;
Optional<bool> SwiftPrivate;
Optional<StringRef> SwiftBridge;
Optional<StringRef> NSErrorDomain;
Optional<api_notes::SwiftWrapperKind> SwiftWrapper;
};
typedef std::vector<Typedef> 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<Versioned> VersionedSeq;
struct Module {
StringRef Name;
AvailabilityItem Availability;
TopLevelItems TopLevel;
VersionedSeq SwiftVersions;
llvm::Optional<bool> 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<NullabilityKind > {
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<FactoryAsInitKind> {
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<MethodKind> {
static void enumeration(IO &io, MethodKind &value) {
io.enumCase(value, "Class", MethodKind::Class);
io.enumCase(value, "Instance", MethodKind::Instance);
}
};
template <>
struct ScalarEnumerationTraits<APIAvailability> {
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<api_notes::SwiftWrapperKind> {
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 ScalarEnumerationTraits<api_notes::EnumExtensibilityKind> {
static void enumeration(IO &io, api_notes::EnumExtensibilityKind &value) {
io.enumCase(value, "none", api_notes::EnumExtensibilityKind::None);
io.enumCase(value, "open", api_notes::EnumExtensibilityKind::Open);
io.enumCase(value, "closed", api_notes::EnumExtensibilityKind::Closed);
}
};
template<>
struct ScalarEnumerationTraits<EnumConvenienceAliasKind> {
static void enumeration(IO &io, EnumConvenienceAliasKind &value) {
io.enumCase(value, "none", EnumConvenienceAliasKind::None);
io.enumCase(value, "CFEnum", EnumConvenienceAliasKind::CFEnum);
io.enumCase(value, "NSEnum", EnumConvenienceAliasKind::CFEnum);
io.enumCase(value, "CFOptions", EnumConvenienceAliasKind::CFOptions);
io.enumCase(value, "NSOptions", EnumConvenienceAliasKind::CFOptions);
io.enumCase(value, "CFClosedEnum",
EnumConvenienceAliasKind::CFClosedEnum);
io.enumCase(value, "NSClosedEnum",
EnumConvenienceAliasKind::CFClosedEnum);
}
};
template <>
struct ScalarTraits<VersionTuple> {
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<Param> {
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<Property> {
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<Method> {
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<Class> {
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<Function> {
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<GlobalVariable> {
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<EnumConstant> {
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<Tag> {
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);
io.mapOptional("EnumExtensibility", t.EnumExtensibility);
io.mapOptional("FlagEnum", t.FlagEnum);
io.mapOptional("EnumKind", t.EnumConvenienceKind);
}
};
template <>
struct MappingTraits<Typedef> {
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<Versioned> {
static void mapping(IO &io, Versioned& v) {
io.mapRequired("Version", v.Version);
mapTopLevelItems(io, v.Items);
}
};
template <>
struct MappingTraits<Module> {
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<bool>(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 &params, 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<NullabilityKind> 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<typename T>
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<typename T>
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<StringRef, 4> 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<std::pair<bool, bool>> 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;
if (t.EnumConvenienceKind) {
if (t.EnumExtensibility) {
emitError(llvm::Twine(
"cannot mix EnumKind and EnumExtensibility (for ") + t.Name +
")");
continue;
}
if (t.FlagEnum) {
emitError(llvm::Twine("cannot mix EnumKind and FlagEnum (for ") +
t.Name + ")");
continue;
}
switch (t.EnumConvenienceKind.getValue()) {
case EnumConvenienceAliasKind::None:
tagInfo.EnumExtensibility = EnumExtensibilityKind::None;
tagInfo.setFlagEnum(false);
break;
case EnumConvenienceAliasKind::CFEnum:
tagInfo.EnumExtensibility = EnumExtensibilityKind::Open;
tagInfo.setFlagEnum(false);
break;
case EnumConvenienceAliasKind::CFOptions:
tagInfo.EnumExtensibility = EnumExtensibilityKind::Open;
tagInfo.setFlagEnum(true);
break;
case EnumConvenienceAliasKind::CFClosedEnum:
tagInfo.EnumExtensibility = EnumExtensibilityKind::Closed;
tagInfo.setFlagEnum(false);
break;
}
} else {
tagInfo.EnumExtensibility = t.EnumExtensibility;
tagInfo.setFlagEnum(t.FlagEnum);
}
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<std::pair<VersionTuple, unsigned>, 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<unsigned, KnownContext> 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<const char *>(ptr), string.size());
}
/// Copy an optional string into allocated memory so it does disappear on us.
Optional<StringRef> maybeCopyString(Optional<StringRef> string) {
if (!string) return None;
return copyString(*string);
}
/// Copy an optional string into allocated memory so it does disappear on us.
Optional<StringRef> maybeCopyString(Optional<std::string> string) {
if (!string) return None;
return copyString(*string);
}
template<typename T>
void handleCommon(T &record, const CommonEntityInfo &info) {
handleAvailability(record.Availability, info);
record.SwiftPrivate = info.isSwiftPrivate();
record.SwiftName = copyString(info.SwiftName);
}
template<typename T>
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 &params,
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<NullabilityKind> &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);
tag.EnumExtensibility = info.EnumExtensibility;
tag.FlagEnum = info.isFlagEnum();
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<MethodKind> 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<unsigned>(lhs.Kind)
< static_cast<unsigned>(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<llvm::MemoryBuffer> 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;
}