├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .swift-version ├── .swiftformat ├── LICENSE ├── Package.resolved ├── Package.swift ├── README.md ├── Sources └── WasmInterpreter │ ├── Heap.swift │ ├── ImportedFunctionCache.swift │ ├── NativeFunction.swift │ ├── NativeFunctionSignature.swift │ ├── String+WasmInterpreter.swift │ ├── String+WasmTypeProtocol.swift │ ├── WasmInterpreter+Call.swift │ ├── WasmInterpreter+ImportHandler.swift │ ├── WasmInterpreter.swift │ ├── WasmInterpreterError.swift │ └── WasmType.swift ├── Tests ├── LinuxMain.swift └── WasmInterpreterTests │ ├── Resources │ ├── add.wat │ ├── constant.wat │ ├── fib64.wat │ ├── imported-add.wat │ └── memory.wat │ ├── String+WasmTypeProtocolTests.swift │ ├── Wasm Modules │ ├── AddModule.swift │ ├── ConstantModule.swift │ ├── FibonacciModule.swift │ ├── ImportedAddModule.swift │ └── MemoryModule.swift │ ├── WasmInterpreterTests.swift │ └── XCTestManifests.swift └── bin └── format.sh /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: push 4 | 5 | jobs: 6 | test: 7 | runs-on: macos-12 8 | 9 | steps: 10 | - uses: actions/checkout@v3 11 | - name: Select Xcode 14 12 | run: sudo xcode-select -s /Applications/Xcode_14.1.app 13 | - name: Test 14 | run: swift test 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # from https://github.com/github/gitignore/blob/master/Swift.gitignore 2 | 3 | .env 4 | 5 | .DS_Store 6 | 7 | # Xcode 8 | # 9 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 10 | 11 | ## Build generated 12 | build/ 13 | DerivedData 14 | 15 | ## Various settings 16 | *.pbxuser 17 | !default.pbxuser 18 | *.mode1v3 19 | !default.mode1v3 20 | *.mode2v3 21 | !default.mode2v3 22 | *.perspectivev3 23 | !default.perspectivev3 24 | xcuserdata 25 | 26 | ## Other 27 | *.xccheckout 28 | *.moved-aside 29 | *.xcuserstate 30 | *.xcscmblueprint 31 | 32 | ## Obj-C/Swift specific 33 | *.hmap 34 | *.ipa 35 | 36 | # Swift Package Manager 37 | # 38 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 39 | # Packages/ 40 | .build/ 41 | .swiftpm/ 42 | 43 | # CocoaPods 44 | # 45 | # We recommend against adding the Pods directory to your .gitignore. However 46 | # you should judge for yourself, the pros and cons are mentioned at: 47 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 48 | # 49 | # Pods/ 50 | 51 | # Carthage 52 | # 53 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 54 | # Carthage/Checkouts 55 | 56 | Carthage/Build 57 | 58 | # fastlane 59 | # 60 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 61 | # screenshots whenever they are needed. 62 | # For more information about the recommended setup visit: 63 | # https://github.com/fastlane/fastlane/blob/master/docs/Gitignore.md 64 | 65 | fastlane/report.xml 66 | fastlane/screenshots 67 | -------------------------------------------------------------------------------- /.swift-version: -------------------------------------------------------------------------------- 1 | 5.7.0 2 | 3 | -------------------------------------------------------------------------------- /.swiftformat: -------------------------------------------------------------------------------- 1 | --disable \ 2 | hoistAwait, \ 3 | hoistTry 4 | 5 | --decimalgrouping 3,5 6 | --funcattributes prev-line 7 | --minversion 0.47.2 8 | --maxwidth 96 9 | --typeattributes prev-line 10 | --wraparguments before-first 11 | --wrapparameters before-first 12 | --wrapcollections before-first 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Shareup Software Corporation 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "pins" : [ 3 | { 4 | "identity" : "synchronized", 5 | "kind" : "remoteSourceControl", 6 | "location" : "https://github.com/shareup/synchronized.git", 7 | "state" : { 8 | "revision" : "3a07451bbd0933933804c7d9a92ef2fa57b6766f", 9 | "version" : "4.0.0" 10 | } 11 | } 12 | ], 13 | "version" : 2 14 | } 15 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.7 2 | import PackageDescription 3 | 4 | let package = Package( 5 | name: "WasmInterpreter", 6 | platforms: [ 7 | .macOS(.v11), .iOS(.v14), 8 | ], 9 | products: [ 10 | .library( 11 | name: "WasmInterpreter", 12 | targets: ["WasmInterpreter"] 13 | ), 14 | ], 15 | dependencies: [ 16 | .package( 17 | url: "https://github.com/shareup/synchronized.git", 18 | from: "4.0.0" 19 | ), 20 | ], 21 | targets: [ 22 | .target( 23 | name: "WasmInterpreter", 24 | dependencies: [ 25 | "CWasm3", 26 | .product(name: "Synchronized", package: "synchronized"), 27 | ], 28 | cSettings: [ 29 | .define("APPLICATION_EXTENSION_API_ONLY", to: "YES"), 30 | ] 31 | ), 32 | .binaryTarget( 33 | name: "CWasm3", 34 | url: "https://github.com/shareup/cwasm3/releases/download/v0.5.2/CWasm3-0.5.0.xcframework.zip", 35 | checksum: "a2b0785be1221767d926cee76b087f168384ec9735b4f46daf26e12fae2109a3" 36 | ), 37 | .testTarget( 38 | name: "WasmInterpreterTests", 39 | dependencies: ["WasmInterpreter"], 40 | exclude: [ 41 | "Resources/constant.wat", 42 | "Resources/memory.wat", 43 | "Resources/fib64.wat", 44 | "Resources/imported-add.wat", 45 | "Resources/add.wat", 46 | ] 47 | ), 48 | ] 49 | ) 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WasmInterpreter 2 | 3 | `WasmInterpreter` is a small Swift wrapper around [`CWasm3`](https://github.com/shareup/cwasm3), which packages [`wasm3`](https://github.com/wasm3/wasm3) inside of a Swift package while adding a few minor conveniences on top. The overriding goal of `WasmInterpreter` is to provide a clean, Swift-y interface to `wasm3`. 4 | 5 | The core class inside of `WasmInterpreter` is, naturally, `WasmInterpreter`. This class is initialized with a stack size _(default: 512 KB)_ and a Wasm module. `WasmInterpreter` takes care of initializing the WebAssembly environment, runtime, and module, and exposes a clean interface for calling Wasm functions, importing native functions into Wasm modules, and extracting data from the runtime's heap. You can find examples for all of these things by looking at the tests. 6 | 7 | ## Installation 8 | 9 | To use `WasmInterpreter` with the Swift Package Manager, add a dependency to your `Package.swift` file: 10 | 11 | ```swift 12 | dependencies: [ 13 | .package(name: "WasmInterpreter", url: "https://github.com/shareup/wasm-interpreter-apple.git", from: "0.5.3"), 14 | ], 15 | ``` 16 | 17 | ## Troubleshooting 18 | 19 | ### Can't build tests with 'ld: Could not open or create -dependency_info file' error 20 | 21 | The Address Sanitizer can't be run when linking against SynchronizedDynamic—the dynamic library version of Synchronized. So, either change WasmInterpreter to use Synchronized or disable the Address Sanitizer. 22 | 23 | ## License 24 | 25 | The license for `WasmInterpreter` is the standard MIT license. You can find it in the `LICENSE` file. 26 | 27 | -------------------------------------------------------------------------------- /Sources/WasmInterpreter/Heap.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | struct Heap { 4 | let pointer: UnsafeMutablePointer 5 | let size: Int 6 | 7 | func isValid(byteOffset: Int, length: Int) -> Bool { 8 | (byteOffset + length) <= size 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Sources/WasmInterpreter/ImportedFunctionCache.swift: -------------------------------------------------------------------------------- 1 | import CWasm3 2 | import Foundation 3 | import Synchronized 4 | 5 | // MARK: - Managing imported functions 6 | 7 | func setImportedFunction( 8 | _ function: @escaping ImportedFunctionSignature, 9 | for context: UnsafeMutableRawPointer, 10 | instanceIdentifier id: UInt64 11 | ) { 12 | lock.locked { 13 | if var functionsForID = importedFunctionCache[id] { 14 | functionsForID[context] = function 15 | importedFunctionCache[id] = functionsForID 16 | } else { 17 | let functionsForID = [context: function] 18 | importedFunctionCache[id] = functionsForID 19 | } 20 | } 21 | } 22 | 23 | func removeImportedFunction( 24 | for context: UnsafeMutableRawPointer, 25 | instanceIdentifier id: UInt64 26 | ) { 27 | lock.locked { 28 | guard var functionsForID = importedFunctionCache[id] else { return } 29 | functionsForID.removeValue(forKey: context) 30 | importedFunctionCache[id] = functionsForID 31 | } 32 | } 33 | 34 | func removeImportedFunctions(forInstanceIdentifier id: UInt64) { 35 | lock.locked { 36 | _ = importedFunctionCache.removeValue(forKey: id) 37 | } 38 | } 39 | 40 | func importedFunction( 41 | for userData: UnsafeMutableRawPointer?, 42 | instanceIdentifier id: UInt64 43 | ) -> ImportedFunctionSignature? { 44 | guard let context = userData else { return nil } 45 | return lock.locked { importedFunctionCache[id]?[context] } 46 | } 47 | 48 | func handleImportedFunction( 49 | _ runtime: UnsafeMutablePointer?, 50 | _ context: UnsafeMutablePointer?, 51 | _ stackPointer: UnsafeMutablePointer?, 52 | _ heap: UnsafeMutableRawPointer? 53 | ) -> UnsafeRawPointer? { 54 | guard let id = m3_GetUserData(runtime)?.load(as: UInt64.self) 55 | else { return UnsafeRawPointer(m3Err_trapUnreachable) } 56 | 57 | guard let userData = context?.pointee.userdata 58 | else { return UnsafeRawPointer(m3Err_trapUnreachable) } 59 | 60 | guard let function = importedFunction(for: userData, instanceIdentifier: id) 61 | else { return UnsafeRawPointer(m3Err_trapUnreachable) } 62 | 63 | return function(stackPointer, heap) 64 | } 65 | 66 | // MARK: - Generating instance identifiers 67 | 68 | var nextInstanceIdentifier: UInt64 { 69 | lock.locked { 70 | lastInstanceIdentifier += 1 71 | return lastInstanceIdentifier 72 | } 73 | } 74 | 75 | func makeRawPointer(for id: UInt64) -> UnsafeMutableRawPointer { 76 | let ptr = UnsafeMutableRawPointer.allocate( 77 | byteCount: MemoryLayout.size, 78 | alignment: MemoryLayout.alignment 79 | ) 80 | ptr.storeBytes(of: id, as: UInt64.self) 81 | return ptr 82 | } 83 | 84 | // MARK: - Private 85 | 86 | private let lock = Lock() 87 | private var lastInstanceIdentifier: UInt64 = 0 88 | private var importedFunctionCache = 89 | [UInt64: [UnsafeMutableRawPointer: ImportedFunctionSignature]]() 90 | -------------------------------------------------------------------------------- /Sources/WasmInterpreter/NativeFunction.swift: -------------------------------------------------------------------------------- 1 | import CWasm3 2 | import Foundation 3 | 4 | struct NativeFunction { 5 | static func argument( 6 | from stack: UnsafeMutablePointer?, 7 | at index: Int 8 | ) throws -> Arg { 9 | guard let stack = UnsafeMutableRawPointer(stack) 10 | else { throw WasmInterpreterError.invalidStackPointer } 11 | guard isValidWasmType(Arg.self) else { 12 | throw WasmInterpreterError.unsupportedWasmType(String(describing: Arg.self)) 13 | } 14 | 15 | return stack.load( 16 | fromByteOffset: index * MemoryLayout.stride, 17 | as: Arg.self 18 | ) 19 | } 20 | 21 | /// Extracts the imported function's arguments of the specified types from the stack. 22 | /// 23 | /// - Throws: Throws if the stack pointer is `nil`. 24 | /// 25 | /// - Parameters: 26 | /// - types: The expected argument types. 27 | /// - stack: The stack pointer. 28 | /// 29 | /// - Returns: Array of the imported function's arguments. 30 | public static func arguments( 31 | withTypes types: [WasmType], 32 | from stack: UnsafeMutablePointer? 33 | ) throws -> [WasmValue] { 34 | guard let stack = UnsafeMutableRawPointer(stack) 35 | else { throw WasmInterpreterError.invalidStackPointer } 36 | 37 | var values = [WasmValue]() 38 | for (index, type) in types.enumerated() { 39 | switch type { 40 | case .int32: 41 | let value = stack.load( 42 | fromByteOffset: index * MemoryLayout.stride, 43 | as: Int32.self 44 | ) 45 | values.append(WasmValue.int32(value)) 46 | case .int64: 47 | let value = stack.load( 48 | fromByteOffset: index * MemoryLayout.stride, 49 | as: Int64.self 50 | ) 51 | values.append(WasmValue.int64(value)) 52 | case .float32: 53 | let value = stack.load( 54 | fromByteOffset: index * MemoryLayout.stride, 55 | as: Float32.self 56 | ) 57 | values.append(WasmValue.float32(value)) 58 | case .float64: 59 | let value = stack.load( 60 | fromByteOffset: index * MemoryLayout.stride, 61 | as: Float64.self 62 | ) 63 | values.append(WasmValue.float64(value)) 64 | } 65 | } 66 | 67 | return values 68 | } 69 | 70 | /// Places the specified return value on the stack. 71 | /// 72 | /// - Throws: Throws if the stack pointer is `nil` or if `Ret` is not of type 73 | /// `Int32`, `Int64`, `Float32`, or `Float64`. 74 | /// 75 | /// - Parameters: 76 | /// - ret: The value to return from the imported function. 77 | /// - stack: The stack pointer. 78 | static func pushReturnValue( 79 | _ ret: Ret, 80 | to stack: UnsafeMutablePointer? 81 | ) throws { 82 | guard let stack = UnsafeMutableRawPointer(stack) 83 | else { throw WasmInterpreterError.invalidStackPointer } 84 | guard isValidWasmType(Ret.self) else { 85 | throw WasmInterpreterError.unsupportedWasmType(String(describing: Ret.self)) 86 | } 87 | 88 | stack.storeBytes(of: ret, as: Ret.self) 89 | } 90 | 91 | /// Places the specified return value on the stack. 92 | /// 93 | /// - Throws: Throws if the stack pointer is `nil`. 94 | /// 95 | /// - Parameters: 96 | /// - ret: The value to return from the imported function. 97 | /// - stack: The stack pointer. 98 | static func pushReturnValue( 99 | _ ret: WasmValue, 100 | to stack: UnsafeMutablePointer? 101 | ) throws { 102 | guard let stack = UnsafeMutableRawPointer(stack) 103 | else { throw WasmInterpreterError.invalidStackPointer } 104 | 105 | switch ret { 106 | case let .int32(value): 107 | stack.storeBytes(of: value, as: Int32.self) 108 | case let .int64(value): 109 | stack.storeBytes(of: value, as: Int64.self) 110 | case let .float32(value): 111 | stack.storeBytes(of: value, as: Float32.self) 112 | case let .float64(value): 113 | stack.storeBytes(of: value, as: Float64.self) 114 | } 115 | } 116 | } 117 | 118 | let importedFunctionInternalError = UnsafeRawPointer(UnsafeMutableRawPointer.allocate( 119 | byteCount: _importedFunctionInternalError.count, alignment: MemoryLayout.alignment 120 | )) 121 | private let _importedFunctionInternalError = "ImportedFunctionInternalError".utf8CString 122 | -------------------------------------------------------------------------------- /Sources/WasmInterpreter/NativeFunctionSignature.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | func signature() throws -> String { "v()" } 4 | 5 | func signature( 6 | arg1: (some WasmTypeProtocol).Type 7 | ) throws -> String { 8 | var signature = "v" 9 | signature += "(" 10 | signature += try signatureIdentifier(for: arg1.self) 11 | signature += ")" 12 | return signature 13 | } 14 | 15 | func signature( 16 | ret: (some WasmTypeProtocol).Type 17 | ) throws -> String { 18 | var signature = "" 19 | signature += try signatureIdentifier(for: ret.self) 20 | signature += "(" 21 | signature += ")" 22 | return signature 23 | } 24 | 25 | func signature( 26 | arg1: (some WasmTypeProtocol).Type, 27 | ret: (some WasmTypeProtocol).Type 28 | ) throws -> String { 29 | var signature = "" 30 | signature += try signatureIdentifier(for: ret.self) 31 | signature += "(" 32 | signature += try signatureIdentifier(for: arg1.self) 33 | signature += ")" 34 | return signature 35 | } 36 | 37 | func signature( 38 | arg1: (some WasmTypeProtocol).Type, 39 | arg2: (some WasmTypeProtocol).Type 40 | ) throws -> String { 41 | var signature = "v" 42 | signature += "(" 43 | signature += try signatureIdentifier(for: arg1.self) + " " 44 | signature += try signatureIdentifier(for: arg2.self) 45 | signature += ")" 46 | return signature 47 | } 48 | 49 | func signature( 50 | arg1: (some WasmTypeProtocol).Type, 51 | arg2: (some WasmTypeProtocol).Type, 52 | ret: (some WasmTypeProtocol).Type 53 | ) throws -> String { 54 | var signature = "" 55 | signature += try signatureIdentifier(for: ret.self) 56 | signature += "(" 57 | signature += try signatureIdentifier(for: arg1.self) + " " 58 | signature += try signatureIdentifier(for: arg2.self) 59 | signature += ")" 60 | return signature 61 | } 62 | 63 | func signature( 64 | arg1: (some WasmTypeProtocol).Type, 65 | arg2: (some WasmTypeProtocol).Type, 66 | arg3: (some WasmTypeProtocol).Type 67 | ) throws -> String { 68 | var signature = "v" 69 | signature += "(" 70 | signature += try signatureIdentifier(for: arg1.self) + " " 71 | signature += try signatureIdentifier(for: arg2.self) + " " 72 | signature += try signatureIdentifier(for: arg3.self) 73 | signature += ")" 74 | return signature 75 | } 76 | 77 | func signature( 78 | arg1: (some WasmTypeProtocol).Type, 79 | arg2: (some WasmTypeProtocol).Type, 80 | arg3: (some WasmTypeProtocol).Type, 81 | ret: (some WasmTypeProtocol).Type 82 | ) throws -> String { 83 | var signature = "" 84 | signature += try signatureIdentifier(for: ret.self) 85 | signature += "(" 86 | signature += try signatureIdentifier(for: arg1.self) + " " 87 | signature += try signatureIdentifier(for: arg2.self) + " " 88 | signature += try signatureIdentifier(for: arg3.self) 89 | signature += ")" 90 | return signature 91 | } 92 | 93 | func signature( 94 | arg1: (some WasmTypeProtocol).Type, 95 | arg2: (some WasmTypeProtocol).Type, 96 | arg3: (some WasmTypeProtocol).Type, 97 | arg4: (some WasmTypeProtocol).Type 98 | ) throws -> String { 99 | var signature = "v" 100 | signature += "(" 101 | signature += try signatureIdentifier(for: arg1.self) + " " 102 | signature += try signatureIdentifier(for: arg2.self) + " " 103 | signature += try signatureIdentifier(for: arg3.self) + " " 104 | signature += try signatureIdentifier(for: arg4.self) 105 | signature += ")" 106 | return signature 107 | } 108 | 109 | func signature( 110 | arg1: (some WasmTypeProtocol).Type, 111 | arg2: (some WasmTypeProtocol).Type, 112 | arg3: (some WasmTypeProtocol).Type, 113 | arg4: (some WasmTypeProtocol).Type, 114 | ret: (some WasmTypeProtocol).Type 115 | ) throws -> String { 116 | var signature = "" 117 | signature += try signatureIdentifier(for: ret.self) 118 | signature += "(" 119 | signature += try signatureIdentifier(for: arg1.self) + " " 120 | signature += try signatureIdentifier(for: arg2.self) + " " 121 | signature += try signatureIdentifier(for: arg3.self) + " " 122 | signature += try signatureIdentifier(for: arg4.self) 123 | signature += ")" 124 | return signature 125 | } 126 | 127 | func signature( 128 | arg1: (some WasmTypeProtocol).Type, 129 | arg2: (some WasmTypeProtocol).Type, 130 | arg3: (some WasmTypeProtocol).Type, 131 | arg4: (some WasmTypeProtocol).Type, 132 | arg5: (some WasmTypeProtocol).Type 133 | ) throws -> String { 134 | var signature = "v" 135 | signature += "(" 136 | signature += try signatureIdentifier(for: arg1.self) + " " 137 | signature += try signatureIdentifier(for: arg2.self) + " " 138 | signature += try signatureIdentifier(for: arg3.self) + " " 139 | signature += try signatureIdentifier(for: arg4.self) + " " 140 | signature += try signatureIdentifier(for: arg5.self) 141 | signature += ")" 142 | return signature 143 | } 144 | 145 | func signature( 146 | arg1: (some WasmTypeProtocol).Type, 147 | arg2: (some WasmTypeProtocol).Type, 148 | arg3: (some WasmTypeProtocol).Type, 149 | arg4: (some WasmTypeProtocol).Type, 150 | arg5: (some WasmTypeProtocol).Type, 151 | ret: (some WasmTypeProtocol).Type 152 | ) throws -> String { 153 | var signature = "" 154 | signature += try signatureIdentifier(for: ret.self) 155 | signature += "(" 156 | signature += try signatureIdentifier(for: arg1.self) + " " 157 | signature += try signatureIdentifier(for: arg2.self) + " " 158 | signature += try signatureIdentifier(for: arg3.self) + " " 159 | signature += try signatureIdentifier(for: arg4.self) + " " 160 | signature += try signatureIdentifier(for: arg5.self) 161 | signature += ")" 162 | return signature 163 | } 164 | 165 | func signature( 166 | arg1: (some WasmTypeProtocol).Type, 167 | arg2: (some WasmTypeProtocol).Type, 168 | arg3: (some WasmTypeProtocol).Type, 169 | arg4: (some WasmTypeProtocol).Type, 170 | arg5: (some WasmTypeProtocol).Type, 171 | arg6: (some WasmTypeProtocol).Type 172 | ) throws -> String { 173 | var signature = "v" 174 | signature += "(" 175 | signature += try signatureIdentifier(for: arg1.self) + " " 176 | signature += try signatureIdentifier(for: arg2.self) + " " 177 | signature += try signatureIdentifier(for: arg3.self) + " " 178 | signature += try signatureIdentifier(for: arg4.self) + " " 179 | signature += try signatureIdentifier(for: arg5.self) + " " 180 | signature += try signatureIdentifier(for: arg6.self) 181 | signature += ")" 182 | return signature 183 | } 184 | 185 | func signature( 186 | arg1: (some WasmTypeProtocol).Type, 187 | arg2: (some WasmTypeProtocol).Type, 188 | arg3: (some WasmTypeProtocol).Type, 189 | arg4: (some WasmTypeProtocol).Type, 190 | arg5: (some WasmTypeProtocol).Type, 191 | arg6: (some WasmTypeProtocol).Type, 192 | ret: (some WasmTypeProtocol).Type 193 | ) throws -> String { 194 | var signature = "" 195 | signature += try signatureIdentifier(for: ret.self) 196 | signature += "(" 197 | signature += try signatureIdentifier(for: arg1.self) + " " 198 | signature += try signatureIdentifier(for: arg2.self) + " " 199 | signature += try signatureIdentifier(for: arg3.self) + " " 200 | signature += try signatureIdentifier(for: arg4.self) + " " 201 | signature += try signatureIdentifier(for: arg5.self) + " " 202 | signature += try signatureIdentifier(for: arg6.self) 203 | signature += ")" 204 | return signature 205 | } 206 | 207 | func signature( 208 | arg1: (some WasmTypeProtocol).Type, 209 | arg2: (some WasmTypeProtocol).Type, 210 | arg3: (some WasmTypeProtocol).Type, 211 | arg4: (some WasmTypeProtocol).Type, 212 | arg5: (some WasmTypeProtocol).Type, 213 | arg6: (some WasmTypeProtocol).Type, 214 | arg7: (some WasmTypeProtocol).Type 215 | ) throws -> String { 216 | var signature = "v" 217 | signature += "(" 218 | signature += try signatureIdentifier(for: arg1.self) + " " 219 | signature += try signatureIdentifier(for: arg2.self) + " " 220 | signature += try signatureIdentifier(for: arg3.self) + " " 221 | signature += try signatureIdentifier(for: arg4.self) + " " 222 | signature += try signatureIdentifier(for: arg5.self) + " " 223 | signature += try signatureIdentifier(for: arg6.self) + " " 224 | signature += try signatureIdentifier(for: arg7.self) 225 | signature += ")" 226 | return signature 227 | } 228 | 229 | func signature( 230 | arg1: (some WasmTypeProtocol).Type, 231 | arg2: (some WasmTypeProtocol).Type, 232 | arg3: (some WasmTypeProtocol).Type, 233 | arg4: (some WasmTypeProtocol).Type, 234 | arg5: (some WasmTypeProtocol).Type, 235 | arg6: (some WasmTypeProtocol).Type, 236 | arg7: (some WasmTypeProtocol).Type, 237 | ret: (some WasmTypeProtocol).Type 238 | ) throws -> String { 239 | var signature = "" 240 | signature += try signatureIdentifier(for: ret.self) 241 | signature += "(" 242 | signature += try signatureIdentifier(for: arg1.self) + " " 243 | signature += try signatureIdentifier(for: arg2.self) + " " 244 | signature += try signatureIdentifier(for: arg3.self) + " " 245 | signature += try signatureIdentifier(for: arg4.self) + " " 246 | signature += try signatureIdentifier(for: arg5.self) + " " 247 | signature += try signatureIdentifier(for: arg6.self) + " " 248 | signature += try signatureIdentifier(for: arg7.self) 249 | signature += ")" 250 | return signature 251 | } 252 | 253 | func signature( 254 | arg1: (some WasmTypeProtocol).Type, 255 | arg2: (some WasmTypeProtocol).Type, 256 | arg3: (some WasmTypeProtocol).Type, 257 | arg4: (some WasmTypeProtocol).Type, 258 | arg5: (some WasmTypeProtocol).Type, 259 | arg6: (some WasmTypeProtocol).Type, 260 | arg7: (some WasmTypeProtocol).Type, 261 | arg8: (some WasmTypeProtocol).Type 262 | ) throws -> String { 263 | var signature = "v" 264 | signature += "(" 265 | signature += try signatureIdentifier(for: arg1.self) + " " 266 | signature += try signatureIdentifier(for: arg2.self) + " " 267 | signature += try signatureIdentifier(for: arg3.self) + " " 268 | signature += try signatureIdentifier(for: arg4.self) + " " 269 | signature += try signatureIdentifier(for: arg5.self) + " " 270 | signature += try signatureIdentifier(for: arg6.self) + " " 271 | signature += try signatureIdentifier(for: arg7.self) + " " 272 | signature += try signatureIdentifier(for: arg8.self) 273 | signature += ")" 274 | return signature 275 | } 276 | 277 | func signature( 278 | arg1: (some WasmTypeProtocol).Type, 279 | arg2: (some WasmTypeProtocol).Type, 280 | arg3: (some WasmTypeProtocol).Type, 281 | arg4: (some WasmTypeProtocol).Type, 282 | arg5: (some WasmTypeProtocol).Type, 283 | arg6: (some WasmTypeProtocol).Type, 284 | arg7: (some WasmTypeProtocol).Type, 285 | arg8: (some WasmTypeProtocol).Type, 286 | ret: (some WasmTypeProtocol).Type 287 | ) throws -> String { 288 | var signature = "" 289 | signature += try signatureIdentifier(for: ret.self) 290 | signature += "(" 291 | signature += try signatureIdentifier(for: arg1.self) + " " 292 | signature += try signatureIdentifier(for: arg2.self) + " " 293 | signature += try signatureIdentifier(for: arg3.self) + " " 294 | signature += try signatureIdentifier(for: arg4.self) + " " 295 | signature += try signatureIdentifier(for: arg5.self) + " " 296 | signature += try signatureIdentifier(for: arg6.self) + " " 297 | signature += try signatureIdentifier(for: arg7.self) + " " 298 | signature += try signatureIdentifier(for: arg8.self) 299 | signature += ")" 300 | return signature 301 | } 302 | 303 | func signatureIdentifier(for _: T.Type) throws -> String { 304 | if Int32.self == T.self { return "i" } 305 | else if Int64.self == T.self { return "I" } 306 | else if Float32.self == T.self { return "f" } 307 | else if Float64.self == T.self { return "F" } 308 | else { throw WasmInterpreterError.unsupportedWasmType(String(describing: T.self)) } 309 | } 310 | -------------------------------------------------------------------------------- /Sources/WasmInterpreter/String+WasmInterpreter.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension [String] { 4 | func withCStrings( 5 | _ body: ([UnsafePointer?]) throws -> Result 6 | ) rethrows -> Result { 7 | let lengths = map { $0.utf8.count + 1 } 8 | let (offsets, totalLength) = lengths.offsetsAndTotalLength() 9 | 10 | var buffer: [UInt8] = [] 11 | buffer.reserveCapacity(totalLength) 12 | for string in self { 13 | buffer.append(contentsOf: string.utf8) 14 | buffer.append(0) 15 | } 16 | 17 | return try buffer.withUnsafeBufferPointer { buffer -> Result in 18 | let pointer = UnsafeRawPointer(buffer.baseAddress!) 19 | .bindMemory(to: CChar.self, capacity: buffer.count) 20 | var cStrings: [UnsafePointer?] = offsets.map { pointer + $0 } 21 | cStrings.append(nil) 22 | return try body(cStrings) 23 | } 24 | } 25 | } 26 | 27 | private extension [Int] { 28 | func offsetsAndTotalLength() -> ([Int], Int) { 29 | var output = [0] 30 | var total = 0 31 | for length in self { 32 | total += length 33 | output.append(total) 34 | } 35 | return (output.dropLast(), total) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Sources/WasmInterpreter/String+WasmTypeProtocol.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension String { 4 | init(wasmType: T) throws { 5 | if Int32.self == T.self { 6 | self = "\(wasmType as! Int32)" 7 | } else if Int64.self == T.self { 8 | self = "\(wasmType as! Int64)" 9 | } else if Float32.self == T.self { 10 | self = "\(wasmType as! Float32)" 11 | } else if Float64.self == T.self { 12 | self = "\(wasmType as! Float64)" 13 | } else { 14 | throw WasmInterpreterError.unsupportedWasmType(String(describing: wasmType.self)) 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Sources/WasmInterpreter/WasmInterpreter+Call.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public extension WasmInterpreter { 4 | func call(_ name: String) throws { 5 | try _call(try function(named: name), args: []) 6 | } 7 | 8 | func call(_ name: String) throws -> Ret where Ret: WasmTypeProtocol { 9 | try _call(try function(named: name), args: []) 10 | } 11 | 12 | func call(_ name: String, _ arg1: some WasmTypeProtocol) throws { 13 | try _call(try function(named: name), args: [try String(wasmType: arg1)]) 14 | } 15 | 16 | func call( 17 | _ name: String, _ arg1: some WasmTypeProtocol 18 | ) throws -> Ret where Ret: WasmTypeProtocol { 19 | try _call(try function(named: name), args: [try String(wasmType: arg1)]) 20 | } 21 | 22 | func call( 23 | _ name: String, _ arg1: some WasmTypeProtocol, _ arg2: some WasmTypeProtocol 24 | ) throws { 25 | try _call( 26 | try function(named: name), 27 | args: [try String(wasmType: arg1), try String(wasmType: arg2)] 28 | ) 29 | } 30 | 31 | func call( 32 | _ name: String, 33 | _ arg1: some WasmTypeProtocol, 34 | _ arg2: some WasmTypeProtocol 35 | ) throws -> Ret where Ret: WasmTypeProtocol { 36 | try _call( 37 | try function(named: name), 38 | args: [try String(wasmType: arg1), try String(wasmType: arg2)] 39 | ) 40 | } 41 | 42 | func call( 43 | _ name: String, _ arg1: some WasmTypeProtocol, _ arg2: some WasmTypeProtocol, 44 | _ arg3: some WasmTypeProtocol 45 | ) throws { 46 | try _call( 47 | try function(named: name), 48 | args: [ 49 | try String(wasmType: arg1), 50 | try String(wasmType: arg2), 51 | try String(wasmType: arg3), 52 | ] 53 | ) 54 | } 55 | 56 | func call( 57 | _ name: String, _ arg1: some WasmTypeProtocol, _ arg2: some WasmTypeProtocol, 58 | _ arg3: some WasmTypeProtocol 59 | ) throws -> Ret 60 | where Ret: WasmTypeProtocol 61 | { 62 | try _call( 63 | try function(named: name), 64 | args: [ 65 | try String(wasmType: arg1), 66 | try String(wasmType: arg2), 67 | try String(wasmType: arg3), 68 | ] 69 | ) 70 | } 71 | 72 | func call( 73 | _ name: String, _ arg1: some WasmTypeProtocol, _ arg2: some WasmTypeProtocol, 74 | _ arg3: some WasmTypeProtocol, _ arg4: some WasmTypeProtocol 75 | ) throws { 76 | try _call( 77 | try function(named: name), 78 | args: [ 79 | try String(wasmType: arg1), 80 | try String(wasmType: arg2), 81 | try String(wasmType: arg3), 82 | try String(wasmType: arg4), 83 | ] 84 | ) 85 | } 86 | 87 | func call( 88 | _ name: String, _ arg1: some WasmTypeProtocol, _ arg2: some WasmTypeProtocol, 89 | _ arg3: some WasmTypeProtocol, _ arg4: some WasmTypeProtocol 90 | ) throws -> Ret 91 | where Ret: WasmTypeProtocol 92 | { 93 | try _call( 94 | try function(named: name), 95 | args: [ 96 | try String(wasmType: arg1), 97 | try String(wasmType: arg2), 98 | try String(wasmType: arg3), 99 | try String(wasmType: arg4), 100 | ] 101 | ) 102 | } 103 | 104 | func call( 105 | _ name: String, _ arg1: some WasmTypeProtocol, _ arg2: some WasmTypeProtocol, 106 | _ arg3: some WasmTypeProtocol, _ arg4: some WasmTypeProtocol, 107 | _ arg5: some WasmTypeProtocol 108 | ) throws { 109 | try _call( 110 | try function(named: name), 111 | args: [ 112 | try String(wasmType: arg1), 113 | try String(wasmType: arg2), 114 | try String(wasmType: arg3), 115 | try String(wasmType: arg4), 116 | try String(wasmType: arg5), 117 | ] 118 | ) 119 | } 120 | 121 | func call( 122 | _ name: String, _ arg1: some WasmTypeProtocol, _ arg2: some WasmTypeProtocol, 123 | _ arg3: some WasmTypeProtocol, _ arg4: some WasmTypeProtocol, 124 | _ arg5: some WasmTypeProtocol 125 | ) throws -> Ret 126 | where Ret: WasmTypeProtocol 127 | { 128 | try _call( 129 | try function(named: name), 130 | args: [ 131 | try String(wasmType: arg1), 132 | try String(wasmType: arg2), 133 | try String(wasmType: arg3), 134 | try String(wasmType: arg4), 135 | try String(wasmType: arg5), 136 | ] 137 | ) 138 | } 139 | 140 | func call( 141 | _ name: String, _ arg1: some WasmTypeProtocol, _ arg2: some WasmTypeProtocol, 142 | _ arg3: some WasmTypeProtocol, 143 | _ arg4: some WasmTypeProtocol, _ arg5: some WasmTypeProtocol, 144 | _ arg6: some WasmTypeProtocol 145 | ) throws { 146 | try _call( 147 | try function(named: name), 148 | args: [ 149 | try String(wasmType: arg1), 150 | try String(wasmType: arg2), 151 | try String(wasmType: arg3), 152 | try String(wasmType: arg4), 153 | try String(wasmType: arg5), 154 | try String(wasmType: arg6), 155 | ] 156 | ) 157 | } 158 | 159 | func call( 160 | _ name: String, _ arg1: some WasmTypeProtocol, _ arg2: some WasmTypeProtocol, 161 | _ arg3: some WasmTypeProtocol, 162 | _ arg4: some WasmTypeProtocol, _ arg5: some WasmTypeProtocol, 163 | _ arg6: some WasmTypeProtocol 164 | ) throws -> Ret 165 | where Ret: WasmTypeProtocol 166 | { 167 | try _call( 168 | try function(named: name), 169 | args: [ 170 | try String(wasmType: arg1), 171 | try String(wasmType: arg2), 172 | try String(wasmType: arg3), 173 | try String(wasmType: arg4), 174 | try String(wasmType: arg5), 175 | try String(wasmType: arg6), 176 | ] 177 | ) 178 | } 179 | 180 | func call( 181 | _ name: String, _ arg1: some WasmTypeProtocol, _ arg2: some WasmTypeProtocol, 182 | _ arg3: some WasmTypeProtocol, 183 | _ arg4: some WasmTypeProtocol, _ arg5: some WasmTypeProtocol, 184 | _ arg6: some WasmTypeProtocol, _ arg7: some WasmTypeProtocol 185 | ) throws { 186 | try _call( 187 | try function(named: name), 188 | args: [ 189 | try String(wasmType: arg1), 190 | try String(wasmType: arg2), 191 | try String(wasmType: arg3), 192 | try String(wasmType: arg4), 193 | try String(wasmType: arg5), 194 | try String(wasmType: arg6), 195 | try String(wasmType: arg7), 196 | ] 197 | ) 198 | } 199 | 200 | func call( 201 | _ name: String, _ arg1: some WasmTypeProtocol, _ arg2: some WasmTypeProtocol, 202 | _ arg3: some WasmTypeProtocol, 203 | _ arg4: some WasmTypeProtocol, _ arg5: some WasmTypeProtocol, 204 | _ arg6: some WasmTypeProtocol, _ arg7: some WasmTypeProtocol 205 | ) throws -> Ret 206 | where Ret: WasmTypeProtocol 207 | { 208 | try _call( 209 | try function(named: name), 210 | args: [ 211 | try String(wasmType: arg1), 212 | try String(wasmType: arg2), 213 | try String(wasmType: arg3), 214 | try String(wasmType: arg4), 215 | try String(wasmType: arg5), 216 | try String(wasmType: arg6), 217 | try String(wasmType: arg7), 218 | ] 219 | ) 220 | } 221 | 222 | func call( 223 | _ name: String, _ arg1: some WasmTypeProtocol, _ arg2: some WasmTypeProtocol, 224 | _ arg3: some WasmTypeProtocol, 225 | _ arg4: some WasmTypeProtocol, _ arg5: some WasmTypeProtocol, 226 | _ arg6: some WasmTypeProtocol, _ arg7: some WasmTypeProtocol, 227 | _ arg8: some WasmTypeProtocol 228 | ) throws { 229 | try _call( 230 | try function(named: name), 231 | args: [ 232 | try String(wasmType: arg1), 233 | try String(wasmType: arg2), 234 | try String(wasmType: arg3), 235 | try String(wasmType: arg4), 236 | try String(wasmType: arg5), 237 | try String(wasmType: arg6), 238 | try String(wasmType: arg7), 239 | try String(wasmType: arg8), 240 | ] 241 | ) 242 | } 243 | 244 | func call( 245 | _ name: String, _ arg1: some WasmTypeProtocol, _ arg2: some WasmTypeProtocol, 246 | _ arg3: some WasmTypeProtocol, 247 | _ arg4: some WasmTypeProtocol, _ arg5: some WasmTypeProtocol, 248 | _ arg6: some WasmTypeProtocol, _ arg7: some WasmTypeProtocol, 249 | _ arg8: some WasmTypeProtocol 250 | ) throws -> Ret 251 | where Ret: WasmTypeProtocol 252 | { 253 | try _call( 254 | try function(named: name), 255 | args: [ 256 | try String(wasmType: arg1), 257 | try String(wasmType: arg2), 258 | try String(wasmType: arg3), 259 | try String(wasmType: arg4), 260 | try String(wasmType: arg5), 261 | try String(wasmType: arg6), 262 | try String(wasmType: arg7), 263 | try String(wasmType: arg8), 264 | ] 265 | ) 266 | } 267 | } 268 | -------------------------------------------------------------------------------- /Sources/WasmInterpreter/WasmInterpreter+ImportHandler.swift: -------------------------------------------------------------------------------- 1 | import CWasm3 2 | import Foundation 3 | 4 | // Arguments and return values are passed in and out through the stack pointer 5 | // of imported functions. 6 | // 7 | // Placeholder return value slots are first and arguments after. So, the first 8 | // argument is at _sp [numReturns]. 9 | // 10 | // Return values should be written into _sp [0] to _sp [num_returns - 1]. 11 | // 12 | // Wasm3 always aligns the stack to 64 bits. 13 | 14 | public extension WasmInterpreter { 15 | func addImportHandler( 16 | named name: String, 17 | namespace: String, 18 | block: @escaping () throws -> Void 19 | ) throws { 20 | let importedFunction: ImportedFunctionSignature = 21 | { (_: UnsafeMutablePointer?, _: UnsafeMutableRawPointer?) -> UnsafeRawPointer? in 22 | do { 23 | try block() 24 | return nil 25 | } catch { 26 | return importedFunctionInternalError 27 | } 28 | } 29 | let sig = try signature() 30 | try importNativeFunction( 31 | named: name, 32 | namespace: namespace, 33 | signature: sig, 34 | handler: importedFunction 35 | ) 36 | } 37 | 38 | func addImportHandler( 39 | named name: String, 40 | namespace: String, 41 | block: @escaping (UnsafeMutableRawPointer?) throws -> Void 42 | ) throws { 43 | let importedFunction: ImportedFunctionSignature = 44 | { (_: UnsafeMutablePointer?, heap: UnsafeMutableRawPointer?) -> UnsafeRawPointer? in 45 | do { 46 | try block(heap) 47 | return nil 48 | } catch { 49 | return importedFunctionInternalError 50 | } 51 | } 52 | let sig = try signature() 53 | try importNativeFunction( 54 | named: name, 55 | namespace: namespace, 56 | signature: sig, 57 | handler: importedFunction 58 | ) 59 | } 60 | 61 | func addImportHandler( 62 | named name: String, 63 | namespace: String, 64 | block: @escaping () throws -> Ret 65 | ) throws where Ret: WasmTypeProtocol { 66 | let importedFunction: ImportedFunctionSignature = 67 | { (stack: UnsafeMutablePointer?, _: UnsafeMutableRawPointer?) -> UnsafeRawPointer? in 68 | do { 69 | let ret = try block() 70 | try NativeFunction.pushReturnValue(ret, to: stack) 71 | return nil 72 | } catch { 73 | return importedFunctionInternalError 74 | } 75 | } 76 | let sig = try signature(ret: Ret.self) 77 | try importNativeFunction( 78 | named: name, 79 | namespace: namespace, 80 | signature: sig, 81 | handler: importedFunction 82 | ) 83 | } 84 | 85 | func addImportHandler( 86 | named name: String, 87 | namespace: String, 88 | block: @escaping (UnsafeMutableRawPointer?) throws -> Ret 89 | ) throws where Ret: WasmTypeProtocol { 90 | let importedFunction: ImportedFunctionSignature = 91 | { (stack: UnsafeMutablePointer?, heap: UnsafeMutableRawPointer?) -> UnsafeRawPointer? in 92 | do { 93 | let ret = try block(heap) 94 | try NativeFunction.pushReturnValue(ret, to: stack) 95 | return nil 96 | } catch { 97 | return importedFunctionInternalError 98 | } 99 | } 100 | let sig = try signature(ret: Ret.self) 101 | try importNativeFunction( 102 | named: name, 103 | namespace: namespace, 104 | signature: sig, 105 | handler: importedFunction 106 | ) 107 | } 108 | 109 | func addImportHandler( 110 | named name: String, 111 | namespace: String, 112 | block: @escaping (Arg1) throws -> Void 113 | ) throws where Arg1: WasmTypeProtocol { 114 | let importedFunction: ImportedFunctionSignature = 115 | { (stack: UnsafeMutablePointer?, _: UnsafeMutableRawPointer?) -> UnsafeRawPointer? in 116 | do { 117 | let arg1: Arg1 = try NativeFunction.argument(from: stack, at: 0) 118 | try block(arg1) 119 | return nil 120 | } catch { 121 | return importedFunctionInternalError 122 | } 123 | } 124 | let sig = try signature(arg1: Arg1.self) 125 | try importNativeFunction( 126 | named: name, 127 | namespace: namespace, 128 | signature: sig, 129 | handler: importedFunction 130 | ) 131 | } 132 | 133 | func addImportHandler( 134 | named name: String, 135 | namespace: String, 136 | block: @escaping (Arg1, UnsafeMutableRawPointer?) throws -> Void 137 | ) throws where Arg1: WasmTypeProtocol { 138 | let importedFunction: ImportedFunctionSignature = 139 | { (stack: UnsafeMutablePointer?, heap: UnsafeMutableRawPointer?) -> UnsafeRawPointer? in 140 | do { 141 | let arg1: Arg1 = try NativeFunction.argument(from: stack, at: 0) 142 | try block(arg1, heap) 143 | return nil 144 | } catch { 145 | return importedFunctionInternalError 146 | } 147 | } 148 | let sig = try signature(arg1: Arg1.self) 149 | try importNativeFunction( 150 | named: name, 151 | namespace: namespace, 152 | signature: sig, 153 | handler: importedFunction 154 | ) 155 | } 156 | 157 | func addImportHandler( 158 | named name: String, 159 | namespace: String, 160 | block: @escaping (Arg1) throws -> Ret 161 | ) throws where Arg1: WasmTypeProtocol, Ret: WasmTypeProtocol { 162 | let importedFunction: ImportedFunctionSignature = 163 | { (stack: UnsafeMutablePointer?, _: UnsafeMutableRawPointer?) -> UnsafeRawPointer? in 164 | do { 165 | let arg1: Arg1 = try NativeFunction.argument(from: stack, at: 1) 166 | let ret = try block(arg1) 167 | try NativeFunction.pushReturnValue(ret, to: stack) 168 | return nil 169 | } catch { 170 | return importedFunctionInternalError 171 | } 172 | } 173 | let sig = try signature(arg1: Arg1.self, ret: Ret.self) 174 | try importNativeFunction( 175 | named: name, 176 | namespace: namespace, 177 | signature: sig, 178 | handler: importedFunction 179 | ) 180 | } 181 | 182 | func addImportHandler( 183 | named name: String, 184 | namespace: String, 185 | block: @escaping (Arg1, UnsafeMutableRawPointer?) throws -> Ret 186 | ) throws where Arg1: WasmTypeProtocol, Ret: WasmTypeProtocol { 187 | let importedFunction: ImportedFunctionSignature = 188 | { (stack: UnsafeMutablePointer?, heap: UnsafeMutableRawPointer?) -> UnsafeRawPointer? in 189 | do { 190 | let arg1: Arg1 = try NativeFunction.argument(from: stack, at: 1) 191 | let ret = try block(arg1, heap) 192 | try NativeFunction.pushReturnValue(ret, to: stack) 193 | return nil 194 | } catch { 195 | return importedFunctionInternalError 196 | } 197 | } 198 | let sig = try signature(arg1: Arg1.self, ret: Ret.self) 199 | try importNativeFunction( 200 | named: name, 201 | namespace: namespace, 202 | signature: sig, 203 | handler: importedFunction 204 | ) 205 | } 206 | 207 | func addImportHandler( 208 | named name: String, 209 | namespace: String, 210 | block: @escaping (Arg1, Arg2) throws -> Void 211 | ) throws where Arg1: WasmTypeProtocol, Arg2: WasmTypeProtocol { 212 | let importedFunction: ImportedFunctionSignature = 213 | { (stack: UnsafeMutablePointer?, _: UnsafeMutableRawPointer?) -> UnsafeRawPointer? in 214 | do { 215 | let arg1: Arg1 = try NativeFunction.argument(from: stack, at: 0) 216 | let arg2: Arg2 = try NativeFunction.argument(from: stack, at: 1) 217 | try block(arg1, arg2) 218 | return nil 219 | } catch { 220 | return importedFunctionInternalError 221 | } 222 | } 223 | let sig = try signature(arg1: Arg1.self, arg2: Arg2.self) 224 | try importNativeFunction( 225 | named: name, 226 | namespace: namespace, 227 | signature: sig, 228 | handler: importedFunction 229 | ) 230 | } 231 | 232 | func addImportHandler( 233 | named name: String, 234 | namespace: String, 235 | block: @escaping (Arg1, Arg2, UnsafeMutableRawPointer?) throws -> Void 236 | ) throws where Arg1: WasmTypeProtocol, Arg2: WasmTypeProtocol { 237 | let importedFunction: ImportedFunctionSignature = 238 | { (stack: UnsafeMutablePointer?, heap: UnsafeMutableRawPointer?) -> UnsafeRawPointer? in 239 | do { 240 | let arg1: Arg1 = try NativeFunction.argument(from: stack, at: 0) 241 | let arg2: Arg2 = try NativeFunction.argument(from: stack, at: 1) 242 | try block(arg1, arg2, heap) 243 | return nil 244 | } catch { 245 | return importedFunctionInternalError 246 | } 247 | } 248 | let sig = try signature(arg1: Arg1.self, arg2: Arg2.self) 249 | try importNativeFunction( 250 | named: name, 251 | namespace: namespace, 252 | signature: sig, 253 | handler: importedFunction 254 | ) 255 | } 256 | 257 | func addImportHandler( 258 | named name: String, 259 | namespace: String, 260 | block: @escaping (Arg1, Arg2) throws -> Ret 261 | ) throws where Arg1: WasmTypeProtocol, Arg2: WasmTypeProtocol, Ret: WasmTypeProtocol { 262 | let importedFunction: ImportedFunctionSignature = 263 | { (stack: UnsafeMutablePointer?, _: UnsafeMutableRawPointer?) -> UnsafeRawPointer? in 264 | do { 265 | let arg1: Arg1 = try NativeFunction.argument(from: stack, at: 1) 266 | let arg2: Arg2 = try NativeFunction.argument(from: stack, at: 2) 267 | let ret = try block(arg1, arg2) 268 | try NativeFunction.pushReturnValue(ret, to: stack) 269 | return nil 270 | } catch { 271 | return importedFunctionInternalError 272 | } 273 | } 274 | let sig = try signature(arg1: Arg1.self, arg2: Arg2.self, ret: Ret.self) 275 | try importNativeFunction( 276 | named: name, 277 | namespace: namespace, 278 | signature: sig, 279 | handler: importedFunction 280 | ) 281 | } 282 | 283 | func addImportHandler( 284 | named name: String, 285 | namespace: String, 286 | block: @escaping (Arg1, Arg2, UnsafeMutableRawPointer?) throws -> Ret 287 | ) throws where Arg1: WasmTypeProtocol, Arg2: WasmTypeProtocol, Ret: WasmTypeProtocol { 288 | let importedFunction: ImportedFunctionSignature = 289 | { (stack: UnsafeMutablePointer?, heap: UnsafeMutableRawPointer?) -> UnsafeRawPointer? in 290 | do { 291 | let arg1: Arg1 = try NativeFunction.argument(from: stack, at: 1) 292 | let arg2: Arg2 = try NativeFunction.argument(from: stack, at: 2) 293 | let ret = try block(arg1, arg2, heap) 294 | try NativeFunction.pushReturnValue(ret, to: stack) 295 | return nil 296 | } catch { 297 | return importedFunctionInternalError 298 | } 299 | } 300 | let sig = try signature(arg1: Arg1.self, arg2: Arg2.self, ret: Ret.self) 301 | try importNativeFunction( 302 | named: name, 303 | namespace: namespace, 304 | signature: sig, 305 | handler: importedFunction 306 | ) 307 | } 308 | 309 | func addImportHandler( 310 | named name: String, 311 | namespace: String, 312 | block: @escaping (Arg1, Arg2, Arg3) throws -> Void 313 | ) throws where Arg1: WasmTypeProtocol, Arg2: WasmTypeProtocol, Arg3: WasmTypeProtocol { 314 | let importedFunction: ImportedFunctionSignature = 315 | { (stack: UnsafeMutablePointer?, _: UnsafeMutableRawPointer?) -> UnsafeRawPointer? in 316 | do { 317 | let arg1: Arg1 = try NativeFunction.argument(from: stack, at: 0) 318 | let arg2: Arg2 = try NativeFunction.argument(from: stack, at: 1) 319 | let arg3: Arg3 = try NativeFunction.argument(from: stack, at: 2) 320 | try block(arg1, arg2, arg3) 321 | return nil 322 | } catch { 323 | return importedFunctionInternalError 324 | } 325 | } 326 | let sig = try signature(arg1: Arg1.self, arg2: Arg2.self, arg3: Arg3.self) 327 | try importNativeFunction( 328 | named: name, 329 | namespace: namespace, 330 | signature: sig, 331 | handler: importedFunction 332 | ) 333 | } 334 | 335 | func addImportHandler( 336 | named name: String, 337 | namespace: String, 338 | block: @escaping (Arg1, Arg2, Arg3, UnsafeMutableRawPointer?) throws -> Void 339 | ) throws where Arg1: WasmTypeProtocol, Arg2: WasmTypeProtocol, Arg3: WasmTypeProtocol { 340 | let importedFunction: ImportedFunctionSignature = 341 | { (stack: UnsafeMutablePointer?, heap: UnsafeMutableRawPointer?) -> UnsafeRawPointer? in 342 | do { 343 | let arg1: Arg1 = try NativeFunction.argument(from: stack, at: 0) 344 | let arg2: Arg2 = try NativeFunction.argument(from: stack, at: 1) 345 | let arg3: Arg3 = try NativeFunction.argument(from: stack, at: 2) 346 | try block(arg1, arg2, arg3, heap) 347 | return nil 348 | } catch { 349 | return importedFunctionInternalError 350 | } 351 | } 352 | let sig = try signature(arg1: Arg1.self, arg2: Arg2.self, arg3: Arg3.self) 353 | try importNativeFunction( 354 | named: name, 355 | namespace: namespace, 356 | signature: sig, 357 | handler: importedFunction 358 | ) 359 | } 360 | 361 | func addImportHandler( 362 | named name: String, 363 | namespace: String, 364 | block: @escaping (Arg1, Arg2, Arg3) throws -> Ret 365 | ) throws where Arg1: WasmTypeProtocol, Arg2: WasmTypeProtocol, Arg3: WasmTypeProtocol, 366 | Ret: WasmTypeProtocol 367 | { 368 | let importedFunction: ImportedFunctionSignature = 369 | { (stack: UnsafeMutablePointer?, _: UnsafeMutableRawPointer?) -> UnsafeRawPointer? in 370 | do { 371 | let arg1: Arg1 = try NativeFunction.argument(from: stack, at: 1) 372 | let arg2: Arg2 = try NativeFunction.argument(from: stack, at: 2) 373 | let arg3: Arg3 = try NativeFunction.argument(from: stack, at: 3) 374 | let ret = try block(arg1, arg2, arg3) 375 | try NativeFunction.pushReturnValue(ret, to: stack) 376 | return nil 377 | } catch { 378 | return importedFunctionInternalError 379 | } 380 | } 381 | let sig = try signature( 382 | arg1: Arg1.self, 383 | arg2: Arg2.self, 384 | arg3: Arg3.self, 385 | ret: Ret.self 386 | ) 387 | try importNativeFunction( 388 | named: name, 389 | namespace: namespace, 390 | signature: sig, 391 | handler: importedFunction 392 | ) 393 | } 394 | 395 | func addImportHandler( 396 | named name: String, 397 | namespace: String, 398 | block: @escaping (Arg1, Arg2, Arg3, UnsafeMutableRawPointer?) throws -> Ret 399 | ) throws where Arg1: WasmTypeProtocol, Arg2: WasmTypeProtocol, Arg3: WasmTypeProtocol, 400 | Ret: WasmTypeProtocol 401 | { 402 | let importedFunction: ImportedFunctionSignature = 403 | { (stack: UnsafeMutablePointer?, heap: UnsafeMutableRawPointer?) -> UnsafeRawPointer? in 404 | do { 405 | let arg1: Arg1 = try NativeFunction.argument(from: stack, at: 1) 406 | let arg2: Arg2 = try NativeFunction.argument(from: stack, at: 2) 407 | let arg3: Arg3 = try NativeFunction.argument(from: stack, at: 3) 408 | let ret = try block(arg1, arg2, arg3, heap) 409 | try NativeFunction.pushReturnValue(ret, to: stack) 410 | return nil 411 | } catch { 412 | return importedFunctionInternalError 413 | } 414 | } 415 | let sig = try signature( 416 | arg1: Arg1.self, 417 | arg2: Arg2.self, 418 | arg3: Arg3.self, 419 | ret: Ret.self 420 | ) 421 | try importNativeFunction( 422 | named: name, 423 | namespace: namespace, 424 | signature: sig, 425 | handler: importedFunction 426 | ) 427 | } 428 | 429 | func addImportHandler( 430 | named name: String, 431 | namespace: String, 432 | block: @escaping (Arg1, Arg2, Arg3, Arg4) throws -> Void 433 | ) throws 434 | where Arg1: WasmTypeProtocol, Arg2: WasmTypeProtocol, Arg3: WasmTypeProtocol, 435 | Arg4: WasmTypeProtocol 436 | { 437 | let importedFunction: ImportedFunctionSignature = 438 | { (stack: UnsafeMutablePointer?, _: UnsafeMutableRawPointer?) -> UnsafeRawPointer? in 439 | do { 440 | let arg1: Arg1 = try NativeFunction.argument(from: stack, at: 0) 441 | let arg2: Arg2 = try NativeFunction.argument(from: stack, at: 1) 442 | let arg3: Arg3 = try NativeFunction.argument(from: stack, at: 2) 443 | let arg4: Arg4 = try NativeFunction.argument(from: stack, at: 3) 444 | try block(arg1, arg2, arg3, arg4) 445 | return nil 446 | } catch { 447 | return importedFunctionInternalError 448 | } 449 | } 450 | let sig = try signature( 451 | arg1: Arg1.self, 452 | arg2: Arg2.self, 453 | arg3: Arg3.self, 454 | arg4: Arg4.self 455 | ) 456 | try importNativeFunction( 457 | named: name, 458 | namespace: namespace, 459 | signature: sig, 460 | handler: importedFunction 461 | ) 462 | } 463 | 464 | func addImportHandler( 465 | named name: String, 466 | namespace: String, 467 | block: @escaping (Arg1, Arg2, Arg3, Arg4, UnsafeMutableRawPointer?) throws -> Void 468 | ) throws 469 | where Arg1: WasmTypeProtocol, Arg2: WasmTypeProtocol, Arg3: WasmTypeProtocol, 470 | Arg4: WasmTypeProtocol 471 | { 472 | let importedFunction: ImportedFunctionSignature = 473 | { (stack: UnsafeMutablePointer?, heap: UnsafeMutableRawPointer?) -> UnsafeRawPointer? in 474 | do { 475 | let arg1: Arg1 = try NativeFunction.argument(from: stack, at: 0) 476 | let arg2: Arg2 = try NativeFunction.argument(from: stack, at: 1) 477 | let arg3: Arg3 = try NativeFunction.argument(from: stack, at: 2) 478 | let arg4: Arg4 = try NativeFunction.argument(from: stack, at: 3) 479 | try block(arg1, arg2, arg3, arg4, heap) 480 | return nil 481 | } catch { 482 | return importedFunctionInternalError 483 | } 484 | } 485 | let sig = try signature( 486 | arg1: Arg1.self, 487 | arg2: Arg2.self, 488 | arg3: Arg3.self, 489 | arg4: Arg4.self 490 | ) 491 | try importNativeFunction( 492 | named: name, 493 | namespace: namespace, 494 | signature: sig, 495 | handler: importedFunction 496 | ) 497 | } 498 | 499 | func addImportHandler( 500 | named name: String, 501 | namespace: String, 502 | block: @escaping (Arg1, Arg2, Arg3, Arg4) throws -> Ret 503 | ) throws 504 | where Arg1: WasmTypeProtocol, Arg2: WasmTypeProtocol, Arg3: WasmTypeProtocol, 505 | Arg4: WasmTypeProtocol, Ret: WasmTypeProtocol 506 | { 507 | let importedFunction: ImportedFunctionSignature = 508 | { (stack: UnsafeMutablePointer?, _: UnsafeMutableRawPointer?) -> UnsafeRawPointer? in 509 | do { 510 | let arg1: Arg1 = try NativeFunction.argument(from: stack, at: 1) 511 | let arg2: Arg2 = try NativeFunction.argument(from: stack, at: 2) 512 | let arg3: Arg3 = try NativeFunction.argument(from: stack, at: 3) 513 | let arg4: Arg4 = try NativeFunction.argument(from: stack, at: 4) 514 | let ret = try block(arg1, arg2, arg3, arg4) 515 | try NativeFunction.pushReturnValue(ret, to: stack) 516 | return nil 517 | } catch { 518 | return importedFunctionInternalError 519 | } 520 | } 521 | let sig = try signature( 522 | arg1: Arg1.self, 523 | arg2: Arg2.self, 524 | arg3: Arg3.self, 525 | arg4: Arg4.self, 526 | ret: Ret.self 527 | ) 528 | try importNativeFunction( 529 | named: name, 530 | namespace: namespace, 531 | signature: sig, 532 | handler: importedFunction 533 | ) 534 | } 535 | 536 | func addImportHandler( 537 | named name: String, 538 | namespace: String, 539 | block: @escaping (Arg1, Arg2, Arg3, Arg4, UnsafeMutableRawPointer?) throws -> Ret 540 | ) throws 541 | where Arg1: WasmTypeProtocol, Arg2: WasmTypeProtocol, Arg3: WasmTypeProtocol, 542 | Arg4: WasmTypeProtocol, Ret: WasmTypeProtocol 543 | { 544 | let importedFunction: ImportedFunctionSignature = 545 | { (stack: UnsafeMutablePointer?, heap: UnsafeMutableRawPointer?) -> UnsafeRawPointer? in 546 | do { 547 | let arg1: Arg1 = try NativeFunction.argument(from: stack, at: 1) 548 | let arg2: Arg2 = try NativeFunction.argument(from: stack, at: 2) 549 | let arg3: Arg3 = try NativeFunction.argument(from: stack, at: 3) 550 | let arg4: Arg4 = try NativeFunction.argument(from: stack, at: 4) 551 | let ret = try block(arg1, arg2, arg3, arg4, heap) 552 | try NativeFunction.pushReturnValue(ret, to: stack) 553 | return nil 554 | } catch { 555 | return importedFunctionInternalError 556 | } 557 | } 558 | let sig = try signature( 559 | arg1: Arg1.self, 560 | arg2: Arg2.self, 561 | arg3: Arg3.self, 562 | arg4: Arg4.self, 563 | ret: Ret.self 564 | ) 565 | try importNativeFunction( 566 | named: name, 567 | namespace: namespace, 568 | signature: sig, 569 | handler: importedFunction 570 | ) 571 | } 572 | 573 | func addImportHandler( 574 | named name: String, 575 | namespace: String, 576 | block: @escaping (Arg1, Arg2, Arg3, Arg4, Arg5) throws -> Void 577 | ) throws 578 | where Arg1: WasmTypeProtocol, Arg2: WasmTypeProtocol, Arg3: WasmTypeProtocol, 579 | Arg4: WasmTypeProtocol, Arg5: WasmTypeProtocol 580 | { 581 | let importedFunction: ImportedFunctionSignature = 582 | { (stack: UnsafeMutablePointer?, _: UnsafeMutableRawPointer?) -> UnsafeRawPointer? in 583 | do { 584 | let arg1: Arg1 = try NativeFunction.argument(from: stack, at: 0) 585 | let arg2: Arg2 = try NativeFunction.argument(from: stack, at: 1) 586 | let arg3: Arg3 = try NativeFunction.argument(from: stack, at: 2) 587 | let arg4: Arg4 = try NativeFunction.argument(from: stack, at: 3) 588 | let arg5: Arg5 = try NativeFunction.argument(from: stack, at: 4) 589 | try block(arg1, arg2, arg3, arg4, arg5) 590 | return nil 591 | } catch { 592 | return importedFunctionInternalError 593 | } 594 | } 595 | let sig = try signature( 596 | arg1: Arg1.self, 597 | arg2: Arg2.self, 598 | arg3: Arg3.self, 599 | arg4: Arg4.self, 600 | arg5: Arg5.self 601 | ) 602 | try importNativeFunction( 603 | named: name, 604 | namespace: namespace, 605 | signature: sig, 606 | handler: importedFunction 607 | ) 608 | } 609 | 610 | func addImportHandler( 611 | named name: String, 612 | namespace: String, 613 | block: @escaping (Arg1, Arg2, Arg3, Arg4, Arg5, UnsafeMutableRawPointer?) throws -> Void 614 | ) throws 615 | where Arg1: WasmTypeProtocol, Arg2: WasmTypeProtocol, Arg3: WasmTypeProtocol, 616 | Arg4: WasmTypeProtocol, Arg5: WasmTypeProtocol 617 | { 618 | let importedFunction: ImportedFunctionSignature = 619 | { (stack: UnsafeMutablePointer?, heap: UnsafeMutableRawPointer?) -> UnsafeRawPointer? in 620 | do { 621 | let arg1: Arg1 = try NativeFunction.argument(from: stack, at: 0) 622 | let arg2: Arg2 = try NativeFunction.argument(from: stack, at: 1) 623 | let arg3: Arg3 = try NativeFunction.argument(from: stack, at: 2) 624 | let arg4: Arg4 = try NativeFunction.argument(from: stack, at: 3) 625 | let arg5: Arg5 = try NativeFunction.argument(from: stack, at: 4) 626 | try block(arg1, arg2, arg3, arg4, arg5, heap) 627 | return nil 628 | } catch { 629 | return importedFunctionInternalError 630 | } 631 | } 632 | let sig = try signature( 633 | arg1: Arg1.self, 634 | arg2: Arg2.self, 635 | arg3: Arg3.self, 636 | arg4: Arg4.self, 637 | arg5: Arg5.self 638 | ) 639 | try importNativeFunction( 640 | named: name, 641 | namespace: namespace, 642 | signature: sig, 643 | handler: importedFunction 644 | ) 645 | } 646 | 647 | func addImportHandler( 648 | named name: String, 649 | namespace: String, 650 | block: @escaping (Arg1, Arg2, Arg3, Arg4, Arg5) throws -> Ret 651 | ) throws 652 | where Arg1: WasmTypeProtocol, Arg2: WasmTypeProtocol, Arg3: WasmTypeProtocol, 653 | Arg4: WasmTypeProtocol, Arg5: WasmTypeProtocol, Ret: WasmTypeProtocol 654 | { 655 | let importedFunction: ImportedFunctionSignature = 656 | { (stack: UnsafeMutablePointer?, _: UnsafeMutableRawPointer?) -> UnsafeRawPointer? in 657 | do { 658 | let arg1: Arg1 = try NativeFunction.argument(from: stack, at: 1) 659 | let arg2: Arg2 = try NativeFunction.argument(from: stack, at: 2) 660 | let arg3: Arg3 = try NativeFunction.argument(from: stack, at: 3) 661 | let arg4: Arg4 = try NativeFunction.argument(from: stack, at: 4) 662 | let arg5: Arg5 = try NativeFunction.argument(from: stack, at: 5) 663 | let ret = try block(arg1, arg2, arg3, arg4, arg5) 664 | try NativeFunction.pushReturnValue(ret, to: stack) 665 | return nil 666 | } catch { 667 | return importedFunctionInternalError 668 | } 669 | } 670 | let sig = try signature( 671 | arg1: Arg1.self, arg2: Arg2.self, arg3: Arg3.self, arg4: Arg4.self, arg5: Arg5.self, 672 | ret: Ret.self 673 | ) 674 | try importNativeFunction( 675 | named: name, 676 | namespace: namespace, 677 | signature: sig, 678 | handler: importedFunction 679 | ) 680 | } 681 | 682 | func addImportHandler( 683 | named name: String, 684 | namespace: String, 685 | block: @escaping (Arg1, Arg2, Arg3, Arg4, Arg5, UnsafeMutableRawPointer?) throws -> Ret 686 | ) throws 687 | where Arg1: WasmTypeProtocol, Arg2: WasmTypeProtocol, Arg3: WasmTypeProtocol, 688 | Arg4: WasmTypeProtocol, Arg5: WasmTypeProtocol, Ret: WasmTypeProtocol 689 | { 690 | let importedFunction: ImportedFunctionSignature = 691 | { (stack: UnsafeMutablePointer?, heap: UnsafeMutableRawPointer?) -> UnsafeRawPointer? in 692 | do { 693 | let arg1: Arg1 = try NativeFunction.argument(from: stack, at: 1) 694 | let arg2: Arg2 = try NativeFunction.argument(from: stack, at: 2) 695 | let arg3: Arg3 = try NativeFunction.argument(from: stack, at: 3) 696 | let arg4: Arg4 = try NativeFunction.argument(from: stack, at: 4) 697 | let arg5: Arg5 = try NativeFunction.argument(from: stack, at: 5) 698 | let ret = try block(arg1, arg2, arg3, arg4, arg5, heap) 699 | try NativeFunction.pushReturnValue(ret, to: stack) 700 | return nil 701 | } catch { 702 | return importedFunctionInternalError 703 | } 704 | } 705 | let sig = try signature( 706 | arg1: Arg1.self, arg2: Arg2.self, arg3: Arg3.self, arg4: Arg4.self, arg5: Arg5.self, 707 | ret: Ret.self 708 | ) 709 | try importNativeFunction( 710 | named: name, 711 | namespace: namespace, 712 | signature: sig, 713 | handler: importedFunction 714 | ) 715 | } 716 | 717 | func addImportHandler( 718 | named name: String, 719 | namespace: String, 720 | block: @escaping (Arg1, Arg2, Arg3, Arg4, Arg5, Arg6) throws -> Void 721 | ) throws 722 | where Arg1: WasmTypeProtocol, Arg2: WasmTypeProtocol, Arg3: WasmTypeProtocol, 723 | Arg4: WasmTypeProtocol, Arg5: WasmTypeProtocol, Arg6: WasmTypeProtocol 724 | { 725 | let importedFunction: ImportedFunctionSignature = 726 | { (stack: UnsafeMutablePointer?, _: UnsafeMutableRawPointer?) -> UnsafeRawPointer? in 727 | do { 728 | let arg1: Arg1 = try NativeFunction.argument(from: stack, at: 0) 729 | let arg2: Arg2 = try NativeFunction.argument(from: stack, at: 1) 730 | let arg3: Arg3 = try NativeFunction.argument(from: stack, at: 2) 731 | let arg4: Arg4 = try NativeFunction.argument(from: stack, at: 3) 732 | let arg5: Arg5 = try NativeFunction.argument(from: stack, at: 4) 733 | let arg6: Arg6 = try NativeFunction.argument(from: stack, at: 5) 734 | try block(arg1, arg2, arg3, arg4, arg5, arg6) 735 | return nil 736 | } catch { 737 | return importedFunctionInternalError 738 | } 739 | } 740 | let sig = try signature( 741 | arg1: Arg1.self, arg2: Arg2.self, arg3: Arg3.self, 742 | arg4: Arg4.self, arg5: Arg5.self, arg6: Arg6.self 743 | ) 744 | try importNativeFunction( 745 | named: name, 746 | namespace: namespace, 747 | signature: sig, 748 | handler: importedFunction 749 | ) 750 | } 751 | 752 | func addImportHandler( 753 | named name: String, 754 | namespace: String, 755 | block: @escaping (Arg1, Arg2, Arg3, Arg4, Arg5, Arg6, UnsafeMutableRawPointer?) throws 756 | -> Void 757 | ) throws 758 | where Arg1: WasmTypeProtocol, Arg2: WasmTypeProtocol, Arg3: WasmTypeProtocol, 759 | Arg4: WasmTypeProtocol, Arg5: WasmTypeProtocol, Arg6: WasmTypeProtocol 760 | { 761 | let importedFunction: ImportedFunctionSignature = 762 | { (stack: UnsafeMutablePointer?, heap: UnsafeMutableRawPointer?) -> UnsafeRawPointer? in 763 | do { 764 | let arg1: Arg1 = try NativeFunction.argument(from: stack, at: 0) 765 | let arg2: Arg2 = try NativeFunction.argument(from: stack, at: 1) 766 | let arg3: Arg3 = try NativeFunction.argument(from: stack, at: 2) 767 | let arg4: Arg4 = try NativeFunction.argument(from: stack, at: 3) 768 | let arg5: Arg5 = try NativeFunction.argument(from: stack, at: 4) 769 | let arg6: Arg6 = try NativeFunction.argument(from: stack, at: 5) 770 | try block(arg1, arg2, arg3, arg4, arg5, arg6, heap) 771 | return nil 772 | } catch { 773 | return importedFunctionInternalError 774 | } 775 | } 776 | let sig = try signature( 777 | arg1: Arg1.self, arg2: Arg2.self, arg3: Arg3.self, 778 | arg4: Arg4.self, arg5: Arg5.self, arg6: Arg6.self 779 | ) 780 | try importNativeFunction( 781 | named: name, 782 | namespace: namespace, 783 | signature: sig, 784 | handler: importedFunction 785 | ) 786 | } 787 | 788 | func addImportHandler( 789 | named name: String, 790 | namespace: String, 791 | block: @escaping (Arg1, Arg2, Arg3, Arg4, Arg5, Arg6) throws -> Ret 792 | ) throws 793 | where Arg1: WasmTypeProtocol, Arg2: WasmTypeProtocol, Arg3: WasmTypeProtocol, 794 | Arg4: WasmTypeProtocol, Arg5: WasmTypeProtocol, Arg6: WasmTypeProtocol, 795 | Ret: WasmTypeProtocol 796 | { 797 | let importedFunction: ImportedFunctionSignature = 798 | { (stack: UnsafeMutablePointer?, _: UnsafeMutableRawPointer?) -> UnsafeRawPointer? in 799 | do { 800 | let arg1: Arg1 = try NativeFunction.argument(from: stack, at: 1) 801 | let arg2: Arg2 = try NativeFunction.argument(from: stack, at: 2) 802 | let arg3: Arg3 = try NativeFunction.argument(from: stack, at: 3) 803 | let arg4: Arg4 = try NativeFunction.argument(from: stack, at: 4) 804 | let arg5: Arg5 = try NativeFunction.argument(from: stack, at: 5) 805 | let arg6: Arg6 = try NativeFunction.argument(from: stack, at: 6) 806 | let ret = try block(arg1, arg2, arg3, arg4, arg5, arg6) 807 | try NativeFunction.pushReturnValue(ret, to: stack) 808 | return nil 809 | } catch { 810 | return importedFunctionInternalError 811 | } 812 | } 813 | let sig = try signature( 814 | arg1: Arg1.self, arg2: Arg2.self, arg3: Arg3.self, arg4: Arg4.self, 815 | arg5: Arg5.self, arg6: Arg6.self, ret: Ret.self 816 | ) 817 | try importNativeFunction( 818 | named: name, 819 | namespace: namespace, 820 | signature: sig, 821 | handler: importedFunction 822 | ) 823 | } 824 | 825 | func addImportHandler( 826 | named name: String, 827 | namespace: String, 828 | block: @escaping (Arg1, Arg2, Arg3, Arg4, Arg5, Arg6, UnsafeMutableRawPointer?) throws 829 | -> Ret 830 | ) throws 831 | where Arg1: WasmTypeProtocol, Arg2: WasmTypeProtocol, Arg3: WasmTypeProtocol, 832 | Arg4: WasmTypeProtocol, Arg5: WasmTypeProtocol, Arg6: WasmTypeProtocol, 833 | Ret: WasmTypeProtocol 834 | { 835 | let importedFunction: ImportedFunctionSignature = 836 | { (stack: UnsafeMutablePointer?, heap: UnsafeMutableRawPointer?) -> UnsafeRawPointer? in 837 | do { 838 | let arg1: Arg1 = try NativeFunction.argument(from: stack, at: 1) 839 | let arg2: Arg2 = try NativeFunction.argument(from: stack, at: 2) 840 | let arg3: Arg3 = try NativeFunction.argument(from: stack, at: 3) 841 | let arg4: Arg4 = try NativeFunction.argument(from: stack, at: 4) 842 | let arg5: Arg5 = try NativeFunction.argument(from: stack, at: 5) 843 | let arg6: Arg6 = try NativeFunction.argument(from: stack, at: 6) 844 | let ret = try block(arg1, arg2, arg3, arg4, arg5, arg6, heap) 845 | try NativeFunction.pushReturnValue(ret, to: stack) 846 | return nil 847 | } catch { 848 | return importedFunctionInternalError 849 | } 850 | } 851 | let sig = try signature( 852 | arg1: Arg1.self, arg2: Arg2.self, arg3: Arg3.self, arg4: Arg4.self, 853 | arg5: Arg5.self, arg6: Arg6.self, ret: Ret.self 854 | ) 855 | try importNativeFunction( 856 | named: name, 857 | namespace: namespace, 858 | signature: sig, 859 | handler: importedFunction 860 | ) 861 | } 862 | 863 | func addImportHandler( 864 | named name: String, 865 | namespace: String, 866 | block: @escaping (Arg1, Arg2, Arg3, Arg4, Arg5, Arg6, Arg7) throws -> Void 867 | ) throws 868 | where Arg1: WasmTypeProtocol, Arg2: WasmTypeProtocol, Arg3: WasmTypeProtocol, 869 | Arg4: WasmTypeProtocol, Arg5: WasmTypeProtocol, Arg6: WasmTypeProtocol, 870 | Arg7: WasmTypeProtocol 871 | { 872 | let importedFunction: ImportedFunctionSignature = 873 | { (stack: UnsafeMutablePointer?, _: UnsafeMutableRawPointer?) -> UnsafeRawPointer? in 874 | do { 875 | let arg1: Arg1 = try NativeFunction.argument(from: stack, at: 0) 876 | let arg2: Arg2 = try NativeFunction.argument(from: stack, at: 1) 877 | let arg3: Arg3 = try NativeFunction.argument(from: stack, at: 2) 878 | let arg4: Arg4 = try NativeFunction.argument(from: stack, at: 3) 879 | let arg5: Arg5 = try NativeFunction.argument(from: stack, at: 4) 880 | let arg6: Arg6 = try NativeFunction.argument(from: stack, at: 5) 881 | let arg7: Arg7 = try NativeFunction.argument(from: stack, at: 6) 882 | try block(arg1, arg2, arg3, arg4, arg5, arg6, arg7) 883 | return nil 884 | } catch { 885 | return importedFunctionInternalError 886 | } 887 | } 888 | let sig = try signature( 889 | arg1: Arg1.self, arg2: Arg2.self, arg3: Arg3.self, 890 | arg4: Arg4.self, arg5: Arg5.self, arg6: Arg6.self, arg7: Arg7.self 891 | ) 892 | try importNativeFunction( 893 | named: name, 894 | namespace: namespace, 895 | signature: sig, 896 | handler: importedFunction 897 | ) 898 | } 899 | 900 | func addImportHandler( 901 | named name: String, 902 | namespace: String, 903 | block: @escaping ( 904 | Arg1, 905 | Arg2, 906 | Arg3, 907 | Arg4, 908 | Arg5, 909 | Arg6, 910 | Arg7, 911 | UnsafeMutableRawPointer? 912 | ) throws -> Void 913 | ) throws 914 | where Arg1: WasmTypeProtocol, Arg2: WasmTypeProtocol, Arg3: WasmTypeProtocol, 915 | Arg4: WasmTypeProtocol, Arg5: WasmTypeProtocol, Arg6: WasmTypeProtocol, 916 | Arg7: WasmTypeProtocol 917 | { 918 | let importedFunction: ImportedFunctionSignature = 919 | { (stack: UnsafeMutablePointer?, heap: UnsafeMutableRawPointer?) -> UnsafeRawPointer? in 920 | do { 921 | let arg1: Arg1 = try NativeFunction.argument(from: stack, at: 0) 922 | let arg2: Arg2 = try NativeFunction.argument(from: stack, at: 1) 923 | let arg3: Arg3 = try NativeFunction.argument(from: stack, at: 2) 924 | let arg4: Arg4 = try NativeFunction.argument(from: stack, at: 3) 925 | let arg5: Arg5 = try NativeFunction.argument(from: stack, at: 4) 926 | let arg6: Arg6 = try NativeFunction.argument(from: stack, at: 5) 927 | let arg7: Arg7 = try NativeFunction.argument(from: stack, at: 6) 928 | try block(arg1, arg2, arg3, arg4, arg5, arg6, arg7, heap) 929 | return nil 930 | } catch { 931 | return importedFunctionInternalError 932 | } 933 | } 934 | let sig = try signature( 935 | arg1: Arg1.self, arg2: Arg2.self, arg3: Arg3.self, 936 | arg4: Arg4.self, arg5: Arg5.self, arg6: Arg6.self, arg7: Arg7.self 937 | ) 938 | try importNativeFunction( 939 | named: name, 940 | namespace: namespace, 941 | signature: sig, 942 | handler: importedFunction 943 | ) 944 | } 945 | 946 | func addImportHandler( 947 | named name: String, 948 | namespace: String, 949 | block: @escaping (Arg1, Arg2, Arg3, Arg4, Arg5, Arg6, Arg7) throws -> Ret 950 | ) throws 951 | where Arg1: WasmTypeProtocol, Arg2: WasmTypeProtocol, Arg3: WasmTypeProtocol, 952 | Arg4: WasmTypeProtocol, Arg5: WasmTypeProtocol, Arg6: WasmTypeProtocol, 953 | Arg7: WasmTypeProtocol, Ret: WasmTypeProtocol 954 | { 955 | let importedFunction: ImportedFunctionSignature = 956 | { (stack: UnsafeMutablePointer?, _: UnsafeMutableRawPointer?) -> UnsafeRawPointer? in 957 | do { 958 | let arg1: Arg1 = try NativeFunction.argument(from: stack, at: 1) 959 | let arg2: Arg2 = try NativeFunction.argument(from: stack, at: 2) 960 | let arg3: Arg3 = try NativeFunction.argument(from: stack, at: 3) 961 | let arg4: Arg4 = try NativeFunction.argument(from: stack, at: 4) 962 | let arg5: Arg5 = try NativeFunction.argument(from: stack, at: 5) 963 | let arg6: Arg6 = try NativeFunction.argument(from: stack, at: 6) 964 | let arg7: Arg7 = try NativeFunction.argument(from: stack, at: 7) 965 | let ret = try block(arg1, arg2, arg3, arg4, arg5, arg6, arg7) 966 | try NativeFunction.pushReturnValue(ret, to: stack) 967 | return nil 968 | } catch { 969 | return importedFunctionInternalError 970 | } 971 | } 972 | let sig = try signature( 973 | arg1: Arg1.self, arg2: Arg2.self, arg3: Arg3.self, arg4: Arg4.self, 974 | arg5: Arg5.self, arg6: Arg6.self, arg7: Arg7.self, ret: Ret.self 975 | ) 976 | try importNativeFunction( 977 | named: name, 978 | namespace: namespace, 979 | signature: sig, 980 | handler: importedFunction 981 | ) 982 | } 983 | 984 | func addImportHandler( 985 | named name: String, 986 | namespace: String, 987 | block: @escaping ( 988 | Arg1, 989 | Arg2, 990 | Arg3, 991 | Arg4, 992 | Arg5, 993 | Arg6, 994 | Arg7, 995 | UnsafeMutableRawPointer? 996 | ) throws -> Ret 997 | ) throws 998 | where Arg1: WasmTypeProtocol, Arg2: WasmTypeProtocol, Arg3: WasmTypeProtocol, 999 | Arg4: WasmTypeProtocol, Arg5: WasmTypeProtocol, Arg6: WasmTypeProtocol, 1000 | Arg7: WasmTypeProtocol, Ret: WasmTypeProtocol 1001 | { 1002 | let importedFunction: ImportedFunctionSignature = 1003 | { (stack: UnsafeMutablePointer?, heap: UnsafeMutableRawPointer?) -> UnsafeRawPointer? in 1004 | do { 1005 | let arg1: Arg1 = try NativeFunction.argument(from: stack, at: 1) 1006 | let arg2: Arg2 = try NativeFunction.argument(from: stack, at: 2) 1007 | let arg3: Arg3 = try NativeFunction.argument(from: stack, at: 3) 1008 | let arg4: Arg4 = try NativeFunction.argument(from: stack, at: 4) 1009 | let arg5: Arg5 = try NativeFunction.argument(from: stack, at: 5) 1010 | let arg6: Arg6 = try NativeFunction.argument(from: stack, at: 6) 1011 | let arg7: Arg7 = try NativeFunction.argument(from: stack, at: 7) 1012 | let ret = try block(arg1, arg2, arg3, arg4, arg5, arg6, arg7, heap) 1013 | try NativeFunction.pushReturnValue(ret, to: stack) 1014 | return nil 1015 | } catch { 1016 | return importedFunctionInternalError 1017 | } 1018 | } 1019 | let sig = try signature( 1020 | arg1: Arg1.self, arg2: Arg2.self, arg3: Arg3.self, arg4: Arg4.self, 1021 | arg5: Arg5.self, arg6: Arg6.self, arg7: Arg7.self, ret: Ret.self 1022 | ) 1023 | try importNativeFunction( 1024 | named: name, 1025 | namespace: namespace, 1026 | signature: sig, 1027 | handler: importedFunction 1028 | ) 1029 | } 1030 | 1031 | func addImportHandler( 1032 | named name: String, 1033 | namespace: String, 1034 | block: @escaping (Arg1, Arg2, Arg3, Arg4, Arg5, Arg6, Arg7, Arg8) throws -> Void 1035 | ) throws 1036 | where Arg1: WasmTypeProtocol, Arg2: WasmTypeProtocol, Arg3: WasmTypeProtocol, 1037 | Arg4: WasmTypeProtocol, Arg5: WasmTypeProtocol, Arg6: WasmTypeProtocol, 1038 | Arg7: WasmTypeProtocol, Arg8: WasmTypeProtocol 1039 | { 1040 | let importedFunction: ImportedFunctionSignature = 1041 | { (stack: UnsafeMutablePointer?, _: UnsafeMutableRawPointer?) -> UnsafeRawPointer? in 1042 | do { 1043 | let arg1: Arg1 = try NativeFunction.argument(from: stack, at: 0) 1044 | let arg2: Arg2 = try NativeFunction.argument(from: stack, at: 1) 1045 | let arg3: Arg3 = try NativeFunction.argument(from: stack, at: 2) 1046 | let arg4: Arg4 = try NativeFunction.argument(from: stack, at: 3) 1047 | let arg5: Arg5 = try NativeFunction.argument(from: stack, at: 4) 1048 | let arg6: Arg6 = try NativeFunction.argument(from: stack, at: 5) 1049 | let arg7: Arg7 = try NativeFunction.argument(from: stack, at: 6) 1050 | let arg8: Arg8 = try NativeFunction.argument(from: stack, at: 7) 1051 | try block(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) 1052 | return nil 1053 | } catch { 1054 | return importedFunctionInternalError 1055 | } 1056 | } 1057 | let sig = try signature( 1058 | arg1: Arg1.self, arg2: Arg2.self, arg3: Arg3.self, 1059 | arg4: Arg4.self, arg5: Arg5.self, arg6: Arg6.self, arg7: Arg7.self, arg8: Arg8.self 1060 | ) 1061 | try importNativeFunction( 1062 | named: name, 1063 | namespace: namespace, 1064 | signature: sig, 1065 | handler: importedFunction 1066 | ) 1067 | } 1068 | 1069 | func addImportHandler( 1070 | named name: String, 1071 | namespace: String, 1072 | block: @escaping ( 1073 | Arg1, 1074 | Arg2, 1075 | Arg3, 1076 | Arg4, 1077 | Arg5, 1078 | Arg6, 1079 | Arg7, 1080 | Arg8, 1081 | UnsafeMutableRawPointer? 1082 | ) throws -> Void 1083 | ) throws 1084 | where Arg1: WasmTypeProtocol, Arg2: WasmTypeProtocol, Arg3: WasmTypeProtocol, 1085 | Arg4: WasmTypeProtocol, Arg5: WasmTypeProtocol, Arg6: WasmTypeProtocol, 1086 | Arg7: WasmTypeProtocol, Arg8: WasmTypeProtocol 1087 | { 1088 | let importedFunction: ImportedFunctionSignature = 1089 | { (stack: UnsafeMutablePointer?, heap: UnsafeMutableRawPointer?) -> UnsafeRawPointer? in 1090 | do { 1091 | let arg1: Arg1 = try NativeFunction.argument(from: stack, at: 0) 1092 | let arg2: Arg2 = try NativeFunction.argument(from: stack, at: 1) 1093 | let arg3: Arg3 = try NativeFunction.argument(from: stack, at: 2) 1094 | let arg4: Arg4 = try NativeFunction.argument(from: stack, at: 3) 1095 | let arg5: Arg5 = try NativeFunction.argument(from: stack, at: 4) 1096 | let arg6: Arg6 = try NativeFunction.argument(from: stack, at: 5) 1097 | let arg7: Arg7 = try NativeFunction.argument(from: stack, at: 6) 1098 | let arg8: Arg8 = try NativeFunction.argument(from: stack, at: 7) 1099 | try block(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, heap) 1100 | return nil 1101 | } catch { 1102 | return importedFunctionInternalError 1103 | } 1104 | } 1105 | let sig = try signature( 1106 | arg1: Arg1.self, arg2: Arg2.self, arg3: Arg3.self, 1107 | arg4: Arg4.self, arg5: Arg5.self, arg6: Arg6.self, arg7: Arg7.self, arg8: Arg8.self 1108 | ) 1109 | try importNativeFunction( 1110 | named: name, 1111 | namespace: namespace, 1112 | signature: sig, 1113 | handler: importedFunction 1114 | ) 1115 | } 1116 | 1117 | func addImportHandler( 1118 | named name: String, 1119 | namespace: String, 1120 | block: @escaping (Arg1, Arg2, Arg3, Arg4, Arg5, Arg6, Arg7, Arg8) throws -> Ret 1121 | ) throws 1122 | where Arg1: WasmTypeProtocol, Arg2: WasmTypeProtocol, Arg3: WasmTypeProtocol, 1123 | Arg4: WasmTypeProtocol, Arg5: WasmTypeProtocol, Arg6: WasmTypeProtocol, 1124 | Arg7: WasmTypeProtocol, Arg8: WasmTypeProtocol, Ret: WasmTypeProtocol 1125 | { 1126 | let importedFunction: ImportedFunctionSignature = 1127 | { (stack: UnsafeMutablePointer?, _: UnsafeMutableRawPointer?) -> UnsafeRawPointer? in 1128 | do { 1129 | let arg1: Arg1 = try NativeFunction.argument(from: stack, at: 1) 1130 | let arg2: Arg2 = try NativeFunction.argument(from: stack, at: 2) 1131 | let arg3: Arg3 = try NativeFunction.argument(from: stack, at: 3) 1132 | let arg4: Arg4 = try NativeFunction.argument(from: stack, at: 4) 1133 | let arg5: Arg5 = try NativeFunction.argument(from: stack, at: 5) 1134 | let arg6: Arg6 = try NativeFunction.argument(from: stack, at: 6) 1135 | let arg7: Arg7 = try NativeFunction.argument(from: stack, at: 7) 1136 | let arg8: Arg8 = try NativeFunction.argument(from: stack, at: 8) 1137 | let ret = try block(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) 1138 | try NativeFunction.pushReturnValue(ret, to: stack) 1139 | return nil 1140 | } catch { 1141 | return importedFunctionInternalError 1142 | } 1143 | } 1144 | let sig = try signature( 1145 | arg1: Arg1.self, arg2: Arg2.self, arg3: Arg3.self, arg4: Arg4.self, 1146 | arg5: Arg5.self, arg6: Arg6.self, arg7: Arg7.self, arg8: Arg8.self, ret: Ret.self 1147 | ) 1148 | try importNativeFunction( 1149 | named: name, 1150 | namespace: namespace, 1151 | signature: sig, 1152 | handler: importedFunction 1153 | ) 1154 | } 1155 | 1156 | func addImportHandler( 1157 | named name: String, 1158 | namespace: String, 1159 | block: @escaping ( 1160 | Arg1, 1161 | Arg2, 1162 | Arg3, 1163 | Arg4, 1164 | Arg5, 1165 | Arg6, 1166 | Arg7, 1167 | Arg8, 1168 | UnsafeMutableRawPointer? 1169 | ) throws -> Ret 1170 | ) throws 1171 | where Arg1: WasmTypeProtocol, Arg2: WasmTypeProtocol, Arg3: WasmTypeProtocol, 1172 | Arg4: WasmTypeProtocol, Arg5: WasmTypeProtocol, Arg6: WasmTypeProtocol, 1173 | Arg7: WasmTypeProtocol, Arg8: WasmTypeProtocol, Ret: WasmTypeProtocol 1174 | { 1175 | let importedFunction: ImportedFunctionSignature = 1176 | { (stack: UnsafeMutablePointer?, heap: UnsafeMutableRawPointer?) -> UnsafeRawPointer? in 1177 | do { 1178 | let arg1: Arg1 = try NativeFunction.argument(from: stack, at: 1) 1179 | let arg2: Arg2 = try NativeFunction.argument(from: stack, at: 2) 1180 | let arg3: Arg3 = try NativeFunction.argument(from: stack, at: 3) 1181 | let arg4: Arg4 = try NativeFunction.argument(from: stack, at: 4) 1182 | let arg5: Arg5 = try NativeFunction.argument(from: stack, at: 5) 1183 | let arg6: Arg6 = try NativeFunction.argument(from: stack, at: 6) 1184 | let arg7: Arg7 = try NativeFunction.argument(from: stack, at: 7) 1185 | let arg8: Arg8 = try NativeFunction.argument(from: stack, at: 8) 1186 | let ret = try block(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, heap) 1187 | try NativeFunction.pushReturnValue(ret, to: stack) 1188 | return nil 1189 | } catch { 1190 | return importedFunctionInternalError 1191 | } 1192 | } 1193 | let sig = try signature( 1194 | arg1: Arg1.self, arg2: Arg2.self, arg3: Arg3.self, arg4: Arg4.self, 1195 | arg5: Arg5.self, arg6: Arg6.self, arg7: Arg7.self, arg8: Arg8.self, ret: Ret.self 1196 | ) 1197 | try importNativeFunction( 1198 | named: name, 1199 | namespace: namespace, 1200 | signature: sig, 1201 | handler: importedFunction 1202 | ) 1203 | } 1204 | } 1205 | -------------------------------------------------------------------------------- /Sources/WasmInterpreter/WasmInterpreter.swift: -------------------------------------------------------------------------------- 1 | import CWasm3 2 | import Foundation 3 | import Synchronized 4 | 5 | public final class WasmInterpreter { 6 | private var id: UInt64 7 | private var idPointer: UnsafeMutableRawPointer 8 | 9 | private var environment: IM3Environment 10 | private var runtime: IM3Runtime 11 | private var moduleAndBytes: (IM3Module, [UInt8]) 12 | private var module: IM3Module { moduleAndBytes.0 } 13 | 14 | private var functionCache = [String: IM3Function]() 15 | private var importedFunctionContexts = [UnsafeMutableRawPointer]() 16 | 17 | private let lock = Lock() 18 | 19 | public convenience init(module: URL) throws { 20 | try self.init(stackSize: 512 * 1024, module: module) 21 | } 22 | 23 | public convenience init(stackSize: UInt32, module: URL) throws { 24 | try self.init(stackSize: stackSize, module: [UInt8](try Data(contentsOf: module))) 25 | } 26 | 27 | public convenience init(module bytes: [UInt8]) throws { 28 | try self.init(stackSize: 512 * 1024, module: bytes) 29 | } 30 | 31 | public init(stackSize: UInt32, module bytes: [UInt8]) throws { 32 | id = nextInstanceIdentifier 33 | idPointer = makeRawPointer(for: id) 34 | 35 | guard let environment = m3_NewEnvironment() else { 36 | throw WasmInterpreterError.couldNotLoadEnvironment 37 | } 38 | 39 | guard let runtime = m3_NewRuntime(environment, stackSize, idPointer) else { 40 | throw WasmInterpreterError.couldNotLoadRuntime 41 | } 42 | 43 | var mod: IM3Module? 44 | try WasmInterpreter.check(m3_ParseModule(environment, &mod, bytes, UInt32(bytes.count))) 45 | guard let module = mod else { throw WasmInterpreterError.couldNotParseModule } 46 | try WasmInterpreter.check(m3_LoadModule(runtime, module)) 47 | 48 | self.environment = environment 49 | self.runtime = runtime 50 | moduleAndBytes = (module, bytes) 51 | } 52 | 53 | deinit { 54 | m3_FreeRuntime(runtime) 55 | m3_FreeEnvironment(environment) 56 | removeImportedFunctions(forInstanceIdentifier: id) 57 | idPointer.deallocate() 58 | } 59 | } 60 | 61 | public extension WasmInterpreter { 62 | func stringFromHeap(byteOffset: Int, length: Int) throws -> String { 63 | let data = try dataFromHeap(byteOffset: byteOffset, length: length) 64 | 65 | guard let string = String(data: data, encoding: .utf8) 66 | else { throw WasmInterpreterError.invalidUTF8String } 67 | 68 | return string 69 | } 70 | 71 | func valueFromHeap(byteOffset: Int) throws -> T { 72 | let values = try valuesFromHeap(byteOffset: byteOffset, length: 1) as [T] 73 | guard let value = values.first 74 | else { throw WasmInterpreterError.couldNotLoadMemory } 75 | return value 76 | } 77 | 78 | func valuesFromHeap(byteOffset: Int, length: Int) throws -> [T] { 79 | let heap = try heap() 80 | 81 | guard heap.isValid(byteOffset: byteOffset, length: length) 82 | else { throw WasmInterpreterError.invalidMemoryAccess } 83 | 84 | let ptr = UnsafeRawPointer(heap.pointer) 85 | .advanced(by: byteOffset) 86 | .bindMemory(to: T.self, capacity: length) 87 | 88 | return (0 ..< length).map { ptr[$0] } 89 | } 90 | 91 | func dataFromHeap(byteOffset: Int, length: Int) throws -> Data { 92 | let heap = try heap() 93 | 94 | guard heap.isValid(byteOffset: byteOffset, length: length) 95 | else { throw WasmInterpreterError.invalidMemoryAccess } 96 | 97 | return Data(bytes: heap.pointer.advanced(by: byteOffset), count: length) 98 | } 99 | 100 | func bytesFromHeap(byteOffset: Int, length: Int) throws -> [UInt8] { 101 | let heap = try heap() 102 | 103 | guard heap.isValid(byteOffset: byteOffset, length: length) 104 | else { throw WasmInterpreterError.invalidMemoryAccess } 105 | 106 | let bufferPointer = UnsafeBufferPointer( 107 | start: heap.pointer.advanced(by: byteOffset), 108 | count: length 109 | ) 110 | 111 | return Array(bufferPointer) 112 | } 113 | 114 | func writeToHeap(string: String, byteOffset: Int) throws { 115 | try writeToHeap(data: Data(string.utf8), byteOffset: byteOffset) 116 | } 117 | 118 | func writeToHeap(value: some WasmTypeProtocol, byteOffset: Int) throws { 119 | try writeToHeap(values: [value], byteOffset: byteOffset) 120 | } 121 | 122 | func writeToHeap(values: [T], byteOffset: Int) throws { 123 | var values = values 124 | try writeToHeap( 125 | data: Data(bytes: &values, count: values.count * MemoryLayout.size), 126 | byteOffset: byteOffset 127 | ) 128 | } 129 | 130 | func writeToHeap(data: Data, byteOffset: Int) throws { 131 | let heap = try heap() 132 | 133 | guard heap.isValid(byteOffset: byteOffset, length: data.count) 134 | else { throw WasmInterpreterError.invalidMemoryAccess } 135 | 136 | try data.withUnsafeBytes { (rawPointer: UnsafeRawBufferPointer) in 137 | guard let pointer = rawPointer.bindMemory(to: UInt8.self).baseAddress 138 | else { throw WasmInterpreterError.couldNotBindMemory } 139 | heap.pointer 140 | .advanced(by: byteOffset) 141 | .initialize(from: pointer, count: data.count) 142 | } 143 | } 144 | 145 | func writeToHeap(bytes: [UInt8], byteOffset: Int) throws { 146 | let heap = try heap() 147 | 148 | guard heap.isValid(byteOffset: byteOffset, length: bytes.count) 149 | else { throw WasmInterpreterError.invalidMemoryAccess } 150 | 151 | heap.pointer 152 | .advanced(by: byteOffset) 153 | .initialize(from: bytes, count: bytes.count) 154 | } 155 | 156 | private func heap() throws -> Heap { 157 | let totalBytes = UnsafeMutablePointer.allocate(capacity: 1) 158 | defer { totalBytes.deallocate() } 159 | 160 | guard let bytesPointer = m3_GetMemory(runtime, totalBytes, 0) 161 | else { throw WasmInterpreterError.invalidMemoryAccess } 162 | 163 | return Heap(pointer: bytesPointer, size: Int(totalBytes.pointee)) 164 | } 165 | } 166 | 167 | typealias ImportedFunctionSignature = (UnsafeMutablePointer?, UnsafeMutableRawPointer?) 168 | -> UnsafeRawPointer? 169 | 170 | extension WasmInterpreter { 171 | /// Imports the specified block into the module matching the supplied name. The 172 | /// imported block must be included in the compiled module as an `import`. 173 | /// 174 | /// The function's signature must conform to `wasm3`'s format, which matches the following 175 | /// form: 176 | /// 177 | /// ```c 178 | /// u8 ConvertTypeCharToTypeId (char i_code) 179 | /// { 180 | /// switch (i_code) { 181 | /// case 'v': return c_m3Type_void; 182 | /// case 'i': return c_m3Type_i32; 183 | /// case 'I': return c_m3Type_i64; 184 | /// case 'f': return c_m3Type_f32; 185 | /// case 'F': return c_m3Type_f64; 186 | /// case '*': return c_m3Type_ptr; 187 | /// } 188 | /// return c_m3Type_none; 189 | /// } 190 | /// ``` 191 | /// 192 | /// For example, a block taking two arguments of types `Int64` and `Float32` and 193 | /// no return value would have this signature: `v(I f)` 194 | /// 195 | /// - Throws: Throws if a module matching the given name can't be found or if the 196 | /// underlying `wasm3` function returns an error. 197 | /// 198 | /// - Parameters: 199 | /// - name: The name of the function to import, matching the name specified inside the 200 | /// WebAssembly module. 201 | /// - namespace: The namespace of the function to import, matching the namespace 202 | /// specified inside the WebAssembly module. 203 | /// - signature: The signature of the function to import, conforming to `wasm3`'s 204 | /// guidelines 205 | /// as outlined above. 206 | /// - handler: The function to import into the specified WebAssembly module. 207 | func importNativeFunction( 208 | named name: String, 209 | namespace: String, 210 | signature: String, 211 | handler: @escaping ImportedFunctionSignature 212 | ) throws { 213 | guard let context = UnsafeMutableRawPointer(bitPattern: (namespace + name).hashValue) 214 | else { throw WasmInterpreterError.couldNotGenerateFunctionContext } 215 | 216 | do { 217 | setImportedFunction(handler, for: context, instanceIdentifier: id) 218 | try WasmInterpreter.check( 219 | m3_LinkRawFunctionEx( 220 | module, 221 | namespace, 222 | name, 223 | signature, 224 | handleImportedFunction, 225 | context 226 | ) 227 | ) 228 | lock.locked { importedFunctionContexts.append(context) } 229 | } catch { 230 | removeImportedFunction(for: context, instanceIdentifier: id) 231 | throw error 232 | } 233 | } 234 | } 235 | 236 | extension WasmInterpreter { 237 | func function(named name: String) throws -> IM3Function { 238 | try lock.locked { () throws -> IM3Function in 239 | if let compiledFunction = functionCache[name] { 240 | return compiledFunction 241 | } else { 242 | var f: IM3Function? 243 | try WasmInterpreter.check(m3_FindFunction(&f, runtime, name)) 244 | guard let function = f 245 | else { throw WasmInterpreterError.couldNotFindFunction(name) } 246 | functionCache[name] = function 247 | return function 248 | } 249 | } 250 | } 251 | 252 | func _call(_ function: IM3Function, args: [String]) throws { 253 | try args.withCStrings { cStrings throws in 254 | var mutableCStrings = cStrings 255 | let size = UnsafeMutablePointer.allocate(capacity: 1) 256 | let r = wasm3_CallWithArgs( 257 | function, 258 | UInt32(args.count), 259 | &mutableCStrings, 260 | size, 261 | nil 262 | ) 263 | if let result = r { 264 | throw WasmInterpreterError.onCallFunction(String(cString: result)) 265 | } else if size.pointee != 0 { 266 | throw WasmInterpreterError.invalidFunctionReturnType 267 | } else { 268 | return () 269 | } 270 | } 271 | } 272 | 273 | func _call(_ function: IM3Function, args: [String]) throws -> T { 274 | try args.withCStrings { cStrings throws -> T in 275 | var mutableCStrings = cStrings 276 | let size = UnsafeMutablePointer.allocate(capacity: 1) 277 | let output = UnsafeMutablePointer.allocate(capacity: 1) 278 | let r = wasm3_CallWithArgs( 279 | function, 280 | UInt32(args.count), 281 | &mutableCStrings, 282 | size, 283 | output 284 | ) 285 | if let result = r { 286 | throw WasmInterpreterError.onCallFunction(String(cString: result)) 287 | } else if MemoryLayout.size != size.pointee { 288 | throw WasmInterpreterError.invalidFunctionReturnType 289 | } else { 290 | return output.pointee 291 | } 292 | } 293 | } 294 | } 295 | 296 | extension WasmInterpreter { 297 | private static func check(_ block: @autoclosure () throws -> M3Result?) throws { 298 | if let result = try block() { 299 | throw WasmInterpreterError.wasm3Error(String(cString: result)) 300 | } 301 | } 302 | } 303 | -------------------------------------------------------------------------------- /Sources/WasmInterpreter/WasmInterpreterError.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public enum WasmInterpreterError: Error { 4 | case couldNotLoadEnvironment 5 | case couldNotLoadRuntime 6 | case couldNotLoadWasmBinary(String) 7 | case couldNotParseModule 8 | case couldNotLoadModule 9 | case couldNotFindFunction(String) 10 | case onCallFunction(String) 11 | case invalidFunctionReturnType 12 | case invalidStackPointer 13 | case invalidMemoryAccess 14 | case invalidUTF8String 15 | case couldNotGenerateFunctionContext 16 | case incorrectArguments 17 | case missingHeap 18 | case couldNotLoadMemory 19 | case couldNotBindMemory 20 | case unsupportedWasmType(String) 21 | case wasm3Error(String) 22 | } 23 | -------------------------------------------------------------------------------- /Sources/WasmInterpreter/WasmType.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public protocol WasmTypeProtocol {} 4 | 5 | extension Int32: WasmTypeProtocol {} 6 | extension Int64: WasmTypeProtocol {} 7 | extension Float32: WasmTypeProtocol {} 8 | extension Float64: WasmTypeProtocol {} 9 | 10 | func isValidWasmType(_: T.Type) -> Bool { 11 | Int32.self == T.self || Int64.self == T.self || 12 | Float32.self == T.self || Float64.self == T.self 13 | } 14 | 15 | enum WasmType: Hashable { 16 | case int32 17 | case int64 18 | case float32 19 | case float64 20 | } 21 | 22 | enum WasmValue: Hashable { 23 | case int32(Int32) 24 | case int64(Int64) 25 | case float32(Float32) 26 | case float64(Float64) 27 | } 28 | -------------------------------------------------------------------------------- /Tests/LinuxMain.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | import WasmInterpreterTests 4 | 5 | var tests = [XCTestCaseEntry]() 6 | tests += WasmInterpreterTests.allTests() 7 | XCTMain(tests) 8 | -------------------------------------------------------------------------------- /Tests/WasmInterpreterTests/Resources/add.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (type (;0;) (func (param i32 i32) (result i32))) 3 | (func (;0;) (type 0) (param i32 i32) (result i32) 4 | local.get 0 5 | local.get 1 6 | i32.add) 7 | (export "add" (func 0))) 8 | -------------------------------------------------------------------------------- /Tests/WasmInterpreterTests/Resources/constant.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (type (;0;) (func (result i32))) 3 | (func (;0;) (type 0) (result i32) 4 | i32.const 65536) 5 | (export "constant_1" (func 0)) 6 | (export "constant_2" (func 0)) 7 | (export "constant_3" (func 0)) 8 | (export "constant_4" (func 0)) 9 | (export "constant_5" (func 0)) 10 | (export "constant_6" (func 0)) 11 | (export "constant_7" (func 0)) 12 | (export "constant_8" (func 0)) 13 | (export "constant_9" (func 0)) 14 | (export "constant_10" (func 0)) 15 | (export "constant_11" (func 0))) 16 | -------------------------------------------------------------------------------- /Tests/WasmInterpreterTests/Resources/fib64.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (type (;0;) (func (param i64) (result i64))) 3 | (func (;0;) (type 0) (param i64) (result i64) 4 | local.get 0 5 | i64.const 2 6 | i64.lt_u 7 | if ;; label = @1 8 | local.get 0 9 | return 10 | end 11 | local.get 0 12 | i64.const 2 13 | i64.sub 14 | call 0 15 | local.get 0 16 | i64.const 1 17 | i64.sub 18 | call 0 19 | i64.add 20 | return) 21 | (export "fib" (func 0))) 22 | -------------------------------------------------------------------------------- /Tests/WasmInterpreterTests/Resources/imported-add.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (import "imports" "imported_add_func" (func $imported_add_func (param $rhs i32) (param $lhs i64) (result i32))) 3 | (func (export "integer_provider_func") (result i32) 4 | i32.const -3333 5 | i64.const 42 6 | (call $imported_add_func))) 7 | -------------------------------------------------------------------------------- /Tests/WasmInterpreterTests/Resources/memory.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (memory 1)) ;; at least 64 KB 3 | -------------------------------------------------------------------------------- /Tests/WasmInterpreterTests/String+WasmTypeProtocolTests.swift: -------------------------------------------------------------------------------- 1 | @testable import WasmInterpreter 2 | import XCTest 3 | 4 | final class StringWasmTypeProtocolTests: XCTestCase { 5 | func testConversionFromInt32() throws { 6 | XCTAssertEqual("0", try String(wasmType: Int32(0))) 7 | XCTAssertEqual("1", try String(wasmType: Int32(1))) 8 | XCTAssertEqual("-1", try String(wasmType: Int32(-1))) 9 | XCTAssertEqual("198273", try String(wasmType: Int32(198_273))) 10 | XCTAssertEqual("-198273", try String(wasmType: Int32(-198_273))) 11 | XCTAssertEqual("0", try String(wasmType: Int32.zero)) 12 | XCTAssertEqual("-2147483648", try String(wasmType: Int32.min)) 13 | XCTAssertEqual("2147483647", try String(wasmType: Int32.max)) 14 | } 15 | 16 | func testConversionFromInt64() throws { 17 | XCTAssertEqual("0", try String(wasmType: Int64(0))) 18 | XCTAssertEqual("1", try String(wasmType: Int64(1))) 19 | XCTAssertEqual("-1", try String(wasmType: Int64(-1))) 20 | XCTAssertEqual("198273", try String(wasmType: Int64(198_273))) 21 | XCTAssertEqual("-198273", try String(wasmType: Int64(-198_273))) 22 | XCTAssertEqual("0", try String(wasmType: Int64.zero)) 23 | XCTAssertEqual("-9223372036854775808", try String(wasmType: Int64.min)) 24 | XCTAssertEqual("9223372036854775807", try String(wasmType: Int64.max)) 25 | } 26 | 27 | func testConversionFromFloat32() throws { 28 | XCTAssertEqual("0.0", try String(wasmType: Float32(0))) 29 | XCTAssertEqual("1.0", try String(wasmType: Float32(1))) 30 | XCTAssertEqual("-1.0", try String(wasmType: Float32(-1))) 31 | XCTAssertEqual("198273.55", try String(wasmType: Float32(198_273.543789))) 32 | XCTAssertEqual("-198273.55", try String(wasmType: Float32(-198_273.543789))) 33 | XCTAssertEqual("0.0", try String(wasmType: Float32.zero)) 34 | XCTAssertEqual("9999.998", try String(wasmType: Float32(9999.998))) 35 | XCTAssertEqual("-9999.998", try String(wasmType: Float32(-9999.998))) 36 | } 37 | 38 | func testConversionFromFloat64() throws { 39 | XCTAssertEqual("0.0", try String(wasmType: Float64(0))) 40 | XCTAssertEqual("1.0", try String(wasmType: Float64(1))) 41 | XCTAssertEqual("-1.0", try String(wasmType: Float64(-1))) 42 | XCTAssertEqual("198273.543789", try String(wasmType: Float64(198_273.543789))) 43 | XCTAssertEqual("-198273.543789", try String(wasmType: Float64(-198_273.543789))) 44 | XCTAssertEqual("0.0", try String(wasmType: Float64.zero)) 45 | XCTAssertEqual("999999.9999999998", try String(wasmType: Float64(999_999.9999999998))) 46 | XCTAssertEqual("-999999.9999999998", try String(wasmType: Float64(-999_999.9999999998))) 47 | } 48 | 49 | static var allTests = [ 50 | ("testConversionFromInt32", testConversionFromInt32), 51 | ("testConversionFromInt64", testConversionFromInt64), 52 | ("testConversionFromFloat32", testConversionFromFloat32), 53 | ("testConversionFromFloat64", testConversionFromFloat64), 54 | ] 55 | } 56 | -------------------------------------------------------------------------------- /Tests/WasmInterpreterTests/Wasm Modules/AddModule.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import WasmInterpreter 3 | 4 | public struct AddModule { 5 | private let _vm: WasmInterpreter 6 | 7 | init() throws { 8 | _vm = try WasmInterpreter(module: AddModule.wasm) 9 | } 10 | 11 | func add(_ first: Int, _ second: Int) throws -> Int { 12 | Int(try _vm.call("add", Int32(first), Int32(second)) as Int32) 13 | } 14 | 15 | // `wat2wasm -o >(base64) Tests/WasmInterpreterTests/Resources/add.wat | pbcopy` 16 | private static var wasm: [UInt8] { 17 | let base64 = "AGFzbQEAAAABBwFgAn9/AX8DAgEABwcBA2FkZAAACgkBBwAgACABags=" 18 | return [UInt8](Data(base64Encoded: base64)!) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Tests/WasmInterpreterTests/Wasm Modules/ConstantModule.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import WasmInterpreter 3 | 4 | public struct ConstantModule { 5 | private let _vm: WasmInterpreter 6 | 7 | init() throws { 8 | _vm = try WasmInterpreter(module: ConstantModule.wasm) 9 | } 10 | 11 | func constant(version: Int) throws -> Int { 12 | Int(try _vm.call("constant_\(version)") as Int32) 13 | } 14 | 15 | // `wat2wasm -o >(base64) Tests/WasmInterpreterTests/Resources/constant.wat | pbcopy` 16 | private static var wasm: [UInt8] { 17 | let base64 = 18 | "AGFzbQEAAAABBQFgAAF/AwIBAAeSAQsKY29uc3RhbnRfMQAACmNvbnN0YW50XzIAAApjb25zdGFudF8zAAAKY29uc3RhbnRfNAAACmNvbnN0YW50XzUAAApjb25zdGFudF82AAAKY29uc3RhbnRfNwAACmNvbnN0YW50XzgAAApjb25zdGFudF85AAALY29uc3RhbnRfMTAAAAtjb25zdGFudF8xMQAACggBBgBBgIAECw==" 19 | return [UInt8](Data(base64Encoded: base64)!) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Tests/WasmInterpreterTests/Wasm Modules/FibonacciModule.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import WasmInterpreter 3 | 4 | public struct FibonacciModule { 5 | private let _vm: WasmInterpreter 6 | 7 | init() throws { 8 | _vm = try WasmInterpreter(stackSize: 1 * 1024 * 1024, module: FibonacciModule.wasm) 9 | } 10 | 11 | func calculateValue(at index: Int) throws -> Int { 12 | Int(try _vm.call("fib", Int64(index)) as Int64) 13 | } 14 | 15 | // `wat2wasm -o >(base64) Tests/WasmInterpreterTests/Resources/fib64.wat | pbcopy` 16 | private static var wasm: [UInt8] { 17 | let base64 = 18 | "AGFzbQEAAAABBgFgAX4BfgMCAQAHBwEDZmliAAAKHwEdACAAQgJUBEAgAA8LIABCAn0QACAAQgF9EAB8Dws=" 19 | return [UInt8](Data(base64Encoded: base64)!) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Tests/WasmInterpreterTests/Wasm Modules/ImportedAddModule.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import WasmInterpreter 3 | 4 | public struct ImportedAddModule { 5 | private let _vm: WasmInterpreter 6 | 7 | init() throws { 8 | _vm = try WasmInterpreter(module: ImportedAddModule.wasm) 9 | try _vm.addImportHandler( 10 | named: "imported_add_func", 11 | namespace: "imports", 12 | block: importedAdd 13 | ) 14 | } 15 | 16 | func askModuleToCallImportedFunction() throws -> Int { 17 | Int(try _vm.call("integer_provider_func") as Int32) 18 | } 19 | 20 | private var importedAdd: (Int32, Int64) -> Int32 { 21 | { Int32(Int64($0) + $1) } 22 | } 23 | 24 | // `wat2wasm -o >(base64) Tests/WasmInterpreterTests/Resources/imported-add.wat | pbcopy` 25 | private static var wasm: [UInt8] { 26 | let base64 = 27 | "AGFzbQEAAAABCwJgAn9+AX9gAAF/Ah0BB2ltcG9ydHMRaW1wb3J0ZWRfYWRkX2Z1bmMAAAMCAQEHGQEVaW50ZWdlcl9wcm92aWRlcl9mdW5jAAEKCwEJAEH7ZUIqEAAL" 28 | return [UInt8](Data(base64Encoded: base64)!) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Tests/WasmInterpreterTests/Wasm Modules/MemoryModule.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import WasmInterpreter 3 | 4 | public struct MemoryModule { 5 | private let _vm: WasmInterpreter 6 | 7 | init() throws { 8 | _vm = try WasmInterpreter(module: MemoryModule.wasm) 9 | } 10 | 11 | func string(at byteOffset: Int, length: Int) throws -> String { 12 | try _vm.stringFromHeap(byteOffset: byteOffset, length: length) 13 | } 14 | 15 | func integers(at byteOffset: Int, length: Int) throws -> [Int] { 16 | (try _vm.valuesFromHeap(byteOffset: byteOffset, length: length) as [Int32]) 17 | .map(Int.init) 18 | } 19 | 20 | func asciiString(at byteOffset: Int, length: Int) throws -> String { 21 | String( 22 | try _vm.bytesFromHeap(byteOffset: byteOffset, length: length) 23 | .map(UnicodeScalar.init) 24 | .map(Character.init) 25 | ) 26 | } 27 | 28 | func write(_ string: String, to byteOffset: Int) throws { 29 | try _vm.writeToHeap(string: string, byteOffset: byteOffset) 30 | } 31 | 32 | func write(_ integers: [Int], to byteOffset: Int) throws { 33 | try _vm.writeToHeap(values: integers.map(Int32.init), byteOffset: byteOffset) 34 | } 35 | 36 | func writeASCIICharacters(in string: String, to byteOffset: Int) throws { 37 | let bytes = string.compactMap(\.asciiValue) 38 | 39 | enum _Error: Error { 40 | case invalidString(String) 41 | } 42 | 43 | guard string.count == bytes.count 44 | else { throw _Error.invalidString(string) } 45 | 46 | try _vm.writeToHeap(bytes: bytes, byteOffset: byteOffset) 47 | } 48 | 49 | // `wat2wasm -o >(base64) Tests/WasmInterpreterTests/Resources/memory.wat | pbcopy` 50 | private static var wasm: [UInt8] { 51 | let base64 = "AGFzbQEAAAAFAwEAAQ==" 52 | return [UInt8](Data(base64Encoded: base64)!) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Tests/WasmInterpreterTests/WasmInterpreterTests.swift: -------------------------------------------------------------------------------- 1 | @testable import WasmInterpreter 2 | import XCTest 3 | 4 | final class WasmInterpreterTests: XCTestCase { 5 | func testCallingTwoFunctionsWithSameImplementation() throws { 6 | let mod = try ConstantModule() 7 | 8 | try (1 ... 10).forEach { XCTAssertEqual(65_536, try mod.constant(version: $0)) } 9 | 10 | XCTAssertThrowsError(try mod.constant(version: 11)) { error in 11 | guard case let .wasm3Error(msg) = error as? WasmInterpreterError 12 | else { XCTFail(); return } 13 | XCTAssertEqual("function lookup failed", msg) 14 | } 15 | } 16 | 17 | func testPassingAndReturning32BitValues() throws { 18 | let mod = try AddModule() 19 | XCTAssertEqual(0, try mod.add(-1, 1)) 20 | XCTAssertEqual(0, try mod.add(0, 0)) 21 | XCTAssertEqual(3, try mod.add(1, 2)) 22 | XCTAssertEqual(910_861, try mod.add(13_425, 897_436)) 23 | } 24 | 25 | func testPassingAndReturning64BitValues() throws { 26 | let mod = try FibonacciModule() 27 | XCTAssertEqual(0, try mod.calculateValue(at: 0)) 28 | XCTAssertEqual(1, try mod.calculateValue(at: 1)) 29 | XCTAssertEqual(1, try mod.calculateValue(at: 2)) 30 | XCTAssertEqual(5, try mod.calculateValue(at: 5)) 31 | XCTAssertEqual(75_025, try mod.calculateValue(at: 25)) 32 | } 33 | 34 | func testUsingImportedFunction() throws { 35 | let mod = try ImportedAddModule() 36 | XCTAssertEqual(-3291, try mod.askModuleToCallImportedFunction()) 37 | } 38 | 39 | func testConcurrentModulesWithImportedFunctions() throws { 40 | var mod1: ImportedAddModule? = try ImportedAddModule() 41 | let mod2 = try ImportedAddModule() 42 | 43 | XCTAssertEqual(-3291, try mod1?.askModuleToCallImportedFunction()) 44 | mod1 = nil 45 | 46 | XCTAssertEqual(-3291, try mod2.askModuleToCallImportedFunction()) 47 | } 48 | 49 | func testAccessingAndModifyingHeapMemory() throws { 50 | let mod = try MemoryModule() 51 | 52 | XCTAssertEqual("\u{0}\u{0}\u{0}\u{0}", try mod.string(at: 0, length: 4)) 53 | 54 | let hello = "Hello, everyone! 👋" 55 | try mod.write(hello, to: 0) 56 | XCTAssertEqual(hello, try mod.string(at: 0, length: hello.utf8.count)) 57 | 58 | let numbers = [1, 2, 3, 4] 59 | try mod.write(numbers, to: 0) 60 | XCTAssertEqual(numbers, try mod.integers(at: 0, length: 4)) 61 | 62 | let fortyTwo = [42] 63 | try mod.write(fortyTwo, to: 1) 64 | XCTAssertEqual(42, try mod.integers(at: 1, length: 1).first) 65 | 66 | XCTAssertEqual(10_753, try mod.integers(at: 0, length: 1).first) 67 | 68 | let goodbye = "Goodbye!" 69 | XCTAssertNoThrow(try mod.writeASCIICharacters(in: goodbye, to: 2)) 70 | XCTAssertEqual(goodbye, try mod.asciiString(at: 2, length: goodbye.count)) 71 | 72 | XCTAssertEqual("👋", try mod.string(at: 17, length: "👋".utf8.count)) 73 | } 74 | 75 | func testAccessingInvalidMemoryAddresses() throws { 76 | let mod = try MemoryModule() 77 | let size = 64 * 1024 // 1 page size = 64 KiB 78 | 79 | let message = "Hello" 80 | 81 | let validOffset = size - message.utf8.count 82 | XCTAssertNoThrow(try mod.write(message, to: validOffset)) 83 | XCTAssertEqual( 84 | message, 85 | try mod.string(at: validOffset, length: message.utf8.count) 86 | ) 87 | 88 | let invalidOffset = size - message.utf8.count + 1 89 | XCTAssertThrowsError(try mod.write(message, to: invalidOffset)) { error in 90 | guard let wasmError = error as? WasmInterpreterError 91 | else { return XCTFail() } 92 | 93 | guard case .invalidMemoryAccess = wasmError 94 | else { return XCTFail() } 95 | } 96 | 97 | // Ensure memory hasn't been modified 98 | XCTAssertEqual( 99 | message, 100 | try mod.string(at: validOffset, length: message.utf8.count) 101 | ) 102 | } 103 | 104 | static var allTests = [ 105 | ( 106 | "testCallingTwoFunctionsWithSameImplementation", 107 | testCallingTwoFunctionsWithSameImplementation 108 | ), 109 | ("testPassingAndReturning32BitValues", testPassingAndReturning32BitValues), 110 | ("testPassingAndReturning64BitValues", testPassingAndReturning64BitValues), 111 | ("testUsingImportedFunction", testUsingImportedFunction), 112 | ( 113 | "testConcurrentModulesWithImportedFunctions", 114 | testConcurrentModulesWithImportedFunctions 115 | ), 116 | ("testAccessingAndModifyingHeapMemory", testAccessingAndModifyingHeapMemory), 117 | ("testAccessingInvalidMemoryAddresses", testAccessingInvalidMemoryAddresses), 118 | ] 119 | } 120 | -------------------------------------------------------------------------------- /Tests/WasmInterpreterTests/XCTestManifests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | #if !canImport(ObjectiveC) 4 | public func allTests() -> [XCTestCaseEntry] { 5 | [ 6 | testCase(WasmInterpreterTests.allTests), 7 | ] 8 | } 9 | #endif 10 | -------------------------------------------------------------------------------- /bin/format.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # https://sharats.me/posts/shell-script-best-practices/ 3 | 4 | set -o errexit 5 | set -o nounset 6 | set -o pipefail 7 | if [[ "${TRACE-0}" == "1" ]]; then 8 | set -o xtrace 9 | fi 10 | 11 | if [[ "${1-}" =~ ^-*h(elp)?$ ]]; then 12 | echo 'Usage: ./format.sh' 13 | exit 14 | fi 15 | 16 | DIR=`dirname "$0"` 17 | pushd "$DIR/.." &>/dev/null 18 | 19 | if command -v swiftformat >/dev/null 2>&1; then 20 | swiftformat --quiet --config .swiftformat . 21 | else 22 | echo "warning: Install swiftformat by running 'brew install swiftformat'" >&2 23 | fi 24 | 25 | popd &>/dev/null 26 | --------------------------------------------------------------------------------