Skip to content

Commit bceaefe

Browse files
committed
[Macros] Add swift-plugin-server executable
This executable is intended to be installed in the toolchain and act as an executable compiler plugin just like other 'macro' plugins. This plugin server has an optional method 'loadPluginLibrary' that dynamically loads dylib plugins. The compiler has a newly added option '-external-plugin-path'. This option receives a pair of the plugin library search path (just like '-plugin-path') and the corresponding "plugin server" path, separated by '#'. i.e. -external-plugin-path <plugin library search path>#<plugin server executable path> For exmaple, when there's a macro decl: @freestanding(expression) macro stringify<T>(T) -> (T, String) = #externalMacro(module: "BasicMacro", type: "StringifyMacro") The compiler look for 'libBasicMacro.dylib' in '-plugin-path' paths, if not found, it falls back to '-external-plugin-path' and tries to find 'libBasicMacro.dylib' in them. If it's found, the "plugin server" path is launched just like an executable plugin, then 'loadPluginLibrary' method is invoked via IPC, which 'dlopen' the library path in the plugin server. At the actual macro expansion, the mangled name for 'BasicMacro.StringifyMacro' is used to resolve the macro just like dylib plugins in the compiler. This is useful for * Isolating the plugin process, so the plugin crashes doesn't result the compiler crash * Being able to use library plugins linked with other `swift-syntax` versions rdar://105104850
1 parent af1e0f7 commit bceaefe

24 files changed

+709
-55
lines changed

include/swift/AST/ASTContext.h

+18-2
Original file line numberDiff line numberDiff line change
@@ -1464,8 +1464,24 @@ class ASTContext final {
14641464

14651465
Type getNamedSwiftType(ModuleDecl *module, StringRef name);
14661466

1467-
LoadedExecutablePlugin *
1468-
lookupExecutablePluginByModuleName(Identifier moduleName);
1467+
/// Lookup an executable plugin that is declared to handle \p moduleName
1468+
/// module by '-load-plugin-executable'. Note that the returned path might be
1469+
/// in the current VFS. i.e. use FS.getRealPath() to get the real path.
1470+
Optional<StringRef> lookupExecutablePluginByModuleName(Identifier moduleName);
1471+
1472+
/// From paths '-external-plugin-path', look for dylib file that has
1473+
/// 'lib${moduleName}.dylib' (or equialent depending on the platform) and
1474+
/// return the found dylib path and the path to the "plugin server" for that.
1475+
/// Note that the returned path might be in the current VFS.
1476+
Optional<std::pair<std::string, std::string>>
1477+
lookupExternalLibraryPluginByModuleName(Identifier moduleName);
1478+
1479+
/// Launch the specified executable plugin path resolving the path with the
1480+
/// current VFS. If it fails to load the plugin, a diagnostic is emitted, and
1481+
/// returns a nullptr.
1482+
/// NOTE: This method is idempotent. If the plugin is already loaded, the same
1483+
/// instance is simply returned.
1484+
LoadedExecutablePlugin *loadExecutablePlugin(StringRef path);
14691485

14701486
/// Get the plugin registry this ASTContext is using.
14711487
PluginRegistry *getPluginRegistry() const;

include/swift/AST/SearchPathOptions.h

+5
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,11 @@ class SearchPathOptions {
382382
/// macro implementations.
383383
std::vector<std::string> PluginSearchPaths;
384384

385+
/// Paths that contain compiler plugins and the path to the plugin server
386+
/// executable.
387+
/// e.g. '/path/to/usr/lib/swift/host/plugins#/path/to/usr/bin/plugin-server'.
388+
std::vector<std::string> ExternalPluginSearchPaths;
389+
385390
/// Don't look in for compiler-provided modules.
386391
bool SkipRuntimeLibraryImportPaths = false;
387392

include/swift/Demangling/Demangle.h

+6
Original file line numberDiff line numberDiff line change
@@ -725,6 +725,12 @@ bool isFunctionAttr(Node::Kind kind);
725725
/// contain symbolic references.
726726
llvm::StringRef makeSymbolicMangledNameStringRef(const char *base);
727727

728+
/// Produce the mangled name for the nominal type descriptor of a type
729+
/// referenced by its module and type name.
730+
std::string mangledNameForTypeMetadataAccessor(llvm::StringRef moduleName,
731+
llvm::StringRef typeName,
732+
Node::Kind typeKind);
733+
728734
SWIFT_END_INLINE_NAMESPACE
729735
} // end namespace Demangle
730736
} // end namespace swift

include/swift/Option/Options.td

