Skip to content

Commit 16386aa

Browse files
committed
[Macros] Create plugin search lookup table.
Iterating all options and potential file system access is not great for every plugin lookup request. Instead, lazily create a single lookup table keyed by module name.
1 parent 13a29a0 commit 16386aa

File tree

3 files changed

+101
-52
lines changed

3 files changed

+101
-52
lines changed

include/swift/AST/PluginLoader.h

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ class ASTContext;
2828
/// * Load plugins resolving VFS paths
2929
/// * Track plugin dependencies
3030
class PluginLoader {
31+
public:
32+
using PluginEntry = std::pair<StringRef, StringRef>;
33+
34+
private:
3135
/// Plugin registry. Lazily populated by get/setRegistry().
3236
/// NOTE: Do not reference this directly. Use getRegistry().
3337
PluginRegistry *Registry = nullptr;
@@ -38,16 +42,15 @@ class PluginLoader {
3842
ASTContext &Ctx;
3943
DependencyTracker *DepTracker;
4044

41-
/// Map a module name to an executable plugin path that provides the module.
42-
llvm::DenseMap<swift::Identifier, llvm::StringRef> ExecutablePluginPaths;
45+
/// Map a module name to an plugin entry that provides the module.
46+
llvm::Optional<llvm::DenseMap<swift::Identifier, PluginEntry>> PluginMap;
4347

44-
void createModuleToExecutablePluginMap();
48+
/// Get or lazily create and populate 'PluginMap'.
49+
llvm::DenseMap<swift::Identifier, PluginEntry> &getPluginMap();
4550

4651
public:
4752
PluginLoader(ASTContext &Ctx, DependencyTracker *DepTracker)
48-
: Ctx(Ctx), DepTracker(DepTracker) {
49-
createModuleToExecutablePluginMap();
50-
}
53+
: Ctx(Ctx), DepTracker(DepTracker) {}
5154

5255
void setRegistry(PluginRegistry *newValue);
5356
PluginRegistry *getRegistry();
@@ -63,8 +66,7 @@ class PluginLoader {
6366
/// 'loadExecutablePlugin()'.
6467
/// * (libPath: some, execPath: some) - load the executable path by
6568
/// 'loadExecutablePlugin()' and let the plugin load the libPath via IPC.
66-
std::pair<std::string, std::string>
67-
lookupPluginByModuleName(Identifier moduleName);
69+
const PluginEntry &lookupPluginByModuleName(Identifier moduleName);
6870

6971
/// Load the specified dylib plugin path resolving the path with the
7072
/// current VFS. If it fails to load the plugin, a diagnostic is emitted, and

lib/AST/PluginLoader.cpp

Lines changed: 89 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -15,23 +15,11 @@
1515
#include "swift/AST/DiagnosticEngine.h"
1616
#include "swift/AST/DiagnosticsFrontend.h"
1717
#include "swift/Basic/SourceManager.h"
18+
#include "swift/Parse/Lexer.h"
1819
#include "llvm/Config/config.h"
1920

2021
using namespace swift;
2122

22-
void PluginLoader::createModuleToExecutablePluginMap() {
23-
for (auto &elem : Ctx.SearchPathOpts.PluginSearchOpts) {
24-
if (auto *arg = elem.dyn_cast<PluginSearchOption::LoadPluginExecutable>()) {
25-
// Create a moduleName -> pluginPath mapping.
26-
assert(!arg->ExecutablePath.empty() && "empty plugin path");
27-
StringRef pathStr = Ctx.AllocateCopy(arg->ExecutablePath);
28-
for (auto moduleName : arg->ModuleNames) {
29-
ExecutablePluginPaths[Ctx.getIdentifier(moduleName)] = pathStr;
30-
}
31-
}
32-
}
33-
}
34-
3523
void PluginLoader::setRegistry(PluginRegistry *newValue) {
3624
assert(Registry == nullptr && "Too late to set a new plugin registry");
3725
Registry = newValue;
@@ -48,65 +36,124 @@ PluginRegistry *PluginLoader::getRegistry() {
4836
return Registry;
4937
}
5038

51-
std::pair<std::string, std::string>
52-
PluginLoader::lookupPluginByModuleName(Identifier moduleName) {
53-
auto fs = Ctx.SourceMgr.getFileSystem();
54-
55-
// Look for 'lib${module name}(.dylib|.so)'.
39+
/// Get plug module name from \p path if the path looks like a shared library
40+
/// path. Otherwise, returns an empty string.
41+
static StringRef pluginModuleNameStringFromPath(StringRef path) {
42+
// Plugin library must be named 'lib${module name}(.dylib|.so|.dll)'.
5643
// FIXME: Shared library prefix might be different between platforms.
57-
SmallString<64> pluginLibBasename;
58-
pluginLibBasename.append("lib");
59-
pluginLibBasename.append(moduleName.str());
60-
pluginLibBasename.append(LTDL_SHLIB_EXT);
44+
constexpr StringRef libPrefix = "lib";
45+
constexpr StringRef libSuffix = LTDL_SHLIB_EXT;
46+
47+
StringRef filename = llvm::sys::path::filename(path);
48+
if (filename.starts_with(libPrefix) && filename.ends_with(libSuffix)) {
49+
auto moduleName =
50+
filename.drop_front(libPrefix.size()).drop_back(libSuffix.size());
51+
52+
// FIXME: layering violation / circular linkage.
53+
// libAST should not call libParse.
54+
if (Lexer::isIdentifier(moduleName))
55+
return moduleName;
56+
}
57+
return "";
58+
}
59+
60+
llvm::DenseMap<Identifier, PluginLoader::PluginEntry> &
61+
PluginLoader::getPluginMap() {
62+
if (PluginMap.has_value()) {
63+
return PluginMap.value();
64+
}
65+
66+
// Create and populate the map.
67+
PluginMap.emplace();
68+
auto &map = PluginMap.value();
69+
70+
// Helper function to try inserting an entry if there's no existing entry
71+
// associated with the module name.
72+
auto try_emplace = [&](StringRef moduleName, StringRef libPath,
73+
StringRef execPath) {
74+
auto moduleNameIdentifier = Ctx.getIdentifier(moduleName);
75+
if (map.find(moduleNameIdentifier) != map.end()) {
76+
// Specified module name is already in the map.
77+
return;
78+
}
79+
80+
libPath = libPath.empty() ? "" : Ctx.AllocateCopy(libPath);
81+
execPath = execPath.empty() ? "" : Ctx.AllocateCopy(execPath);
82+
auto result = map.try_emplace(moduleNameIdentifier, libPath, execPath);
83+
assert(result.second);
84+
(void)result;
85+
};
86+
87+
auto fs = Ctx.SourceMgr.getFileSystem();
88+
std::error_code ec;
6189

62-
// FIXME: Should we create a lookup table keyed by module name?
6390
for (auto &entry : Ctx.SearchPathOpts.PluginSearchOpts) {
6491
switch (entry.getKind()) {
65-
// Try '-load-plugin-library'.
92+
93+
// '-load-plugin-library <library path>'.
6694
case PluginSearchOption::Kind::LoadPluginLibrary: {
6795
auto &val = entry.get<PluginSearchOption::LoadPluginLibrary>();
68-
if (llvm::sys::path::filename(val.LibraryPath) == pluginLibBasename) {
69-
return {val.LibraryPath, ""};
96+
auto moduleName = pluginModuleNameStringFromPath(val.LibraryPath);
97+
if (!moduleName.empty()) {
98+
try_emplace(moduleName, val.LibraryPath, /*executablePath=*/"");
7099
}
71100
continue;
72101
}
73102

74-
// Try '-load-plugin-executable'.
103+
// '-load-plugin-executable <executable path>#<module name>, ...'.
75104
case PluginSearchOption::Kind::LoadPluginExecutable: {
76105
auto &val = entry.get<PluginSearchOption::LoadPluginExecutable>();
77-
auto found = ExecutablePluginPaths.find(moduleName);
78-
if (found != ExecutablePluginPaths.end() &&
79-
found->second == val.ExecutablePath) {
80-
return {"", val.ExecutablePath};
106+
assert(!val.ExecutablePath.empty() && "empty plugin path");
107+
for (auto &moduleName : val.ModuleNames) {
108+
try_emplace(moduleName, /*libraryPath=*/"", val.ExecutablePath);
81109
}
82110
continue;
83111
}
84112

85-
// Try '-plugin-path'.
113+
// '-plugin-path <library search path>'.
86114
case PluginSearchOption::Kind::PluginPath: {
87115
auto &val = entry.get<PluginSearchOption::PluginPath>();
88-
SmallString<128> fullPath(val.SearchPath);
89-
llvm::sys::path::append(fullPath, pluginLibBasename);
90-
if (fs->exists(fullPath)) {
91-
return {std::string(fullPath), ""};
116+
for (auto i = fs->dir_begin(val.SearchPath, ec);
117+
i != llvm::vfs::directory_iterator(); i = i.increment(ec)) {
118+
auto libPath = i->path();
119+
auto moduleName = pluginModuleNameStringFromPath(libPath);
120+
if (!moduleName.empty()) {
121+
try_emplace(moduleName, libPath, /*executablePath=*/"");
122+
}
92123
}
93124
continue;
94125
}
95126

96-
// Try '-external-plugin-path'.
127+
// '-external-plugin-path <library search path>#<server path>'.
97128
case PluginSearchOption::Kind::ExternalPluginPath: {
98129
auto &val = entry.get<PluginSearchOption::ExternalPluginPath>();
99-
SmallString<128> fullPath(val.SearchPath);
100-
llvm::sys::path::append(fullPath, pluginLibBasename);
101-
if (fs->exists(fullPath)) {
102-
return {std::string(fullPath), val.ServerPath};
130+
for (auto i = fs->dir_begin(val.SearchPath, ec);
131+
i != llvm::vfs::directory_iterator(); i = i.increment(ec)) {
132+
auto libPath = i->path();
133+
auto moduleName = pluginModuleNameStringFromPath(libPath);
134+
if (!moduleName.empty()) {
135+
try_emplace(moduleName, libPath, val.ServerPath);
136+
}
103137
}
104138
continue;
105139
}
106140
}
141+
llvm_unreachable("unhandled PluginSearchOption::Kind");
107142
}
108143

109-
return {};
144+
return map;
145+
}
146+
147+
const PluginLoader::PluginEntry &
148+
PluginLoader::lookupPluginByModuleName(Identifier moduleName) {
149+
auto &map = getPluginMap();
150+
auto found = map.find(moduleName);
151+
if (found != map.end()) {
152+
return found->second;
153+
} else {
154+
static PluginEntry notFound{"", ""};
155+
return notFound;
156+
}
110157
}
111158

112159
LoadedLibraryPlugin *PluginLoader::loadLibraryPlugin(StringRef path) {

lib/Sema/TypeCheckMacros.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -343,8 +343,8 @@ CompilerPluginLoadRequest::evaluate(Evaluator &evaluator, ASTContext *ctx,
343343
Identifier moduleName) const {
344344
PluginLoader &loader = ctx->getPluginLoader();
345345

346-
std::string libraryPath;
347-
std::string executablePath;
346+
StringRef libraryPath;
347+
StringRef executablePath;
348348
std::tie(libraryPath, executablePath) =
349349
loader.lookupPluginByModuleName(moduleName);
350350

0 commit comments

Comments
 (0)