Skip to content

Commit 7d9aadb

Browse files
Engine API: Migrate CLI module
1 parent 11c4099 commit 7d9aadb

File tree

7 files changed

+122
-28
lines changed

7 files changed

+122
-28
lines changed

Sources/CLI/Commands/Explore.swift

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,24 +14,28 @@ struct Explore: ParsableCommand {
1414

1515
func run() throws {
1616
let module = try parseWasm(filePath: FilePath(path))
17-
var hostModuleStubs: [String: HostModule] = [:]
17+
// Instruction dumping requires token threading model for now
18+
let configuration = EngineConfiguration(threadingModel: .token)
19+
let engine = Engine(configuration: configuration)
20+
let store = Store(engine: engine)
21+
22+
var imports: Imports = [:]
1823
for importEntry in module.imports {
19-
var hostModule = hostModuleStubs[importEntry.module] ?? HostModule()
2024
switch importEntry.descriptor {
2125
case .function(let typeIndex):
2226
let type = module.types[Int(typeIndex)]
23-
hostModule.functions[importEntry.name] = HostFunction(type: type) { _, _ in
24-
fatalError("unreachable")
25-
}
27+
imports.define(
28+
module: importEntry.module,
29+
name: importEntry.name,
30+
Function(store: store, type: type) { _, _ in
31+
fatalError("unreachable")
32+
}
33+
)
2634
default:
2735
fatalError("Import \(importEntry) not supported in explore mode yet")
2836
}
29-
hostModuleStubs[importEntry.module] = hostModule
3037
}
31-
// Instruction dumping requires token threading model for now
32-
let configuration = EngineConfiguration(threadingModel: .token)
33-
let runtime = Runtime(hostModules: hostModuleStubs, configuration: configuration)
34-
let instance = try runtime.instantiate(module: module)
38+
let instance = try module.instantiate(store: store, imports: imports)
3539
var stdout = Stdout()
3640
try instance.dumpFunctions(to: &stdout, module: module)
3741
}

Sources/CLI/Commands/Run.swift

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -161,10 +161,13 @@ struct Run: ParsableCommand {
161161
$0[$1] = $1
162162
}
163163
let wasi = try WASIBridgeToHost(args: [path] + arguments, environment: environment, preopens: preopens)
164-
let runtime = Runtime(hostModules: wasi.hostModules, interceptor: interceptor, configuration: deriveRuntimeConfiguration())
165-
let moduleInstance = try runtime.instantiate(module: module)
164+
let engine = Engine(configuration: deriveRuntimeConfiguration(), interceptor: interceptor)
165+
let store = Store(engine: engine)
166+
var imports = Imports()
167+
wasi.link(to: &imports, store: store)
168+
let moduleInstance = try module.instantiate(store: store, imports: imports)
166169
return {
167-
let exitCode = try wasi.start(moduleInstance, runtime: runtime)
170+
let exitCode = try wasi.start(moduleInstance)
168171
throw ExitCode(Int32(exitCode))
169172
}
170173
}
@@ -192,11 +195,16 @@ struct Run: ParsableCommand {
192195
return nil
193196
}
194197

195-
let runtime = Runtime(interceptor: interceptor, configuration: deriveRuntimeConfiguration())
196-
let moduleInstance = try runtime.instantiate(module: module)
198+
let engine = Engine(configuration: deriveRuntimeConfiguration(), interceptor: interceptor)
199+
let store = Store(engine: engine)
200+
let instance = try module.instantiate(store: store)
197201
return {
198202
log("Started invoking function \"\(functionName)\" with parameters: \(parameters)", verbose: true)
199-
let results = try runtime.invoke(moduleInstance, function: functionName, with: parameters)
203+
guard let toInvoke = instance.exports[function: functionName] else {
204+
log("Error: Function \"\(functionName)\" not found in the module.")
205+
return
206+
}
207+
let results = try toInvoke.invoke(parameters)
200208
print(results.description)
201209
}
202210
}

Sources/WasmKit/Engine.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ public final class Engine {
1616
self.interceptor = interceptor
1717
self.funcTypeInterner = Interner()
1818
}
19+
20+
/// Migration aid for the old ``Runtime/instantiate(module:)``
21+
@available(*, unavailable, message: "Use ``Module/instantiate(store:imports:)`` instead")
22+
public func instantiate(module: Module) -> Instance { fatalError() }
1923
}
2024

