Skip to content

[5.9] Add support for an externally defined block list configuration file #66688

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Jun 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions include/swift/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include "swift/Basic/LangOptions.h"
#include "swift/Basic/Located.h"
#include "swift/Basic/Malloc.h"
#include "swift/Basic/BlockList.h"
#include "swift/SymbolGraphGen/SymbolGraphOptions.h"
#include "clang/AST/DeclTemplate.h"
#include "llvm/ADT/ArrayRef.h"
Expand Down Expand Up @@ -360,6 +361,8 @@ class ASTContext final {
/// The Swift module currently being compiled.
ModuleDecl *MainModule = nullptr;

/// The block list where we can find special actions based on module name;
BlockListStore blockListConfig;
private:
/// The current generation number, which reflects the number of
/// times that external modules have been loaded.
Expand Down
52 changes: 52 additions & 0 deletions include/swift/Basic/BlockList.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
//===--- BlockList.h ---------------------------------------------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2023 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
//
//===----------------------------------------------------------------------===//
//
// This file defines some miscellaneous overloads of hash_value() and
// simple_display().
//
//===----------------------------------------------------------------------===//

#ifndef SWIFT_BASIC_BLOCKLIST_H
#define SWIFT_BASIC_BLOCKLIST_H

#include "swift/Basic/LLVM.h"
#include "llvm/ADT/StringRef.h"

namespace swift {

enum class BlockListAction: uint8_t {
Undefined = 0,
#define BLOCKLIST_ACTION(NAME) NAME,
#include "BlockListAction.def"
};

enum class BlockListKeyKind: uint8_t {
Undefined = 0,
ModuleName,
ProjectName
};

class BlockListStore {
public:
struct Implementation;
void addConfigureFilePath(StringRef path);
bool hasBlockListAction(StringRef key, BlockListKeyKind keyKind,
BlockListAction action);
BlockListStore();
~BlockListStore();
private:
Implementation &Impl;
};

} // namespace swift

#endif // SWIFT_BASIC_BLOCKLIST_H
26 changes: 26 additions & 0 deletions include/swift/Basic/BlockListAction.def
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//===--- BlockListAction.def - Define all blocklist actions -----*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2023 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
//
//===----------------------------------------------------------------------===//
//
// This definition file describes all block list actions for meta-programming
// purposes.
//
//===----------------------------------------------------------------------===//

#ifndef BLOCKLIST_ACTION
#define BLOCKLIST_ACTION(NAME)
#endif

BLOCKLIST_ACTION(ShouldUseBinaryModule)
BLOCKLIST_ACTION(ShouldUseTextualModule)
BLOCKLIST_ACTION(DowngradeInterfaceVerificationFailure)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do I understand correctly that only DowngradeInterfaceVerificationFailure is enforced by this PR, and not ShouldUseBinaryModule nor ShouldUseTextualModule?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right. I suspect honoring ShouldUseBinaryModule and ShouldUseTextualModule here may cause a later merge conflict.


#undef BLOCKLIST_ACTION
3 changes: 3 additions & 0 deletions include/swift/Basic/LangOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,9 @@ namespace swift {
/// The model of concurrency to be used.
ConcurrencyModel ActiveConcurrencyModel = ConcurrencyModel::Standard;

/// All block list configuration files to be honored in this compilation.
std::vector<std::string> BlocklistConfigFilePaths;

bool isConcurrencyModelTaskToThread() const {
return ActiveConcurrencyModel == ConcurrencyModel::TaskToThread;
}
Expand Down
3 changes: 3 additions & 0 deletions include/swift/Frontend/Frontend.h
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,9 @@ class CompilerInvocation {
/// options have been parsed.
void setDefaultPrebuiltCacheIfNecessary();

/// If we haven't explicitly passed -blocklist-paths, set it to the default value.
void setDefaultBlocklistsIfNecessary();

/// Computes the runtime resource path relative to the given Swift
/// executable.
static void computeRuntimeResourcePathFromExecutablePath(
Expand Down
2 changes: 2 additions & 0 deletions include/swift/Frontend/FrontendOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,8 @@ class FrontendOptions {
/// textual imports
bool EmitClangHeaderWithNonModularIncludes = false;

/// All block list configuration files to be honored in this compilation.
std::vector<std::string> BlocklistConfigFilePaths;
private:
static bool canActionEmitDependencies(ActionType);
static bool canActionEmitReferenceDependencies(ActionType);
Expand Down
3 changes: 3 additions & 0 deletions include/swift/Option/FrontendOptions.td
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,9 @@ def Raccess_note : Separate<["-"], "Raccess-note">,
def Raccess_note_EQ : Joined<["-"], "Raccess-note=">,
Alias<Raccess_note>;

def block_list_file
: Separate<["-"], "blocklist-file">, MetaVarName<"<path>">,
HelpText<"The path to a blocklist configuration file">;
} // end let Flags = [FrontendOption, NoDriverOption]

def debug_crash_Group : OptionGroup<"<automatic crashing options>">;
Expand Down
4 changes: 4 additions & 0 deletions lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -694,6 +694,10 @@ ASTContext::ASTContext(
// Register any request-evaluator functions available at the AST layer.
registerAccessRequestFunctions(evaluator);
registerNameLookupRequestFunctions(evaluator);
// Insert all block list config paths.
for (auto path: langOpts.BlocklistConfigFilePaths) {
blockListConfig.addConfigureFilePath(path);
}
}

ASTContext::~ASTContext() {
Expand Down
131 changes: 131 additions & 0 deletions lib/Basic/BlockList.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
//===--- BlockList.cpp - BlockList utilities ------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2023 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 "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/YAMLParser.h"
#include "llvm/Support/YAMLTraits.h"
#include "swift/Basic/BlockList.h"
#include "swift/Basic/SourceManager.h"

struct swift::BlockListStore::Implementation {
SourceManager SM;
llvm::StringMap<std::vector<BlockListAction>> ModuleActionDict;
llvm::StringMap<std::vector<BlockListAction>> ProjectActionDict;
void addConfigureFilePath(StringRef path);
bool hasBlockListAction(StringRef key, BlockListKeyKind keyKind,
BlockListAction action);
void collectBlockList(llvm::yaml::Node *N, BlockListAction action);

llvm::StringMap<std::vector<BlockListAction>> *getDictToUse(BlockListKeyKind kind) {
switch (kind) {
case BlockListKeyKind::ModuleName:
return &ModuleActionDict;
case BlockListKeyKind::ProjectName:
return &ProjectActionDict;
case BlockListKeyKind::Undefined:
return nullptr;
}
}
static std::string getScalaString(llvm::yaml::Node *N) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo on scalar?

llvm::SmallString<64> Buffer;
if (auto *scala = dyn_cast<llvm::yaml::ScalarNode>(N)) {
return scala->getValue(Buffer).str();
}
return std::string();
}
};

swift::BlockListStore::BlockListStore(): Impl(*new Implementation()) {}

swift::BlockListStore::~BlockListStore() { delete &Impl; }

bool swift::BlockListStore::hasBlockListAction(StringRef key,
BlockListKeyKind keyKind, BlockListAction action) {
return Impl.hasBlockListAction(key, keyKind, action);
}

void swift::BlockListStore::addConfigureFilePath(StringRef path) {
Impl.addConfigureFilePath(path);
}

bool swift::BlockListStore::Implementation::hasBlockListAction(StringRef key,
BlockListKeyKind keyKind, BlockListAction action) {
auto *dict = getDictToUse(keyKind);
assert(dict);
auto it = dict->find(key);
if (it == dict->end())
return false;
return llvm::is_contained(it->second, action);
}

void swift::BlockListStore::Implementation::collectBlockList(llvm::yaml::Node *N,
BlockListAction action) {
namespace yaml = llvm::yaml;
auto *pair = dyn_cast<yaml::KeyValueNode>(N);
if (!pair)
return;
std::string rawKey = getScalaString(pair->getKey());
auto keyKind = llvm::StringSwitch<BlockListKeyKind>(rawKey)
#define CASE(X) .Case(#X, BlockListKeyKind::X)
CASE(ModuleName)
CASE(ProjectName)
#undef CASE
.Default(BlockListKeyKind::Undefined);
if (keyKind == BlockListKeyKind::Undefined)
return;
auto *dictToUse = getDictToUse(keyKind);
assert(dictToUse);
auto *seq = dyn_cast<yaml::SequenceNode>(pair->getValue());
if (!seq)
return;
for (auto &node: *seq) {
std::string name = getScalaString(&node);
dictToUse->insert({name, std::vector<BlockListAction>()})
.first->second.push_back(action);
}
}

void swift::BlockListStore::Implementation::addConfigureFilePath(StringRef path) {
namespace yaml = llvm::yaml;

// Load the input file.
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> FileBufOrErr =
vfs::getFileOrSTDIN(*SM.getFileSystem(), path,
/*FileSize*/-1, /*RequiresNullTerminator*/true,
/*IsVolatile*/false, /*RetryCount*/30);
if (!FileBufOrErr) {
return;
}
StringRef Buffer = FileBufOrErr->get()->getBuffer();
yaml::Stream Stream(llvm::MemoryBufferRef(Buffer, path),
SM.getLLVMSourceMgr());
for (auto DI = Stream.begin(); DI != Stream.end(); ++ DI) {
assert(DI != Stream.end() && "Failed to read a document");
yaml::Node *N = DI->getRoot();
for (auto &pair: *dyn_cast<yaml::MappingNode>(N)) {
std::string key = getScalaString(pair.getKey());
auto action = llvm::StringSwitch<BlockListAction>(key)
#define BLOCKLIST_ACTION(X) .Case(#X, BlockListAction::X)
#include "swift/Basic/BlockListAction.def"
.Default(BlockListAction::Undefined);
if (action == BlockListAction::Undefined)
continue;
auto *map = dyn_cast<yaml::MappingNode>(pair.getValue());
if (!map)
continue;
for (auto &innerPair: *map) {
collectBlockList(&innerPair, action);
}
}
}
}
1 change: 1 addition & 0 deletions lib/Basic/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ add_swift_host_library(swiftBasic STATIC
Unicode.cpp
UUID.cpp
Version.cpp
BlockList.cpp

${llvm_revision_inc}
${clang_revision_inc}
Expand Down
3 changes: 3 additions & 0 deletions lib/Frontend/ArgsToFrontendOptionsConverter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,9 @@ bool ArgsToFrontendOptionsConverter::convert(
Opts.serializedPathObfuscator.addMapping(SplitMap.first, SplitMap.second);
}
Opts.emptyABIDescriptor = Args.hasArg(OPT_empty_abi_descriptor);
for (auto A : Args.getAllArgValues(options::OPT_block_list_file)) {
Opts.BlocklistConfigFilePaths.push_back(A);
}
return false;
}

Expand Down
28 changes: 28 additions & 0 deletions lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,31 @@ void CompilerInvocation::setDefaultPrebuiltCacheIfNecessary() {
(llvm::Twine(pair.first) + "preferred-interfaces" + pair.second).str();
}

void CompilerInvocation::setDefaultBlocklistsIfNecessary() {
if (!LangOpts.BlocklistConfigFilePaths.empty())
return;
if (SearchPathOpts.RuntimeResourcePath.empty())
return;
// XcodeDefault.xctoolchain/usr/lib/swift
SmallString<64> blocklistDir{SearchPathOpts.RuntimeResourcePath};
// XcodeDefault.xctoolchain/usr/lib
llvm::sys::path::remove_filename(blocklistDir);
// XcodeDefault.xctoolchain/usr
llvm::sys::path::remove_filename(blocklistDir);
// XcodeDefault.xctoolchain/usr/local/lib/swift/blocklists
llvm::sys::path::append(blocklistDir, "local", "lib", "swift", "blocklists");
std::error_code EC;
if (llvm::sys::fs::is_directory(blocklistDir)) {
for (llvm::sys::fs::directory_iterator F(blocklistDir, EC), FE;
F != FE; F.increment(EC)) {
StringRef ext = llvm::sys::path::extension(F->path());
if (ext == "yml" || ext == "yaml") {
LangOpts.BlocklistConfigFilePaths.push_back(F->path());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the main advantage of supporting many blocklists like this? My first though would be that one blocklist would be enough, unless we want different owners of each blocklist?

Copy link
Contributor Author

@nkcsgexi nkcsgexi Jun 16, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for taking a look! The reasons for supporting multiple block lists are that (1) one single file may get too long over time, and (2) we could group similar actions into a single block list file, such as module loading related actions.

}
}
}
}

static void updateRuntimeLibraryPaths(SearchPathOptions &SearchPathOpts,
llvm::Triple &Triple) {
llvm::SmallString<128> LibPath(SearchPathOpts.RuntimeResourcePath);
Expand Down Expand Up @@ -1211,6 +1236,8 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,

Opts.DumpTypeWitnessSystems = Args.hasArg(OPT_dump_type_witness_systems);

for (auto &block: FrontendOpts.BlocklistConfigFilePaths)
Opts.BlocklistConfigFilePaths.push_back(block);
if (const Arg *A = Args.getLastArg(options::OPT_concurrency_model)) {
Opts.ActiveConcurrencyModel =
llvm::StringSwitch<ConcurrencyModel>(A->getValue())
Expand Down Expand Up @@ -2933,6 +2960,7 @@ bool CompilerInvocation::parseArgs(

updateRuntimeLibraryPaths(SearchPathOpts, LangOpts.Target);
setDefaultPrebuiltCacheIfNecessary();
setDefaultBlocklistsIfNecessary();

// Now that we've parsed everything, setup some inter-option-dependent state.
setIRGenOutputOptsFromFrontendOptions(IRGenOpts, FrontendOpts);
Expand Down
11 changes: 10 additions & 1 deletion lib/Frontend/ModuleInterfaceBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,14 @@ bool ExplicitModuleInterfaceBuilder::collectDepsForSerialization(
return false;
}

static bool shouldDowngradeInterfaceVerificationError(const FrontendOptions &opts,
ASTContext &ctx) {
return opts.DowngradeInterfaceVerificationError ||
ctx.blockListConfig.hasBlockListAction(opts.ModuleName,
BlockListKeyKind::ModuleName,
BlockListAction::DowngradeInterfaceVerificationFailure);
}

std::error_code ExplicitModuleInterfaceBuilder::buildSwiftModuleFromInterface(
StringRef InterfacePath, StringRef OutputPath, bool ShouldSerializeDeps,
std::unique_ptr<llvm::MemoryBuffer> *ModuleBuffer,
Expand All @@ -206,7 +214,8 @@ std::error_code ExplicitModuleInterfaceBuilder::buildSwiftModuleFromInterface(
<< " to " << OutputPath << "\n");

LLVM_DEBUG(llvm::dbgs() << "Performing sema\n");
if (isTypeChecking && FEOpts.DowngradeInterfaceVerificationError) {
if (isTypeChecking &&
shouldDowngradeInterfaceVerificationError(FEOpts, Instance.getASTContext())) {
ErrorDowngradeConsumerRAII R(Instance.getDiags());
Instance.performSema();
return std::error_code();
Expand Down
7 changes: 7 additions & 0 deletions lib/Frontend/ModuleInterfaceLoader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1723,6 +1723,13 @@ InterfaceSubContextDelegateImpl::InterfaceSubContextDelegateImpl(
// module cache path at this point.
if (buildModuleCacheDirIfAbsent && !moduleCachePath.empty())
(void)llvm::sys::fs::create_directories(moduleCachePath);

// Inherit all block list configuration files
frontendOpts.BlocklistConfigFilePaths = langOpts.BlocklistConfigFilePaths;
for (auto &blocklist: langOpts.BlocklistConfigFilePaths) {
GenericArgs.push_back("-blocklist-file");
GenericArgs.push_back(blocklist);
}
}

/// Calculate an output filename in \p genericSubInvocation's cache path that
Expand Down
Loading