-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[Macros] Add executable plugin support #63793
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@swift-ci Please smoke test |
e5e8e3e
to
c90931e
Compare
@swift-ci Please smoke test |
c90931e
to
97bfd88
Compare
@swift-ci Please smoke test |
97bfd88
to
b940a47
Compare
@swift-ci Please smoke test |
9040212
to
6fefff5
Compare
@swift-ci Please smoke test |
6fefff5
to
43f3364
Compare
@swift-ci Please Python lint |
43f3364
to
dd256f2
Compare
@swift-ci Please test |
Executable compiler plugins are programs invoked by the host compiler and communicate with the host with IPC via standard IO (stdin/stdout.) Each message is serialized in JSON, prefixed with a header which is a 64bit little-endian integer indicating the size of the message. * Basic/ExecuteWithPipe: External program invocation. Lik llvm::sys::ExecuteNoWait() but establishing pipes to the child's stdin/stdout * Basic/Sandbox: Sandboxed execution helper. Create command line arguments to be executed in sandbox environment (similar to SwiftPM's pluging sandbox) * AST/PluginRepository: ASTContext independent plugin manager * ASTGen/PluginHost: Communication with the plugin. Messages are serialized by ASTGen/LLVMJSON rdar://101508815
dd256f2
to
0e31393
Compare
@swift-ci Please test |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very cool!
include/swift/AST/CASTBridging.h
Outdated
/// pointer, it's not used in AST at all. | ||
void Plugin_setCapability(void *handle, const void *data); | ||
|
||
/// Get a coapability data set by \c Plugin_setCapability . |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// Get a coapability data set by \c Plugin_setCapability . | |
/// Get a capability data set by \c Plugin_setCapability . |
include/swift/AST/CASTBridging.h
Outdated
|
||
/// Set a capability data to the plugin object. Since the data is just a opaque | ||
/// pointer, it's not used in AST at all. | ||
void Plugin_setCapability(void *handle, const void *data); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thoughts on adding typedefs for the various void*
? I know they're all still void*
regardless, it just makes it easier when reading through the API (IMO).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree. done.
lib/Basic/Program.cpp
Outdated
|
||
// Execute the program. | ||
if (env.has_value()) { | ||
const char **envp = toNullTerminatedCStringArray(*env, Alloc); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We already have envp
above, assuming I'm reading the #if's correctly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks. I added "has posix_spawn
" branch after fork
implementation. This was a leftover.
include/swift/Basic/Program.h
Outdated
/// \param env An optional array of strings to use for the program's | ||
/// environment. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe worth saying it's an array of "key=value" strings?
lib/AST/ASTContext.cpp
Outdated
|
||
/// Cache of loaded symbols. | ||
llvm::StringMap<void *> LoadedSymbols; | ||
|
||
/// Map a module name to an executable plugin path that provides the module. | ||
llvm::DenseMap<Identifier, std::string> ExecutablePluginPaths; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could shove the paths into the ASTContext's allocator instead since we never remove them. Doesn't really matter, but would mean our size calculations are a little more accurate. (see AllocateCopy
)
lib/Sema/TypeCheckMacros.cpp
Outdated
} | ||
|
||
static Optional<ExternalMacroDefinition> | ||
resolvExecutableMacro(ASTContext &ctx, Identifier moduleName, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resolvExecutableMacro(ASTContext &ctx, Identifier moduleName, | |
resolveExecutableMacro(ASTContext &ctx, Identifier moduleName, |
lib/Sema/TypeCheckMacros.cpp
Outdated
}); | ||
} | ||
|
||
if (auto execMacro = swift_ASTGen_resolveExecutableMacro( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's nice to keep the *
with auto
to make it obvious IMO, up to you though.
utils/mock_plugin.py
Outdated
# respond. For example: | ||
# {'expected': {'testRequest': {'params': []}}, | ||
# 'response': {'testRequestResult': [1,2,"foo"]}} | ||
# this spec matches a request that is an object with 'testRequst' key with an |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
# this spec matches a request that is an object with 'testRequst' key with an | |
# this spec matches a request that is an object with 'testRequest' key with an |
moduleNameLength: Int, | ||
typeName: UnsafePointer<UInt8>, | ||
typeNameLength: Int, | ||
pluginOpaqueHanle: UnsafeMutableRawPointer |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pluginOpaqueHanle: UnsafeMutableRawPointer | |
pluginOpaqueHandle: UnsafeMutableRawPointer |
let discriminator = String(decoding: discriminatorBuffer, as: UTF8.self) | ||
|
||
let expandedSources: [String]? | ||
switch MacroPluginKind(rawValue: macroKind)! { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Possibly over engineering here, but we could make a protocol with the two expand functions instead. They could be static or actual methods, where MacroPluginKind
has an expander
that returns the type or a new struct (depending on whether you choose static methods or not):
enum MacroPluginKind {
...
var expander: some MacroExpander {
switch ...
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a good idea. Let me do it in a follow-ups because it changes the indentation of the current code, which makes the change set large.
@swift-ci Please test |
@swift-ci Please test |
lib/AST/PluginRegistry.cpp
Outdated
} | ||
#else | ||
lib = dlopen(path.str().c_str(), RTLD_LAZY | RTLD_LOCAL); | ||
if (!lib) { | ||
return llvm::createStringError(std::errc::not_supported, | ||
"unsupported platform"); | ||
return llvm::createStringError(std::error_code(), dlerror()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If there's no suitable errc
here it's probably better to use inconvertibleErrorCode
lib/AST/CASTBridging.cpp
Outdated
@@ -651,14 +651,19 @@ void Plugin_unlock(PluginHandle handle) { | |||
bool Plugin_sendMessage(PluginHandle handle, const BridgedData data) { | |||
auto *plugin = static_cast<LoadedExecutablePlugin *>(handle); | |||
StringRef message(data.baseAddress, data.size); | |||
return plugin->sendMessage(message); | |||
return bool(plugin->sendMessage(message)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm fairly sure this will still fail when there's an error. LLVM has a consumeError
to just eat it, though that makes me wonder if we should be passing in an error string to set.
@swift-ci Please smoke test |
Executable compiler plugins are programs invoked by the host compiler and communicate with the host with IPC via standard IO (stdin/stdout.) Each message is serialized in JSON, prefixed with a header which is a 64bit little-endian integer indicating the size of the message.
Basic/ExecuteWithPipe
: External program invocation. Likellvm::sys::ExecuteNoWait()
but establishing pipes to the child's stdin/stdoutBasic/Sandbox
: Sandboxed execution helper. Mutates command line arguments to be executed in sandbox environment (similar to SwiftPM's plugin sandbox)AST/PluginRepository
: ASTContext independent plugin manager.ASTGen/PluginHost
: Communication with the plugin. Messages are serialized byASTGen/LLVMJSON
added in [ASTGen] Add Encoder/Decoder implementation backed by llvm::json::Value #63689This PR implements 3 messages (
ASTGen/PluginMessages
).getCapability
: get plugin capability. currently it only returns fixedprotocolVersion
1.expandFreestandingMacro
: expand freestanding macrosexpandAttachedMacro
: expand attached macrosrdar://101508815