Wacro

Wacro(WebAssembly Macros)是使用 WebAssembly 构建 Swift 宏提案的一个概念验证实现,该提案已提交给 GSoC 2024(提案链接)。

截至目前,Wacro 已经允许库作者使用 WebAssembly 构建宏,并且用户可以像使用其他 Swift 库一样使用它们:请参阅试用部分了解详情。 但是,目前存在一些性能缺陷,这些缺陷可以通过在编译器/SwiftPM 中实现来解决。

Wacro 不是 GSoC 提交项目:它的存在仅仅是为了表明这是可能的(甚至无需修改编译器或 SwiftPM!),因为 GSoC 提案进展缓慢。一个实际的实现应该集成到编译器和 SwiftPM 中,以提高可用性和性能。

为什么?

🐇 目前的 Swift 宏在编译时速度非常慢。 这是因为大多数宏依赖于源代码形式的 swift-syntax(Swift 的语法解析库),而它是一个非常大的库,需要很长时间才能构建。

🤝 SwiftPM 当前只允许链接到软件包的单个版本。 因此,如果两个宏依赖于冲突的 swift-syntax 版本,SwiftPM 将拒绝构建。

🔐 Swift 宏旨在“纯粹”,并且不能访问系统资源。 目前,这在 macOS 上通过系统沙箱实现。 但是,据我所知,宏在 Linux 上没有被沙盒化; 虽然可以使用 命名空间 在某种程度上弥补这一点,但这可能需要相当大的努力,而且命名空间被广泛认为不是安全边界。

如何实现?

Wacro 将宏分解为两个阶段

  1. 原始宏:宏作者将宏源代码构建成 WASM 二进制文件,称为原始宏。 这就像一个常规宏,但 1)它是一个 .executableTarget 而不是 .macro,并且 2)它依赖于 WacroPluginRaw 而不是 SwiftCompilerPlugin。 该二进制文件被检入源代码控制(理想情况下,可以指向远程资源,但这可能需要 SwiftPM 的支持。)
  2. 宿主宏:这是客户端使用的“真正”宏。 这是一个简单的 .macro 目标,其中宏实现了 WacroPluginHost。 此一致性的唯一要求是 providingLibrary: URL,它指向原始宏的文件路径。

当宿主宏被调用时,WacroPluginHost 启动一个 WebAssembly “运行器”,该运行器与原始宏通信以执行实际工作。

WebAssembly 编译

借助 Swift 6.0,对 WASM 和 WASI 的支持现在已经上游化。 这意味着给定 WASI SDK,一个普通的 Swift 6.0 工具链可以开箱即用地构建 WASI 二进制文件。

WebAssembly 运行器

Wacro 目前提供两种执行 WebAssembly 二进制文件的方法

  1. WebKit:这种方法依赖于 WKWebView 的 WebAssembly 支持来及时评估宏。
  1. WasmKit:这种方法使用第三方 WasmKit 库来解释 WebAssembly 宏。

WasmKit 目前是默认的运行器,因为它是跨平台的并且可以在沙箱中运行。 切换到 WebKit 的说明在以下部分中。

理想的运行器可能是这两者的混合。 在 macOS 上,将 WebKit 运行器移动到 Swift Driver 中将允许它在不禁用宏沙箱的情况下使用。 同时,WasmKit 可以在系统不提供 WebKit 的其他平台上使用。

试用

请参阅 WacroExample 存储库。 您可以克隆它并运行 ExampleClient,或者在任何项目中使用 ExampleLibrary 作为依赖项! 请注意,现在可能会出现一些意想不到的性能问题,但如果它进入 Swift 编译器,这些问题将得到补救。 有关详细信息,请参阅以下部分

性能

注意:本节是关于构建时间性能的。 运行时性能将与典型的宏相同。 WebAssembly 执行过程仅在构建时进行。

方法

结果

所有值均以秒为单位。

类型 WasmKit WebKit SwiftSyntax
干净 (debug) 33.8 19.2 29.0
干净 (release) 32.0 18.4 183.2
增量 (debug) 9.8 1.3 0.6
增量 (release) 1.1 1.5 0.8

是的,你没看错。 基于 WasmKit 的宏在发布模式下构建更快,因为 WasmKit 本身是用优化编译的,因此可以更快地解释 wasm 宏。 理想情况下,与 SwiftPM 的更深入集成将确保 WasmKit 本身始终在发布模式下构建。