+5
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,11 @@ def plugin_path : Separate<["-"], "plugin-path">,
303303
Flags<[FrontendOption, ArgumentIsPath, SwiftAPIExtractOption, SwiftSymbolGraphExtractOption, SwiftAPIDigesterOption]>,
304304
HelpText<"Add directory to the plugin search path">;
305305

306+
def external_plugin_path : Separate<["-"], "external-plugin-path">,
307+
Flags<[FrontendOption, ArgumentIsPath, SwiftAPIExtractOption, SwiftSymbolGraphExtractOption, SwiftAPIDigesterOption]>,
308+
HelpText<"Add directory to the plugin search path with a plugin server executable">,
309+
MetaVarName<"<path>#<plugin-server-path>">;
310+
306311
def import_underlying_module : Flag<["-"], "import-underlying-module">,
307312
Flags<[FrontendOption, NoInteractiveOption]>,
308313
HelpText<"Implicitly imports the Objective-C half of a module">;

lib/AST/ASTContext.cpp

+24-4
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
#include "llvm/ADT/Statistic.h"
6868
#include "llvm/ADT/StringMap.h"
6969
#include "llvm/ADT/StringSwitch.h"
70+
#include "llvm/Config/config.h"
7071
#include "llvm/IR/LLVMContext.h"
7172
#include "llvm/Support/Allocator.h"
7273
#include "llvm/Support/Compiler.h"
@@ -6344,15 +6345,34 @@ Type ASTContext::getNamedSwiftType(ModuleDecl *module, StringRef name) {
63446345
return decl->getDeclaredInterfaceType();
63456346
}
63466347

6347-
LoadedExecutablePlugin *
6348+
Optional<StringRef>
63486349
ASTContext::lookupExecutablePluginByModuleName(Identifier moduleName) {
63496350
auto &execPluginPaths = getImpl().ExecutablePluginPaths;
63506351
auto found = execPluginPaths.find(moduleName);
63516352
if (found == execPluginPaths.end())
6352-
return nullptr;
6353+
return None;
6354+
return found->second;
6355+
}
6356+
6357+
Optional<std::pair<std::string, std::string>>
6358+
ASTContext::lookupExternalLibraryPluginByModuleName(Identifier moduleName) {
6359+
auto fs = this->SourceMgr.getFileSystem();
6360+
for (auto &pair : SearchPathOpts.ExternalPluginSearchPaths) {
6361+
StringRef searchPath;
6362+
StringRef serverPath;
6363+
std::tie(searchPath, serverPath) = StringRef(pair).split('#');
6364+
6365+
SmallString<128> fullPath(searchPath);
6366+
llvm::sys::path::append(fullPath, "lib" + moduleName.str() + LTDL_SHLIB_EXT);
6367+
6368+
if (fs->exists(fullPath)) {
6369+
return {{std::string(fullPath), serverPath.str()}};
6370+
}
6371+
}
6372+
return None;
6373+
}
63536374

