[Macros] In-process plugin server

Separate swift-syntax libs for the compiler and for the library plugins.
Compiler communicates with library plugins using serialized messages
just like executable plugins.

* `lib/swift/host/compiler/lib_Compiler*.dylib`(`lib/CompilerSwiftSyntax`):
  swift-syntax libraries for compiler. Library evolution is disabled.
* Compiler (`ASTGen` and `swiftIDEUtilsBridging`) only depends on
  `lib/swift/host/compiler` libraries.
* `SwiftInProcPluginServer`: In-process plugin server shared library.
  This has one `swift_inproc_plugins_handle_message` entry point that
  receives a message and return the response.
* In the compiler
  * Add `-in-process-plugin-server-path` front-end option, which specifies
    the `SwiftInProcPluginServer` shared library path.
  * Remove `LoadedLibraryPlugin`, because all library plugins are managed
    by `SwiftInProcPluginServer`
  * Introduce abstract `CompilerPlugin` class that has 2 subclasses:
    * `LoadedExecutablePlugin` existing class that represents an
      executable plugin
    * `InProcessPlugins` wraps `dlopen`ed `SwiftInProcPluginServer`
  * Unified the code path in `TypeCheckMacros.cpp` and `ASTGen`, the
    difference between executable plugins and library plugins are now
    abstracted by `CompilerPlugin`
This commit is contained in:
Rintaro Ishizaki
2024-05-21 12:56:52 -07:00
parent af1d6017f9
commit 2f7aa428db
42 changed files with 699 additions and 893 deletions

View File

@@ -4,13 +4,12 @@ include(macCatalystUtils)
function(force_add_dependencies TARGET)
foreach(DEPENDENCY ${ARGN})
string(REGEX REPLACE [<>:\"/\\|?*] _ sanitized ${DEPENDENCY})
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/forced-${sanitized}-dep.swift
COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/forced-${sanitized}-dep.swift
set(depfile "${CMAKE_CURRENT_BINARY_DIR}/forced-${sanitized}-dep.swift")
add_custom_command(OUTPUT ${depfile}
COMMAND ${CMAKE_COMMAND} -E touch ${depfile}
DEPENDS ${DEPENDENCY}
)
target_sources(${TARGET} PRIVATE
${CMAKE_CURRENT_BINARY_DIR}/forced-${sanitized}-dep.swift
)
target_sources(${TARGET} PRIVATE ${depfile})
endforeach()
endfunction()
@@ -218,15 +217,6 @@ function(add_pure_swift_host_library name)
# Depends on all '*.h' files in 'include/module.modulemap'.
force_add_dependencies(${name} importedHeaderDependencies)
# Workaround to touch the library and its objects so that we don't
# continually rebuild (again, see corresponding change in swift-syntax).
add_custom_command(
TARGET ${name}
POST_BUILD
COMMAND "${CMAKE_COMMAND}" -E touch_nocreate $<TARGET_FILE:${name}> $<TARGET_OBJECTS:${name}> "${SWIFT_HOST_LIBRARIES_DEST_DIR}/${name}.swiftmodule" "${CMAKE_CURRENT_BINARY_DIR}/${name}.swiftmodule"
COMMAND_EXPAND_LISTS
COMMENT "Update mtime of library outputs workaround")
# Link against dependencies.
target_link_libraries(${name} PUBLIC
${APSHL_DEPENDENCIES}
@@ -236,15 +226,7 @@ function(add_pure_swift_host_library name)
${APSHL_SWIFT_DEPENDENCIES}
)
# Make sure we can use the host libraries.
target_include_directories(${name} PUBLIC
"${SWIFT_HOST_LIBRARIES_DEST_DIR}")
target_link_directories(${name} PUBLIC
"${SWIFT_HOST_LIBRARIES_DEST_DIR}")
if(APSHL_EMIT_MODULE)
# Determine where Swift modules will be built and installed.
set(module_triple "${SWIFT_HOST_MODULE_TRIPLE}")
set(module_dir "${SWIFT_HOST_LIBRARIES_DEST_DIR}")
set(module_base "${module_dir}/${name}.swiftmodule")
@@ -253,14 +235,6 @@ function(add_pure_swift_host_library name)
set(module_private_interface_file "${module_base}/${module_triple}.private.swiftinterface")
set(module_sourceinfo_file "${module_base}/${module_triple}.swiftsourceinfo")
set_target_properties(${name} PROPERTIES
# Set the default module name to the target name.
Swift_MODULE_NAME ${name}
# Install the Swift module into the appropriate location.
Swift_MODULE_DIRECTORY ${module_dir}
# NOTE: workaround for CMake not setting up include flags.
INTERFACE_INCLUDE_DIRECTORIES ${module_dir})
# Create the module directory.
add_custom_command(
TARGET ${name}
@@ -280,12 +254,27 @@ function(add_pure_swift_host_library name)
>)
else()
# Emit a swiftmodule in the current directory.
set_target_properties(${name} PROPERTIES
Swift_MODULE_NAME ${name}
Swift_MODULE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
set(module_file "${CMAKE_CURRENT_BINARY_DIR}/${name}.swiftmodule")
set(module_dir "${CMAKE_CURRENT_BINARY_DIR}/modules")
set(module_file "${module_dir}/${name}.swiftmodule")
endif()
set_target_properties(${name} PROPERTIES
# Set the default module name to the target name.
Swift_MODULE_NAME ${name}
# Install the Swift module into the appropriate location.
Swift_MODULE_DIRECTORY ${module_dir}
# NOTE: workaround for CMake not setting up include flags.
INTERFACE_INCLUDE_DIRECTORIES ${module_dir})
# Workaround to touch the library and its objects so that we don't
# continually rebuild (again, see corresponding change in swift-syntax).
add_custom_command(
TARGET ${name}
POST_BUILD
COMMAND "${CMAKE_COMMAND}" -E touch_nocreate $<TARGET_FILE:${name}> $<TARGET_OBJECTS:${name}> "${module_file}"
COMMAND_EXPAND_LISTS
COMMENT "Update mtime of library outputs workaround")
# Downstream linking should include the swiftmodule in debug builds to allow lldb to
# work correctly. Only do this on Darwin since neither gold (currently used by default
# on Linux), nor the default Windows linker 'link' support '-add_ast_path'.

View File

@@ -1016,12 +1016,12 @@ function(add_swift_host_tool executable)
set_property(
TARGET ${executable}
APPEND PROPERTY INSTALL_RPATH
"@executable_path/../${extra_relative_rpath}lib/swift/host")
"@executable_path/../${extra_relative_rpath}lib/swift/host/compiler")
else()
set_property(
TARGET ${executable}
APPEND PROPERTY INSTALL_RPATH
"$ORIGIN/../${extra_relative_rpath}lib/swift/host")
"$ORIGIN/../${extra_relative_rpath}lib/swift/host/compiler")
endif()
endif()

View File

@@ -133,11 +133,11 @@ function(add_swift_unittest test_dirname)
if(SWIFT_HOST_VARIANT_SDK IN_LIST SWIFT_DARWIN_PLATFORMS)
set_property(
TARGET ${test_dirname}
APPEND PROPERTY INSTALL_RPATH "@executable_path/${relative_lib_path}/swift/host")
APPEND PROPERTY INSTALL_RPATH "@executable_path/${relative_lib_path}/swift/host/compiler")
elseif(SWIFT_HOST_VARIANT_SDK MATCHES "LINUX|ANDROID|OPENBSD|FREEBSD")
set_property(
TARGET ${test_dirname}
APPEND PROPERTY INSTALL_RPATH "$ORIGIN/${relative_lib_path}/swift/host")
APPEND PROPERTY INSTALL_RPATH "$ORIGIN/${relative_lib_path}/swift/host/compiler")
endif()
endif()
endfunction()

View File