2125
public struct EngineConfiguration {

Sources/WasmKit/Execution/Instances.swift

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,61 @@ struct InstanceEntity /* : ~Copyable */ {
8282

8383
typealias InternalInstance = EntityHandle<InstanceEntity>
8484

85+
/// A map of exported entities by name.
86+
public struct Exports: Sequence {
87+
let store: Store
88+
let values: [String: InternalExternalValue]
89+
90+
/// Returns the exported entity with the given name.
91+
public subscript(_ name: String) -> ExternalValue? {
92+
guard let entity = values[name] else { return nil }
93+
return ExternalValue(handle: entity, store: store)
94+
}
95+
96+
/// Returns the exported function with the given name.
97+
public subscript(function name: String) -> Function? {
98+
guard case .function(let function) = self[name] else { return nil }
99+
return function
100+
}
101+
102+
/// Returns the exported table with the given name.
103+
public subscript(table name: String) -> Table? {
104+
guard case .table(let table) = self[name] else { return nil }
105+
return table
106+
}
107+
108+
/// Returns the exported memory with the given name.
109+
public subscript(memory name: String) -> Memory? {
110+
guard case .memory(let memory) = self[name] else { return nil }
111+
return memory
112+
}
113+
114+
/// Returns the exported global with the given name.
115+
public subscript(global name: String) -> Global? {
116+
guard case .global(let global) = self[name] else { return nil }
117+
return global
118+
}
119+
120+
public struct Iterator: IteratorProtocol {
121+
private let store: Store
122+
private var iterator: Dictionary<String, InternalExternalValue>.Iterator
123+
124+
init(parent: Exports) {
125+
self.store = parent.store
126+
self.iterator = parent.values.makeIterator()
127+
}
128+
129+
public mutating func next() -> (String, ExternalValue)? {
130+
guard let (name, entity) = iterator.next() else { return nil }
131+
return (name, ExternalValue(handle: entity, store: store))
132+
}
133+
}
134+
135+
public func makeIterator() -> Iterator {
136+
Iterator(parent: self)
137+
}
138+
}
139+
85140
/// A stateful instance of a WebAssembly module.
86141
/// Usually instantiated by ``Runtime/instantiate(module:)``.
87142
/// > Note:
@@ -113,11 +168,9 @@ public struct Instance {
113168
return function
114169
}
115170

116-
public typealias Exports = [String: ExternalValue]
117-
118171
/// A dictionary of exported entities by name.
119172
public var exports: Exports {
120-
handle.exports.mapValues { ExternalValue(handle: $0, store: store) }
173+
Exports(store: store, values: handle.exports)
121174
}
122175

123176
/// Dumps the textual representation of all functions in the instance.

Sources/WasmKit/Execution/Runtime.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ public final class Runtime {
2424
/// - Parameter hostModules: Host module names mapped to their corresponding ``HostModule`` definitions.
2525
/// - Parameter interceptor: An optional runtime interceptor to intercept execution of instructions.
2626
/// - Parameter configuration: An optional runtime configuration to customize the runtime behavior.
27+
@available(*, deprecated)
2728
public init(
2829
hostModules: [String: HostModule] = [:],
2930
interceptor: EngineInterceptor? = nil,
@@ -59,7 +60,7 @@ extension Runtime {
5960
throw ImportError.moduleInstanceAlreadyRegistered(name)
6061
}
6162

62-
availableExports[name] = instance.exports
63+
availableExports[name] = Dictionary(uniqueKeysWithValues: instance.exports)
6364
}
6465

6566
/// Legacy compatibility method to register a host module with a name.

Sources/WasmKit/Imports.swift

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,15 @@ public struct Imports {
99
}
1010

1111
/// Define a value to be imported by the given module and name.
12-
public mutating func define(module: String, name: String, _ value: ExternalValue) {
13-
definitions[module, default: [:]][name] = value
12+
public mutating func define<Extern: ExternalValueConvertible>(module: String, name: String, _ value: Extern) {
13+
definitions[module, default: [:]][name] = value.externalValue
1414
}
1515

1616
/// Define a set of values to be imported by the given module.
1717
/// - Parameters:
1818
/// - module: The module name to be used for resolving the imports.
1919
/// - values: The values to be imported keyed by their name.
20-
public mutating func define(module: String, _ values: Instance.Exports) {
20+
public mutating func define(module: String, _ values: Exports) {
2121
definitions[module, default: [:]].merge(values, uniquingKeysWith: { _, new in new })
2222
}
2323

@@ -36,6 +36,10 @@ public protocol ExternalValueConvertible {
3636
var externalValue: ExternalValue { get }
3737
}
3838

39+
extension ExternalValue: ExternalValueConvertible {
40+
public var externalValue: ExternalValue { self }
41+
}
42+
3943
extension Memory: ExternalValueConvertible {
4044
public var externalValue: ExternalValue { .memory(self) }
4145
}

Sources/WasmKitWASI/WASIBridgeToHost+WasmKit.swift

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,30 +4,50 @@ import WasmKit
44
public typealias WASIBridgeToHost = WASI.WASIBridgeToHost
55

66
extension WASIBridgeToHost {
7+
public func link(to imports: inout Imports, store: Store) {
8+
var imports = Imports()
9+
for (moduleName, module) in wasiHostModules {
10+
for (name, function) in module.functions {
11+
imports.define(
12+
module: moduleName,
13+
name: name,
14+
Function(store: store, type: function.type, body: makeHostFunction(function))
15+
)
16+
}
17+
}
18+
}
19+
720
public var hostModules: [String: HostModule] {
821
wasiHostModules.mapValues { (module: WASIHostModule) -> HostModule in
922
HostModule(
1023
functions: module.functions.mapValues { function -> HostFunction in
11-
makeHostFunction(function)
24+
HostFunction(type: function.type, implementation: makeHostFunction(function))
1225
})
1326
}
1427
}
1528

16-
private func makeHostFunction(_ function: WASIHostFunction) -> HostFunction {
17-
HostFunction(type: function.type) { caller, values -> [Value] in
29+
private func makeHostFunction(_ function: WASIHostFunction) -> ((Caller, [Value]) throws -> [Value]) {
30+
{ caller, values -> [Value] in
1831
guard case let .memory(memory) = caller.instance?.export("memory") else {
1932
throw WASIError(description: "Missing required \"memory\" export")
2033
}
2134
return try function.implementation(memory, values)
2235
}
2336
}
2437

25-
public func start(_ instance: Instance, runtime: Runtime) throws -> UInt32 {
38+
public func start(_ instance: Instance) throws -> UInt32 {
2639
do {
27-
_ = try runtime.invoke(instance, function: "_start")
40+
guard let start = instance.exports[function: "_start"] else {
41+
throw WASIError(description: "Missing required \"_start\" function")
42+
}
43+
_ = try start()
2844
} catch let code as WASIExitCode {
2945
return code.code
3046
}
3147
return 0
3248
}
49+
50+
public func start(_ instance: Instance, runtime: Runtime) throws -> UInt32 {
51+
return try start(instance)
52+
}
3353
}

0 commit comments

Comments
 (0)