Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
21 changes: 17 additions & 4 deletions Sources/WAT/Encoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,24 @@ struct Encoder {
}

mutating func writeExpression(lexer: inout Lexer, wat: inout Wat) throws {
var parser = ExpressionParser<ExpressionEncoder>(lexer: lexer)
var parser = ExpressionParser<ExpressionEncoder>(lexer: lexer, features: wat.features)
var exprEncoder = ExpressionEncoder()
try parser.parse(visitor: &exprEncoder, wat: &wat)
try exprEncoder.visitEnd()
output.append(contentsOf: exprEncoder.encoder.output)
lexer = parser.parser.lexer
}

mutating func writeInstruction(lexer: inout Lexer, wat: inout Wat) throws {
var parser = ExpressionParser<ExpressionEncoder>(lexer: lexer, features: wat.features)
var exprEncoder = ExpressionEncoder()
guard try parser.instruction(visitor: &exprEncoder, wat: &wat) else {
throw WatParserError("unexpected end of instruction", location: lexer.location())
}
try exprEncoder.visitEnd()
output.append(contentsOf: exprEncoder.encoder.output)
lexer = parser.parser.lexer
}
}

protocol WasmEncodable {
Expand Down Expand Up @@ -156,7 +167,7 @@ struct ElementExprCollector: AnyInstructionVisitor {
mutating func parse(indices: WatParser.ElementDecl.Indices, wat: inout Wat) throws {
switch indices {
case .elementExprList(let lexer):
var parser = ExpressionParser<ElementExprCollector>(lexer: lexer)
var parser = ExpressionParser<ElementExprCollector>(lexer: lexer, features: wat.features)
try parser.parseElemExprList(visitor: &self, wat: &wat)
case .functionList(let lexer):
try self.parseFunctionList(lexer: lexer, wat: wat)
Expand Down Expand Up @@ -235,8 +246,10 @@ extension WAT.WatParser.ElementDecl {
}
if case let .active(_, offset) = self.mode {
switch offset {
case .source(var lexer):
case .expression(var lexer):
try encoder.writeExpression(lexer: &lexer, wat: &wat)
case .singleInstruction(var lexer):
try encoder.writeInstruction(lexer: &lexer, wat: &wat)
case .synthesized(let offset):
var exprEncoder = ExpressionEncoder()
if isMemory64(tableIndex: Int(tableIndex ?? 0)) {
Expand Down Expand Up @@ -557,7 +570,7 @@ func encode(module: inout Wat) throws -> [UInt8] {
encoder.writeUnsignedLEB128(local.count)
local.type.encode(to: &encoder)
}
let funcTypeIndex = try function.parse(visitor: &exprEncoder, wat: &module)
let funcTypeIndex = try function.parse(visitor: &exprEncoder, wat: &module, features: module.features)
functionSection.append(UInt32(funcTypeIndex))
// TODO?
try exprEncoder.visitEnd()
Expand Down
13 changes: 10 additions & 3 deletions Sources/WAT/Lexer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -480,18 +480,25 @@ extension Lexer.Cursor {
/// - Returns: The parsed string without underscores
mutating func parseUnderscoredChars(continueParsing: (Unicode.Scalar) -> Bool) throws -> String {
var value = String.UnicodeScalarView()
var lastChar: Unicode.Scalar?
var lastParsedChar: Unicode.Scalar?
while let char = try peek() {
lastChar = char
if char == "_" {
guard let lastChar = lastParsedChar else {
throw createError("Invalid hex number, leading underscore")
}
guard lastChar != "_" else {
throw createError("Invalid hex number, consecutive underscores")
}
lastParsedChar = char
_ = try next()
continue
}
guard continueParsing(char) else { break }
lastParsedChar = char
value.append(char)
_ = try next()
}
if lastChar == "_" {
if lastParsedChar == "_" {
throw createError("Invalid hex number, trailing underscore")
}
return String(value)
Expand Down
67 changes: 41 additions & 26 deletions Sources/WAT/NameMapping.swift
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
import WasmParser
import WasmTypes

/// A name with its location in the source file
struct Name: Equatable {
/// The name of the module field declaration specified in $id form
let value: String
/// The location of the name in the source file
let location: Location
}

/// A module field declaration that may have its name
protocol NamedModuleFieldDecl {
/// The name of the module field declaration specified in $id form
var id: String? { get }
var id: Name? { get }
}

/// A module field declaration that may be imported from another module
Expand All @@ -22,19 +30,22 @@ struct NameMapping<Decl: NamedModuleFieldDecl> {
/// - Parameter newDecl: The declaration to add
/// - Returns: The index of the added declaration
@discardableResult
mutating func add(_ newDecl: Decl) -> Int {
mutating func add(_ newDecl: Decl) throws -> Int {
let index = decls.count
decls.append(newDecl)
if let name = newDecl.id {
nameToIndex[name] = index
guard nameToIndex[name.value] == nil else {
throw WatParserError("Duplicate \(name.value) identifier", location: name.location)
}
nameToIndex[name.value] = index
}
return index
}

func resolveIndex(use: Parser.IndexOrId) throws -> Int {
switch use {
case .id(let id, _):
guard let byName = nameToIndex[id] else {
guard let byName = nameToIndex[id.value] else {
throw WatParserError("Unknown \(Decl.self) \(id)", location: use.location)
}
return byName
Expand Down Expand Up @@ -91,8 +102,8 @@ struct TypesMap {

/// Adds a new function type to the mapping
@discardableResult
mutating func add(_ decl: WatParser.FunctionTypeDecl) -> Int {
nameMapping.add(decl)
mutating func add(_ decl: WatParser.FunctionTypeDecl) throws -> Int {
try nameMapping.add(decl)
// Normalize the function type signature without parameter names
if let existing = indices[decl.type.signature] {
return existing
Expand All @@ -104,11 +115,11 @@ struct TypesMap {
}

/// Adds a new function type to the mapping without parameter names
private mutating func addAnonymousSignature(_ signature: FunctionType) -> Int {
private mutating func addAnonymousSignature(_ signature: FunctionType) throws -> Int {
if let existing = indices[signature] {
return existing
}
return add(
return try add(
WatParser.FunctionTypeDecl(
id: nil,
type: WatParser.FunctionType(signature: signature, parameterNames: [])
Expand All @@ -118,22 +129,22 @@ struct TypesMap {

private mutating func resolveBlockType(
results: [ValueType],
resolveSignatureIndex: (inout TypesMap) -> Int
resolveSignatureIndex: (inout TypesMap) throws -> Int
) throws -> BlockType {
if let result = results.first {
guard results.count > 1 else { return .type(result) }
return .funcType(UInt32(resolveSignatureIndex(&self)))
return try .funcType(UInt32(resolveSignatureIndex(&self)))
}
return .empty
}
private mutating func resolveBlockType(
signature: WasmTypes.FunctionType,
resolveSignatureIndex: (inout TypesMap) -> Int
resolveSignatureIndex: (inout TypesMap) throws -> Int
) throws -> BlockType {
if signature.parameters.isEmpty {
return try resolveBlockType(results: signature.results, resolveSignatureIndex: resolveSignatureIndex)
}
return .funcType(UInt32(resolveSignatureIndex(&self)))
return try .funcType(UInt32(resolveSignatureIndex(&self)))
}

/// Resolves a block type from a list of result types
Expand All @@ -142,7 +153,7 @@ struct TypesMap {
results: results,
resolveSignatureIndex: {
let signature = FunctionType(parameters: [], results: results)
return $0.addAnonymousSignature(signature)
return try $0.addAnonymousSignature(signature)
})
}

Expand All @@ -151,15 +162,15 @@ struct TypesMap {
return try resolveBlockType(
signature: signature,
resolveSignatureIndex: {
return $0.addAnonymousSignature(signature)
return try $0.addAnonymousSignature(signature)
})
}

/// Resolves a block type from a type use
mutating func resolveBlockType(use: WatParser.TypeUse) throws -> BlockType {
switch (use.index, use.inline) {
case let (indexOrId?, _):
let (type, index) = try resolve(use: indexOrId)
case let (indexOrId?, inline):
let (type, index) = try resolveAndCheck(use: indexOrId, inline: inline)
return try resolveBlockType(signature: type.signature, resolveSignatureIndex: { _ in index })
case (nil, let inline?):
return try resolveBlockType(signature: inline.signature)
Expand All @@ -173,7 +184,7 @@ struct TypesMap {
return try nameMapping.resolveIndex(use: indexOrId)
case (nil, let inline):
let inline = inline?.signature ?? WasmTypes.FunctionType(parameters: [], results: [])
return addAnonymousSignature(inline)
return try addAnonymousSignature(inline)
}
}

Expand All @@ -183,18 +194,22 @@ struct TypesMap {
return (decl.type, index)
}

private func resolveAndCheck(use indexOrId: Parser.IndexOrId, inline: WatParser.FunctionType?) throws -> (type: WatParser.FunctionType, index: Int) {
let (found, index) = try resolve(use: indexOrId)
if let inline {
// If both index and inline type, then they must match
guard found.signature == inline.signature else {
throw WatParserError("Type mismatch \(found) != \(inline)", location: indexOrId.location)
}
}
return (found, Int(index))
}

/// Resolves a function type from a type use with an optional inline type
mutating func resolve(use: WatParser.TypeUse) throws -> (type: WatParser.FunctionType, index: Int) {
switch (use.index, use.inline) {
case let (indexOrId?, inline):
let (found, index) = try resolve(use: indexOrId)
if let inline {
// If both index and inline type, then they must match
guard found.signature == inline.signature else {
throw WatParserError("Type mismatch \(found) != \(inline)", location: indexOrId.location)
}
}
return (found, Int(index))
return try resolveAndCheck(use: indexOrId, inline: inline)
case (nil, let inline):
// If no index and no inline type, then it's a function type with no parameters or results
let inline = inline ?? WatParser.FunctionType(signature: WasmTypes.FunctionType(parameters: [], results: []), parameterNames: [])
Expand All @@ -203,7 +218,7 @@ struct TypesMap {
return (inline, index)
}
// Add inline type to the index space if it doesn't already exist
let index = add(WatParser.FunctionTypeDecl(id: nil, type: inline))
let index = try add(WatParser.FunctionTypeDecl(id: nil, type: inline))
return (inline, index)
}
}
Expand Down
Loading