From 6b677f799c8fd75b0571e10fb7e3d5c1e395bd7f Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Fri, 10 Mar 2023 12:45:59 -0800 Subject: [PATCH] [Plugin] Add 'loadPluginLibrary' message This message is to load a dynamic link library from the specified path. Later on, the loaded macro should be usable just like the normal macro expansion. Also, add 'features' to the capability message. Plugin implementations can declare the optional features they implement. 'CompilerPlugin' doesn't implement any optional features at this point. --- Sources/CMakeLists.txt | 1 + .../CMakeLists.txt | 22 ++++++++ .../CompilerPluginMessageHandler.swift | 56 ++++++++++++++++++- .../PluginMessages.swift | 28 ++++++++-- 4 files changed, 100 insertions(+), 7 deletions(-) create mode 100644 Sources/SwiftCompilerPluginMessageHandling/CMakeLists.txt diff --git a/Sources/CMakeLists.txt b/Sources/CMakeLists.txt index 16088765c7d..a9172ce9150 100644 --- a/Sources/CMakeLists.txt +++ b/Sources/CMakeLists.txt @@ -39,4 +39,5 @@ add_subdirectory(SwiftParserDiagnostics) add_subdirectory(SwiftOperators) add_subdirectory(SwiftSyntaxBuilder) add_subdirectory(SwiftSyntaxMacros) +add_subdirectory(SwiftCompilerPluginMessageHandling) add_subdirectory(IDEUtils) diff --git a/Sources/SwiftCompilerPluginMessageHandling/CMakeLists.txt b/Sources/SwiftCompilerPluginMessageHandling/CMakeLists.txt new file mode 100644 index 00000000000..5a838463580 --- /dev/null +++ b/Sources/SwiftCompilerPluginMessageHandling/CMakeLists.txt @@ -0,0 +1,22 @@ +# 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 http://swift.org/LICENSE.txt for license information +# See http://swift.org/CONTRIBUTORS.txt for Swift project authors + +add_swift_host_library(SwiftCompilerPluginMessageHandling + CompilerPluginMessageHandler.swift + Diagnostics.swift + Macros.swift + PluginMacroExpansionContext.swift + PluginMessages.swift +) + +target_link_libraries(SwiftCompilerPluginMessageHandling PUBLIC + SwiftSyntax + SwiftDiagnostics + SwiftParser + SwiftSyntaxMacros + SwiftOperators) diff --git a/Sources/SwiftCompilerPluginMessageHandling/CompilerPluginMessageHandler.swift b/Sources/SwiftCompilerPluginMessageHandling/CompilerPluginMessageHandler.swift index 9d7f04d2a44..c01ad205b27 100644 --- a/Sources/SwiftCompilerPluginMessageHandling/CompilerPluginMessageHandler.swift +++ b/Sources/SwiftCompilerPluginMessageHandling/CompilerPluginMessageHandler.swift @@ -12,9 +12,23 @@ import SwiftSyntaxMacros +/// Optional features. +public enum PluginFeature: String { + case loadPluginLibrary = "load-plugin-library" +} + /// A type that provides the actual plugin functions. public protocol PluginProvider { + /// Resolve macro type by the module name and the type name. func resolveMacro(moduleName: String, typeName: String) -> Macro.Type? + + /// Load dynamic link library at `libraryPath`. Implementations can use + /// `moduleName` to associate the loaded library with it. + func loadPluginLibrary(libraryPath: String, moduleName: String) throws + + /// Optional plugin features. This is sent to the host so the it can decide + /// the behavior depending on these. + var features: [PluginFeature] { get } } /// Low level message connection to the plugin host. @@ -67,9 +81,11 @@ extension CompilerPluginMessageHandler { fileprivate func handleMessage(_ message: HostToPluginMessage) throws { switch message { case .getCapability: - try self.sendMessage( - .getCapabilityResult(capability: PluginMessage.capability) + let capability = PluginMessage.PluginCapability( + protocolVersion: PluginMessage.PROTOCOL_VERSION_NUMBER, + features: provider.features.map({ $0.rawValue }) ) + try self.sendMessage(.getCapabilityResult(capability: capability)) case .expandFreestandingMacro(let macro, let discriminator, let expandingSyntax): try expandFreestandingMacro( @@ -87,6 +103,42 @@ extension CompilerPluginMessageHandler { declSyntax: declSyntax, parentDeclSyntax: parentDeclSyntax ) + + case .loadPluginLibrary(let libraryPath, let moduleName): + var diags: [PluginMessage.Diagnostic] = [] + do { + try provider.loadPluginLibrary(libraryPath: libraryPath, moduleName: moduleName) + } catch { + diags.append( + PluginMessage.Diagnostic( + message: String(describing: error), + severity: .error, + position: .invalid, + highlights: [], + notes: [], + fixIts: [] + ) + ) + } + try self.sendMessage(.loadPluginLibraryResult(loaded: diags.isEmpty, diagnostics: diags)); } } } + +struct UnimplementedError: Error, CustomStringConvertible { + var description: String { "unimplemented" } +} + +/// Default implementation of 'PluginProvider' requirements. +public extension PluginProvider { + var features: [PluginFeature] { + // No optional features by default. + return [] + } + + func loadPluginLibrary(libraryPath: String, moduleName: String) throws { + // This should be unreachable. The host should not call 'loadPluginLibrary' + // unless the feature is not declared. + throw UnimplementedError() + } +} diff --git a/Sources/SwiftCompilerPluginMessageHandling/PluginMessages.swift b/Sources/SwiftCompilerPluginMessageHandling/PluginMessages.swift index de5e44f0cfa..df67a178f43 100644 --- a/Sources/SwiftCompilerPluginMessageHandling/PluginMessages.swift +++ b/Sources/SwiftCompilerPluginMessageHandling/PluginMessages.swift @@ -14,14 +14,17 @@ // NOTE: Types in this file should be self-contained and should not depend on any non-stdlib types. internal enum HostToPluginMessage: Codable { + /// Get capability of this plugin. case getCapability + /// Expand a '@freestanding' macro. case expandFreestandingMacro( macro: PluginMessage.MacroReference, discriminator: String, syntax: PluginMessage.Syntax ) + /// Expand an '@attached' macro. case expandAttachedMacro( macro: PluginMessage.MacroReference, macroRole: PluginMessage.MacroRole, @@ -30,9 +33,21 @@ internal enum HostToPluginMessage: Codable { declSyntax: PluginMessage.Syntax, parentDeclSyntax: PluginMessage.Syntax? ) + + /// Optionally implemented message to load a dynamic link library. + /// 'moduleName' can be used as a hint indicating that the library + /// provides the specified module. + case loadPluginLibrary( + libraryPath: String, + moduleName: String + ) } internal enum PluginToHostMessage: Codable { + case getCapabilityResult( + capability: PluginMessage.PluginCapability + ) + case expandFreestandingMacroResult( expandedSource: String?, diagnostics: [PluginMessage.Diagnostic] @@ -43,18 +58,21 @@ internal enum PluginToHostMessage: Codable { diagnostics: [PluginMessage.Diagnostic] ) - case getCapabilityResult(capability: PluginMessage.PluginCapability) + case loadPluginLibraryResult( + loaded: Bool, + diagnostics: [PluginMessage.Diagnostic] + ) } /*namespace*/ internal enum PluginMessage { - static var PROTOCOL_VERSION_NUMBER: Int { 3 } // Renamed 'customAttributeSyntax' to 'attributeSyntax'. + static var PROTOCOL_VERSION_NUMBER: Int { 4 } // Added 'loadPluginLibrary'. struct PluginCapability: Codable { var protocolVersion: Int - } - static var capability: PluginCapability { - PluginCapability(protocolVersion: PluginMessage.PROTOCOL_VERSION_NUMBER) + /// Optional features this plugin provides. + /// * 'load-plugin-library': 'loadPluginLibrary' message is implemented. + var features: [String]? } struct MacroReference: Codable {