mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[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:
@@ -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'.
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -84,8 +84,6 @@ namespace swift {
|
||||
class DifferentiableAttr;
|
||||
class ExtensionDecl;
|
||||
struct ExternalSourceLocs;
|
||||
class LoadedExecutablePlugin;
|
||||
class LoadedLibraryPlugin;
|
||||
class ForeignRepresentationInfo;
|
||||
class FuncDecl;
|
||||
class GenericContext;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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]>,
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
],
|
||||
|
||||
@@ -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 })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
52
lib/CompilerSwiftSyntax/CMakeLists.txt
Normal file
52
lib/CompilerSwiftSyntax/CMakeLists.txt
Normal 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)
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
19
test/lit.cfg
19
test/lit.cfg
@@ -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))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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"),
|
||||
]
|
||||
),
|
||||
],
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user