Importing a function from host environments
Swift 6.0 or later
If you are using Swift 6.0 or later, you can use experimental @_extern(wasm)
attribute
Swift 6.0 introduces a new attribute @_extern(wasm)
to import a function from the host environment.
To use this experimental feature, you need to enable it in your SwiftPM manifest file:
.executableTarget(
name: "Example",
swiftSettings: [
.enableExperimentalFeature("Extern")
]),
Then, you can import a function from the host environment as follows without using C headers:
@_extern(wasm, module: "env", name: "add")
func add(_ a: Int, _ b: Int) -> Int
print(add(2, 2))
Swift 5.10 or earlier
You can import a function from your host environment using the integration of Swift Package Manager
with C targets. Firstly, you should declare a signature for your function in a C header with an
appropriate __import_name__
attribute:
__attribute__((__import_name__("add")))
extern int add(int lhs, int rhs);
Here __import_name__
specifies the name under which this function will be exposed to Swift code.
Move this C header to a separate target, we'll call it HostFunction
in this example. Your
Package.swift
manifest for your WebAssembly app would look like this:
// swift-tools-version:5.9
import PackageDescription
let package = Package(
name: "Example",
targets: [
.target(name: "HostFunction", dependencies: []),
.executableTarget(name: "Example", dependencies: ["HostFunction"]),
]
)
Place this header into the include
subdirectory of your HostFunction
target directory. You can
then import this host function module into Swift code just as any other module:
import HostFunction
print(add(2, 2))
Then, you can inject a host function into the produced WebAssembly binary.
Note that we use env
as default import module name. You can specify the module name as
__import_module__
in your C header. The full list of attributes in the header could look
like __attribute__((__import_module__("env"),__import_name__("add")))
.
// File name: main.mjs
import { WASI, File, OpenFile, ConsoleStdout } from "@bjorn3/browser_wasi_shim";
import fs from "fs/promises";
const main = async () => {
// Instantiate a new WASI Instance
// See https://github.com/bjorn3/browser_wasi_shim/ for more detail about constructor options
let wasi = new WASI([], [],
[
new OpenFile(new File([])), // stdin
ConsoleStdout.lineBuffered(msg => console.log(`[WASI stdout] ${msg}`)),
ConsoleStdout.lineBuffered(msg => console.warn(`[WASI stderr] ${msg}`)),
],
{ debug: false }
);
const wasmBinary = await fs.readFile(".build/wasm32-unknown-wasi/debug/Example.wasm");
// Instantiate the WebAssembly file
let { instance } = await WebAssembly.instantiate(wasmBinary, {
wasi_snapshot_preview1: wasi.wasiImport,
env: {
add: (lhs, rhs) => (lhs + rhs),
}
});
wasi.start(instance);
};
main()
If you use Go bindings for Wasmer as your host environment, you should check an example repository from one of our contributors that shows an integration with an imported host function.
A more streamlined way to import host functions will be implemented in the future version of the SwiftWasm toolchain.