Skip to content

Commit

Permalink
Update Wasm3 (#7)
Browse files Browse the repository at this point in the history
  • Loading branch information
atdrendel authored Feb 18, 2021
1 parent 0d021f8 commit 1661826
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 60 deletions.
18 changes: 18 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: ci

on:
push:
branches:
- main
pull_request:
branches:
- '*'

jobs:
build:
name: macOS
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
- name: Run tests
run: swift test
2 changes: 1 addition & 1 deletion Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"repositoryURL": "https://github.com/shareup/cwasm3.git",
"state": {
"branch": null,
"revision": "93bee1c35cee166098cf290fe7b745d44730afce",
"revision": "c4bb509286efdec0d2935c7f5c1645febf36fdd2",
"version": "0.4.8"
}
},
Expand Down
11 changes: 5 additions & 6 deletions Sources/WasmInterpreter/Heap.swift
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import Foundation

@available(*, deprecated, message: "Heap will be removed in a later version")
public struct Heap {
public let pointer: UnsafeMutablePointer<UInt8>
public let size: Int
struct Heap {
let pointer: UnsafeMutablePointer<UInt8>
let size: Int

public func isValid(offset: Int, length: Int) -> Bool {
(offset + length) <= size
func isValid(byteOffset: Int, length: Int) -> Bool {
(byteOffset + length) <= size
}
}
108 changes: 67 additions & 41 deletions Sources/WasmInterpreter/WasmInterpreter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,85 +49,111 @@ public final class WasmInterpreter {
m3_FreeEnvironment(_environment)
removeImportedFunctions(for: _importedFunctionContexts)
}
}

@available(*, deprecated, message: "Heap will be removed in a later version")
public func heap() throws -> Heap {
let totalBytes = UnsafeMutablePointer<UInt32>.allocate(capacity: 1)
defer { totalBytes.deallocate() }

guard let bytesPointer = m3_GetMemory(_runtime, totalBytes, 0)
else { throw WasmInterpreterError.invalidMemoryAccess }

return Heap(pointer: bytesPointer, size: Int(totalBytes.pointee))
}

public func dataFromHeap(offset: Int, length: Int) throws -> Data {
let heap = try self.heap()

guard heap.isValid(offset: offset, length: length)
else { throw WasmInterpreterError.invalidMemoryAccess }

return Data(bytes: heap.pointer.advanced(by: offset), count: length)
}

public func stringFromHeap(offset: Int, length: Int) throws -> String {
let data = try dataFromHeap(offset: offset, length: length)
extension WasmInterpreter {
public func stringFromHeap(byteOffset: Int, length: Int) throws -> String {
let data = try dataFromHeap(byteOffset: byteOffset, length: length)

guard let string = String(data: data, encoding: .utf8)
else { throw WasmInterpreterError.invalidUTF8String }

return string
}

public func valueFromHeap<T: WasmTypeProtocol>(offset: Int) throws -> T {
let values = try valuesFromHeap(offset: offset, length: 1) as [T]
public func valueFromHeap<T: WasmTypeProtocol>(byteOffset: Int) throws -> T {
let values = try valuesFromHeap(byteOffset: byteOffset, length: 1) as [T]
guard let value = values.first
else { throw WasmInterpreterError.couldNotLoadMemory }
return value
}

public func valuesFromHeap<T: WasmTypeProtocol>(offset: Int, length: Int) throws -> [T] {
public func valuesFromHeap<T: WasmTypeProtocol>(byteOffset: Int, length: Int) throws -> [T] {
let heap = try self.heap()

guard heap.isValid(offset: offset, length: length)
guard heap.isValid(byteOffset: byteOffset, length: length)
else { throw WasmInterpreterError.invalidMemoryAccess }

let ptr = UnsafeRawPointer(heap.pointer)
.advanced(by: offset)
.advanced(by: byteOffset)
.bindMemory(to: T.self, capacity: length)

return (0..<length).map { ptr[$0] }
}

public func writeToHeap(data: Data, offset: Int) throws {
public func dataFromHeap(byteOffset: Int, length: Int) throws -> Data {
let heap = try self.heap()

guard heap.isValid(byteOffset: byteOffset, length: length)
else { throw WasmInterpreterError.invalidMemoryAccess }

return Data(bytes: heap.pointer.advanced(by: byteOffset), count: length)
}

public func bytesFromHeap(byteOffset: Int, length: Int) throws -> [UInt8] {
let heap = try self.heap()

guard heap.isValid(offset: offset, length: data.count)
guard heap.isValid(byteOffset: byteOffset, length: length)
else { throw WasmInterpreterError.invalidMemoryAccess }

let bufferPointer = UnsafeBufferPointer(
start: heap.pointer.advanced(by: byteOffset),
count: length
)

return Array(bufferPointer)
}

public func writeToHeap(string: String, byteOffset: Int) throws {
try writeToHeap(data: Data(string.utf8), byteOffset: byteOffset)
}

public func writeToHeap<T: WasmTypeProtocol>(value: T, byteOffset: Int) throws {
try writeToHeap(values: [value], byteOffset: byteOffset)
}

public func writeToHeap<T: WasmTypeProtocol>(values: Array<T>, byteOffset: Int) throws {
var values = values
try writeToHeap(
data: Data(bytes: &values, count: values.count * MemoryLayout<T>.size),
byteOffset: byteOffset
)
}

public func writeToHeap(data: Data, byteOffset: Int) throws {
let heap = try self.heap()

guard heap.isValid(byteOffset: byteOffset, length: data.count)
else { throw WasmInterpreterError.invalidMemoryAccess }

try data.withUnsafeBytes { (rawPointer: UnsafeRawBufferPointer) -> Void in
guard let pointer = rawPointer.bindMemory(to: UInt8.self).baseAddress
else { throw WasmInterpreterError.couldNotBindMemory }
heap.pointer
.advanced(by: offset)
.advanced(by: byteOffset)
.initialize(from: pointer, count: data.count)
}
}

public func writeToHeap(string: String, offset: Int) throws {
try writeToHeap(data: Data(string.utf8), offset: offset)
}
public func writeToHeap(bytes: [UInt8], byteOffset: Int) throws {
let heap = try self.heap()

public func writeToHeap<T: WasmTypeProtocol>(value: T, offset: Int) throws {
try writeToHeap(values: [value], offset: offset)
guard heap.isValid(byteOffset: byteOffset, length: bytes.count)
else { throw WasmInterpreterError.invalidMemoryAccess }

heap.pointer
.advanced(by: byteOffset)
.initialize(from: bytes, count: bytes.count)
}

public func writeToHeap<T: WasmTypeProtocol>(values: Array<T>, offset: Int) throws {
var values = values
try writeToHeap(
data: Data(bytes: &values, count: values.count * MemoryLayout<T>.size),
offset: offset
)
private func heap() throws -> Heap {
let totalBytes = UnsafeMutablePointer<UInt32>.allocate(capacity: 1)
defer { totalBytes.deallocate() }

guard let bytesPointer = m3_GetMemory(_runtime, totalBytes, 0)
else { throw WasmInterpreterError.invalidMemoryAccess }

return Heap(pointer: bytesPointer, size: Int(totalBytes.pointee))
}
}

Expand Down
37 changes: 26 additions & 11 deletions Tests/WasmInterpreterTests/Wasm Modules/MemoryModule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,40 @@ public struct MemoryModule {
_vm = try WasmInterpreter(module: MemoryModule.wasm)
}

func heapSize() throws -> Int {
try _vm.heap().size
func string(at byteOffset: Int, length: Int) throws -> String {
try _vm.stringFromHeap(byteOffset: byteOffset, length: length)
}

func string(at offset: Int, length: Int) throws -> String {
try _vm.stringFromHeap(offset: offset, length: length)
func integers(at byteOffset: Int, length: Int) throws -> [Int] {
(try _vm.valuesFromHeap(byteOffset: byteOffset, length: length) as [Int32])
.map(Int.init)
}

func integers(at offset: Int, length: Int) throws -> [Int] {
(try _vm.valuesFromHeap(offset: offset, length: length) as [Int32])
.map(Int.init)
func asciiString(at byteOffset: Int, length: Int) throws -> String {
String((try _vm.bytesFromHeap(byteOffset: byteOffset, length: length)
.map(UnicodeScalar.init)
.map(Character.init)))
}

func write(_ string: String, to byteOffset: Int) throws {
try _vm.writeToHeap(string: string, byteOffset: byteOffset)
}

func write(_ string: String, to offset: Int) throws {
try _vm.writeToHeap(string: string, offset: offset)
func write(_ integers: [Int], to byteOffset: Int) throws {
try _vm.writeToHeap(values: integers.map(Int32.init), byteOffset: byteOffset)
}

func write(_ integers: [Int], to offset: Int) throws {
try _vm.writeToHeap(values: integers.map(Int32.init), offset: offset)
func writeASCIICharacters(in string: String, to byteOffset: Int) throws {
let bytes = string.compactMap { $0.asciiValue }

enum _Error: Error {
case invalidString(String)
}

guard string.count == bytes.count
else { throw _Error.invalidString(string) }

try _vm.writeToHeap(bytes: bytes, byteOffset: byteOffset)
}

// `wat2wasm -o >(base64) Tests/WasmInterpreterTests/Resources/memory.wat | pbcopy`
Expand Down
6 changes: 5 additions & 1 deletion Tests/WasmInterpreterTests/WasmInterpreterTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,16 @@ final class WasmInterpreterTests: XCTestCase {

XCTAssertEqual(10753, try mod.integers(at: 0, length: 1).first)

let goodbye = "Goodbye!"
XCTAssertNoThrow(try mod.writeASCIICharacters(in: goodbye, to: 2))
XCTAssertEqual(goodbye, try mod.asciiString(at: 2, length: goodbye.count))

XCTAssertEqual("👋", try mod.string(at: 17, length: "👋".utf8.count))
}

func testAccessingInvalidMemoryAddresses() throws {
let mod = try MemoryModule()
let size = try mod.heapSize()
let size = 64 * 1024 // 1 page size = 64 KiB

let message = "Hello"

Expand Down

0 comments on commit 1661826

Please sign in to comment.