6354-
// Let the VFS to map the path.
6355-
auto &path = found->second;
6375+
LoadedExecutablePlugin *ASTContext::loadExecutablePlugin(StringRef path) {
63566376
SmallString<128> resolvedPath;
63576377
auto fs = this->SourceMgr.getFileSystem();
63586378
if (auto err = fs->getRealPath(path, resolvedPath)) {

lib/ASTGen/Package.swift

+2-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ let package = Package(
2222
.macOS(.v10_15)
2323
],
2424
products: [
25-
.library(name: "swiftASTGen", targets: ["swiftASTGen"])
25+
.library(name: "swiftASTGen", targets: ["swiftASTGen"]),
26+
.library(name: "swiftLLVMJSON", targets: ["swiftLLVMJSON"]),
2627
],
2728
dependencies: [
2829
.package(path: "../../../swift-syntax")

lib/ASTGen/Sources/ASTGen/Macros.swift

+3
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,9 @@ public func resolveExecutableMacro(
112112
typeNameLength: Int,
113113
pluginOpaqueHandle: UnsafeMutableRawPointer
114114
) -> UnsafeRawPointer {
115+
// NOTE: This doesn't actually resolve anything.
116+
// Executable plugins is "trusted" to have the macro implementation. If not,
117+
// the actual expansion fails.
115118
let exportedPtr = UnsafeMutablePointer<ExportedExecutableMacro>.allocate(capacity: 1)
116119
exportedPtr.initialize(to: .init(
117120
moduleName: String(bufferStart: moduleName, count: moduleNameLength),

lib/ASTGen/Sources/ASTGen/PluginHost.swift

+37
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,35 @@ public func _deinitializePlugin(
3737
plugin.deinitialize()
3838
}
3939

40+
/// Load the library plugin in the plugin server.
41+
@_cdecl("swift_ASTGen_pluginServerLoadLibraryPlugin")
42+
func swift_ASTGen_pluginServerLoadLibraryPlugin(
43+
opaqueHandle: UnsafeMutableRawPointer,
44+
libraryPath: UnsafePointer<Int8>,
45+
moduleName: UnsafePointer<Int8>,
46+
cxxDiagnosticEngine: UnsafeMutablePointer<UInt8>
47+
) -> Bool {
48+
let plugin = CompilerPlugin(opaqueHandle: opaqueHandle)
49+
assert(plugin.capability.features?.contains("loadPluginLibrary") == true)
50+
let libraryPath = String(cString: libraryPath)
51+
let moduleName = String(cString: moduleName)
52+
let diagEngine = PluginDiagnosticsEngine(cxxDiagnosticEngine: cxxDiagnosticEngine)
53+
54+
do {
55+
let result = try plugin.sendMessageAndWait(
56+
.loadPluginLibrary(libraryPath: libraryPath, moduleName: moduleName)
57+
)
58+
guard case .loadPluginLibraryResult(let loaded, let diagnostics) = result else {
59+
throw PluginError.invalidReponseKind
60+
}
61+
diagEngine.emit(diagnostics);
62+
return loaded
63+
} catch {
64+
diagEngine.diagnose(error: error)
65+
return false
66+
}
67+
}
68+
4069
struct CompilerPlugin {
4170
let opaqueHandle: UnsafeMutableRawPointer
4271

@@ -224,6 +253,14 @@ class PluginDiagnosticsEngine {
224253
}
225254
}
226255

256+
func diagnose(error: Error) {
257+
self.emitSingle(
258+
message: String(describing: error),
259+
severity: .error,
260+
position: .invalid
261+
)
262+
}
263+
227264
/// Produce the C++ source location for a given position based on a
228265
/// syntax node.
229266
private func cxxSourceLocation(

lib/ASTGen/Sources/ASTGen/PluginMessages.swift

+23-5
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,17 @@
1414
// NOTE: Types in this file should be self-contained and should not depend on any non-stdlib types.
1515

1616
internal enum HostToPluginMessage: Codable {
17+
/// Get capability of this plugin.
1718
case getCapability
1819

20+
/// Expand a '@freestanding' macro.
1921
case expandFreestandingMacro(
2022
macro: PluginMessage.MacroReference,
2123
discriminator: String,
2224
syntax: PluginMessage.Syntax
2325
)
2426

27+
/// Expand an '@attached' macro.
2528
case expandAttachedMacro(
2629
macro: PluginMessage.MacroReference,
2730
macroRole: PluginMessage.MacroRole,
@@ -30,9 +33,21 @@ internal enum HostToPluginMessage: Codable {
3033
declSyntax: PluginMessage.Syntax,
3134
parentDeclSyntax: PluginMessage.Syntax?
3235
)
36+
37+
/// Optionally implemented message to load a dynamic link library.
38+
/// 'moduleName' can be used as a hint indicating that the library
39+
/// provides the specified module.
40+
case loadPluginLibrary(
41+
libraryPath: String,
42+
moduleName: String
43+
)
3344
}
3445

3546
internal enum PluginToHostMessage: Codable {
47+
case getCapabilityResult(
48+
capability: PluginMessage.PluginCapability
49+
)
50+
3651
case expandFreestandingMacroResult(
3752
expandedSource: String?,
3853
diagnostics: [PluginMessage.Diagnostic]
@@ -43,18 +58,21 @@ internal enum PluginToHostMessage: Codable {
4358
diagnostics: [PluginMessage.Diagnostic]
4459
)
4560

46-
case getCapabilityResult(capability: PluginMessage.PluginCapability)
61+
case loadPluginLibraryResult(
62+
loaded: Bool,
63+
diagnostics: [PluginMessage.Diagnostic]
64+
)
4765
}
4866

4967
/*namespace*/ internal enum PluginMessage {
50-
static var PROTOCOL_VERSION_NUMBER: Int { 3 } // Renamed 'customAttributeSyntax' to 'attributeSyntax'.
68+
static var PROTOCOL_VERSION_NUMBER: Int { 4 } // Added 'loadPluginLibrary'.
5169

5270
struct PluginCapability: Codable {
5371
var protocolVersion: Int
54-
}
5572

56-
static var capability: PluginCapability {
57-
PluginCapability(protocolVersion: PluginMessage.PROTOCOL_VERSION_NUMBER)
73+
/// Optional features this plugin provides.
74+
/// * resolveLibraryMacro: 'resolveLibraryMacro' message is implemented.
75+
var features: [String]?
5876
}
5977

6078
struct MacroReference: Codable {

lib/ASTGen/Sources/LLVMJSON/LLVMJSON.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ extension String {
2424
public struct LLVMJSON {
2525
/// Encode an `Encodable` value to JSON data, and call `body` is the buffer.
2626
/// Note that the buffer is valid onlu in `body`.
27-
public static func encoding<T: Encodable, R>(_ value: T, body: (UnsafeBufferPointer<Int8>) -> R) throws -> R {
27+
public static func encoding<T: Encodable, R>(_ value: T, body: (UnsafeBufferPointer<Int8>) throws -> R) throws -> R {
2828
let valuePtr = JSON_newValue()
2929
defer { JSON_value_delete(valuePtr) }
3030

@@ -36,7 +36,7 @@ public struct LLVMJSON {
3636
assert(data.baseAddress != nil)
3737
defer { BridgedData_free(data) }
3838
let buffer = UnsafeBufferPointer(start: data.baseAddress, count: data.size)
39-
return body(buffer)
39+
return try body(buffer)
4040
}
4141

4242
/// Decode a JSON data to a Swift value.

lib/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ if (SWIFT_SWIFT_PARSER)
2828
SwiftOperators
2929
SwiftSyntaxBuilder
3030
SwiftSyntaxMacros
31+
SwiftCompilerPluginMessageHandling
3132
)
3233

3334
# Compute the list of SwiftSyntax targets that we will link against.

lib/Demangling/Demangler.cpp

+35
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,41 @@ bool swift::Demangle::isStruct(llvm::StringRef mangledName) {
306306
return isStructNode(Dem.demangleType(mangledName));
307307
}
308308

309+
std::string swift::Demangle::mangledNameForTypeMetadataAccessor(
310+
StringRef moduleName, StringRef typeName, Node::Kind typeKind) {
311+
using namespace Demangle;
312+
313+
// kind=Global
314+
// kind=NominalTypeDescriptor
315+
// kind=Type
316+
// kind=Structure|Enum|Class
317+
// kind=Module, text=moduleName
318+
// kind=Identifier, text=typeName
319+
Demangle::Demangler D;
320+
auto *global = D.createNode(Node::Kind::Global);
321+
{
322+
auto *nominalDescriptor =
323+
D.createNode(Node::Kind::TypeMetadataAccessFunction);
324+
{
325+
auto *type = D.createNode(Node::Kind::Type);
326+
{
327+
auto *module = D.createNode(Node::Kind::Module, moduleName);
328+
auto *identifier = D.createNode(Node::Kind::Identifier, typeName);
329+
auto *structNode = D.createNode(typeKind);
330+
structNode->addChild(module, D);
331+
structNode->addChild(identifier, D);
332+
type->addChild(structNode, D);
333+
}
334+
nominalDescriptor->addChild(type, D);
335+
}
336+
global->addChild(nominalDescriptor, D);
337+
}
338+
339+
auto mangleResult = mangleNode(global);
340+
assert(mangleResult.isSuccess());
341+
return mangleResult.result();
342+
}
343+
309344
using namespace swift;
310345
using namespace Demangle;
311346

lib/Driver/ToolChains.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,7 @@ void ToolChain::addCommonFrontendArgs(const OutputInfo &OI,
237237
inputArgs.AddAllArgs(arguments, options::OPT_F, options::OPT_Fsystem);
238238
inputArgs.AddAllArgs(arguments, options::OPT_vfsoverlay);
239239
inputArgs.AddAllArgs(arguments, options::OPT_plugin_path);
240+
inputArgs.AddAllArgs(arguments, options::OPT_external_plugin_path);
240241

241242
inputArgs.AddLastArg(arguments, options::OPT_AssertConfig);
242243
inputArgs.AddLastArg(arguments, options::OPT_autolink_force_load);

lib/Frontend/CompilerInvocation.cpp

+9
Original file line numberDiff line numberDiff line change
@@ -1494,6 +1494,15 @@ static bool ParseSearchPathArgs(SearchPathOptions &Opts,
14941494
Opts.PluginSearchPaths.push_back(resolveSearchPath(A->getValue()));
14951495
}
14961496

1497+
for (const Arg *A : Args.filtered(OPT_external_plugin_path)) {
1498+
// '<plugin directory>#<plugin server executable path>'.
1499+
StringRef dylibPath;
1500+
StringRef serverPath;
1501+
std::tie(dylibPath, serverPath) = StringRef(A->getValue()).split('#');
1502+
Opts.ExternalPluginSearchPaths.push_back(
1503+
resolveSearchPath(dylibPath) + "#" + resolveSearchPath(serverPath));
1504+
}
1505+
14971506
for (const Arg *A : Args.filtered(OPT_L)) {
14981507
Opts.LibrarySearchPaths.push_back(resolveSearchPath(A->getValue()));
14991508
}

0 commit comments

Comments
 (0)