Skip to content

Commit 83143a3

Browse files
committed
[Macros] Load '-load-plugin-library' plugins lazily
* Argument to '-load-plugin-library' now must have a filename that's '{libprefix}{modulename}.{sharedlibraryextension}' * Load '-load-plugin-library' plugins are now lazily loaded in 'CompilerPluginLoadRequest' * Remove ASTContext.LoadedSymbols cache because they are cached by 'ExternalMacroDefinitionRequest' anyway * `-load-plugin-executable` format validation is now in 'ParseSearchPathArgs' (cherry picked from commit d8c8574)
1 parent fae32c0 commit 83143a3

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() {
@@ -6282,66 +6278,17 @@ PluginRegistry *ASTContext::getPluginRegistry() const {
62826278
return registry;
62836279
}
62846280

6285-
void ASTContext::loadCompilerPlugins() {
6286-
auto fs = this->SourceMgr.getFileSystem();
6287-
for (auto &path : SearchPathOpts.getCompilerPluginLibraryPaths()) {
6288-
SmallString<128> resolvedPath;
6289-
if (auto err = fs->getRealPath(path, resolvedPath)) {
6290-
Diags.diagnose(SourceLoc(), diag::compiler_plugin_not_loaded, path,
6291-
err.message());
6292-
continue;
6293-
}
6294-
auto loaded = getPluginRegistry()->loadLibraryPlugin(resolvedPath);
6295-
if (!loaded) {
6296-
Diags.diagnose(SourceLoc(), diag::compiler_plugin_not_loaded, path,
6297-
llvm::toString(loaded.takeError()));
6298-
}
6299-
}
6300-
6281+
void ASTContext::createModuleToExecutablePluginMap() {
63016282
for (auto &arg : SearchPathOpts.getCompilerPluginExecutablePaths()) {
6302-
// 'arg' is '<path to executable>#<module names>' where the module names are
6303-
// comma separated.
6304-
63056283
// Create a moduleName -> pluginPath mapping.
6306-
StringRef path;
6307-
StringRef modulesStr;
6308-
std::tie(path, modulesStr) = StringRef(arg).rsplit('#');
6309-
SmallVector<StringRef, 1> modules;
6310-
modulesStr.split(modules, ',');
6311-
6312-
if (modules.empty() || path.empty()) {
6313-
// TODO: Error messsage.
6314-
Diags.diagnose(SourceLoc(), diag::compiler_plugin_not_loaded, arg, "");
6315-
}
6316-
auto pathStr = AllocateCopy(path);
6317-
for (auto moduleName : modules) {
6284+
assert(!arg.ExecutablePath.empty() && "empty plugin path");
6285+
auto pathStr = AllocateCopy(arg.ExecutablePath);
6286+
for (auto moduleName : arg.ModuleNames) {
63186287
getImpl().ExecutablePluginPaths[getIdentifier(moduleName)] = pathStr;
63196288
}
63206289
}
63216290
}
63226291

6323-
void *ASTContext::getAddressOfSymbol(const char *name,
6324-
void *libraryHandleHint) {
6325-
auto lookup = getImpl().LoadedSymbols.try_emplace(name, nullptr);
6326-
void *&address = lookup.first->getValue();
6327-
#if !defined(_WIN32)
6328-
if (lookup.second) {
6329-
auto *handle = libraryHandleHint ? libraryHandleHint : RTLD_DEFAULT;
6330-
address = dlsym(handle, name);
6331-
6332-
// If we didn't know where to look, look specifically in each plugin.
6333-
if (!address && !libraryHandleHint) {
6334-
for (const auto &plugin : getPluginRegistry()->getLoadedLibraryPlugins()) {
6335-
address = dlsym(plugin.second, name);
6336-
if (address)
6337-
break;
6338-
}
6339-
}
6340-
}
6341-
#endif
6342-
return address;
6343-
}
6344-
63456292
Type ASTContext::getNamedSwiftType(ModuleDecl *module, StringRef name) {
63466293
if (!module)
63476294
return Type();
@@ -6377,6 +6324,35 @@ Type ASTContext::getNamedSwiftType(ModuleDecl *module, StringRef name) {
63776324
return decl->getDeclaredInterfaceType();
63786325
}
63796326

6327+
Optional<std::string>
6328+
ASTContext::lookupLibraryPluginByModuleName(Identifier moduleName) {
6329+
auto fs = SourceMgr.getFileSystem();
6330+
6331+
// Look for 'lib${module name}(.dylib|.so)'.
6332+
SmallString<64> expectedBasename;
6333+
expectedBasename.append("lib");
6334+
expectedBasename.append(moduleName.str());
6335+
expectedBasename.append(LTDL_SHLIB_EXT);
6336+
6337+
// Try '-plugin-path'.
6338+
for (const auto &searchPath : SearchPathOpts.PluginSearchPaths) {
6339+
SmallString<128> fullPath(searchPath);
6340+
llvm::sys::path::append(fullPath, expectedBasename);
6341+
if (fs->exists(fullPath)) {
6342+
return std::string(fullPath);
6343+
}
6344+
}
6345+
6346+
// Try '-load-plugin-library'.
6347+
for (const auto &libPath : SearchPathOpts.getCompilerPluginLibraryPaths()) {
6348+
if (llvm::sys::path::filename(libPath) == expectedBasename) {
6349+
return libPath;
6350+
}
6351+
}
6352+
6353+
return None;
6354+
}
6355+
63806356
Optional<StringRef>
63816357
ASTContext::lookupExecutablePluginByModuleName(Identifier moduleName) {
63826358
auto &execPluginPaths = getImpl().ExecutablePluginPaths;
@@ -6419,6 +6395,25 @@ LoadedExecutablePlugin *ASTContext::loadExecutablePlugin(StringRef path) {
64196395
return plugin.get();
64206396
}
64216397

6398+
void *ASTContext::loadLibraryPlugin(StringRef path) {
6399+
SmallString<128> resolvedPath;
6400+
auto fs = this->SourceMgr.getFileSystem();
6401+
if (auto err = fs->getRealPath(path, resolvedPath)) {
6402+
Diags.diagnose(SourceLoc(), diag::compiler_plugin_not_loaded, path,
6403+
err.message());
6404+
return nullptr;
6405+
}
6406+
6407+
// Load the plugin.
6408+
auto plugin = getPluginRegistry()->loadLibraryPlugin(resolvedPath);
6409+
if (!plugin) {
6410+
Diags.diagnose(SourceLoc(), diag::compiler_plugin_not_loaded, path,
6411+
llvm::toString(plugin.takeError()));
6412+
}
6413+
6414+
return plugin.get();
6415+
}
6416+
64226417
bool ASTContext::supportsMoveOnlyTypes() const {
64236418
// currently the only thing holding back whether the types can appear is this.
64246419
return SILOpts.LexicalLifetimes != LexicalLifetimesOption::Off;

lib/Frontend/CompilerInvocation.cpp

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1568,14 +1568,28 @@ static bool ParseSearchPathArgs(SearchPathOptions &Opts,
15681568
}
15691569
Opts.setCompilerPluginLibraryPaths(CompilerPluginLibraryPaths);
15701570

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

15801594
return false;
15811595
}

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)