Skip to content

Commit f3524ad

Browse files
authored
Merge pull request #64610 from rintaro/macros-lazy-plugin-library
[Macros] Load '-load-plugin-library' plugins lazily
2 parents 8155d08 + d8c8574 commit f3524ad

File tree

6 files changed

+119
-93
lines changed

6 files changed

+119
-93
lines changed

include/swift/AST/ASTContext.h

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1461,12 +1461,21 @@ class ASTContext final {
14611461
/// The declared interface type of Builtin.TheTupleType.
14621462
BuiltinTupleType *getBuiltinTupleType();
14631463

1464-
/// Finds the address of the given symbol. If `libraryHandleHint` is non-null,
1465-
/// search within the library.
1466-
void *getAddressOfSymbol(const char *name, void *libraryHandleHint = nullptr);
1467-
14681464
Type getNamedSwiftType(ModuleDecl *module, StringRef name);
14691465

1466+
/// Lookup a library plugin that can handle \p moduleName and return the path
1467+
/// to it.
1468+
/// The path is valid within the VFS, use `FS.getRealPath()` for the
1469+
/// underlying path.
1470+
Optional<std::string> lookupLibraryPluginByModuleName(Identifier moduleName);
1471+
1472+
/// Load the specified dylib plugin path resolving the path with the
1473+
/// current VFS. If it fails to load the plugin, a diagnostic is emitted, and
1474+
/// returns a nullptr.
1475+
/// NOTE: This method is idempotent. If the plugin is already loaded, the same
1476+
/// instance is simply returned.
1477+
void *loadLibraryPlugin(StringRef path);
1478+
14701479
/// Lookup an executable plugin that is declared to handle \p moduleName
14711480
/// module by '-load-plugin-executable'.
14721481
/// The path is valid within the VFS, use `FS.getRealPath()` for the
@@ -1506,7 +1515,7 @@ class ASTContext final {
15061515
Optional<StringRef> getBriefComment(const Decl *D);
15071516
void setBriefComment(const Decl *D, StringRef Comment);
15081517

1509-
void loadCompilerPlugins();
1518+
void createModuleToExecutablePluginMap();
15101519

15111520
friend TypeBase;
15121521
friend ArchetypeType;

include/swift/AST/DiagnosticsFrontend.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,9 @@ ERROR(error_invalid_source_location_str,none,
128128
ERROR(error_no_source_location_scope_map,none,
129129
"-dump-scope-maps argument must be 'expanded' or a list of "
130130
"source locations", ())
131+
ERROR(error_load_plugin_executable,none,
132+
"invalid value '%0' in '-load-plugin-executable'; "
133+
"make sure to use format '<plugin path>#<module names>'", (StringRef))
131134

132135
NOTE(note_valid_swift_versions, none,
133136
"valid arguments to '-swift-version' are %0", (StringRef))

include/swift/AST/SearchPathOptions.h

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,12 @@ class ModuleSearchPathLookup {
174174
llvm::vfs::FileSystem *FS, bool IsOSDarwin);
175175
};
176176

177+
/// Pair of a plugin path and the module name that the plugin provides.
178+
struct PluginExecutablePathAndModuleNames {
179+
std::string ExecutablePath;
180+
std::vector<std::string> ModuleNames;
181+
};
182+
177183
/// Pair of a plugin search path and the corresponding plugin server executable
178184
/// path.
179185
struct ExternalPluginSearchPathAndServerPath {
@@ -242,8 +248,7 @@ class SearchPathOptions {
242248
std::vector<std::string> CompilerPluginLibraryPaths;
243249

244250
/// Compiler plugin executable paths and providing module names.
245-
/// Format: '<path>#<module names>'
246-
std::vector<std::string> CompilerPluginExecutablePaths;
251+
std::vector<PluginExecutablePathAndModuleNames> CompilerPluginExecutablePaths;
247252

248253
/// Add a single import search path. Must only be called from
249254
/// \c ASTContext::addSearchPath.
@@ -361,12 +366,13 @@ class SearchPathOptions {
361366
}
362367

363368
void setCompilerPluginExecutablePaths(
364-
std::vector<std::string> NewCompilerPluginExecutablePaths) {
365-
CompilerPluginExecutablePaths = NewCompilerPluginExecutablePaths;
369+
std::vector<PluginExecutablePathAndModuleNames> &&newValue) {
370+
CompilerPluginExecutablePaths = std::move(newValue);
366371
Lookup.searchPathsDidChange();
367372
}
368373

369-
ArrayRef<std::string> getCompilerPluginExecutablePaths() const {
374+
ArrayRef<PluginExecutablePathAndModuleNames>
375+
getCompilerPluginExecutablePaths() const {
370376
return CompilerPluginExecutablePaths;
371377
}
372378

lib/AST/ASTContext.cpp

Lines changed: 53 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -531,9 +531,6 @@ struct ASTContext::Implementation {
531531
/// `Plugins` storage if this ASTContext owns it.
532532
std::unique_ptr<PluginRegistry> OwnedPluginRegistry = nullptr;
533533

534-
/// Cache of loaded symbols.
535-
llvm::StringMap<void *> LoadedSymbols;
536-
537534
/// Map a module name to an executable plugin path that provides the module.
538535
llvm::DenseMap<Identifier, StringRef> ExecutablePluginPaths;
539536

@@ -707,8 +704,7 @@ ASTContext::ASTContext(
707704
registerAccessRequestFunctions(evaluator);
708705
registerNameLookupRequestFunctions(evaluator);
709706

710-
// FIXME: Delay this so the client e.g. SourceKit can inject plugin registry.
711-
loadCompilerPlugins();
707+
createModuleToExecutablePluginMap();
712708
}
713709

714710
ASTContext::~ASTContext() {
@@ -6264,66 +6260,17 @@ PluginRegistry *ASTContext::getPluginRegistry() const {
62646260
return registry;
62656261
}
62666262

6267-
void ASTContext::loadCompilerPlugins() {
6268-
auto fs = this->SourceMgr.getFileSystem();
6269-
for (auto &path : SearchPathOpts.getCompilerPluginLibraryPaths()) {
6270-
SmallString<128> resolvedPath;
6271-
if (auto err = fs->getRealPath(path, resolvedPath)) {
6272-
Diags.diagnose(SourceLoc(), diag::compiler_plugin_not_loaded, path,
6273-
err.message());
6274-
continue;
6275-
}
6276-
auto loaded = getPluginRegistry()->loadLibraryPlugin(resolvedPath);
6277-
if (!loaded) {
6278-
Diags.diagnose(SourceLoc(), diag::compiler_plugin_not_loaded, path,
6279-
llvm::toString(loaded.takeError()));
6280-
}
6281-
}
6282-
6263+
void ASTContext::createModuleToExecutablePluginMap() {
62836264
for (auto &arg : SearchPathOpts.getCompilerPluginExecutablePaths()) {
6284-
// 'arg' is '<path to executable>#<module names>' where the module names are
6285-
// comma separated.
6286-
62876265
// Create a moduleName -> pluginPath mapping.
6288-
StringRef path;
6289-
StringRef modulesStr;
6290-
std::tie(path, modulesStr) = StringRef(arg).rsplit('#');
6291-
SmallVector<StringRef, 1> modules;
6292-
modulesStr.split(modules, ',');
6293-
6294-
if (modules.empty() || path.empty()) {
6295-
// TODO: Error messsage.
6296-
Diags.diagnose(SourceLoc(), diag::compiler_plugin_not_loaded, arg, "");
6297-
}
6298-
auto pathStr = AllocateCopy(path);
6299-
for (auto moduleName : modules) {
6266+
assert(!arg.ExecutablePath.empty() && "empty plugin path");
6267+
auto pathStr = AllocateCopy(arg.ExecutablePath);
6268+
for (auto moduleName : arg.ModuleNames) {
63006269
getImpl().ExecutablePluginPaths[getIdentifier(moduleName)] = pathStr;
63016270
}
63026271
}
63036272
}
63046273

6305-
void *ASTContext::getAddressOfSymbol(const char *name,
6306-
void *libraryHandleHint) {
6307-
auto lookup = getImpl().LoadedSymbols.try_emplace(name, nullptr);
6308-
void *&address = lookup.first->getValue();
6309-
#if !defined(_WIN32)
6310-
if (lookup.second) {
6311-
auto *handle = libraryHandleHint ? libraryHandleHint : RTLD_DEFAULT;
6312-
address = dlsym(handle, name);
6313-
6314-
// If we didn't know where to look, look specifically in each plugin.
6315-
if (!address && !libraryHandleHint) {
6316-
for (const auto &plugin : getPluginRegistry()->getLoadedLibraryPlugins()) {
6317-
address = dlsym(plugin.second, name);
6318-
if (address)
6319-
break;
6320-
}
6321-
}
6322-
}
6323-
#endif
6324-
return address;
6325-
}
6326-
63276274
Type ASTContext::getNamedSwiftType(ModuleDecl *module, StringRef name) {
63286275
if (!module)
63296276
return Type();
@@ -6359,6 +6306,35 @@ Type ASTContext::getNamedSwiftType(ModuleDecl *module, StringRef name) {
63596306
return decl->getDeclaredInterfaceType();
63606307
}
63616308

6309+
Optional<std::string>
6310+
ASTContext::lookupLibraryPluginByModuleName(Identifier moduleName) {
6311+
auto fs = SourceMgr.getFileSystem();
6312+
6313+
// Look for 'lib${module name}(.dylib|.so)'.
6314+
SmallString<64> expectedBasename;
6315+
expectedBasename.append("lib");
6316+
expectedBasename.append(moduleName.str());
6317+
expectedBasename.append(LTDL_SHLIB_EXT);
6318+
6319+
// Try '-plugin-path'.
6320+
for (const auto &searchPath : SearchPathOpts.PluginSearchPaths) {
6321+
SmallString<128> fullPath(searchPath);
6322+
llvm::sys::path::append(fullPath, expectedBasename);
6323+
if (fs->exists(fullPath)) {
6324+
return std::string(fullPath);
6325+
}
6326+
}
6327+
6328+
// Try '-load-plugin-library'.
6329+
for (const auto &libPath : SearchPathOpts.getCompilerPluginLibraryPaths()) {
6330+
if (llvm::sys::path::filename(libPath) == expectedBasename) {
6331+
return libPath;
6332+
}
6333+
}
6334+
6335+
return None;
6336+
}
6337+
63626338
Optional<StringRef>
63636339
ASTContext::lookupExecutablePluginByModuleName(Identifier moduleName) {
63646340
auto &execPluginPaths = getImpl().ExecutablePluginPaths;
@@ -6401,6 +6377,25 @@ LoadedExecutablePlugin *ASTContext::loadExecutablePlugin(StringRef path) {
64016377
return plugin.get();
64026378
}
64036379

6380+
void *ASTContext::loadLibraryPlugin(StringRef path) {
6381+
SmallString<128> resolvedPath;
6382+
auto fs = this->SourceMgr.getFileSystem();
6383+
if (auto err = fs->getRealPath(path, resolvedPath)) {
6384+
Diags.diagnose(SourceLoc(), diag::compiler_plugin_not_loaded, path,
6385+
err.message());
6386+
return nullptr;
6387+
}
6388+
6389+
// Load the plugin.
6390+
auto plugin = getPluginRegistry()->loadLibraryPlugin(resolvedPath);
6391+
if (!plugin) {
6392+
Diags.diagnose(SourceLoc(), diag::compiler_plugin_not_loaded, path,
6393+
llvm::toString(plugin.takeError()));
6394+
}
6395+
6396+
return plugin.get();
6397+
}
6398+
64046399
bool ASTContext::supportsMoveOnlyTypes() const {
64056400
// currently the only thing holding back whether the types can appear is this.
64066401
return SILOpts.LexicalLifetimes != LexicalLifetimesOption::Off;

lib/Frontend/CompilerInvocation.cpp

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1564,14 +1564,28 @@ static bool ParseSearchPathArgs(SearchPathOptions &Opts,
15641564
}
15651565
Opts.setCompilerPluginLibraryPaths(CompilerPluginLibraryPaths);
15661566

1567-
std::vector<std::string> CompilerPluginExecutablePaths(
1567+
std::vector<PluginExecutablePathAndModuleNames> CompilerPluginExecutablePaths(
15681568
Opts.getCompilerPluginExecutablePaths());
15691569
for (const Arg *A : Args.filtered(OPT_load_plugin_executable)) {
1570-
// NOTE: The value has '#<module names>' after the path.
1571-
// But resolveSearchPath() works as long as the value starts with a path.
1572-
CompilerPluginExecutablePaths.push_back(resolveSearchPath(A->getValue()));
1570+
// 'A' is '<path to executable>#<module names>' where the module names are
1571+
// comma separated.
1572+
StringRef path;
1573+
StringRef modulesStr;
1574+
std::tie(path, modulesStr) = StringRef(A->getValue()).rsplit('#');
1575+
std::vector<std::string> moduleNames;
1576+
for (auto name : llvm::split(modulesStr, ',')) {
1577+
moduleNames.emplace_back(name);
1578+
}
1579+
if (path.empty() || moduleNames.empty()) {
1580+
Diags.diagnose(SourceLoc(), diag::error_load_plugin_executable,
1581+
A->getValue());
1582+
} else {
1583+
CompilerPluginExecutablePaths.push_back(
1584+
{resolveSearchPath(path), std::move(moduleNames)});
1585+
}
15731586
}
1574-
Opts.setCompilerPluginExecutablePaths(CompilerPluginExecutablePaths);
1587+
Opts.setCompilerPluginExecutablePaths(
1588+
std::move(CompilerPluginExecutablePaths));
15751589

15761590
return false;
15771591
}

lib/Sema/TypeCheckMacros.cpp

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,11 @@ static void const *lookupMacroTypeMetadataByExternalName(
9393
for (auto typeKind : typeKinds) {
9494
auto symbolName = Demangle::mangledNameForTypeMetadataAccessor(
9595
moduleName, typeName, typeKind);
96-
accessorAddr = ctx.getAddressOfSymbol(symbolName.c_str(), libraryHint);
96+
#if !defined(_WIN32)
97+
/// FIXME: 'PluginRegistry' should vend a wrapper object of the library
98+
/// handle (like llvm::sys::DynamicLibrary) and dlsym should be abstracted.
99+
accessorAddr = dlsym(libraryHint, symbolName.c_str());
100+
#endif
97101
if (accessorAddr)
98102
break;
99103
}
@@ -286,15 +290,16 @@ MacroDefinition MacroDefinitionRequest::evaluate(
286290
}
287291

288292
/// Load a plugin library based on a module name.
289-
static void *loadLibraryPluginByName(StringRef searchPath, StringRef moduleName,
290-
llvm::vfs::FileSystem &fs,
291-
PluginRegistry *registry) {
292-
SmallString<128> fullPath(searchPath);
293-
llvm::sys::path::append(fullPath, "lib" + moduleName + LTDL_SHLIB_EXT);
294-
if (fs.getRealPath(fullPath, fullPath))
293+
static void *loadLibraryPluginByName(ASTContext &ctx, Identifier moduleName) {
294+
std::string libraryPath;
295+
if (auto found = ctx.lookupLibraryPluginByModuleName(moduleName)) {
296+
libraryPath = *found;
297+
} else {
295298
return nullptr;
296-
auto loadResult = registry->loadLibraryPlugin(fullPath);
297-
return loadResult ? *loadResult : nullptr;
299+
}
300+
301+
// Load the plugin.
302+
return ctx.loadLibraryPlugin(libraryPath);
298303
}
299304

300305
static LoadedExecutablePlugin *
@@ -380,12 +385,10 @@ CompilerPluginLoadRequest::evaluate(Evaluator &evaluator, ASTContext *ctx,
380385
auto &searchPathOpts = ctx->SearchPathOpts;
381386
auto *registry = ctx->getPluginRegistry();
382387

383-
// First, check '-plugin-path' paths.
384-
for (const auto &path : searchPathOpts.PluginSearchPaths) {
385-
if (auto found =
386-
loadLibraryPluginByName(path, moduleName.str(), *fs, registry))
387-
return LoadedCompilerPlugin::inProcess(found);
388-
}
388+
// Check dynamic link library plugins.
389+
// i.e. '-plugin-path', and '-load-plugin-library'.
390+
if (auto found = loadLibraryPluginByName(*ctx, moduleName))
391+
return LoadedCompilerPlugin::inProcess(found);
389392

390393
// Fall back to executable plugins.
391394
// i.e. '-external-plugin-path', and '-load-plugin-executable'.
@@ -448,17 +451,13 @@ ExternalMacroDefinitionRequest::evaluate(Evaluator &evaluator, ASTContext *ctx,
448451
CompilerPluginLoadRequest loadRequest{ctx, moduleName};
449452
LoadedCompilerPlugin loaded =
450453
evaluateOrDefault(evaluator, loadRequest, nullptr);
454+
451455
if (auto loadedLibrary = loaded.getAsInProcessPlugin()) {
452456
if (auto inProcess = resolveInProcessMacro(
453457
*ctx, moduleName, typeName, loadedLibrary))
454458
return *inProcess;
455459
}
456460

457-
// Try to resolve in-process.
458-
if (auto inProcess = resolveInProcessMacro(*ctx, moduleName, typeName))
459-
return *inProcess;
460-
461-
// Try executable plugins.
462461
if (auto *executablePlugin = loaded.getAsExecutablePlugin()) {
463462
if (auto executableMacro = resolveExecutableMacro(*ctx, executablePlugin,
464463
moduleName, typeName)) {

0 commit comments

Comments
 (0)