@@ -1862,9 +1862,6 @@ void Plugin_setCapability(PluginHandle handle,
/// Get a capability data set by \c Plugin_setCapability .
PluginCapabilityPtr _Nullable Plugin_getCapability(PluginHandle handle);
/// Get the executable file path of the plugin.
const char *Plugin_getExecutableFilePath(PluginHandle handle);
/// Lock the plugin. Clients should lock it during sending and recving the
/// response.
void Plugin_lock(PluginHandle handle);

View File

@@ -84,8 +84,6 @@ namespace swift {
class DifferentiableAttr;
class ExtensionDecl;
struct ExternalSourceLocs;
class LoadedExecutablePlugin;
class LoadedLibraryPlugin;
class ForeignRepresentationInfo;
class FuncDecl;
class GenericContext;

View File

@@ -26,24 +26,38 @@ namespace swift {
class ASTContext;
/// A reference to an external macro definition that is understood by ASTGen.
struct ExternalMacroDefinition {
enum class PluginKind : int8_t {
InProcess = 0,
Executable = 1,
Error = -1,
class ExternalMacroDefinition {
enum class Status : int8_t {
Success = 0,
Error,
};
PluginKind kind;
Status status;
/// ASTGen's notion of an macro definition, which is opaque to the C++ part
/// of the compiler. If 'kind' is 'PluginKind::Error', this is a C-string to
/// the error message
const void *opaqueHandle = nullptr;
const void *opaqueHandle;
ExternalMacroDefinition(Status status, const void *opaqueHandle)
: status(status), opaqueHandle(opaqueHandle) {}
public:
static ExternalMacroDefinition success(const void *opaqueHandle) {
return ExternalMacroDefinition{Status::Success, opaqueHandle};
}
static ExternalMacroDefinition error(NullTerminatedStringRef message) {
return ExternalMacroDefinition{PluginKind::Error,
return ExternalMacroDefinition{Status::Error,
static_cast<const void *>(message.data())};
}
bool isError() const { return kind == PluginKind::Error; }
const void *get() {
if (status != Status::Success)
return nullptr;
return opaqueHandle;
}
bool isError() const { return status == Status::Error; }
NullTerminatedStringRef getErrorMessage() const {
assert(isError());
return static_cast<const char *>(opaqueHandle);
}
};

View File

@@ -78,15 +78,14 @@ public:
/// returns a nullptr.
/// NOTE: This method is idempotent. If the plugin is already loaded, the same
/// instance is simply returned.
llvm::Expected<LoadedLibraryPlugin *> loadLibraryPlugin(llvm::StringRef path);
llvm::Expected<CompilerPlugin *> getInProcessPlugins();
/// Launch the specified executable plugin path resolving the path with the
/// current VFS. If it fails to load the plugin, a diagnostic is emitted, and
/// returns a nullptr.
/// NOTE: This method is idempotent. If the plugin is already loaded, the same
/// instance is simply returned.
llvm::Expected<LoadedExecutablePlugin *>
loadExecutablePlugin(llvm::StringRef path);
llvm::Expected<CompilerPlugin *> loadExecutablePlugin(llvm::StringRef path);
/// Add the specified plugin associated with the module name to the dependency
/// tracker if needed.

View File

@@ -17,6 +17,7 @@
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Chrono.h"
#include "llvm/Support/DynamicLibrary.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/Program.h"
@@ -25,26 +26,114 @@
namespace swift {
/// Represent a 'dlopen'ed plugin library.
class LoadedLibraryPlugin {
// Opaque handle used to interface with OS-specific dynamic library.
void *handle;
/// Base class for compiler plugins, or plugin servers.
class CompilerPlugin {
const std::string path;
/// Cache of loaded symbols.
llvm::StringMap<void *> resolvedSymbols;
std::mutex mtx;
/// Path to the plugin library.
const std::string LibraryPath;
/// Opaque value of the protocol capability of the plugin. This is a
/// value from ASTGen.
const void *capability = nullptr;
/// Cleanup function to call ASTGen.
std::function<void(void)> cleanup;
/// Callbacks to be called when the connection is restored.
llvm::SmallVector<std::function<void(void)> *, 0> onReconnect;
/// Flag to enable dumping of plugin messages.
bool dumpMessaging = false;
protected:
CompilerPlugin(llvm::StringRef path) : path(path) {}
bool shouldDumpMessaging() const { return dumpMessaging; }
public:
LoadedLibraryPlugin(void *handle, StringRef path)
: handle(handle), LibraryPath(path) {}
NullTerminatedStringRef getPath() const {
return {path.c_str(), path.size()};
}
/// Finds the address of the given symbol within the library.
void *getAddressOfSymbol(const char *symbolName);
void lock() { mtx.lock(); }
void unlock() { mtx.unlock(); }
NullTerminatedStringRef getLibraryPath() {
return {LibraryPath.c_str(), LibraryPath.size()};
bool isInitialized() const { return bool(cleanup); }
void setCleanup(std::function<void(void)> cleanup) {
this->cleanup = cleanup;
}
const void *getCapability() { return capability; };
void setCapability(const void *newValue) { capability = newValue; };
void setDumpMessaging(bool flag) { dumpMessaging = flag; }
virtual ~CompilerPlugin();
/// Launch the plugin if it's not already running, or it's stale. Return an
/// error if it's fails to execute it.
virtual llvm::Error spawnIfNeeded() = 0;
/// Send a message to the plugin.
virtual llvm::Error sendMessage(llvm::StringRef message) = 0;
/// Wait for a message from plugin and returns it.
virtual llvm::Expected<std::string> waitForNextMessage() = 0;
/// Add "on reconnect" callback.
/// These callbacks are called when `spawnIfNeeded()` relaunched the plugin.
void addOnReconnect(std::function<void(void)> *fn) {
onReconnect.push_back(fn);
}
/// Remove "on reconnect" callback.
void removeOnReconnect(std::function<void(void)> *fn) {
llvm::erase_value(onReconnect, fn);
}
ArrayRef<std::function<void(void)> *> getOnReconnectCallbacks() {
return onReconnect;
}
};
/// Represents a in-process plugin server.
class InProcessPlugins : public CompilerPlugin {
/// PluginServer
llvm::sys::DynamicLibrary server;
/// Entry point in the in-process plugin server. It receives the request
/// string and populate the response string. The return value indicates there
/// was an error. If true the returned string contains the error message.
/// 'free'ing the populated `responseDataPtr` is caller's responsibility.
using HandleMessageFunction = bool (*)(const char *requestData,
size_t requestLength,
char **responseDataPtr,
size_t *responseDataLengthPtr);
HandleMessageFunction handleMessageFn;
/// Temporary storage for the response data from 'handleMessageFn'.
std::string receivedResponse;
InProcessPlugins(llvm::StringRef serverPath, llvm::sys::DynamicLibrary server,
HandleMessageFunction handleMessageFn)
: CompilerPlugin(serverPath), server(server),
handleMessageFn(handleMessageFn) {}
public:
/// Create an instance by loading the in-process plugin server at 'serverPath'
/// and return it.
static llvm::Expected<std::unique_ptr<InProcessPlugins>>
create(const char *serverPath);
/// Send a message to the plugin.
llvm::Error sendMessage(llvm::StringRef message) override;
/// Wait for a message from plugin and returns it.
llvm::Expected<std::string> waitForNextMessage() override;
llvm::Error spawnIfNeeded() override {
// NOOP. It's always loaded.
return llvm::Error::success();
}
};
@@ -56,7 +145,7 @@ public:
/// launch it and manages the process. When the plugin process crashes, this
/// should automatically relaunch the process so the clients can keep using this
/// object as the interface.
class LoadedExecutablePlugin {
class LoadedExecutablePlugin : public CompilerPlugin {
/// Represents the current process of the executable plugin.
struct PluginProcess {
@@ -76,38 +165,23 @@ class LoadedExecutablePlugin {
/// Launched current process.
std::unique_ptr<PluginProcess> Process;
/// Path to the plugin executable.
const std::string ExecutablePath;
/// Last modification time of the `ExecutablePath` when this is initialized.
const llvm::sys::TimePoint<> LastModificationTime;
/// Opaque value of the protocol capability of the pluugin. This is a
/// value from ASTGen.
const void *capability = nullptr;
/// Callbacks to be called when the connection is restored.
llvm::SmallVector<std::function<void(void)> *, 0> onReconnect;
/// Disable sandbox.
bool disableSandbox = false;
/// Flag to dump plugin messagings.
bool dumpMessaging = false;
/// Cleanup function to call ASTGen.
std::function<void(void)> cleanup;
std::mutex mtx;
/// Mark the current process "stale" (not usable anymore for some reason,
/// probably crashed).
void setStale() { Process->isStale = true; }
public:
LoadedExecutablePlugin(llvm::StringRef ExecutablePath,
llvm::sys::TimePoint<> LastModificationTime,
bool disableSandbox)
: ExecutablePath(ExecutablePath),
: CompilerPlugin(ExecutablePath),
LastModificationTime(LastModificationTime),
disableSandbox(disableSandbox){};
~LoadedExecutablePlugin();
/// The last modification time of 'ExecutablePath' when this object is
/// created.
@@ -118,54 +192,23 @@ public:
/// Indicates that the current process is usable.
bool isAlive() const { return Process != nullptr && !Process->isStale; }
/// Mark the current process "stale".
void setStale() const { Process->isStale = true; }
void lock() { mtx.lock(); }
void unlock() { mtx.unlock(); }
// Launch the plugin if it's not already running, or it's stale. Return an
// error if it's fails to execute it.
llvm::Error spawnIfNeeded();
llvm::Error spawnIfNeeded() override;
/// Send a message to the plugin.
llvm::Error sendMessage(llvm::StringRef message) const;
llvm::Error sendMessage(llvm::StringRef message) override;
/// Wait for a message from plugin and returns it.
llvm::Expected<std::string> waitForNextMessage() const;
bool isInitialized() const { return bool(cleanup); }
void setCleanup(std::function<void(void)> cleanup) {
this->cleanup = cleanup;
}
/// Add "on reconnect" callback.
/// These callbacks are called when `spawnIfNeeded()` relaunched the plugin.
void addOnReconnect(std::function<void(void)> *fn) {
onReconnect.push_back(fn);
}
/// Remove "on reconnect" callback.
void removeOnReconnect(std::function<void(void)> *fn) {
llvm::erase_value(onReconnect, fn);
}
llvm::Expected<std::string> waitForNextMessage() override;
llvm::sys::procid_t getPid() { return Process->process.Pid; }
llvm::sys::process_t getProcess() { return Process->process.Process; }
NullTerminatedStringRef getExecutablePath() {
return {ExecutablePath.c_str(), ExecutablePath.size()};
}
const void *getCapability() { return capability; };
void setCapability(const void *newValue) { capability = newValue; };
void setDumpMessaging(bool flag) { dumpMessaging = flag; }
};
class PluginRegistry {
/// Record of loaded plugin library modules.
llvm::StringMap<std::unique_ptr<LoadedLibraryPlugin>> LoadedPluginLibraries;
/// The in-process plugin server.
std::unique_ptr<InProcessPlugins> inProcessPlugins;
/// Record of loaded plugin executables.
llvm::StringMap<std::unique_ptr<LoadedExecutablePlugin>>
@@ -179,14 +222,17 @@ class PluginRegistry {
public:
PluginRegistry();
/// Load a dynamic link library specified by \p path.
/// If \p path plugin is already loaded, this returns the cached object.
llvm::Expected<LoadedLibraryPlugin *> loadLibraryPlugin(llvm::StringRef path);
/// Get the in-process plugin server.
/// If it's loaded, returned the cached object. If the loaded instance is
/// from a different 'serverPath', returns an error as we don't support
/// multiple in-process plugin server in a host process.
llvm::Expected<CompilerPlugin *>
getInProcessPlugins(llvm::StringRef serverPath);
/// Load an executable plugin specified by \p path .
/// If \p path plugin is already loaded, this returns the cached object.
llvm::Expected<LoadedExecutablePlugin *>
loadExecutablePlugin(llvm::StringRef path, bool disableSandbox);
llvm::Expected<CompilerPlugin *> loadExecutablePlugin(llvm::StringRef path,
bool disableSandbox);
};
} // namespace swift

View File

@@ -469,6 +469,9 @@ public:
/// Plugin search path options.
std::vector<PluginSearchOption> PluginSearchOpts;
/// Path to in-process plugin server shared library.
std::string InProcessPluginServerPath;
/// Don't look in for compiler-provided modules.
bool SkipRuntimeLibraryImportPaths = false;

View File

@@ -50,7 +50,7 @@ class ContinueStmt;
class DefaultArgumentExpr;
class DefaultArgumentType;
class DoCatchStmt;
struct ExternalMacroDefinition;
class ExternalMacroDefinition;
class ClosureExpr;
class GenericParamList;
class InverseTypeRepr;
@@ -4514,38 +4514,30 @@ public:
/// Represent a loaded plugin either an in-process library or an executable.
class CompilerPluginLoadResult {
enum class PluginKind : uint8_t {
Error = 0,
Library,
Executable,
enum class Status : uint8_t {
Success = 0,
Error,
};
PluginKind Kind;
Status status;
void *opaqueHandle;
CompilerPluginLoadResult(PluginKind K, void *opaque)
: Kind(K), opaqueHandle(opaque) {}
CompilerPluginLoadResult(Status status, void *opaque)
: status(status), opaqueHandle(opaque) {}
public:
CompilerPluginLoadResult(LoadedLibraryPlugin *ptr)
: CompilerPluginLoadResult(PluginKind::Library, ptr){};
CompilerPluginLoadResult(LoadedExecutablePlugin *ptr)
: CompilerPluginLoadResult(PluginKind::Executable, ptr){};
CompilerPluginLoadResult(CompilerPlugin *ptr)
: CompilerPluginLoadResult(Status::Success, ptr){};
static CompilerPluginLoadResult error(NullTerminatedStringRef message) {
return CompilerPluginLoadResult(PluginKind::Error,
return CompilerPluginLoadResult(Status::Error,
const_cast<char *>(message.data()));
}
LoadedLibraryPlugin *getAsLibraryPlugin() const {
if (Kind != PluginKind::Library)
CompilerPlugin *get() const {
if (status != Status::Success)
return nullptr;
return static_cast<LoadedLibraryPlugin *>(opaqueHandle);
return static_cast<CompilerPlugin *>(opaqueHandle);
}
LoadedExecutablePlugin *getAsExecutablePlugin() const {
if (Kind != PluginKind::Executable)
return nullptr;
return static_cast<LoadedExecutablePlugin *>(opaqueHandle);
}
bool isError() const { return Kind == PluginKind::Error; }
bool isError() const { return status == Status::Error; }
NullTerminatedStringRef getErrorMessage() const {
assert(isError());
return static_cast<const char *>(opaqueHandle);

View File

@@ -58,15 +58,12 @@ void swift_ASTGen_buildTopLevelASTNodes(
BridgedLegacyParser legacyParser, void *_Nonnull outputContext,
void (*_Nonnull)(void *_Nonnull, void *_Nonnull));
void *_Nullable swift_ASTGen_resolveMacroType(const void *_Nonnull macroType);
void swift_ASTGen_destroyMacro(void *_Nonnull macro);
void swift_ASTGen_freeBridgedString(BridgedStringRef);
void *_Nonnull swift_ASTGen_resolveExecutableMacro(
void *_Nonnull swift_ASTGen_resolveExternalMacro(
const char *_Nonnull moduleName, const char *_Nonnull typeName,
void *_Nonnull opaquePluginHandle);
void swift_ASTGen_destroyExecutableMacro(void *_Nonnull macro);
void swift_ASTGen_destroyExternalMacro(void *_Nonnull macro);
bool swift_ASTGen_checkDefaultArgumentMacroExpression(
void *_Nonnull diagEngine, void *_Nonnull sourceFile,
@@ -84,13 +81,13 @@ void swift_ASTGen_freeExpansionReplacements(
ptrdiff_t *_Nullable replacementsPtr, ptrdiff_t numReplacements);
ptrdiff_t swift_ASTGen_expandFreestandingMacro(
void *_Nonnull diagEngine, const void *_Nonnull macro, uint8_t externalKind,
void *_Nonnull diagEngine, const void *_Nonnull macro,
const char *_Nonnull discriminator, uint8_t rawMacroRole,
void *_Nonnull sourceFile, const void *_Nullable sourceLocation,
BridgedStringRef *_Nonnull evaluatedSourceOut);
ptrdiff_t swift_ASTGen_expandAttachedMacro(
void *_Nonnull diagEngine, const void *_Nonnull macro, uint8_t externalKind,
void *_Nonnull diagEngine, const void *_Nonnull macro,
const char *_Nonnull discriminator, const char *_Nonnull qualifiedType,
const char *_Nonnull conformances, uint8_t rawMacroRole,
void *_Nonnull customAttrSourceFile,

View File

@@ -2012,6 +2012,10 @@ def load_plugin_executable:
"of module names where the macro types are declared">,
MetaVarName<"<path>#<module-names>">;
def in_process_plugin_server_path : Separate<["-"], "in-process-plugin-server-path">,
Flags<[FrontendOption, ArgumentIsPath]>,
HelpText<"Path to dynamic library plugin server">;
def disable_sandbox:
Flag<["-"], "disable-sandbox">,
Flags<[FrontendOption, DoesNotAffectIncrementalBuild]>,

View File

@@ -2567,32 +2567,27 @@ void BridgedTypeRepr_dump(void *type) { static_cast<TypeRepr *>(type)->dump(); }
//===----------------------------------------------------------------------===//
PluginCapabilityPtr Plugin_getCapability(PluginHandle handle) {
auto *plugin = static_cast<LoadedExecutablePlugin *>(handle);
auto *plugin = static_cast<CompilerPlugin *>(handle);
return plugin->getCapability();
}
void Plugin_setCapability(PluginHandle handle, PluginCapabilityPtr data) {
auto *plugin = static_cast<LoadedExecutablePlugin *>(handle);
auto *plugin = static_cast<CompilerPlugin *>(handle);
plugin->setCapability(data);
}
const char *Plugin_getExecutableFilePath(PluginHandle handle) {
auto *plugin = static_cast<LoadedExecutablePlugin *>(handle);
return plugin->getExecutablePath().data();
}
void Plugin_lock(PluginHandle handle) {
auto *plugin = static_cast<LoadedExecutablePlugin *>(handle);
auto *plugin = static_cast<CompilerPlugin *>(handle);
plugin->lock();
}
void Plugin_unlock(PluginHandle handle) {
auto *plugin = static_cast<LoadedExecutablePlugin *>(handle);
auto *plugin = static_cast<CompilerPlugin *>(handle);
plugin->unlock();
}
bool Plugin_spawnIfNeeded(PluginHandle handle) {
auto *plugin = static_cast<LoadedExecutablePlugin *>(handle);
auto *plugin = static_cast<CompilerPlugin *>(handle);
auto error = plugin->spawnIfNeeded();
bool hadError(error);
llvm::consumeError(std::move(error));
@@ -2600,7 +2595,7 @@ bool Plugin_spawnIfNeeded(PluginHandle handle) {
}
bool Plugin_sendMessage(PluginHandle handle, const BridgedData data) {
auto *plugin = static_cast<LoadedExecutablePlugin *>(handle);
auto *plugin = static_cast<CompilerPlugin *>(handle);
StringRef message(data.BaseAddress, data.Length);
auto error = plugin->sendMessage(message);
if (error) {
@@ -2616,7 +2611,7 @@ bool Plugin_sendMessage(PluginHandle handle, const BridgedData data) {
}
bool Plugin_waitForNextMessage(PluginHandle handle, BridgedData *out) {
auto *plugin = static_cast<LoadedExecutablePlugin *>(handle);
auto *plugin = static_cast<CompilerPlugin *>(handle);
auto result = plugin->waitForNextMessage();
if (!result) {
// FIXME: Pass the error message back to the caller.

View File

@@ -170,31 +170,24 @@ PluginLoader::lookupPluginByModuleName(Identifier moduleName) {
return found->second;
}
llvm::Expected<LoadedLibraryPlugin *>
PluginLoader::loadLibraryPlugin(StringRef path) {
llvm::Expected<CompilerPlugin *> PluginLoader::getInProcessPlugins() {
auto inProcPluginServerPath = Ctx.SearchPathOpts.InProcessPluginServerPath;
if (inProcPluginServerPath.empty()) {
return llvm::createStringError(
llvm::inconvertibleErrorCode(),
"library plugins require -in-process-plugin-server-path");
}
auto fs = getPluginLoadingFS(Ctx);
SmallString<128> resolvedPath;
if (auto err = fs->getRealPath(path, resolvedPath)) {
if (auto err = fs->getRealPath(inProcPluginServerPath, resolvedPath)) {
return llvm::createStringError(err, err.message());
}
// Load the plugin.
auto plugin = getRegistry()->loadLibraryPlugin(resolvedPath);
if (!plugin) {
resolvedPath.push_back(0);
return llvm::handleErrors(
plugin.takeError(), [&](const llvm::ErrorInfoBase &err) {
return llvm::createStringError(
err.convertToErrorCode(),
"compiler plugin '%s' could not be loaded; %s",
resolvedPath.data(), err.message().data());
});
}
return plugin;
return getRegistry()->getInProcessPlugins(resolvedPath);
}
llvm::Expected<LoadedExecutablePlugin *>
llvm::Expected<CompilerPlugin *>
PluginLoader::loadExecutablePlugin(StringRef path) {
auto fs = getPluginLoadingFS(Ctx);
SmallString<128> resolvedPath;

View File

@@ -44,46 +44,96 @@ PluginRegistry::PluginRegistry() {
dumpMessaging = ::getenv("SWIFT_DUMP_PLUGIN_MESSAGING") != nullptr;
}
llvm::Expected<LoadedLibraryPlugin *>
PluginRegistry::loadLibraryPlugin(StringRef path) {
CompilerPlugin::~CompilerPlugin() {
// Let ASTGen do cleanup things.
if (this->cleanup)
this->cleanup();
}
llvm::Expected<std::unique_ptr<InProcessPlugins>>
InProcessPlugins::create(const char *serverPath) {
std::string err;
auto server = llvm::sys::DynamicLibrary::getLibrary(serverPath, &err);
if (!server.isValid()) {
return llvm::createStringError(llvm::inconvertibleErrorCode(), err);
}
auto funcPtr =
server.getAddressOfSymbol("swift_inproc_plugins_handle_message");
if (!funcPtr) {
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"entry point not found in '%s'", serverPath);
}
return std::unique_ptr<InProcessPlugins>(new InProcessPlugins(
serverPath, server, reinterpret_cast<HandleMessageFunction>(funcPtr)));
}
llvm::Error InProcessPlugins::sendMessage(llvm::StringRef message) {
assert(receivedResponse.empty() &&
"sendMessage() called before consuming previous response?");
if (shouldDumpMessaging()) {
llvm::dbgs() << "->(plugin:0) " << message << '\n';
}
char *responseData = nullptr;
size_t responseLength = 0;
bool hadError = handleMessageFn(message.data(), message.size(), &responseData,
&responseLength);
// 'responseData' now holds a response message or error message depending on
// 'hadError'. Either way, it's our responsibility to deallocate it.
SWIFT_DEFER { free(responseData); };
if (hadError) {
return llvm::createStringError(llvm::inconvertibleErrorCode(),
StringRef(responseData, responseLength));
}
// Store the response and wait for 'waitForNextMessage()' call.
receivedResponse = std::string(responseData, responseLength);
if (shouldDumpMessaging()) {
llvm::dbgs() << "<-(plugin:0) " << receivedResponse << "\n";
}
assert(!receivedResponse.empty() && "received empty response");
return llvm::Error::success();
}
llvm::Expected<std::string> InProcessPlugins::waitForNextMessage() {
assert(!receivedResponse.empty() &&
"waitForNextMessage() called without response data.");
SWIFT_DEFER { receivedResponse = ""; };
return std::move(receivedResponse);
}
llvm::Expected<CompilerPlugin *>
PluginRegistry::getInProcessPlugins(llvm::StringRef serverPath) {
std::lock_guard<std::mutex> lock(mtx);
auto &storage = LoadedPluginLibraries[path];
if (storage) {
// Already loaded.
return storage.get();
if (!inProcessPlugins) {
auto server = InProcessPlugins::create(serverPath.str().c_str());
if (!server) {
return llvm::handleErrors(
server.takeError(), [&](const llvm::ErrorInfoBase &err) {
return llvm::createStringError(
err.convertToErrorCode(),
"failed to load in-process plugin server: " + serverPath +
"; " + err.message());
});
}
inProcessPlugins = std::move(server.get());
} else if (inProcessPlugins->getPath() != serverPath) {
return llvm::createStringError(
llvm::inconvertibleErrorCode(),
"loading multiple in-process servers are not supported: '%s' and '%s'",
inProcessPlugins->getPath().data(), serverPath.str().c_str());
}
inProcessPlugins->setDumpMessaging(dumpMessaging);
void *lib = nullptr;
#if defined(_WIN32)
lib = LoadLibraryA(path.str().c_str());
if (!lib) {
std::error_code ec(GetLastError(), std::system_category());
return llvm::errorCodeToError(ec);
}
#else
lib = dlopen(path.str().c_str(), RTLD_LAZY | RTLD_LOCAL);
if (!lib) {
return llvm::createStringError(llvm::inconvertibleErrorCode(), dlerror());
}
#endif
storage = std::make_unique<LoadedLibraryPlugin>(lib, path);
return storage.get();
return inProcessPlugins.get();
}
void *LoadedLibraryPlugin::getAddressOfSymbol(const char *symbolName) {
auto &cached = resolvedSymbols[symbolName];
if (cached)
return cached;
#if defined(_WIN32)
cached = GetProcAddress(static_cast<HMODULE>(handle), symbolName);
#else
cached = dlsym(handle, symbolName);
#endif
return cached;
}
llvm::Expected<LoadedExecutablePlugin *>
llvm::Expected<CompilerPlugin *>
PluginRegistry::loadExecutablePlugin(StringRef path, bool disableSandbox) {
llvm::sys::fs::file_status stat;
if (auto err = llvm::sys::fs::status(path, stat)) {
@@ -143,7 +193,7 @@ llvm::Error LoadedExecutablePlugin::spawnIfNeeded() {
}
// Create command line arguments.
SmallVector<StringRef, 4> command{ExecutablePath};
SmallVector<StringRef, 4> command{getPath()};
// Apply sandboxing.
llvm::BumpPtrAllocator Allocator;
@@ -161,7 +211,7 @@ llvm::Error LoadedExecutablePlugin::spawnIfNeeded() {
childInfo->Read, childInfo->Write);
// Call "on reconnect" callbacks.
for (auto *callback : onReconnect) {
for (auto *callback : getOnReconnectCallbacks()) {
(*callback)();
}
@@ -179,12 +229,6 @@ LoadedExecutablePlugin::PluginProcess::~PluginProcess() {
llvm::sys::Wait(process, /*SecondsToWait=*/0);
}
LoadedExecutablePlugin::~LoadedExecutablePlugin() {
// Let ASTGen to cleanup things.
if (this->cleanup)
this->cleanup();
}
ssize_t LoadedExecutablePlugin::PluginProcess::read(void *buf,
size_t nbyte) const {
#if defined(_WIN32)
@@ -260,10 +304,10 @@ ssize_t LoadedExecutablePlugin::PluginProcess::write(const void *buf,
#endif
}
llvm::Error LoadedExecutablePlugin::sendMessage(llvm::StringRef message) const {
llvm::Error LoadedExecutablePlugin::sendMessage(llvm::StringRef message) {
ssize_t writtenSize = 0;
if (dumpMessaging) {
if (shouldDumpMessaging()) {
llvm::dbgs() << "->(plugin:" << Process->process.Pid << ") " << message << '\n';
}
@@ -291,7 +335,7 @@ llvm::Error LoadedExecutablePlugin::sendMessage(llvm::StringRef message) const {
return llvm::Error::success();
}
llvm::Expected<std::string> LoadedExecutablePlugin::waitForNextMessage() const {
llvm::Expected<std::string> LoadedExecutablePlugin::waitForNextMessage() {
ssize_t readSize = 0;
// Read header (message size).
@@ -323,7 +367,7 @@ llvm::Expected<std::string> LoadedExecutablePlugin::waitForNextMessage() const {
message.append(buffer, readSize);
}
if (dumpMessaging) {
if (shouldDumpMessaging()) {
llvm::dbgs() << "<-(plugin:" << Process->process.Pid << ") " << message << "\n";
}

View File

@@ -11,7 +11,7 @@ if(SWIFT_BUILD_REGEX_PARSER_IN_COMPILER)
endforeach()
message(STATUS "Using Experimental String Processing library for _CompilerRegexParser (${SWIFT_PATH_TO_STRING_PROCESSING_SOURCE}).")
add_pure_swift_host_library(_CompilerRegexParser STATIC EMIT_MODULE
add_pure_swift_host_library(_CompilerRegexParser STATIC
"${COMPILER_REGEX_PARSER_SOURCES}"
)
@@ -36,7 +36,6 @@ add_pure_swift_host_library(swiftASTGen STATIC
Sources/ASTGen/Regex.swift
Sources/ASTGen/SourceFile.swift
Sources/ASTGen/SourceManager.swift
Sources/ASTGen/SourceManager+MacroExpansionContext.swift
Sources/ASTGen/Stmts.swift
Sources/ASTGen/TypeAttrs.swift
Sources/ASTGen/Types.swift
@@ -44,16 +43,15 @@ add_pure_swift_host_library(swiftASTGen STATIC
DEPENDENCIES
swiftAST
SWIFT_DEPENDENCIES
SwiftBasicFormat
SwiftCompilerPluginMessageHandling
SwiftDiagnostics
SwiftOperators
SwiftParser
SwiftParserDiagnostics
SwiftSyntax
SwiftSyntaxBuilder
SwiftSyntaxMacros
SwiftSyntaxMacroExpansion
_CompilerSwiftSyntax
_CompilerSwiftOperators
_CompilerSwiftSyntaxBuilder
_CompilerSwiftParser
_CompilerSwiftParserDiagnostics
_CompilerSwiftCompilerPluginMessageHandling
_CompilerSwiftSyntaxMacroExpansion
_CompilerSwiftDiagnostics
_CompilerSwiftIDEUtils
${ASTGen_Swift_dependencies}
)
@@ -62,14 +60,11 @@ add_pure_swift_host_library(swiftIDEUtilsBridging
DEPENDENCIES
swiftAST
swiftASTGen
SWIFT_DEPENDENCIES
SwiftIDEUtils
SwiftSyntax
_CompilerSwiftSyntax
swiftASTGen
)
set(c_include_paths
# LLVM modules and headers.
"${LLVM_MAIN_INCLUDE_DIR}"
@@ -88,7 +83,6 @@ endforeach()
set(compile_options
${c_include_paths_args}
"SHELL: -DRESILIENT_SWIFT_SYNTAX"
"SHELL: -Xcc -std=c++17 -Xcc -DCOMPILED_WITH_SWIFT"
# FIXME: Needed to work around an availability issue with CxxStdlib

View File

@@ -57,7 +57,6 @@ let package = Package(
.target(
name: "swiftASTGen",
dependencies: [
.product(name: "SwiftBasicFormat", package: "swift-syntax"),
.product(name: "SwiftCompilerPluginMessageHandling", package: "swift-syntax"),
.product(name: "SwiftDiagnostics", package: "swift-syntax"),
.product(name: "SwiftOperators", package: "swift-syntax"),
@@ -65,7 +64,6 @@ let package = Package(
.product(name: "SwiftParserDiagnostics", package: "swift-syntax"),
.product(name: "SwiftSyntax", package: "swift-syntax"),
.product(name: "SwiftSyntaxBuilder", package: "swift-syntax"),
.product(name: "SwiftSyntaxMacros", package: "swift-syntax"),
.product(name: "SwiftSyntaxMacroExpansion", package: "swift-syntax"),
"_CompilerRegexParser",
],

View File

@@ -19,25 +19,13 @@ import SwiftParser
import SwiftSyntax
import SwiftSyntaxBuilder
@_spi(ExperimentalLanguageFeature) @_spi(Compiler) import SwiftSyntaxMacroExpansion
@_spi(ExperimentalLanguageFeature) import SwiftSyntaxMacros
/// Describes a macro that has been "exported" to the C++ part of the
/// compiler, with enough information to interface with the C++ layer.
struct ExportedMacro {
var macro: Macro.Type
}
struct ExportedExecutableMacro {
struct ExportedExternalMacro {
var moduleName: String
var typeName: String
var plugin: CompilerPlugin
}
enum MacroPluginKind: UInt8 {
case InProcess = 0
case Executable = 1
}
extension MacroRole {
init(rawMacroRole: UInt8) {
switch rawMacroRole {
@@ -58,40 +46,8 @@ extension MacroRole {
}
}
/// Resolve a reference to type metadata into a macro, if posible.
///
/// Returns an unmanaged pointer to an ExportedMacro instance that describes
/// the specified macro. If there is no macro with the given name, produces
/// nil.
@_cdecl("swift_ASTGen_resolveMacroType")
public func resolveMacroType(
macroTypePtr: UnsafePointer<UInt8>
) -> UnsafeRawPointer? {
let macroType = unsafeBitCast(macroTypePtr, to: Any.Type.self)
guard let macro = macroType as? Macro.Type else {
return nil
}
// Allocate and initialize the exported macro.
let exportedPtr = UnsafeMutablePointer<ExportedMacro>.allocate(capacity: 1)
exportedPtr.initialize(to: .init(macro: macro))
return UnsafeRawPointer(exportedPtr)
}
/// Destroys the given macro.
@_cdecl("swift_ASTGen_destroyMacro")
public func destroyMacro(
macroPtr: UnsafeMutablePointer<UInt8>
) {
macroPtr.withMemoryRebound(to: ExportedMacro.self, capacity: 1) { macro in
macro.deinitialize(count: 1)
macro.deallocate()
}
}
@_cdecl("swift_ASTGen_resolveExecutableMacro")
public func resolveExecutableMacro(
@_cdecl("swift_ASTGen_resolveExternalMacro")
public func resolveExternalMacro(
moduleName: UnsafePointer<CChar>,
typeName: UnsafePointer<CChar>,
pluginOpaqueHandle: UnsafeMutableRawPointer
@@ -99,7 +55,7 @@ public func resolveExecutableMacro(
// NOTE: This doesn't actually resolve anything.
// Executable plugins is "trusted" to have the macro implementation. If not,
// the actual expansion fails.
let exportedPtr = UnsafeMutablePointer<ExportedExecutableMacro>.allocate(capacity: 1)
let exportedPtr = UnsafeMutablePointer<ExportedExternalMacro>.allocate(capacity: 1)
exportedPtr.initialize(
to: .init(
moduleName: String(cString: moduleName),
@@ -110,11 +66,11 @@ public func resolveExecutableMacro(
return UnsafeRawPointer(exportedPtr)
}
@_cdecl("swift_ASTGen_destroyExecutableMacro")
public func destroyExecutableMacro(
@_cdecl("swift_ASTGen_destroyExternalMacro")
public func destroyExternalMacro(
macroPtr: UnsafeMutableRawPointer
) {
let macroPtr = macroPtr.assumingMemoryBound(to: ExportedExecutableMacro.self)
let macroPtr = macroPtr.assumingMemoryBound(to: ExportedExternalMacro.self)
macroPtr.deinitialize(count: 1)
macroPtr.deallocate()
}
@@ -470,7 +426,6 @@ func makeExpansionOutputResult(
func expandFreestandingMacro(
diagEnginePtr: UnsafeMutableRawPointer,
macroPtr: UnsafeRawPointer,
macroKind: UInt8,
discriminatorText: UnsafePointer<CChar>,
rawMacroRole: UInt8,
sourceFilePtr: UnsafeRawPointer,
@@ -508,27 +463,14 @@ func expandFreestandingMacro(
let discriminator = String(cString: discriminatorText)
let macroRole = MacroRole(rawMacroRole: rawMacroRole)
let expandedSource: String?
switch MacroPluginKind(rawValue: macroKind)! {
case .InProcess:
expandedSource = expandFreestandingMacroInProcess(
macroPtr: macroPtr,
macroRole: macroRole,
diagEnginePtr: diagEnginePtr,
expansionSyntax: expansion,
sourceFilePtr: sourceFilePtr,
discriminator: discriminator
)
case .Executable:
expandedSource = expandFreestandingMacroIPC(
macroPtr: macroPtr,
macroRole: macroRole,
diagEnginePtr: diagEnginePtr,
expansionSyntax: expansion,
sourceFilePtr: sourceFilePtr,
discriminator: discriminator
)
}
let expandedSource: String? = expandFreestandingMacroImpl(
macroPtr: macroPtr,
macroRole: macroRole,
diagEnginePtr: diagEnginePtr,
expansionSyntax: expansion,
sourceFilePtr: sourceFilePtr,
discriminator: discriminator
)
return makeExpansionOutputResult(
expandedSource: expandedSource,
@@ -536,7 +478,7 @@ func expandFreestandingMacro(
)
}
func expandFreestandingMacroIPC(
func expandFreestandingMacroImpl(
macroPtr: UnsafeRawPointer,
macroRole: MacroRole,
diagEnginePtr: UnsafeMutableRawPointer,
@@ -554,7 +496,7 @@ func expandFreestandingMacroIPC(
fatalError("unknown syntax")
}
let macro = macroPtr.assumingMemoryBound(to: ExportedExecutableMacro.self).pointee
let macro = macroPtr.assumingMemoryBound(to: ExportedExternalMacro.self).pointee
// Map the macro role.
let pluginMacroRole: PluginMessage.MacroRole
@@ -615,55 +557,6 @@ func expandFreestandingMacroIPC(
}
}
func expandFreestandingMacroInProcess(
macroPtr: UnsafeRawPointer,
macroRole: MacroRole,
diagEnginePtr: UnsafeMutableRawPointer,
expansionSyntax: FreestandingMacroExpansionSyntax,
sourceFilePtr: UnsafePointer<ExportedSourceFile>,
discriminator: String
) -> String? {
// Get the macro.
let macroPtr = macroPtr.bindMemory(to: ExportedMacro.self, capacity: 1)
let macro = macroPtr.pointee.macro
// Create a source manager. This should probably persist and be given to us.
let sourceManager = SourceManager(cxxDiagnosticEngine: diagEnginePtr)
sourceManager.insert(sourceFilePtr)
let context = sourceManager.createMacroExpansionContext(
lexicalContext: lexicalContext(of: expansionSyntax),
discriminator: discriminator
)
let macroName = expansionSyntax.macroName.text
// Make sure we emit all of the diagnostics from the context.
defer {
// Emit diagnostics accumulated in the context.
for diag in context.diagnostics {
sourceManager.diagnose(
diagnostic: diag,
messageSuffix: " (from macro '\(macroName)')"
)
}
context.diagnostics = []
}
let node = sourceManager.detach(
expansionSyntax,
foldingWith: OperatorTable.standardOperators
)
return SwiftSyntaxMacroExpansion.expandFreestandingMacro(
definition: macro,
macroRole: macroRole,
node: node,
in: context
)
}
/// Retrieve a syntax node in the given source file, with the given type.
func findSyntaxNodeInSourceFile<Node: SyntaxProtocol>(
sourceFilePtr: UnsafeRawPointer,
@@ -730,7 +623,6 @@ func findSyntaxNodeInSourceFile<Node: SyntaxProtocol>(
func expandAttachedMacro(
diagEnginePtr: UnsafeMutableRawPointer,
macroPtr: UnsafeRawPointer,
macroKind: UInt8,
discriminatorText: UnsafePointer<CChar>,
qualifiedTypeText: UnsafePointer<CChar>,
conformanceListText: UnsafePointer<CChar>,
@@ -786,39 +678,20 @@ func expandAttachedMacro(
let qualifiedType = String(cString: qualifiedTypeText)
let conformanceList = String(cString: conformanceListText)
let expandedSource: String?
switch MacroPluginKind(rawValue: macroKind)! {
case .Executable:
expandedSource = expandAttachedMacroIPC(
diagEnginePtr: diagEnginePtr,
macroPtr: macroPtr,
rawMacroRole: rawMacroRole,
discriminator: discriminator,
qualifiedType: qualifiedType,
conformanceList: conformanceList,
customAttrSourceFilePtr: customAttrSourceFilePtr,
customAttrNode: customAttrNode,
declarationSourceFilePtr: declarationSourceFilePtr,
attachedTo: declarationNode,
parentDeclSourceFilePtr: parentDeclSourceFilePtr,
parentDeclNode: parentDeclNode
)
case .InProcess:
expandedSource = expandAttachedMacroInProcess(
diagEnginePtr: diagEnginePtr,
macroPtr: macroPtr,
rawMacroRole: rawMacroRole,
discriminator: discriminator,
qualifiedType: qualifiedType,
conformanceList: conformanceList,
customAttrSourceFilePtr: customAttrSourceFilePtr,
customAttrNode: customAttrNode,
declarationSourceFilePtr: declarationSourceFilePtr,
attachedTo: declarationNode,
parentDeclSourceFilePtr: parentDeclSourceFilePtr,
parentDeclNode: parentDeclNode
)
}
let expandedSource: String? = expandAttachedMacroImpl(
diagEnginePtr: diagEnginePtr,
macroPtr: macroPtr,
rawMacroRole: rawMacroRole,
discriminator: discriminator,
qualifiedType: qualifiedType,
conformanceList: conformanceList,
customAttrSourceFilePtr: customAttrSourceFilePtr,
customAttrNode: customAttrNode,
declarationSourceFilePtr: declarationSourceFilePtr,
attachedTo: declarationNode,
parentDeclSourceFilePtr: parentDeclSourceFilePtr,
parentDeclNode: parentDeclNode
)
return makeExpansionOutputResult(
expandedSource: expandedSource,
@@ -840,7 +713,7 @@ private func pluginLexicalContext(of node: some SyntaxProtocol) -> [PluginMessag
lexicalContext(of: node).compactMap { .init(syntax: $0) }
}
func expandAttachedMacroIPC(
func expandAttachedMacroImpl(
diagEnginePtr: UnsafeMutableRawPointer,
macroPtr: UnsafeRawPointer,
rawMacroRole: UInt8,
@@ -855,7 +728,7 @@ func expandAttachedMacroIPC(
parentDeclNode: DeclSyntax?
) -> String? {
let macroName: String = customAttrNode.attributeName.description
let macro = macroPtr.assumingMemoryBound(to: ExportedExecutableMacro.self).pointee
let macro = macroPtr.assumingMemoryBound(to: ExportedExternalMacro.self).pointee
// Map the macro role.
let macroRole: PluginMessage.MacroRole
@@ -988,104 +861,3 @@ func expandAttachedMacroIPC(
}
}
func expandAttachedMacroInProcess(
diagEnginePtr: UnsafeMutableRawPointer,
macroPtr: UnsafeRawPointer,
rawMacroRole: UInt8,
discriminator: String,
qualifiedType: String,
conformanceList: String,
customAttrSourceFilePtr: UnsafePointer<ExportedSourceFile>,
customAttrNode: AttributeSyntax,
declarationSourceFilePtr: UnsafePointer<ExportedSourceFile>,
attachedTo declarationNode: DeclSyntax,
parentDeclSourceFilePtr: UnsafePointer<ExportedSourceFile>?,
parentDeclNode: DeclSyntax?
) -> String? {
// Get the macro.
let macroPtr = macroPtr.bindMemory(to: ExportedMacro.self, capacity: 1)
let macro = macroPtr.pointee.macro
// Create a source manager covering the files we know about.
let sourceManager = SourceManager(cxxDiagnosticEngine: diagEnginePtr)
sourceManager.insert(customAttrSourceFilePtr)
sourceManager.insert(declarationSourceFilePtr)
if let parentDeclSourceFilePtr = parentDeclSourceFilePtr {
sourceManager.insert(parentDeclSourceFilePtr)
}
// Create an expansion context
let context = sourceManager.createMacroExpansionContext(
lexicalContext: lexicalContext(of: declarationNode),
discriminator: discriminator
)
let macroName = customAttrNode.attributeName.trimmedDescription
// Emit all of the accumulated diagnostics before we exit.
defer {
// Emit diagnostics accumulated in the context.
for diag in context.diagnostics {
sourceManager.diagnose(
diagnostic: diag,
messageSuffix: " (from macro '\(macroName)')"
)
}
context.diagnostics = []
}
let attributeNode = sourceManager.detach(
customAttrNode,
foldingWith: OperatorTable.standardOperators
)
let declarationNode = sourceManager.detach(declarationNode)
let parentDeclNode = parentDeclNode.map { sourceManager.detach($0) }
let extendedType: TypeSyntax = "\(raw: qualifiedType)"
let conformanceListSyntax: InheritedTypeListSyntax?
if (conformanceList.isEmpty) {
conformanceListSyntax = nil
} else {
let placeholderDecl: DeclSyntax =
"""
struct Placeholder: \(raw: conformanceList) {}
"""
let placeholderStruct = placeholderDecl.cast(StructDeclSyntax.self)
if let inheritanceClause = placeholderStruct.inheritanceClause {
conformanceListSyntax = inheritanceClause.inheritedTypes
} else {
conformanceListSyntax = nil
}
}
return SwiftSyntaxMacroExpansion.expandAttachedMacro(
definition: macro,
macroRole: MacroRole(rawMacroRole: rawMacroRole),
attributeNode: attributeNode,
declarationNode: declarationNode,
parentDeclNode: parentDeclNode,
extendedType: extendedType,
conformanceList: conformanceListSyntax,
in: context
)
}
fileprivate extension SyntaxProtocol {
/// Perform a format if required and then trim any leading/trailing
/// whitespace.
func formattedExpansion(_ mode: FormatMode) -> String {
let formatted: Syntax
switch mode {
case .auto:
formatted = self.formatted()
case .disabled:
formatted = Syntax(self)
#if RESILIENT_SWIFT_SYNTAX
@unknown default:
fatalError()
#endif
}
return formatted.trimmedDescription(matching: { $0.isWhitespace })
}
}

View File

@@ -194,10 +194,6 @@ struct CompilerPlugin {
}
return nil
}
var executableFilePath: String {
return String(cString: Plugin_getExecutableFilePath(opaqueHandle))
}
}
class PluginDiagnosticsEngine {
@@ -319,7 +315,7 @@ class PluginDiagnosticsEngine {
messageSuffix: String? = nil
) {
for diagnostic in diagnostics {
self.emit(diagnostic)
self.emit(diagnostic, messageSuffix: messageSuffix)
}
}
@@ -378,6 +374,26 @@ class PluginDiagnosticsEngine {
}
}
extension String {
/// Retrieve the base name of a string that represents a path, removing the
/// directory.
var basename: String {
guard
let lastSlash = lastIndex(where: {
#if os(iOS) || os(macOS) || os(tvOS) || os(watchOS) || os(visionOS) || os(Android) || os(Linux)
["/"].contains($0)
#else
["/", "\\"].contains($0)
#endif
})
else {
return self
}
return String(self[index(after: lastSlash)...])
}
}
extension PluginMessage.Syntax {
init?(syntax: Syntax, in sourceFilePtr: UnsafePointer<ExportedSourceFile>) {
let kind: PluginMessage.Syntax.Kind

View File

@@ -1,192 +0,0 @@
//===--- SourceManager+MacroExpansionContext.swift ------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2022-2023 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
import SwiftDiagnostics
import SwiftSyntax
import SwiftSyntaxMacros
extension SourceManager {
class MacroExpansionContext {
/// The source manager.
private let sourceManager: SourceManager
/// The lexical context for this expansion.
let lexicalContext: [Syntax]
/// The set of diagnostics that were emitted as part of expanding the
/// macro.
var diagnostics: [Diagnostic] = []
/// The macro expansion discriminator, which is used to form unique names
/// when requested.
///
/// The expansion discriminator is combined with the `uniqueNames` counters
/// to produce unique names.
private var discriminator: String
/// Counter for each of the uniqued names.
///
/// Used in conjunction with `expansionDiscriminator`.
private var uniqueNames: [String: Int] = [:]
init(
sourceManager: SourceManager,
lexicalContext: [Syntax],
discriminator: String
) {
self.sourceManager = sourceManager
self.lexicalContext = lexicalContext
self.discriminator = discriminator
}
}
/// Create a new macro expansion context
func createMacroExpansionContext(
lexicalContext: [Syntax],
discriminator: String = ""
) -> MacroExpansionContext {
return MacroExpansionContext(
sourceManager: self,
lexicalContext: lexicalContext,
discriminator: discriminator
)
}
}
extension String {
/// Retrieve the base name of a string that represents a path, removing the
/// directory.
var basename: String {
guard
let lastSlash = lastIndex(where: {
#if os(iOS) || os(macOS) || os(tvOS) || os(watchOS) || os(Android) || os(Linux)
["/"].contains($0)
#else
["/", "\\"].contains($0)
#endif
})
else {
return self
}
return String(self[index(after: lastSlash)...])
}
}
extension SourceManager.MacroExpansionContext: MacroExpansionContext {
/// Generate a unique name for use in the macro.
public func makeUniqueName(_ providedName: String) -> TokenSyntax {
// If provided with an empty name, substitute in something.
let name = providedName.isEmpty ? "__local" : providedName
// Grab a unique index value for this name.
let uniqueIndex = uniqueNames[name, default: 0]
uniqueNames[name] = uniqueIndex + 1
// Start with the discriminator.
var resultString = discriminator
// Mangle the name
resultString += "\(name.count)\(name)"
// Mangle the operator for unique macro names.
resultString += "fMu"
// Mangle the index.
if uniqueIndex > 0 {
resultString += "\(uniqueIndex - 1)"
}
resultString += "_"
return TokenSyntax(.identifier(resultString), presence: .present)
}
/// Produce a diagnostic while expanding the macro.
public func diagnose(_ diagnostic: Diagnostic) {
diagnostics.append(diagnostic)
}
public func location<Node: SyntaxProtocol>(
of node: Node,
at position: PositionInSyntaxNode,
filePathMode: SourceLocationFilePathMode
) -> AbstractSourceLocation? {
guard let (sourceFile, rootPosition) = sourceManager.rootSourceFile(of: node),
let exportedSourceFile =
sourceManager.exportedSourceFilesBySyntax[sourceFile]?.pointee
else {
return nil
}
// Find the node's offset relative to its root.
let rawPosition: AbsolutePosition
switch position {
case .beforeLeadingTrivia:
rawPosition = node.position
case .afterLeadingTrivia:
rawPosition = node.positionAfterSkippingLeadingTrivia
case .beforeTrailingTrivia:
rawPosition = node.endPositionBeforeTrailingTrivia
case .afterTrailingTrivia:
rawPosition = node.endPosition
#if RESILIENT_SWIFT_SYNTAX
@unknown default:
fatalError()
#endif
}
let offsetWithinSyntaxNode =
rawPosition.utf8Offset - node.position.utf8Offset
var location = exportedSourceFile.sourceLocationConverter.location(
for: rootPosition.advanced(by: offsetWithinSyntaxNode)
)
switch filePathMode {
case .fileID:
// The `SourceLocationConverter` in `exportedSourceFile` uses `filePath` as the file mode. When the `fileID` mode
// is requested, we need to adjust the file and presumed file to the `fileID`.
let fileID = "\(exportedSourceFile.moduleName)/\(exportedSourceFile.fileName.basename)"
var adjustedFile = location.file
if adjustedFile == exportedSourceFile.fileName {
adjustedFile = fileID
}
var adjustedPresumedFile = location.presumedFile
if adjustedPresumedFile == exportedSourceFile.fileName {
adjustedPresumedFile = fileID
}
location = SourceLocation(
line: location.line,
column: location.column,
offset: location.offset,
file: adjustedFile,
presumedLine: location.presumedLine,
presumedFile: adjustedPresumedFile
)
case .filePath:
break
#if RESILIENT_SWIFT_SYNTAX
@unknown default:
fatalError()
#endif
}
// Do the location lookup.
return AbstractSourceLocation(location)
}
}

View File

@@ -14,7 +14,6 @@ import ASTBridging
import BasicBridging
import SwiftOperators
import SwiftSyntax
import SwiftSyntaxMacros
/// A source manager that keeps track of the source files in the program.
class SourceManager {

View File

@@ -40,6 +40,7 @@ add_subdirectory(AST)
add_subdirectory(ASTGen)
add_subdirectory(ASTSectionImporter)
add_subdirectory(Basic)
add_subdirectory(CompilerSwiftSyntax)
add_subdirectory(ConstExtract)
add_subdirectory(ClangImporter)
add_subdirectory(Demangling)

View File

@@ -0,0 +1,52 @@
if(NOT SWIFT_BUILD_SWIFT_SYNTAX)
return()
endif()
if(NOT EXISTS "${SWIFT_PATH_TO_SWIFT_SYNTAX_SOURCE}")
message(SEND_ERROR "swift-syntax is required to build the Swift compiler. Please run update-checkout or specify SWIFT_PATH_TO_SWIFT_SYNTAX_SOURCE")
return()
endif()
# Build swift-syntax libraries with FetchContent.
function(includeSwiftSyntax)
set(CMAKE_Swift_COMPILER_TARGET ${SWIFT_HOST_TRIPLE})
set(BUILD_SHARED_LIBS ON)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${SWIFT_HOST_LIBRARIES_DEST_DIR}/compiler")
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${SWIFT_HOST_LIBRARIES_DEST_DIR}/compiler")
if(SWIFT_HOST_VARIANT_SDK MATCHES "LINUX|ANDROID|OPENBSD|FREEBSD")
set(SWIFT_HOST_LIBRARIES_RPATH "$ORIGIN;$ORIGIN/../../${SWIFT_SDK_${SWIFT_HOST_VARIANT_SDK}_LIB_SUBDIR}")
endif()
# Add unique ABI prefix to swift-syntax libraries so that compiler libraries (e.g. sourcekitdInProc)
# can be used from tools that has its own swift-syntax libraries as SwiftPM dependencies.
set(SWIFT_MODULE_ABI_NAME_PREFIX "_Compiler")
set(SWIFTSYNTAX_TARGET_NAMESPACE "_Compiler")
set(SWIFTSYNTAX_EMIT_MODULE OFF)
file(TO_CMAKE_PATH "${SWIFT_PATH_TO_SWIFT_SYNTAX_SOURCE}" swift_syntax_path)
FetchContent_Declare(CompilerSwiftSyntax SOURCE_DIR "${swift_syntax_path}")
FetchContent_MakeAvailable(CompilerSwiftSyntax)
endfunction()
includeSwiftSyntax()
set(compiler_swiftsyntax_libs
_CompilerSwiftSyntax
_CompilerSwiftOperators
_CompilerSwiftSyntaxBuilder
_CompilerSwiftParser
_CompilerSwiftParserDiagnostics
_CompilerSwiftCompilerPluginMessageHandling
_CompilerSwiftSyntaxMacroExpansion
_CompilerSwiftSyntaxMacros
_CompilerSwiftBasicFormat
_CompilerSwiftDiagnostics
_CompilerSwiftIDEUtils
)
foreach(lib ${compiler_swiftsyntax_libs})
target_compile_options(${lib} PRIVATE "SHELL:-module-link-name ${lib}")
endforeach()
swift_install_in_component(TARGETS ${compiler_swiftsyntax_libs}
ARCHIVE DESTINATION "lib${LLVM_LIBDIR_SUFFIX}/swift/host/compiler" COMPONENT compiler
LIBRARY DESTINATION "lib${LLVM_LIBDIR_SUFFIX}/swift/host/compiler" COMPONENT compiler
RUNTIME DESTINATION "bin" COMPONENT compiler)

View File

@@ -136,10 +136,16 @@ toolchains::Darwin::addPluginArguments(const ArgList &Args,
CompilerInvocation::computeRuntimeResourcePathFromExecutablePath(
programPath, /*shared=*/true, pluginPath);
auto defaultPluginPath = pluginPath;
llvm::sys::path::append(defaultPluginPath, "host", "plugins");
// In-process plugin server path.
auto inProcPluginServerPath = pluginPath;
llvm::sys::path::append(inProcPluginServerPath, "host",
"libSwiftInProcPluginServer.dylib");
Arguments.push_back("-in-process-plugin-server-path");
Arguments.push_back(Args.MakeArgString(inProcPluginServerPath));
// Default plugin path.
auto defaultPluginPath = pluginPath;
llvm::sys::path::append(defaultPluginPath, "host", "plugins");
Arguments.push_back("-plugin-path");
Arguments.push_back(Args.MakeArgString(defaultPluginPath));

View File

@@ -57,10 +57,16 @@ toolchains::GenericUnix::addPluginArguments(const ArgList &Args,
CompilerInvocation::computeRuntimeResourcePathFromExecutablePath(
programPath, /*shared=*/true, pluginPath);
auto defaultPluginPath = pluginPath;
llvm::sys::path::append(defaultPluginPath, "host", "plugins");
// In-process plugin server path.
auto inProcPluginServerPath = pluginPath;
llvm::sys::path::append(inProcPluginServerPath, "host",
"libSwiftInProcPluginServer.so");
Arguments.push_back("-in-process-plugin-server-path");
Arguments.push_back(Args.MakeArgString(inProcPluginServerPath));
// Default plugin path.
auto defaultPluginPath = pluginPath;
llvm::sys::path::append(defaultPluginPath, "host", "plugins");
Arguments.push_back("-plugin-path");
Arguments.push_back(Args.MakeArgString(defaultPluginPath));

View File

@@ -49,6 +49,13 @@ toolchains::Windows::addPluginArguments(const ArgList &Args,
SmallString<261> LibraryPath = StringRef(getDriver().getSwiftProgramPath());
llvm::sys::path::remove_filename(LibraryPath); // Remove `swift`
// In-process plugin server path.
SmallString<261> InProcPluginServerPath = LibraryPath;
llvm::sys::path::append(InProcPluginServerPath,
"SwiftInProcPluginServer.dll");
Arguments.push_back("-in-process-plugin-server-path");
Arguments.push_back(Args.MakeArgString(InProcPluginServerPath));
// Default plugin path.
Arguments.push_back("-plugin-path");
Arguments.push_back(Args.MakeArgString(LibraryPath));

View File

@@ -30,6 +30,17 @@ target_link_libraries(swiftDriverTool
PUBLIC
${driver_common_libs})
if (SWIFT_BUILD_SWIFT_SYNTAX)
target_link_libraries(swiftDriverTool
PRIVATE
swiftASTGen
)
add_dependencies(swiftDriverTool
swiftASTGen
)
endif()
# If building as part of clang, make sure the headers are installed.
if(NOT SWIFT_BUILT_STANDALONE)
add_dependencies(swiftDriverTool clang-resource-headers)

View File

@@ -37,4 +37,15 @@ target_link_libraries(swiftFrontend PRIVATE
swiftSerialization
swiftSymbolGraphGen)
if (SWIFT_BUILD_SWIFT_SYNTAX)
target_link_libraries(swiftFrontend
PRIVATE
swiftASTGen
)
add_dependencies(swiftFrontend
swiftASTGen
)
endif()
set_swift_llvm_is_available(swiftFrontend)

View File

@@ -1983,6 +1983,9 @@ static bool ParseSearchPathArgs(SearchPathOptions &Opts, ArgList &Args,
}
Opts.setFrameworkSearchPaths(FrameworkSearchPaths);
if (const Arg *A = Args.getLastArg(OPT_in_process_plugin_server_path))
Opts.InProcessPluginServerPath = A->getValue();
// All plugin search options, i.e. '-load-plugin-library',
// '-load-plugin-executable', '-plugin-path', and '-external-plugin-path'
// are grouped, and plugins are searched by the order of these options.

View File

@@ -29,26 +29,10 @@ target_link_libraries(swiftParse PRIVATE
if (SWIFT_BUILD_SWIFT_SYNTAX)
target_link_libraries(swiftParse
PRIVATE
SwiftBasicFormat
SwiftParser
SwiftParserDiagnostics
SwiftDiagnostics
SwiftSyntax
SwiftOperators
SwiftSyntaxBuilder
SwiftSyntaxMacros
swiftASTGen
)
add_dependencies(swiftParse
SwiftBasicFormat
SwiftParser
SwiftParserDiagnostics
SwiftDiagnostics
SwiftSyntax
SwiftOperators
SwiftSyntaxBuilder
SwiftSyntaxMacros
swiftASTGen
)
endif()

View File

@@ -48,38 +48,6 @@
using namespace swift;
#if SWIFT_BUILD_SWIFT_SYNTAX
/// Look for macro's type metadata given its external module and type name.
static void const *
lookupMacroTypeMetadataByExternalName(ASTContext &ctx, StringRef moduleName,
StringRef typeName,
LoadedLibraryPlugin *plugin) {
// Look up the type metadata accessor as a struct, enum, or class.
const Demangle::Node::Kind typeKinds[] = {
Demangle::Node::Kind::Structure,
Demangle::Node::Kind::Enum,
Demangle::Node::Kind::Class
};
void *accessorAddr = nullptr;
for (auto typeKind : typeKinds) {
auto symbolName = Demangle::mangledNameForTypeMetadataAccessor(
moduleName, typeName, typeKind);
accessorAddr = plugin->getAddressOfSymbol(symbolName.c_str());
if (accessorAddr)
break;
}
if (!accessorAddr)
return nullptr;
// Call the accessor to form type metadata.
using MetadataAccessFunc = const void *(MetadataRequest);
auto accessor = reinterpret_cast<MetadataAccessFunc*>(accessorAddr);
return accessor(MetadataRequest(MetadataState::Complete));
}
#endif
/// Translate an argument provided as a string literal into an identifier,
/// or return \c None and emit an error if it cannot be done.
std::optional<Identifier> getIdentifierFromStringLiteralArgument(
@@ -266,36 +234,31 @@ MacroDefinition MacroDefinitionRequest::evaluate(
#endif
}
static llvm::Expected<LoadedExecutablePlugin *>
initializeExecutablePlugin(ASTContext &ctx,
LoadedExecutablePlugin *executablePlugin,
StringRef libraryPath, Identifier moduleName) {
static llvm::Expected<CompilerPlugin *>
initializePlugin(ASTContext &ctx, CompilerPlugin *plugin, StringRef libraryPath,
Identifier moduleName) {
// Lock the plugin while initializing.
// Note that'executablePlugn' can be shared between multiple ASTContext.
executablePlugin->lock();
SWIFT_DEFER { executablePlugin->unlock(); };
plugin->lock();
SWIFT_DEFER { plugin->unlock(); };
// FIXME: Ideally this should be done right after invoking the plugin.
// But plugin loading is in libAST and it can't link ASTGen symbols.
if (!executablePlugin->isInitialized()) {
if (!plugin->isInitialized()) {
#if SWIFT_BUILD_SWIFT_SYNTAX
if (!swift_ASTGen_initializePlugin(executablePlugin, &ctx.Diags)) {
return llvm::createStringError(
llvm::inconvertibleErrorCode(), "'%s' produced malformed response",
executablePlugin->getExecutablePath().data());
if (!swift_ASTGen_initializePlugin(plugin, &ctx.Diags)) {
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"'%s' produced malformed response",
plugin->getPath().data());
}
// Resend the compiler capability on reconnect.
auto *callback = new std::function<void(void)>(
[executablePlugin]() {
(void)swift_ASTGen_initializePlugin(
executablePlugin, /*diags=*/nullptr);
});
executablePlugin->addOnReconnect(callback);
executablePlugin->setCleanup([executablePlugin] {
swift_ASTGen_deinitializePlugin(executablePlugin);
auto *callback = new std::function<void(void)>([plugin]() {
(void)swift_ASTGen_initializePlugin(plugin, /*diags=*/nullptr);
});
plugin->addOnReconnect(callback);
plugin->setCleanup([plugin] { swift_ASTGen_deinitializePlugin(plugin); });
#endif
}
@@ -315,7 +278,7 @@ initializeExecutablePlugin(ASTContext &ctx,
BridgedStringRef bridgedErrorOut{nullptr, 0};
bool loaded = swift_ASTGen_pluginServerLoadLibraryPlugin(
executablePlugin, resolvedLibraryPathStr.c_str(), moduleNameStr.c_str(),
plugin, resolvedLibraryPathStr.c_str(), moduleNameStr.c_str(),
&bridgedErrorOut);
auto errorOut = bridgedErrorOut.unbridged();
@@ -324,31 +287,30 @@ initializeExecutablePlugin(ASTContext &ctx,
return llvm::createStringError(
llvm::inconvertibleErrorCode(),
"failed to load library plugin '%s' in plugin server '%s'; %s",
resolvedLibraryPathStr.c_str(),
executablePlugin->getExecutablePath().data(), errorOut.data());
resolvedLibraryPathStr.c_str(), plugin->getPath().data(),
errorOut.data());
}
assert(errorOut.data() == nullptr);
// Set a callback to load the library again on reconnections.
auto *callback = new std::function<void(void)>(
[executablePlugin, resolvedLibraryPathStr, moduleNameStr]() {
[plugin, resolvedLibraryPathStr, moduleNameStr]() {
(void)swift_ASTGen_pluginServerLoadLibraryPlugin(
executablePlugin, resolvedLibraryPathStr.c_str(),
moduleNameStr.c_str(),
plugin, resolvedLibraryPathStr.c_str(), moduleNameStr.c_str(),
/*errorOut=*/nullptr);
});
executablePlugin->addOnReconnect(callback);
plugin->addOnReconnect(callback);
// Remove the callback and deallocate it when this ASTContext is destructed.
ctx.addCleanup([executablePlugin, callback]() {
executablePlugin->removeOnReconnect(callback);
ctx.addCleanup([plugin, callback]() {
plugin->removeOnReconnect(callback);
delete callback;
});
#endif
}
return executablePlugin;
return plugin;
}
CompilerPluginLoadResult
@@ -360,21 +322,21 @@ CompilerPluginLoadRequest::evaluate(Evaluator &evaluator, ASTContext *ctx,
SmallString<0> errorMessage;
if (!entry.executablePath.empty()) {
llvm::Expected<LoadedExecutablePlugin *> executablePlugin =
llvm::Expected<CompilerPlugin *> plugin =
loader.loadExecutablePlugin(entry.executablePath);
if (executablePlugin) {
if (plugin) {
if (ctx->LangOpts.EnableMacroLoadingRemarks) {
unsigned tag = entry.libraryPath.empty() ? 1 : 2;
ctx->Diags.diagnose(SourceLoc(), diag::macro_loaded, moduleName, tag,
entry.executablePath, entry.libraryPath);
}
executablePlugin = initializeExecutablePlugin(
*ctx, executablePlugin.get(), entry.libraryPath, moduleName);
plugin =
initializePlugin(*ctx, plugin.get(), entry.libraryPath, moduleName);
}
if (executablePlugin)
return executablePlugin.get();
llvm::handleAllErrors(executablePlugin.takeError(),
if (plugin)
return plugin.get();
llvm::handleAllErrors(plugin.takeError(),
[&](const llvm::ErrorInfoBase &err) {
if (!errorMessage.empty())
errorMessage += ", ";
@@ -382,17 +344,21 @@ CompilerPluginLoadRequest::evaluate(Evaluator &evaluator, ASTContext *ctx,
});
} else if (!entry.libraryPath.empty()) {
llvm::Expected<LoadedLibraryPlugin *> libraryPlugin =
loader.loadLibraryPlugin(entry.libraryPath);
if (libraryPlugin) {
if (ctx->LangOpts.EnableMacroLoadingRemarks) {
ctx->Diags.diagnose(SourceLoc(), diag::macro_loaded, moduleName, 0,
entry.libraryPath, StringRef());
llvm::Expected<CompilerPlugin *> plugins = loader.getInProcessPlugins();
if (plugins) {
plugins =
initializePlugin(*ctx, plugins.get(), entry.libraryPath, moduleName);
if (plugins) {
if (ctx->LangOpts.EnableMacroLoadingRemarks) {
ctx->Diags.diagnose(SourceLoc(), diag::macro_loaded, moduleName, 0,
entry.libraryPath, StringRef());
}
return plugins.get();
}
}
return libraryPlugin.get();
} else {
llvm::handleAllErrors(libraryPlugin.takeError(),
if (!plugins) {
llvm::handleAllErrors(plugins.takeError(),
[&](const llvm::ErrorInfoBase &err) {
if (!errorMessage.empty())
errorMessage += ", ";
@@ -410,63 +376,23 @@ CompilerPluginLoadRequest::evaluate(Evaluator &evaluator, ASTContext *ctx,
}
}
static ExternalMacroDefinition
resolveInProcessMacro(ASTContext &ctx, Identifier moduleName,
Identifier typeName, LoadedLibraryPlugin *plugin) {
static ExternalMacroDefinition resolveExternalMacro(ASTContext &ctx,
CompilerPlugin *plugin,
Identifier moduleName,
Identifier typeName) {
#if SWIFT_BUILD_SWIFT_SYNTAX
/// Look for the type metadata given the external module and type names.
auto macroMetatype = lookupMacroTypeMetadataByExternalName(
ctx, moduleName.str(), typeName.str(), plugin);
if (macroMetatype) {
// Check whether the macro metatype is in-process.
if (auto inProcess = swift_ASTGen_resolveMacroType(macroMetatype)) {
// Make sure we clean up after the macro.
ctx.addCleanup([inProcess]() {
swift_ASTGen_destroyMacro(inProcess);
});
return ExternalMacroDefinition{
ExternalMacroDefinition::PluginKind::InProcess, inProcess};
} else {
NullTerminatedStringRef err(
"'" + moduleName.str() + "." + typeName.str() +
"' is not a valid macro implementation type in library plugin '" +
StringRef(plugin->getLibraryPath()) + "'",
ctx);
return ExternalMacroDefinition::error(err);
}
}
NullTerminatedStringRef err("'" + moduleName.str() + "." + typeName.str() +
"' could not be found in library plugin '" +
StringRef(plugin->getLibraryPath()) + "'",
ctx);
return ExternalMacroDefinition::error(err);
#endif
return ExternalMacroDefinition::error(
"the current compiler was not built with macro support");
}
static ExternalMacroDefinition
resolveExecutableMacro(ASTContext &ctx,
LoadedExecutablePlugin *executablePlugin,
Identifier moduleName, Identifier typeName) {
#if SWIFT_BUILD_SWIFT_SYNTAX
if (auto *execMacro = swift_ASTGen_resolveExecutableMacro(
moduleName.get(), typeName.get(), executablePlugin)) {
if (auto *macro = swift_ASTGen_resolveExternalMacro(moduleName.get(),
typeName.get(), plugin)) {
// Make sure we clean up after the macro.
ctx.addCleanup(
[execMacro]() { swift_ASTGen_destroyExecutableMacro(execMacro); });
return ExternalMacroDefinition{
ExternalMacroDefinition::PluginKind::Executable, execMacro};
ctx.addCleanup([macro]() { swift_ASTGen_destroyExternalMacro(macro); });
return ExternalMacroDefinition::success(macro);
}
// NOTE: this is not reachable because executable macro resolution always
// succeeds.
NullTerminatedStringRef err(
"'" + moduleName.str() + "." + typeName.str() +
"' could not be found in executable plugin" +
StringRef(executablePlugin->getExecutablePath()),
ctx);
NullTerminatedStringRef err("'" + moduleName.str() + "." + typeName.str() +
"' could not be found in executable plugin" +
StringRef(plugin->getPath()),
ctx);
return ExternalMacroDefinition::error(err);
#endif
return ExternalMacroDefinition::error(
@@ -483,12 +409,8 @@ ExternalMacroDefinitionRequest::evaluate(Evaluator &evaluator, ASTContext *ctx,
CompilerPluginLoadResult loaded = evaluateOrDefault(
evaluator, loadRequest, CompilerPluginLoadResult::error("request error"));
if (auto loadedLibrary = loaded.getAsLibraryPlugin()) {
return resolveInProcessMacro(*ctx, moduleName, typeName, loadedLibrary);
}
if (auto *executablePlugin = loaded.getAsExecutablePlugin()) {
return resolveExecutableMacro(*ctx, executablePlugin, moduleName, typeName);
if (auto plugin = loaded.get()) {
return resolveExternalMacro(*ctx, plugin, moduleName, typeName);
}
return ExternalMacroDefinition::error(loaded.getErrorMessage());
@@ -1222,8 +1144,7 @@ evaluateFreestandingMacro(FreestandingMacroExpansion *expansion,
BridgedStringRef evaluatedSourceOut{nullptr, 0};
assert(!externalDef.isError());
swift_ASTGen_expandFreestandingMacro(
&ctx.Diags, externalDef.opaqueHandle,
static_cast<uint32_t>(externalDef.kind), discriminator->c_str(),
&ctx.Diags, externalDef.get(), discriminator->c_str(),
getRawMacroRole(macroRole), astGenSourceFile,
expansion->getSourceRange().Start.getOpaquePointerValue(),
&evaluatedSourceOut);
@@ -1547,8 +1468,7 @@ static SourceFile *evaluateAttachedMacro(MacroDecl *macro, Decl *attachedTo,
BridgedStringRef evaluatedSourceOut{nullptr, 0};
assert(!externalDef.isError());
swift_ASTGen_expandAttachedMacro(
&ctx.Diags, externalDef.opaqueHandle,
static_cast<uint32_t>(externalDef.kind), discriminator->c_str(),
&ctx.Diags, externalDef.get(), discriminator->c_str(),
extendedType.c_str(), conformanceList.c_str(), getRawMacroRole(role),
astGenAttrSourceFile, attr->AtLoc.getOpaquePointerValue(),
astGenDeclSourceFile, searchDecl->getStartLoc().getOpaquePointerValue(),

View File

@@ -629,8 +629,8 @@ struct HasNestedType {
#if TEST_DIAGNOSTICS
@freestanding(expression)
macro missingMacro() = #externalMacro(module: "MacroDefinition", type: "BluhBlah")
// expected-warning@-1 {{external macro implementation type 'MacroDefinition.BluhBlah' could not be found for macro 'missingMacro()'; 'MacroDefinition.BluhBlah' could not be found in library plugin '}}
// FIXME: xpected-warning@-1 {{external macro implementation type 'MacroDefinition.BluhBlah' could not be found for macro 'missingMacro()'; 'MacroDefinition.BluhBlah' could not be found in library plugin '}}
@freestanding(expression)
macro notMacro() = #externalMacro(module: "MacroDefinition", type: "NotMacroStruct")
// expected-warning@-1 {{macro implementation type 'MacroDefinition.NotMacroStruct' could not be found for macro 'notMacro()'; 'MacroDefinition.NotMacroStruct' is not a valid macro implementation type in library plugin '}}
// FIXME: xpected-warning@-1 {{macro implementation type 'MacroDefinition.NotMacroStruct' could not be found for macro 'notMacro()'; 'MacroDefinition.NotMacroStruct' is not a valid macro implementation type in library plugin '}}
#endif

View File

@@ -34,8 +34,8 @@
// RUN: c-index-test -read-diagnostics %t/macro_expand_inproc.dia 2>&1 | %FileCheck -check-prefix INPROC %s
// INPROC-NOT: {{error|warning}}
// INPROC: test.swift:1:33: warning: external macro implementation type 'TestPlugin.FooMacro' could not be found for macro 'fooMacro'; compiler plugin 'BUILD_DIR/{{.*}}/libTestPlugin.dylib' could not be loaded; dlopen(BUILD_DIR/{{.*}}/libTestPlugin.dylib, 0x0005): tried: 'BUILD_DIR/{{.*}}/libTestPlugin.dylib'
// INPROC: test.swift:4:7: error: external macro implementation type 'TestPlugin.FooMacro' could not be found for macro 'fooMacro'; compiler plugin 'BUILD_DIR/{{.*}}/libTestPlugin.dylib' could not be loaded; dlopen(BUILD_DIR/{{.*}}/libTestPlugin.dylib, 0x0005): tried: 'BUILD_DIR/{{.*}}/libTestPlugin.dylib'
// INPROC: test.swift:1:33: warning: external macro implementation type 'TestPlugin.FooMacro' could not be found for macro 'fooMacro'; failed to load library plugin 'BUILD_DIR/{{.*}}/libTestPlugin.dylib' in plugin server 'BUILD_DIR/{{.*}}/libSwiftInProcPluginServer.dylib'; loader error: dlopen(BUILD_DIR/{{.*}}/libTestPlugin.dylib, 0x0005): tried: 'BUILD_DIR/{{.*}}/libTestPlugin.dylib'
// INPROC: test.swift:4:7: error: external macro implementation type 'TestPlugin.FooMacro' could not be found for macro 'fooMacro'; failed to load library plugin 'BUILD_DIR/{{.*}}/libTestPlugin.dylib' in plugin server 'BUILD_DIR/{{.*}}/libSwiftInProcPluginServer.dylib'; loader error: dlopen(BUILD_DIR/{{.*}}/libTestPlugin.dylib, 0x0005): tried: 'BUILD_DIR/{{.*}}/libTestPlugin.dylib'
// INPROC: test.swift:1:33: note: 'fooMacro' declared here
// INPROC-NOT: {{error|warning}}

View File

@@ -35,7 +35,7 @@ func test() {
// FIXME: -module-abi-name ABI name is leaking.
let _: String = #fooMacro(1)
// expected-error @-1 {{typeMismatch(CompilerSwiftCompilerPluginMessageHandling.PluginToHostMessage}}
// expected-error @-1 {{typeMismatch(_CompilerSwiftCompilerPluginMessageHandling.PluginToHostMessage}}
let _: String = #fooMacro(2)
// expected-error @-1 {{failed to receive result from plugin (from macro 'fooMacro')}}
let _: String = #fooMacro(3)

View File

@@ -1272,6 +1272,9 @@ if run_vendor == 'apple':
config.resource_dir_opt = "-resource-dir %s" % new_resource_dir
lit_config.note('Using freestanding resource dir: ' + new_resource_dir)
config.swift_in_process_plugin_server_path = make_path(config.swift_lib_dir, 'swift', 'host', 'libSwiftInProcPluginServer.dylib')
config.swift_test_options += " -in-process-plugin-server-path %s" % shell_quote(config.swift_in_process_plugin_server_path)
# Auto-linking does not work when stdlib is built with LTO, because linked
# libraries are discovered too late (after optimizations are applied), and
# ld64 hits an assert and crashes, or worse, deadlocks. Until ld64 fixes
@@ -1622,6 +1625,9 @@ elif run_os in ['windows-msvc']:
config.target_msvc_runtime_opt += ' -D_DLL'
config.target_env_prefix = ''
config.swift_in_process_plugin_server_path = make_path(config.swift_bin_dir, 'SwiftInProcPluginServer.dll')
config.swift_test_options += " -in-process-plugin-server-path %s" % shell_quote(config.swift_in_process_plugin_server_path)
config.target_build_swift = \
('%r -target %s %s %s %s %s %s -libc %s' % \
(config.swiftc, config.variant_triple, \
@@ -1753,6 +1759,9 @@ elif (run_os in ['linux-gnu', 'linux-gnueabihf', 'freebsd', 'openbsd', 'windows-
config.target_runtime = "native"
config.target_swift_autolink_extract = inferSwiftBinary("swift-autolink-extract")
config.swift_in_process_plugin_server_path = make_path(config.swift_lib_dir, 'swift', 'host', 'libSwiftInProcPluginServer.so')
config.swift_test_options += " -in-process-plugin-server-path %s" % shell_quote(config.swift_in_process_plugin_server_path)
libdispatch_artifact_dir = config.libdispatch_build_path
libdispatch_swift_module_dir = make_path(libdispatch_artifact_dir, 'src', 'swift', 'swift')
libdispatch_source_dir = make_path(config.swift_src_root, os.pardir, 'swift-corelibs-libdispatch')
@@ -2717,14 +2726,14 @@ config.substitutions.append(('%target-sil-nm', config.target_sil_nm))
config.substitutions.append(('%batch-code-completion', '%empty-directory(%t/batch-code-completion) && %target-swift-ide-test -batch-code-completion -source-filename %s -filecheck %raw-FileCheck -completion-output-dir %t/batch-code-completion'))
config.substitutions.append(('%target-swift-ide-test\(mock-sdk:([^)]+)\)',
SubstituteCaptures(r'%s \1 %s -swift-version %s' % (
SubstituteCaptures(r'%s \1 %s %s' % (
escape_for_substitute_captures(subst_target_swift_ide_test_mock_sdk),
escape_for_substitute_captures(subst_target_swift_ide_test_mock_sdk_after),
escape_for_substitute_captures(swift_version)))))
escape_for_substitute_captures(config.swift_test_options)))))
config.substitutions.append(('%target-swift-ide-test',
"%s -swift-version %s %s" % (config.target_swift_ide_test,
swift_version,
config.clang_system_overlay_opt)))
"%s %s %s" % (config.target_swift_ide_test,
config.swift_test_options,
config.clang_system_overlay_opt)))
config.substitutions.append(('%target-swift-symbolgraph-extract', config.target_swift_symbolgraph_extract))
config.substitutions.append(('%target-swift-api-extract', config.target_swift_api_extract))

View File

@@ -156,11 +156,11 @@ function(add_sourcekit_swift_runtime_link_flags target path HAS_SWIFT_MODULES)
if(SWIFT_BUILD_SWIFT_SYNTAX)
if(SWIFT_HOST_VARIANT_SDK IN_LIST SWIFT_DARWIN_PLATFORMS)
# Add rpath to the host Swift libraries.
file(RELATIVE_PATH relative_hostlib_path "${path}" "${SWIFTLIB_DIR}/host")
file(RELATIVE_PATH relative_hostlib_path "${path}" "${SWIFTLIB_DIR}/host/compiler")
list(APPEND RPATH_LIST "@loader_path/${relative_hostlib_path}")
elseif(SWIFT_HOST_VARIANT_SDK MATCHES "LINUX|ANDROID|OPENBSD")
# Add rpath to the host Swift libraries.
file(RELATIVE_PATH relative_hostlib_path "${path}" "${SWIFTLIB_DIR}/host")
file(RELATIVE_PATH relative_hostlib_path "${path}" "${SWIFTLIB_DIR}/host/compiler")
list(APPEND RPATH_LIST "$ORIGIN/${relative_hostlib_path}")
else()
target_link_directories(${target} PRIVATE

View File

@@ -12,6 +12,7 @@ function(add_swift_parser_link_libraries target)
foreach(macrolib ${SWIFT_MACRO_PLUGINS})
add_dependencies(${target} ${macrolib})
endforeach()
add_dependencies(${target} SwiftInProcPluginServer)
endif()
endfunction()

View File

@@ -34,7 +34,7 @@ set_target_properties(libSwiftScan
if(SWIFT_HOST_VARIANT_SDK MATCHES "LINUX|ANDROID|OPENBSD|FREEBSD" AND BOOTSTRAPPING_MODE STREQUAL "HOSTTOOLS")
# Runtime INSTALL_RPATH are set by 'add_swift_host_library', but that expects
# libSwiftScan be installed in 'lib'. But since it's actually installed in 'lib/swift/host',
# we need to have correct runtime path to 'lib/swift/{platform}'.
# we need to have correct swift runtime path to 'lib/swift/{platform}'.
# FIXME: BUILD_RPATH and INSTALL_PATH should be different
# FIXME: add_swift_host_library should accept 'DESTINATION' and handle installation
# FIXME: Build this library into 'lib/swift/host/' instead of 'lib/'
@@ -46,20 +46,20 @@ endif()
if(SWIFT_BUILD_SWIFT_SYNTAX)
if(SWIFT_HOST_VARIANT_SDK IN_LIST SWIFT_DARWIN_PLATFORMS)
# Ensure that we can find the host shared libraries.
# Ensure that we can find the shared swift-syntax libraries.
set_property(
TARGET libSwiftScan
APPEND PROPERTY INSTALL_RPATH "@loader_path/swift/host")
APPEND PROPERTY INSTALL_RPATH "@loader_path/swift/host/compiler")
set_property(
TARGET libSwiftScan
APPEND PROPERTY INSTALL_RPATH "@loader_path/../host")
APPEND PROPERTY INSTALL_RPATH "@loader_path/../host/compiler")
elseif(SWIFT_HOST_VARIANT_SDK MATCHES "LINUX|ANDROID|OPENBSD|FREEBSD")
set_property(
TARGET libSwiftScan
APPEND PROPERTY INSTALL_RPATH "$ORIGIN/swift/host")
APPEND PROPERTY INSTALL_RPATH "$ORIGIN/swift/host/compiler")
set_property(
TARGET libSwiftScan
APPEND PROPERTY INSTALL_RPATH "$ORIGIN/../host")
APPEND PROPERTY INSTALL_RPATH "$ORIGIN/../host/compiler")
endif()
endif()

View File

@@ -332,6 +332,11 @@ ImportObjCHeader("import-objc-header",
llvm::cl::desc("header to implicitly import"),
llvm::cl::cat(Category));
static llvm::cl::opt<std::string>
InProcessPluginServerPath("in-process-plugin-server-path",
llvm::cl::desc("in-process plugin server"),
llvm::cl::cat(Category));
static llvm::cl::list<std::string>
PluginPath("plugin-path",
llvm::cl::desc("plugin-path"),
@@ -4541,6 +4546,10 @@ int main(int argc, char *argv[]) {
}
}
if (!options::InProcessPluginServerPath.empty()) {
InitInvok.getSearchPathOptions().InProcessPluginServerPath =
options::InProcessPluginServerPath;
}
if (!options::LoadPluginLibrary.empty()) {
std::vector<std::string> paths;
for (auto path: options::LoadPluginLibrary) {

View File

@@ -7,4 +7,32 @@ if (SWIFT_BUILD_SWIFT_SYNTAX)
SwiftCompilerPluginMessageHandling
SwiftLibraryPluginProvider
)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${SWIFT_HOST_LIBRARIES_DEST_DIR}")
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${SWIFT_HOST_LIBRARIES_DEST_DIR}")
add_pure_swift_host_library(SwiftInProcPluginServer SHARED
Sources/SwiftInProcPluginServer/InProcPluginServer.swift
SWIFT_DEPENDENCIES
SwiftCompilerPluginMessageHandling
SwiftLibraryPluginProvider
)
if(SWIFT_HOST_VARIANT_SDK IN_LIST SWIFT_DARWIN_PLATFORMS)
set_property(TARGET SwiftInProcPluginServer
APPEND PROPERTY INSTALL_RPATH
"@loader_path")
elseif(SWIFT_HOST_VARIANT_SDK MATCHES "LINUX|ANDROID|OPENBSD|FREEBSD")
set_property(TARGET SwiftInProcPluginServer
APPEND PROPERTY INSTALL_RPATH
"$ORIGIN")
endif()
set_property(TARGET ${name}
PROPERTY BUILD_WITH_INSTALL_RPATH YES)
add_dependencies(compiler SwiftInProcPluginServer)
swift_install_in_component(TARGETS SwiftInProcPluginServer
ARCHIVE DESTINATION "lib${LLVM_LIBDIR_SUFFIX}/swift/host" COMPONENT compiler
LIBRARY DESTINATION "lib${LLVM_LIBDIR_SUFFIX}/swift/host" COMPONENT compiler
RUNTIME DESTINATION "bin" COMPONENT compiler)
endif()

View File

@@ -7,6 +7,10 @@ let package = Package(
platforms: [
.macOS(.v10_15)
],
products: [
.executable(name: "swift-plugin-server", targets: ["swift-plugin-server"]),
.library(name: "SwiftInProcPluginServer", type: .dynamic, targets: ["SwiftInProcPluginServer"]),
],
dependencies: [
.package(path: "../../../swift-syntax"),
],
@@ -15,6 +19,14 @@ let package = Package(
name: "swift-plugin-server",
dependencies: [
.product(name: "SwiftCompilerPluginMessageHandling", package: "swift-syntax"),
.product(name: "SwiftLibraryPluginProvider", package: "swift-syntax"),
]
),
.target(
name: "SwiftInProcPluginServer",
dependencies: [
.product(name: "SwiftCompilerPluginMessageHandling", package: "swift-syntax"),
.product(name: "SwiftLibraryPluginProvider", package: "swift-syntax"),
]
),
],

View File

@@ -0,0 +1,88 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift open source project
//
// Copyright (c) 2024 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 the list of Swift project authors
//
//===----------------------------------------------------------------------===//
@_spi(PluginMessage) import SwiftCompilerPluginMessageHandling
@_spi(PluginMessage) import SwiftLibraryPluginProvider
#if canImport(Darwin)
import Darwin
#elseif canImport(Glibc)
import Glibc
#elseif canImport(Musl)
import Musl
#elseif canImport(ucrt)
import ucrt
#else
#error("'malloc' not found")
#endif
/// Entry point.
///
/// Compiler 'dlopen' this 'SwiftInProcPluginServer' library, and 'dlsym' this
/// function. When the compiler wants to use dylib plugins, it calls this
/// function with the same message as `swift-plugin-server`.
///
/// The caller must `free` the returned buffer
@_cdecl("swift_inproc_plugins_handle_message")
@MainActor
public func handleMessage(
_ inputData: UnsafePointer<UInt8>!,
_ inputLength: Int,
_ outputData: UnsafeMutablePointer<UnsafeMutablePointer<UInt8>?>!,
_ outputLength: UnsafeMutablePointer<Int>!
) -> Bool {
do {
let input = UnsafeBufferPointer(start: inputData, count: inputLength)
let output = try InProcPluginServer.shared.handleMessage(input)
output.withUnsafeBufferPointer(fillOutput(_:))
return false // Success.
} catch {
var message = "Internal Error: \(error)"
message.withUTF8(fillOutput(_:))
return true // Error.
}
func fillOutput(_ responseData: UnsafeBufferPointer<UInt8>) {
// NOTE: Use 'malloc' instead of 'UnsafeMutablePointer.allocate()' so that
// C/C++ clients can deallocate it without using Swift.
let buffer = malloc(responseData.count)!
buffer.initializeMemory(
as: UInt8.self,
from: responseData.baseAddress!,
count: responseData.count
)
outputData.pointee = buffer.assumingMemoryBound(to: UInt8.self)
outputLength.pointee = responseData.count
}
}
/// Singleton "plugin server".
struct InProcPluginServer {
private let handler: CompilerPluginMessageHandler<LibraryPluginProvider>
@MainActor
private init() {
self.handler = CompilerPluginMessageHandler(
provider: LibraryPluginProvider.shared
)
}
func handleMessage(_ input: UnsafeBufferPointer<UInt8>) throws -> [UInt8] {
let request = try JSON.decode(HostToPluginMessage.self, from: input)
let response = handler.handleMessage(request)
return try JSON.encode(response)
}
@MainActor
static let shared = Self()
}