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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Sources/WasmParser/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
add_wasmkit_library(WasmParser
Stream/ByteStream.swift
Stream/FileHandleStream.swift
Stream/Stream.swift
BinaryInstructionDecoder.swift
InstructionVisitor.swift
LEB.swift
Expand Down
39 changes: 31 additions & 8 deletions Sources/WasmParser/Stream/ByteStream.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
public protocol ByteStream: Stream where Element == UInt8 {}
public protocol ByteStream: ~Copyable {
var currentIndex: Int { get }

func consumeAny() throws(WasmParserError) -> UInt8
func consume(_ expected: Set<UInt8>) throws(WasmParserError) -> UInt8
func consume(count: Int) throws(WasmParserError) -> ArraySlice<UInt8>

func peek() throws(WasmParserError) -> UInt8?
}

extension ByteStream {
func consume(_ expected: UInt8) throws(WasmParserError) -> UInt8 {
try consume(Set([expected]))
}

@usableFromInline
func hasReachedEnd() throws(WasmParserError) -> Bool {
try peek() == nil
}
}
public final class StaticByteStream: ByteStream {
public let bytes: ArraySlice<UInt8>
public var currentIndex: Int
Expand All @@ -15,9 +33,9 @@ public final class StaticByteStream: ByteStream {
}

@discardableResult
public func consumeAny() throws -> UInt8 {
public func consumeAny() throws(WasmParserError) -> UInt8 {
guard bytes.indices.contains(currentIndex) else {
throw StreamError<Element>.unexpectedEnd(expected: nil)
throw WasmParserError(kind: .unexpectedEnd(expected: nil), offset: self.currentIndex)
}

let consumed = bytes[currentIndex]
Expand All @@ -26,26 +44,31 @@ public final class StaticByteStream: ByteStream {
}

@discardableResult
public func consume(_ expected: Set<UInt8>) throws -> UInt8 {
public func consume(_ expected: Set<UInt8>) throws(WasmParserError) -> UInt8 {
guard bytes.indices.contains(currentIndex) else {
throw StreamError<Element>.unexpectedEnd(expected: Set(expected))
throw WasmParserError(kind: .unexpectedEnd(expected: Set(expected)), offset: currentIndex)
}

let consumed = bytes[currentIndex]
guard expected.contains(consumed) else {
throw StreamError<Element>.unexpected(consumed, index: currentIndex, expected: Set(expected))
throw WasmParserError(
kind: .unexpectedByte(
consumed,
index: currentIndex,
expected: Set(expected)
), offset: currentIndex)
}

currentIndex = bytes.index(after: currentIndex)
return consumed
}

public func consume(count: Int) throws -> ArraySlice<UInt8> {
public func consume(count: Int) throws(WasmParserError) -> ArraySlice<UInt8> {
guard count > 0 else { return [] }
let updatedIndex = currentIndex + count

guard bytes.indices.contains(updatedIndex - 1) else {
throw StreamError<Element>.unexpectedEnd(expected: nil)
throw WasmParserError(kind: .unexpectedEnd(expected: nil), offset: currentIndex)
}

defer { currentIndex = updatedIndex }
Expand Down
36 changes: 25 additions & 11 deletions Sources/WasmParser/Stream/FileHandleStream.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,22 @@ public final class FileHandleStream: ByteStream {
try readMoreIfNeeded()
}

private func readMoreIfNeeded() throws {
private func readMoreIfNeeded() throws(WasmParserError) {
guard Int(endOffset) == currentIndex else { return }
startOffset = currentIndex

let data = try fileHandle.read(upToCount: bufferLength)
do {
let data = try fileHandle.read(upToCount: bufferLength)

bytes = [UInt8](data)
bytes = [UInt8](data)
} catch {
throw WasmParserError(kind: .unclassified(error), offset: currentIndex)
}
endOffset = startOffset + bytes.count
}

@discardableResult
public func consumeAny() throws -> UInt8 {
public func consumeAny() throws(WasmParserError) -> UInt8 {
guard let consumed = try peek() else {
throw WasmParserError(.unexpectedEnd, offset: currentIndex)
}
Expand All @@ -37,18 +41,23 @@ public final class FileHandleStream: ByteStream {
}

@discardableResult
public func consume(_ expected: Set<UInt8>) throws -> UInt8 {
public func consume(_ expected: Set<UInt8>) throws(WasmParserError) -> UInt8 {
guard let consumed = try peek() else {
throw StreamError<UInt8>.unexpectedEnd(expected: Set(expected))
throw WasmParserError(kind: .unexpectedEnd(expected: Set(expected)), offset: currentIndex)
}
guard expected.contains(consumed) else {
throw StreamError<Element>.unexpected(consumed, index: currentIndex, expected: Set(expected))
throw WasmParserError(
kind: .unexpectedByte(
consumed,
index: currentIndex,
expected: Set(expected)
), offset: currentIndex)
}
currentIndex = bytes.index(after: currentIndex)
return consumed
}

public func consume(count: Int) throws -> ArraySlice<UInt8> {
public func consume(count: Int) throws(WasmParserError) -> ArraySlice<UInt8> {
let bytesToRead = currentIndex + count - endOffset

guard bytesToRead > 0 else {
Expand All @@ -58,9 +67,14 @@ public final class FileHandleStream: ByteStream {
return result
}

let data = try fileHandle.read(upToCount: bytesToRead)
let data: [UInt8]
do {
data = try fileHandle.read(upToCount: bytesToRead)
} catch {
throw WasmParserError(kind: .unclassified(error), offset: currentIndex)
}
guard data.count == bytesToRead else {
throw StreamError<UInt8>.unexpectedEnd(expected: nil)
throw WasmParserError(kind: .unexpectedEnd(expected: nil), offset: currentIndex)
}

bytes.append(contentsOf: [UInt8](data))
Expand All @@ -74,7 +88,7 @@ public final class FileHandleStream: ByteStream {
return result
}

public func peek() throws -> UInt8? {
public func peek() throws(WasmParserError) -> UInt8? {
try readMoreIfNeeded()

let index = currentIndex - startOffset
Expand Down
28 changes: 0 additions & 28 deletions Sources/WasmParser/Stream/Stream.swift

This file was deleted.

59 changes: 52 additions & 7 deletions Sources/WasmParser/WasmParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -208,28 +208,70 @@ public struct WasmFeatureSet: OptionSet, Sendable {

/// An error that occurs during parsing of a WebAssembly binary
public struct WasmParserError: Swift.Error {
Comment thread
MaxDesiatov marked this conversation as resolved.
@usableFromInline
struct Message: Sendable {
public struct Message: Sendable {
Comment thread
MaxDesiatov marked this conversation as resolved.
Outdated
let text: String

init(_ text: String) {
self.text = text
}
}

let message: Message
let offset: Int
@usableFromInline
enum Kind: Sendable {
case message(Message)
case unexpectedEnd(expected: Set<UInt8>?)
case unexpectedByte(UInt8, index: Int, expected: Set<UInt8>?)
case unclassified(any Error)
}

let kind: Kind
let offset: Int?

@usableFromInline
init(kind: Kind, offset: Int) {
self.kind = kind
self.offset = offset
}
}

extension WasmParserError {
@usableFromInline
init(_ message: Message, offset: Int) {
self.message = message
self.kind = .message(message)
self.offset = offset
}
}

extension BinaryInteger {
var hexString: String {
"0x\(String(self, radix: 16))"
}
}

extension WasmParserError: CustomStringConvertible {
public var description: String {
return "\"\(message)\" at offset 0x\(String(offset, radix: 16))"
switch self.kind {
case .message(let message):
if let offset {
return "\"\(message)\" at offset 0x\(String(offset, radix: 16))"
} else {
return message.text
}
case .unexpectedEnd(let expected):
var result = "Unexpected end of byte sequence."
if let expected, expected.count > 0 {
result.append(contentsOf: " Expected one of \(expected.map {$0.hexString})")
}
return result
case .unexpectedByte(let byte, let index, let expected):
var result = "Unexpected byte \(byte.hexString) at index \(index.hexString)."
if let expected, expected.count > 0 {
result.append(contentsOf: " Expected one of \(expected.map {$0.hexString})")
}
return result
case .unclassified(let error):
return "\(error)"
}
Comment thread
MaxDesiatov marked this conversation as resolved.
}
}

Expand Down Expand Up @@ -595,7 +637,10 @@ extension Parser {
case 0x6F:
elementType = .externRef
default:
throw StreamError.unexpected(b, index: offset, expected: [0x6F, 0x70])
throw WasmParserError(
kind: .unexpectedByte(b, index: offset, expected: [0x6F, 0x70]),
offset: stream.currentIndex
)
}

let limits = try parseLimits()
Expand Down