blob: ce37003e9b582e68396e27ebee2e384782c68741 [file] [log] [blame]
//===--- ExperimentalDependencies.cpp - Generates swiftdeps files ---------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#include <stdio.h>
#include "swift/AST/ExperimentalDependencies.h"
// may not all be needed
#include "swift/Basic/FileSystem.h"
#include "swift/Basic/LLVM.h"
#include "swift/Demangling/Demangle.h"
#include "swift/Frontend/FrontendOptions.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/YAMLParser.h"
// This file holds the definitions for the experimental dependency system
// that are likely to be stable as it moves away from the status quo.
// These include the graph structures common to both programs and also
// the frontend graph, which must be read by the driver.
using namespace swift;
using namespace experimental_dependencies;
//==============================================================================
// MARK: SourceFileDepGraph access
//==============================================================================
SourceFileDepGraphNode *
SourceFileDepGraph::getNode(size_t sequenceNumber) const {
assert(sequenceNumber < allNodes.size() && "Bad node index");
SourceFileDepGraphNode *n = allNodes[sequenceNumber];
assert(n->getSequenceNumber() == sequenceNumber &&
"Bad sequence number in node or bad entry in allNodes.");
return n;
}
InterfaceAndImplementationPair<SourceFileDepGraphNode>
SourceFileDepGraph::getSourceFileNodePair() const {
assert(getNode(0)->getKey().getKind() == NodeKind::sourceFileProvide &&
"First node must be sourceFileProvide.");
assert(getNode(1)->getKey().getKind() == NodeKind::sourceFileProvide &&
"Second node must be sourceFileProvide.");
return InterfaceAndImplementationPair<SourceFileDepGraphNode>(getNode(0),
getNode(1));
}
StringRef SourceFileDepGraph::getSwiftDepsFromSourceFileProvide() const {
return getSourceFileNodePair()
.getInterface()
->getKey()
.getSwiftDepsFromSourceFileProvide();
}
void SourceFileDepGraph::forEachArc(
function_ref<void(const SourceFileDepGraphNode *def,
const SourceFileDepGraphNode *use)>
fn) const {
forEachNode([&](const SourceFileDepGraphNode *useNode) {
forEachDefDependedUponBy(useNode, [&](SourceFileDepGraphNode *defNode) {
fn(defNode, useNode);
});
});
}
InterfaceAndImplementationPair<SourceFileDepGraphNode>
SourceFileDepGraph::findExistingNodePairOrCreateAndAddIfNew(
NodeKind k, StringRef context, StringRef name,
Optional<std::string> fingerprint) {
InterfaceAndImplementationPair<SourceFileDepGraphNode> nodePair{
findExistingNodeOrCreateIfNew(
DependencyKey(k, DeclAspect::interface, context, name), fingerprint,
true /* = isProvides */),
findExistingNodeOrCreateIfNew(
DependencyKey(k, DeclAspect::implementation, context, name),
fingerprint, true /* = isProvides */)};
// if interface changes, have to rebuild implementation
addArc(nodePair.getInterface(), nodePair.getImplementation());
return nodePair;
}
SourceFileDepGraphNode *SourceFileDepGraph::findExistingNodeOrCreateIfNew(
DependencyKey key, Optional<std::string> fingerprint,
const bool isProvides) {
SourceFileDepGraphNode *result = memoizedNodes.findExistingOrCreateIfNew(
key, [&](DependencyKey key) -> SourceFileDepGraphNode * {
SourceFileDepGraphNode *n =
new SourceFileDepGraphNode(key, fingerprint, isProvides);
addNode(n);
return n;
});
// If have provides and depends with same key, result is one node that
// isProvides
if (isProvides)
result->setIsProvides();
assert(result->getKey() == key && "Keys must match.");
return result;
}
std::string DependencyKey::demangleTypeAsContext(StringRef s) {
return swift::Demangle::demangleTypeAsString(s.str());
}
//==============================================================================
// MARK: Debugging
//==============================================================================
bool SourceFileDepGraph::verify() const {
DependencyKey::verifyNodeKindNames();
DependencyKey::verifyDeclAspectNames();
// Ensure Keys are unique
std::unordered_map<DependencyKey, SourceFileDepGraphNode *> nodesSeen;
// Ensure each node only appears once.
std::unordered_set<void *> nodes;
forEachNode([&](SourceFileDepGraphNode *n) {
n->verify();
assert(nodes.insert(n).second && "Frontend nodes are identified by "
"sequence number, therefore must be "
"unique.");
auto iterInserted = nodesSeen.insert(std::make_pair(n->getKey(), n));
if (!iterInserted.second) {
llvm::errs() << "Duplicate frontend keys: ";
iterInserted.first->second->dump();
llvm::errs() << " and ";
n->dump();
llvm::errs() << "\n";
llvm_unreachable("duplicate frontend keys");
}
forEachDefDependedUponBy(n, [&](SourceFileDepGraphNode *def) {
assert(def != n && "Uses should be irreflexive.");
});
});
return true;
}
bool SourceFileDepGraph::verifyReadsWhatIsWritten(StringRef path) const {
auto loadedGraph = SourceFileDepGraph::loadFromPath(path);
assert(loadedGraph.hasValue() &&
"Should be able to read the exported graph.");
verifySame(loadedGraph.getValue());
return true;
}
std::string DependencyKey::humanReadableName() const {
switch (kind) {
case NodeKind::member:
return demangleTypeAsContext(context) + "." + name;
case NodeKind::externalDepend:
case NodeKind::sourceFileProvide:
return llvm::sys::path::filename(name);
case NodeKind::potentialMember:
return demangleTypeAsContext(context) + ".*";
case NodeKind::nominal:
return demangleTypeAsContext(context);
case NodeKind::topLevel:
case NodeKind::dynamicLookup:
return name;
default:
llvm_unreachable("bad kind");
}
}
std::string DependencyKey::asString() const {
return NodeKindNames[size_t(kind)] + " " +
"aspect: " + DeclAspectNames[size_t(aspect)] + ", " +
humanReadableName();
}
/// Needed for TwoStageMap::verify:
raw_ostream &experimental_dependencies::operator<<(raw_ostream &out,
const DependencyKey &key) {
out << key.asString();
return out;
}
bool DependencyKey::verify() const {
assert((getKind() != NodeKind::externalDepend || isInterface()) &&
"All external dependencies must be interfaces.");
return true;
}
/// Since I don't have Swift enums, ensure name corresspondence here.
void DependencyKey::verifyNodeKindNames() {
for (size_t i = 0; i < size_t(NodeKind::kindCount); ++i)
switch (NodeKind(i)) {
#define CHECK_NAME(n) \
case NodeKind::n: \
assert(#n == NodeKindNames[i]); \
break;
CHECK_NAME(topLevel)
CHECK_NAME(nominal)
CHECK_NAME(potentialMember)
CHECK_NAME(member)
CHECK_NAME(dynamicLookup)
CHECK_NAME(externalDepend)
CHECK_NAME(sourceFileProvide)
case NodeKind::kindCount:
llvm_unreachable("impossible");
}
#undef CHECK_NAME
}
/// Since I don't have Swift enums, ensure name corresspondence here.
void DependencyKey::verifyDeclAspectNames() {
for (size_t i = 0; i < size_t(DeclAspect::aspectCount); ++i)
switch (DeclAspect(i)) {
#define CHECK_NAME(n) \
case DeclAspect::n: \
assert(#n == DeclAspectNames[i]); \
break;
CHECK_NAME(interface)
CHECK_NAME(implementation)
case DeclAspect::aspectCount:
llvm_unreachable("impossible");
}
#undef CHECK_NAME
}
void DepGraphNode::dump() const {
key.dump();
if (fingerprint.hasValue())
llvm::errs() << "fingerprint: " << fingerprint.getValue() << "";
else
llvm::errs() << "no fingerprint";
}
std::string DepGraphNode::humanReadableName(StringRef where) const {
return getKey().humanReadableName() +
(getKey().getKind() == NodeKind::sourceFileProvide || where.empty()
? std::string()
: std::string(" in ") + where.str());
}
void SourceFileDepGraph::verifySame(const SourceFileDepGraph &other) const {
assert(allNodes.size() == other.allNodes.size() &&
"Both graphs must have same number of nodes.");
for (size_t i : indices(allNodes)) {
assert(*allNodes[i] == *other.allNodes[i] &&
"Both graphs must have corresponding nodes");
}
}
//==============================================================================
// MARK: SourceFileDepGraph YAML reading & writing
//==============================================================================
namespace llvm {
namespace yaml {
// This introduces a redefinition for Linux.
#if !(defined(__linux__) || defined(_WIN64))
void ScalarTraits<size_t>::output(const size_t &Val, void *, raw_ostream &out) {
out << Val;
}
StringRef ScalarTraits<size_t>::input(StringRef scalar, void *ctxt,
size_t &value) {
return scalar.getAsInteger(10, value) ? "could not parse size_t" : "";
}
#endif
void ScalarEnumerationTraits<swift::experimental_dependencies::NodeKind>::
enumeration(IO &io, swift::experimental_dependencies::NodeKind &value) {
using NodeKind = swift::experimental_dependencies::NodeKind;
io.enumCase(value, "topLevel", NodeKind::topLevel);
io.enumCase(value, "nominal", NodeKind::nominal);
io.enumCase(value, "potentialMember", NodeKind::potentialMember);
io.enumCase(value, "member", NodeKind::member);
io.enumCase(value, "dynamicLookup", NodeKind::dynamicLookup);
io.enumCase(value, "externalDepend", NodeKind::externalDepend);
io.enumCase(value, "sourceFileProvide", NodeKind::sourceFileProvide);
}
void ScalarEnumerationTraits<DeclAspect>::enumeration(
IO &io, swift::experimental_dependencies::DeclAspect &value) {
using DeclAspect = swift::experimental_dependencies::DeclAspect;
io.enumCase(value, "interface", DeclAspect::interface);
io.enumCase(value, "implementation", DeclAspect::implementation);
}
void MappingTraits<DependencyKey>::mapping(
IO &io, swift::experimental_dependencies::DependencyKey &key) {
io.mapRequired("kind", key.kind);
io.mapRequired("aspect", key.aspect);
io.mapRequired("context", key.context);
io.mapRequired("name", key.name);
}
void MappingTraits<DepGraphNode>::mapping(
IO &io, swift::experimental_dependencies::DepGraphNode &node) {
io.mapRequired("key", node.key);
io.mapOptional("fingerprint", node.fingerprint);
}
void MappingContextTraits<SourceFileDepGraphNode, SourceFileDepGraph>::mapping(
IO &io, SourceFileDepGraphNode &node, SourceFileDepGraph &g) {
MappingTraits<DepGraphNode>::mapping(io, node);
io.mapRequired("sequenceNumber", node.sequenceNumber);
std::vector<size_t> defsIDependUponVec(node.defsIDependUpon.begin(),
node.defsIDependUpon.end());
io.mapRequired("defsIDependUpon", defsIDependUponVec);
io.mapRequired("isProvides", node.isProvides);
if (!io.outputting()) {
for (size_t u : defsIDependUponVec)
node.defsIDependUpon.insert(u);
}
assert(g.getNode(node.sequenceNumber) && "Bad sequence number");
}
size_t SequenceTraits<std::vector<SourceFileDepGraphNode *>>::size(
IO &, std::vector<SourceFileDepGraphNode *> &vec) {
return vec.size();
}
SourceFileDepGraphNode &
SequenceTraits<std::vector<SourceFileDepGraphNode *>>::element(
IO &, std::vector<SourceFileDepGraphNode *> &vec, size_t index) {
while (vec.size() <= index)
vec.push_back(new SourceFileDepGraphNode());
return *vec[index];
}
void MappingTraits<SourceFileDepGraph>::mapping(IO &io, SourceFileDepGraph &g) {
io.mapRequired("allNodes", g.allNodes, g);
}
} // namespace yaml
} // namespace llvm