Skip to content

Commit 5c2f24a

Browse files
Merge pull request #69 from MaxDesiatov/function-reentrancy
Support reentrant function call between host/guest
2 parents a5e045d + f2eef10 commit 5c2f24a

File tree

4 files changed

+67
-3
lines changed

4 files changed

+67
-3
lines changed

Sources/WasmKit/Execution/Runtime/ExecutionState.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ extension ExecutionState {
8484
switch try runtime.store.function(at: address) {
8585
case let .host(function):
8686
let parameters = try stack.popValues(count: function.type.parameters.count)
87-
let caller = Caller(store: runtime.store, instance: stack.currentFrame.module)
87+
let caller = Caller(runtime: runtime, instance: stack.currentFrame.module)
8888
stack.push(values: try function.implementation(caller, parameters))
8989

9090
programCounter += 1

Sources/WasmKit/Execution/Runtime/Function.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/// A WebAssembly guest function or host function
12
public struct Function: Equatable {
23
internal let address: FunctionAddress
34

@@ -16,7 +17,7 @@ public struct Function: Equatable {
1617

1718
let parameters = try execution.stack.popValues(count: function.type.parameters.count)
1819

19-
let caller = Caller(store: runtime.store, instance: execution.stack.currentFrame.module)
20+
let caller = Caller(runtime: runtime, instance: execution.stack.currentFrame.module)
2021
let results = try function.implementation(caller, parameters)
2122
try check(functionType: function.type, results: results)
2223
execution.stack.push(values: results)

Sources/WasmKit/Execution/Runtime/Store.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,11 @@ public final class Store {
6060

6161
/// A caller context passed to host functions
6262
public struct Caller {
63-
public let store: Store
63+
public let runtime: Runtime
6464
public let instance: ModuleInstance
65+
public var store: Store {
66+
runtime.store
67+
}
6568
}
6669

6770
/// A host-defined function which can be imported by a WebAssembly module instance.

Tests/WasmKitTests/Execution/HostModuleTests.swift

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,64 @@ final class HostModuleTests: XCTestCase {
1818
// Ensure the allocated address is valid
1919
_ = runtime.store.memory(at: memoryAddr)
2020
}
21+
22+
func testReentrancy() throws {
23+
let runtime = Runtime()
24+
let voidSignature = FunctionType(parameters: [], results: [])
25+
let module = Module(
26+
types: [voidSignature],
27+
functions: [
28+
// [0] (import "env" "bar" func)
29+
// [1] (import "env" "qux" func)
30+
// [2] "foo"
31+
GuestFunction(
32+
type: 0, locals: [],
33+
body: [
34+
.control(.call(functionIndex: 0)),
35+
.control(.call(functionIndex: 0)),
36+
.control(.call(functionIndex: 0)),
37+
]),
38+
// [3] "bar"
39+
GuestFunction(
40+
type: 0, locals: [],
41+
body: [
42+
.control(.call(functionIndex: 1))
43+
]),
44+
],
45+
imports: [
46+
Import(module: "env", name: "bar", descriptor: .function(0)),
47+
Import(module: "env", name: "qux", descriptor: .function(0)),
48+
],
49+
exports: [
50+
Export(name: "foo", descriptor: .function(2)),
51+
Export(name: "baz", descriptor: .function(3)),
52+
]
53+
)
54+
55+
var isExecutingFoo = false
56+
var isQuxCalled = false
57+
let hostModule = HostModule(
58+
functions: [
59+
"bar": HostFunction(type: voidSignature) { caller, _ in
60+
// Ensure "invoke" executes instructions under the current call
61+
XCTAssertFalse(isExecutingFoo, "bar should not be called recursively")
62+
isExecutingFoo = true
63+
defer { isExecutingFoo = false }
64+
let foo = try XCTUnwrap(caller.instance.exportedFunction(name: "baz"))
65+
_ = try foo.invoke([], runtime: caller.runtime)
66+
return []
67+
},
68+
"qux": HostFunction(type: voidSignature) { _, _ in
69+
XCTAssertTrue(isExecutingFoo)
70+
isQuxCalled = true
71+
return []
72+
},
73+
]
74+
)
75+
try runtime.store.register(hostModule, as: "env")
76+
let instance = try runtime.instantiate(module: module)
77+
// Check foo(wasm) -> bar(host) -> baz(wasm) -> qux(host)
78+
_ = try runtime.invoke(instance, function: "foo")
79+
XCTAssertTrue(isQuxCalled)
80+
}
2181
}

0 commit comments

Comments
 (0)