Skip to content

Commit 0e31393

Browse files
committed
[Macros] Add executable plugin support
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
1 parent 75f53a9 commit 0e31393

26 files changed

+1812
-180
lines changed

include/swift/AST/ASTContext.h

+12
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ namespace swift {
7979
class DifferentiableAttr;
8080
class ExtensionDecl;
8181
struct ExternalSourceLocs;
82+
class LoadedExecutablePlugin;
8283
class ForeignRepresentationInfo;
8384
class FuncDecl;
8485
class GenericContext;
@@ -91,6 +92,7 @@ namespace swift {
9192
class ModuleDependencyInfo;
9293
class PatternBindingDecl;
9394
class PatternBindingInitializer;
95+
class PluginRegistry;
9496
class SourceFile;
9597
class SourceLoc;
9698
class Type;
@@ -1458,6 +1460,16 @@ class ASTContext final {
14581460

14591461
Type getNamedSwiftType(ModuleDecl *module, StringRef name);
14601462

1463+
LoadedExecutablePlugin *
1464+
lookupExecutablePluginByModuleName(Identifier moduleName);
1465+
1466+
/// Get the plugin registry this ASTContext is using.
1467+
PluginRegistry *getPluginRegistry() const;
1468+
1469+
/// Set the plugin registory this ASTContext should use.
1470+
/// This should be called before any plugin is loaded.
1471+
void setPluginRegistry(PluginRegistry *newValue);
1472+
14611473
private:
14621474
friend Decl;
14631475

include/swift/AST/CASTBridging.h

+30
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,13 @@
1313
#ifndef SWIFT_C_AST_ASTBRIDGING_H
1414
#define SWIFT_C_AST_ASTBRIDGING_H
1515

16+
#include "swift/Basic/CBasicBridging.h"
1617
#include "swift/Basic/Compiler.h"
18+
1719
#include <inttypes.h>
20+
#include <stdbool.h>
21+
#include <stddef.h>
22+
#include <stdint.h>
1823

1924
#if __clang__
2025
// Provide macros to temporarily suppress warning about the use of
@@ -280,6 +285,31 @@ void Decl_dump(void *);
280285
void Stmt_dump(void *);
281286
void Type_dump(void *);
282287

288+
//===----------------------------------------------------------------------===//
289+
// Plugins
290+
//===----------------------------------------------------------------------===//
291+
292+
/// Set a capability data to the plugin object. Since the data is just a opaque
293+
/// pointer, it's not used in AST at all.
294+
void Plugin_setCapability(void *handle, const void *data);
295+
296+
/// Get a coapability data set by \c Plugin_setCapability .
297+
const void *_Nullable Plugin_getCapability(void *handle);
298+
299+
/// Lock the plugin. Clients should lock it during sending and recving the
300+
/// response.
301+
void Plugin_lock(void *handle);
302+
303+
/// Unlock the plugin.
304+
void Plugin_unlock(void *handle);
305+
306+
/// Sends the message to the plugin, returns true if there was an error.
307+
/// Clients should receive the response by \c Plugin_waitForNextMessage .
308+
_Bool Plugin_sendMessage(void *handle, const BridgedData data);
309+
310+
/// Receive a message from the plugin.
311+
_Bool Plugin_waitForNextMessage(void *handle, BridgedData *data);
312+
283313
#ifdef __cplusplus
284314
}
285315
#endif

include/swift/AST/MacroDefinition.h

+5
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ namespace swift {
2424

2525
/// A reference to an external macro definition that is understood by ASTGen.
2626
struct ExternalMacroDefinition {
27+
enum class PluginKind {
28+
InProcess = 0,
29+
Executable = 1,
30+
};
31+
PluginKind kind;
2732
/// ASTGen's notion of an macro definition, which is opaque to the C++ part
2833
/// of the compiler.
2934
void *opaqueHandle = nullptr;

include/swift/AST/PluginRegistry.h

+89
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
//===--- PluginRegistry.h ---------------------------------------*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2023 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "llvm/ADT/ArrayRef.h"
14+
#include "llvm/ADT/StringMap.h"
15+
#include "llvm/ADT/StringRef.h"
16+
#include "llvm/Support/Chrono.h"
17+
#include "llvm/Support/Program.h"
18+
19+
#include <mutex>
20+
#include <vector>
21+
22+
namespace swift {
23+
24+
class LoadedExecutablePlugin {
25+
const llvm::sys::procid_t pid;
26+
const llvm::sys::TimePoint<> LastModificationTime;
27+
const int inputFileDescriptor;
28+
const int outputFileDescriptor;
29+
30+
/// Opaque value of the protocol capability of the pluugin. This is a
31+
/// value from ASTGen.
32+
const void *capability = nullptr;
33+
34+
/// Cleanup function to call ASTGen.
35+
std::function<void(void)> cleanup;
36+
37+
std::mutex mtx;
38+
39+
ssize_t write(const void *buf, size_t nbyte) const;
40+
ssize_t read(void *buf, size_t nbyte) const;
41+
42+
public:
43+
LoadedExecutablePlugin(llvm::sys::procid_t pid,
44+
llvm::sys::TimePoint<> LastModificationTime,
45+
int inputFileDescriptor, int outputFileDescriptor);
46+
~LoadedExecutablePlugin();
47+
llvm::sys::TimePoint<> getLastModificationTime() const {
48+
return LastModificationTime;
49+
}
50+
51+
void lock() { mtx.lock(); }
52+
void unlock() { mtx.unlock(); }
53+
54+
/// Send a message to the plugin.
55+
bool sendMessage(llvm::StringRef message) const;
56+
57+
/// Wait for a message from plugin and returns it.
58+
std::string waitForNextMessage() const;
59+
60+
bool isInitialized() const { return bool(cleanup); }
61+
void setCleanup(std::function<void(void)> cleanup) {
62+
this->cleanup = cleanup;
63+
}
64+
65+
llvm::sys::procid_t getPid() { return pid; }
66+
67+
const void *getCapability() { return capability; };
68+
void setCapability(const void *newValue) { capability = newValue; };
69+
};
70+
71+
class PluginRegistry {
72+
/// Record of loaded plugin library modules.
73+
llvm::StringMap<void *> LoadedPluginLibraries;
74+
75+
/// Record of loaded plugin executables.
76+
llvm::StringMap<std::unique_ptr<LoadedExecutablePlugin>>
77+
LoadedPluginExecutables;
78+
79+
public:
80+
bool loadLibraryPlugin(llvm::StringRef path, const char *&errorMsg);
81+
LoadedExecutablePlugin *loadExecutablePlugin(llvm::StringRef path,
82+
const char *&errorMsg);
83+
84+
const llvm::StringMap<void *> &getLoadedLibraryPlugins() const {
85+
return LoadedPluginLibraries;
86+
}
87+
};
88+
89+
} // namespace swift

include/swift/AST/TypeCheckRequests.h

+4-4
Original file line numberDiff line numberDiff line change
@@ -3931,7 +3931,7 @@ class ExpandPeerMacroRequest
39313931
/// Resolve an external macro given its module and type name.
39323932
class ExternalMacroDefinitionRequest
39333933
: public SimpleRequest<ExternalMacroDefinitionRequest,
3934-
ExternalMacroDefinition(
3934+
llvm::Optional<ExternalMacroDefinition>(
39353935
ASTContext *, Identifier, Identifier),
39363936
RequestFlags::Cached> {
39373937
public:
@@ -3940,9 +3940,9 @@ class ExternalMacroDefinitionRequest
39403940
private:
39413941
friend SimpleRequest;
39423942

3943-
ExternalMacroDefinition evaluate(
3944-
Evaluator &evaluator, ASTContext *ctx, Identifier moduleName,
3945-
Identifier typeName
3943+
llvm::Optional<ExternalMacroDefinition> evaluate(
3944+
Evaluator &evaluator, ASTContext *ctx,
3945+
Identifier moduleName, Identifier typeName
39463946
) const;
39473947

39483948
public:

include/swift/AST/TypeCheckerTypeIDZone.def

+1-1
Original file line numberDiff line numberDiff line change
@@ -426,7 +426,7 @@ SWIFT_REQUEST(TypeChecker, CompilerPluginLoadRequest,
426426
void *(ASTContext *, Identifier),
427427
Cached, NoLocationInfo)
428428
SWIFT_REQUEST(TypeChecker, ExternalMacroDefinitionRequest,
429-
ExternalMacroDefinition(ASTContext *, Identifier, Identifier),
429+
Optional<ExternalMacroDefinition>(ASTContext *, Identifier, Identifier),
430430
Cached, NoLocationInfo)
431431
SWIFT_REQUEST(TypeChecker, ExpandMacroExpansionDeclRequest,
432432
ArrayRef<Decl *>(MacroExpansionDecl *),

include/swift/Basic/Program.h

+31
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@
1313
#ifndef SWIFT_BASIC_PROGRAM_H
1414
#define SWIFT_BASIC_PROGRAM_H
1515

16+
#include "llvm/ADT/ArrayRef.h"
17+
#include "llvm/ADT/Optional.h"
18+
#include "llvm/ADT/StringRef.h"
19+
#include "llvm/Support/ErrorOr.h"
20+
#include "llvm/Support/Program.h"
21+
1622
namespace swift {
1723

1824
/// This function executes the program using the arguments provided,
@@ -33,6 +39,31 @@ namespace swift {
3339
int ExecuteInPlace(const char *Program, const char **args,
3440
const char **env = nullptr);
3541

42+
struct ChildProcessInfo {
43+
llvm::sys::procid_t Pid;
44+
int WriteFileDescriptor;
45+
int ReadFileDescriptor;
46+
47+
ChildProcessInfo(llvm::sys::procid_t Pid, int WriteFileDescriptor,
48+
int ReadFileDescriptor)
49+
: Pid(Pid), WriteFileDescriptor(WriteFileDescriptor),
50+
ReadFileDescriptor(ReadFileDescriptor) {}
51+
};
52+
53+
/// This function executes the program using the argument provided.
54+
/// Establish pipes between the current process and the spawned process.
55+
/// Returned a \c ChildProcessInfo where WriteFileDescriptor is piped to
56+
/// child's STDIN, and ReadFileDescriptor is piped from child's STDOUT.
57+
///
58+
/// \param program Path of the program to be executed
59+
/// \param args An array of strings that are passed to the program. The first
60+
/// element should be the name of the program.
61+
/// \param env An optional array of strings to use for the program's
62+
/// environment.
63+
llvm::ErrorOr<swift::ChildProcessInfo> ExecuteWithPipe(
64+
llvm::StringRef program, llvm::ArrayRef<llvm::StringRef> args,
65+
llvm::Optional<llvm::ArrayRef<llvm::StringRef>> env = llvm::None);
66+
3667
} // end namespace swift
3768

3869
#endif // SWIFT_BASIC_PROGRAM_H

include/swift/Basic/Sandbox.h

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
//===--- Sandbox.h ----------------------------------------------*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef SWIFT_BASIC_SANDBOX_H
14+
#define SWIFT_BASIC_SANDBOX_H
15+
16+
#include "llvm/ADT/ArrayRef.h"
17+
#include "llvm/ADT/SmallVector.h"
18+
#include "llvm/ADT/StringRef.h"
19+
#include "llvm/Support/Allocator.h"
20+
21+
namespace swift {
22+
namespace Sandbox {
23+
24+
/// Applies a sandbox invocation to the given command line (if the platform
25+
/// supports it), and returns the modified command line. On platforms that don't
26+
/// support sandboxing, the command line is returned unmodified.
27+
///
28+
/// - Parameters:
29+
/// - command: The command line to sandbox (including executable as first
30+
/// argument)
31+
/// - strictness: The basic strictness level of the standbox.
32+
/// - writableDirectories: Paths under which writing should be allowed, even
33+
/// if they would otherwise be read-only based on the strictness or paths in
34+
/// `readOnlyDirectories`.
35+
/// - readOnlyDirectories: Paths under which writing should be denied, even if
36+
/// they would have otherwise been allowed by the rules implied by the
37+
/// strictness level.
38+
bool apply(llvm::SmallVectorImpl<llvm::StringRef> &command,
39+
llvm::BumpPtrAllocator &Alloc);
40+
41+
} // namespace Sandbox
42+
} // namespace swift
43+
44+
#endif // SWIFT_BASIC_SANDBOX_H

0 commit comments

Comments
 (0)