Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions Examples/Sources/Factorial/Factorial.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@ struct Example {
)

// Create a module instance from the parsed module.
let runtime = Runtime()
let instance = try runtime.instantiate(module: module)
let engine = Engine()
let store = Store(engine: engine)
let instance = try module.instantiate(store: store)
let input: UInt64 = 5
// Invoke the exported function "fac" with a single argument.
let result = try runtime.invoke(instance, function: "fac", with: [.i64(input)])
let fac = instance.exports[function: "fac"]!
let result = try fac([.i64(input)])
print("fac(\(input)) = \(result[0].i64)")
}
}
32 changes: 20 additions & 12 deletions Examples/Sources/PrintAdd/PrintAdd.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,26 @@ struct Example {
)
)

// Define a host function that prints an i32 value.
let hostPrint = HostFunction(type: FunctionType(parameters: [.i32])) { _, args in
// This function is called from "print_add" in the WebAssembly module.
print(args[0])
return []
}
// Create a runtime importing the host function.
let runtime = Runtime(hostModules: [
"printer": HostModule(functions: ["print_i32": hostPrint])
])
let instance = try runtime.instantiate(module: module)
// Create engine and store
let engine = Engine()
let store = Store(engine: engine)

// Instantiate a parsed module with importing a host function
let instance = try module.instantiate(
store: store,
// Import a host function that prints an i32 value.
imports: [
"printer": [
"print_i32": Function(store: store, parameters: [.i32]) { _, args in
// This function is called from "print_add" in the WebAssembly module.
print(args[0])
return []
}
]
]
)
// Invoke the exported function "print_add"
_ = try runtime.invoke(instance, function: "print_add", with: [.i32(42), .i32(3)])
let printAdd = instance.exports[function: "print_add"]!
try printAdd([.i32(42), .i32(3)])
}
}
12 changes: 8 additions & 4 deletions Examples/Sources/WASI-Hello/Hello.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,16 @@ struct Example {

// Create a WASI instance forwarding to the host environment.
let wasi = try WASIBridgeToHost()
// Create a runtime with WASI host modules.
let runtime = Runtime(hostModules: wasi.hostModules)
let instance = try runtime.instantiate(module: module)
// Create engine and store
let engine = Engine()
let store = Store(engine: engine)
// Instantiate a parsed module importing WASI
var imports = Imports()
wasi.link(to: &imports, store: store)
let instance = try module.instantiate(store: store, imports: imports)

// Start the WASI command-line application.
let exitCode = try wasi.start(instance, runtime: runtime)
let exitCode = try wasi.start(instance)
// Exit the Swift program with the WASI exit code.
exit(Int32(exitCode))
}
Expand Down
9 changes: 5 additions & 4 deletions FuzzTesting/Sources/FuzzDifferential/FuzzDifferential.swift
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,10 @@ extension wasm_name_t {
struct WasmKitEngine: Engine {
func run(moduleBytes: [UInt8]) throws -> ExecResult {
let module = try WasmKit.parseWasm(bytes: moduleBytes)
let runtime = Runtime()
let instance = try runtime.instantiate(module: module)
let exports = instance.exports.sorted(by: { $0.key < $1.key })
let engine = WasmKit.Engine()
let store = WasmKit.Store(engine: engine)
let instance = try module.instantiate(store: store)
let exports = instance.exports.sorted(by: { $0.name < $1.name })
let memories: [Memory] = exports.compactMap {
guard case let .memory(memory) = $0.value else {
return nil
Expand All @@ -117,7 +118,7 @@ struct WasmKitEngine: Engine {
let type = fn.type
let arguments = type.parameters.map { $0.defaultValue }
do {
let results = try fn.invoke(arguments, runtime: runtime)
let results = try fn(arguments)
return ExecResult(values: results, trap: nil, memory: memory?.data)
} catch {
return ExecResult(values: nil, trap: String(describing: error), memory: memory?.data)
Expand Down
9 changes: 5 additions & 4 deletions FuzzTesting/Sources/FuzzExecute/FuzzExecute.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,17 @@ public func FuzzCheck(_ start: UnsafePointer<UInt8>, _ count: Int) -> CInt {
let bytes = Array(UnsafeBufferPointer(start: start, count: count))
do {
let module = try WasmKit.parseWasm(bytes: bytes)
let runtime = WasmKit.Runtime()
runtime.store.resourceLimiter = FuzzerResourceLimiter()
let instance = try runtime.instantiate(module: module)
let engine = WasmKit.Engine()
let store = WasmKit.Store(engine: engine)
store.resourceLimiter = FuzzerResourceLimiter()
let instance = try module.instantiate(store: store)
for export in instance.exports.values {
guard case let .function(fn) = export else {
continue
}
let type = fn.type
let arguments = type.parameters.map { $0.defaultValue }
_ = try fn.invoke(arguments, runtime: runtime)
_ = try fn(arguments)
}
} catch {
// Ignore errors
Expand Down
26 changes: 15 additions & 11 deletions Sources/CLI/Commands/Explore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,29 @@ struct Explore: ParsableCommand {

func run() throws {
let module = try parseWasm(filePath: FilePath(path))
var hostModuleStubs: [String: HostModule] = [:]
// Instruction dumping requires token threading model for now
let configuration = EngineConfiguration(threadingModel: .token)
let engine = Engine(configuration: configuration)
let store = Store(engine: engine)

var imports: Imports = [:]
for importEntry in module.imports {
var hostModule = hostModuleStubs[importEntry.module] ?? HostModule()
switch importEntry.descriptor {
case .function(let typeIndex):
let type = module.types[Int(typeIndex)]
hostModule.functions[importEntry.name] = HostFunction(type: type) { _, _ in
fatalError("unreachable")
}
imports.define(
module: importEntry.module,
name: importEntry.name,
Function(store: store, type: type) { _, _ in
fatalError("unreachable")
}
)
default:
fatalError("Import \(importEntry) not supported in explore mode yet")
}
hostModuleStubs[importEntry.module] = hostModule
}
// Instruction dumping requires token threading model for now
let configuration = RuntimeConfiguration(threadingModel: .token)
let runtime = Runtime(hostModules: hostModuleStubs, configuration: configuration)
let instance = try runtime.instantiate(module: module)
let instance = try module.instantiate(store: store, imports: imports)
var stdout = Stdout()
try instance.dumpFunctions(to: &stdout, module: module, runtime: runtime)
try instance.dumpFunctions(to: &stdout, module: module)
}
}
36 changes: 22 additions & 14 deletions Sources/CLI/Commands/Run.swift
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,8 @@ struct Run: ParsableCommand {
}

/// Derives the runtime interceptor based on the command line arguments
func deriveInterceptor() throws -> (interceptor: RuntimeInterceptor?, finalize: () -> Void) {
var interceptors: [RuntimeInterceptor] = []
func deriveInterceptor() throws -> (interceptor: EngineInterceptor?, finalize: () -> Void) {
var interceptors: [EngineInterceptor] = []
var finalizers: [() -> Void] = []

if self.signpost {
Expand Down Expand Up @@ -131,7 +131,7 @@ struct Run: ParsableCommand {
return (MultiplexingInterceptor(interceptors), { finalizers.forEach { $0() } })
}

private func deriveSignpostTracer() -> RuntimeInterceptor? {
private func deriveSignpostTracer() -> EngineInterceptor? {
#if canImport(os.signpost)
if #available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) {
let signposter = SignpostTracer(signposter: OSSignposter())
Expand All @@ -142,17 +142,17 @@ struct Run: ParsableCommand {
return nil
}

private func deriveRuntimeConfiguration() -> RuntimeConfiguration {
let threadingModel: RuntimeConfiguration.ThreadingModel?
private func deriveRuntimeConfiguration() -> EngineConfiguration {
let threadingModel: EngineConfiguration.ThreadingModel?
switch self.threadingModel {
case .direct: threadingModel = .direct
case .token: threadingModel = .token
case nil: threadingModel = nil
}
return RuntimeConfiguration(threadingModel: threadingModel)
return EngineConfiguration(threadingModel: threadingModel)
}

func instantiateWASI(module: Module, interceptor: RuntimeInterceptor?) throws -> () throws -> Void {
func instantiateWASI(module: Module, interceptor: EngineInterceptor?) throws -> () throws -> Void {
// Flatten environment variables into a dictionary (Respect the last value if a key is duplicated)
let environment = environment.reduce(into: [String: String]()) {
$0[$1.key] = $1.value
Expand All @@ -161,15 +161,18 @@ struct Run: ParsableCommand {
$0[$1] = $1
}
let wasi = try WASIBridgeToHost(args: [path] + arguments, environment: environment, preopens: preopens)
let runtime = Runtime(hostModules: wasi.hostModules, interceptor: interceptor, configuration: deriveRuntimeConfiguration())
let moduleInstance = try runtime.instantiate(module: module)
let engine = Engine(configuration: deriveRuntimeConfiguration(), interceptor: interceptor)
let store = Store(engine: engine)
var imports = Imports()
wasi.link(to: &imports, store: store)
let moduleInstance = try module.instantiate(store: store, imports: imports)
return {
let exitCode = try wasi.start(moduleInstance, runtime: runtime)
let exitCode = try wasi.start(moduleInstance)
throw ExitCode(Int32(exitCode))
}
}

func instantiateNonWASI(module: Module, interceptor: RuntimeInterceptor?) throws -> (() throws -> Void)? {
func instantiateNonWASI(module: Module, interceptor: EngineInterceptor?) throws -> (() throws -> Void)? {
let functionName = arguments.first
let arguments = arguments.dropFirst()

Expand All @@ -192,11 +195,16 @@ struct Run: ParsableCommand {
return nil
}

let runtime = Runtime(interceptor: interceptor, configuration: deriveRuntimeConfiguration())
let moduleInstance = try runtime.instantiate(module: module)
let engine = Engine(configuration: deriveRuntimeConfiguration(), interceptor: interceptor)
let store = Store(engine: engine)
let instance = try module.instantiate(store: store)
return {
log("Started invoking function \"\(functionName)\" with parameters: \(parameters)", verbose: true)
let results = try runtime.invoke(moduleInstance, function: functionName, with: parameters)
guard let toInvoke = instance.exports[function: functionName] else {
log("Error: Function \"\(functionName)\" not found in the module.")
return
}
let results = try toInvoke.invoke(parameters)
print(results.description)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -252,23 +252,28 @@ struct HostExportFunction {
)
var signature = try signatureTranslation.signature(function: function, name: ConvertCase.camelCase(kebab: name.apiSwiftName))
let witParameters = signature.parameters.map(\.label)
signature.parameters.insert(("runtime", "Runtime"), at: 0)
signature.hasThrows = true
printer.write(line: signature.description + " {")
try printer.indent {
let optionsVar = builder.variable("options")
printer.write(line: "let \(optionsVar) = CanonicalOptions._derive(from: instance, exportName: \"\(name.abiName)\")")
printer.write(line: "let \(context.contextVar) = CanonicalCallContext(options: \(optionsVar), instance: instance, runtime: runtime)")
printer.write(line: "let \(context.contextVar) = CanonicalCallContext(options: \(optionsVar), instance: instance)")
// Suppress unused variable warning for "context"
printer.write(line: "_ = \(context.contextVar)")

let arguments = try printLowerArguments(
parameterNames: witParameters, coreSignature: coreSignature,
typeResolver: typeResolver, printer: printer
)
var call = "try runtime.invoke(instance, function: \"\(name.abiName)\""
let functionVar = builder.variable("function")
printer.write(multiline: """
guard let \(functionVar) = instance.exports[function: \"\(name.abiName)\"] else {
throw CanonicalABIError(description: "Function \\"\(name.abiName)\\" not found in the instance")
}
""")
var call = "try \(functionVar)("
if !arguments.isEmpty {
call += ", with: [\(arguments.map(\.description).joined(separator: ", "))]"
call += "[\(arguments.map(\.description).joined(separator: ", "))]"
}
call += ")"
if coreSignature.isIndirectResult {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ struct HostWorldGenerator: ASTVisitor {
struct \(ConvertCase.pascalCase(world.name)) {
let instance: WasmKit.Instance

static func link(_ hostModules: inout [String: HostModule]) {
static func link(to imports: inout Imports, store: Store) {
}
""")
// Enter world struct body
Expand Down
5 changes: 4 additions & 1 deletion Sources/WasmKit/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
add_wasmkit_library(WasmKit
Engine.swift
Imports.swift
Module.swift
ModuleParser.swift
Translator.swift
Expand All @@ -14,7 +16,9 @@ add_wasmkit_library(WasmKit
Execution/Instructions/Memory.swift
Execution/Instructions/Misc.swift
Execution/Instructions/InstructionSupport.swift
Execution/ConstEvaluation.swift
Execution/DispatchInstruction.swift
Execution/EngineInterceptor.swift
Execution/Errors.swift
Execution/Execution.swift
Execution/Function.swift
Expand All @@ -23,7 +27,6 @@ add_wasmkit_library(WasmKit
Execution/NameRegistry.swift
Execution/Profiler.swift
Execution/Runtime.swift
Execution/RuntimeInterceptor.swift
Execution/SignpostTracer.swift
Execution/Store.swift
Execution/StoreAllocator.swift
Expand Down
23 changes: 13 additions & 10 deletions Sources/WasmKit/Component/CanonicalCall.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
@_exported import WasmTypes

struct CanonicalABIError: Error, CustomStringConvertible {
let description: String
/// Error type for canonical ABI operations.
public struct CanonicalABIError: Error, CustomStringConvertible {
public let description: String

@_documentation(visibility: internal)
public init(description: String) {
self.description = description
}
}

/// Call context for `(canon lift)` or `(canon lower)` operations.
Expand All @@ -14,17 +20,14 @@ public struct CanonicalCallContext {
public let options: CanonicalOptions
/// The module instance that defines the lift/lower operation.
public let instance: Instance
/// The executing `Runtime` instance
public let runtime: Runtime
/// A reference to the guest memory.
public var guestMemory: Memory {
options.memory
}

public init(options: CanonicalOptions, instance: Instance, runtime: Runtime) {
public init(options: CanonicalOptions, instance: Instance) {
self.options = options
self.instance = instance
self.runtime = runtime
}

/// Call `cabi_realloc` export with the given arguments.
Expand All @@ -37,8 +40,8 @@ public struct CanonicalCallContext {
guard let realloc = options.realloc else {
throw CanonicalABIError(description: "Missing required \"cabi_realloc\" export")
}
let results = try realloc.invoke(
[.i32(old), .i32(oldSize), .i32(oldAlign), .i32(newSize)], runtime: runtime
let results = try realloc(
[.i32(old), .i32(oldSize), .i32(oldAlign), .i32(newSize)]
)
guard results.count == 1 else {
throw CanonicalABIError(description: "\"cabi_realloc\" export should return a single value")
Expand All @@ -56,9 +59,9 @@ extension CanonicalCallContext {
return instance
}

@available(*, deprecated, renamed: "init(options:instance:runtime:)")
@available(*, deprecated, renamed: "init(options:instance:)")
public init(options: CanonicalOptions, moduleInstance: Instance, runtime: Runtime) {
self.init(options: options, instance: moduleInstance, runtime: runtime)
self.init(options: options, instance: moduleInstance)
}
}

Expand Down
8 changes: 4 additions & 4 deletions Sources/WasmKit/Component/CanonicalOptions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,13 @@ public struct CanonicalOptions {
/// FIXME: This deriviation is wrong because the options should be determined by `(canon lift)` or `(canon lower)`
/// in an encoded component at componetizing-time. (e.g. wit-component tool is one of the componetizers)
/// Remove this temporary method after we will accept binary form of component file.
public static func _derive(from moduleInstance: Instance, exportName: String) -> CanonicalOptions {
guard case let .memory(memory) = moduleInstance.exports["memory"] else {
public static func _derive(from instance: Instance, exportName: String) -> CanonicalOptions {
guard case let .memory(memory) = instance.exports["memory"] else {
fatalError("Missing required \"memory\" export")
}
return CanonicalOptions(
memory: memory, stringEncoding: .utf8,
realloc: moduleInstance.exportedFunction(name: "cabi_realloc"),
postReturn: moduleInstance.exportedFunction(name: "cabi_post_\(exportName)"))
realloc: instance.exportedFunction(name: "cabi_realloc"),
postReturn: instance.exportedFunction(name: "cabi_post_\(exportName)"))
}
}
Loading