Skip to content

Commit ed04d10

Browse files
WAT: Enable smoke encoding test for the proposal
1 parent e174304 commit ed04d10

File tree

7 files changed

+154
-52
lines changed

7 files changed

+154
-52
lines changed

Sources/WAT/Parser/ExpressionParser.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,17 @@ struct ExpressionParser<Visitor: InstructionVisitor> {
162162
{
163163
return value
164164
}
165+
166+
// WAST predication allows omitting some concrete specifiers
167+
if try parser.takeParenBlockStart("ref.null"), try parser.isEndOfParen() {
168+
return .refNull(nil)
169+
}
170+
if try parser.takeParenBlockStart("ref.func"), try parser.isEndOfParen() {
171+
return .refFunc(functionIndex: nil)
172+
}
173+
if try parser.takeParenBlockStart("ref.extern"), try parser.isEndOfParen() {
174+
return .refFunc(functionIndex: nil)
175+
}
165176
parser = initialParser
166177
return nil
167178
}

Sources/WAT/Parser/WastParser.swift

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,19 @@ struct WastParser {
8282

8383
mutating func expectationValues() throws -> [WastExpectValue] {
8484
var values: [WastExpectValue] = []
85-
var collector = ConstExpressionCollector(addValue: { values.append(.value($0)) })
85+
var collector = ConstExpressionCollector(addValue: {
86+
let value: WastExpectValue
87+
switch $0 {
88+
case .i32(let v): value = .i32(v)
89+
case .i64(let v): value = .i64(v)
90+
case .f32(let v): value = .f32(v)
91+
case .f64(let v): value = .f64(v)
92+
case .refNull(let heapTy): value = .refNull(heapTy)
93+
case .refFunc(let index): value = .refFunc(functionIndex: index)
94+
case .refExtern(let v): value = .refExtern(value: v)
95+
}
96+
values.append(value)
97+
})
8698
var exprParser = ExpressionParser<ConstExpressionCollector>(lexer: parser.lexer, features: features)
8799
while true {
88100
if let expectValue = try exprParser.parseWastExpectValue() {
@@ -158,8 +170,20 @@ public struct WastInvoke {
158170
}
159171

160172
public enum WastExpectValue {
161-
/// A concrete value that is expected to be returned.
162-
case value(WastConstValue)
173+
case i32(UInt32)
174+
case i64(UInt64)
175+
case f32(UInt32)
176+
case f64(UInt64)
177+
178+
/// A value that is expected to be a null reference,
179+
/// optionally with a specific type.
180+
case refNull(HeapType?)
181+
/// A value that is expected to be a non-null reference
182+
/// to a function, optionally with a specific index.
183+
case refFunc(functionIndex: UInt32?)
184+
/// A value that is expected to be a non-null reference
185+
/// to an extern, optionally with a specific value.
186+
case refExtern(value: UInt32?)
163187
/// A value that is expected to be a canonical NaN.
164188
/// Corresponds to `f32.const nan:canonical` in WAST.
165189
case f32CanonicalNaN

Sources/WAT/Parser/WatParser.swift

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,13 @@ struct WatParser {
4646
self.make = { _ in value }
4747
}
4848

49+
func project<U>(_ keyPath: KeyPath<T, U>) -> UnresolvedType<U> {
50+
return UnresolvedType<U> {
51+
let parent = try make($0)
52+
return parent[keyPath: keyPath]
53+
}
54+
}
55+
4956
func map<U>(_ transform: @escaping (T) -> U) -> UnresolvedType<U> {
5057
return UnresolvedType<U>(make: { try transform(resolve($0)) })
5158
}
@@ -267,19 +274,25 @@ struct WatParser {
267274
var inlineElement: ElementDecl?
268275
let isMemory64 = try expectAddressSpaceType()
269276

277+
// elemexpr ::= '(' 'item' expr ')' | '(' instr ')'
278+
func parseExprList() throws -> (UInt64, ElementDecl.Indices) {
279+
var numberOfItems: UInt64 = 0
280+
let indices: ElementDecl.Indices = .elementExprList(parser.lexer)
281+
while try parser.take(.leftParen) {
282+
numberOfItems += 1
283+
try parser.skipParenBlock()
284+
}
285+
return (numberOfItems, indices)
286+
}
287+
270288
if let refType = try takeRefType() {
271289
guard try parser.takeParenBlockStart("elem") else {
272290
throw WatParserError("expected elem", location: parser.lexer.location())
273291
}
274292
var numberOfItems: UInt64 = 0
275293
let indices: ElementDecl.Indices
276294
if try parser.peek(.leftParen) != nil {
277-
// elemexpr ::= '(' 'item' expr ')' | '(' instr ')'
278-
indices = .elementExprList(parser.lexer)
279-
while try parser.take(.leftParen) {
280-
numberOfItems += 1
281-
try parser.skipParenBlock()
282-
}
295+
(numberOfItems, indices) = try parseExprList()
283296
} else {
284297
// Consume function indices
285298
indices = .functionList(parser.lexer)
@@ -302,7 +315,19 @@ struct WatParser {
302315
)
303316
}
304317
} else {
305-
type = try tableType(isMemory64: isMemory64)
318+
var tableType = try tableType(isMemory64: isMemory64)
319+
if try parser.peek(.leftParen) != nil {
320+
let (numberOfItems, indices) = try parseExprList()
321+
inlineElement = ElementDecl(
322+
mode: .inline, type: tableType.project(\.elementType), indices: indices
323+
)
324+
tableType = tableType.map {
325+
var value = $0
326+
value.limits.min = numberOfItems
327+
return value
328+
}
329+
}
330+
type = tableType
306331
}
307332
kind = .table(
308333
TableDecl(

Sources/WasmParser/WasmTypes.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,10 @@ public enum BlockType: Equatable {
4949
/// > Note:
5050
/// <https://webassembly.github.io/spec/core/syntax/types.html#limits>
5151
public struct Limits: Equatable {
52-
public let min: UInt64
53-
public let max: UInt64?
54-
public let isMemory64: Bool
55-
public let shared: Bool
52+
public var min: UInt64
53+
public var max: UInt64?
54+
public var isMemory64: Bool
55+
public var shared: Bool
5656

5757
public init(min: UInt64, max: UInt64? = nil, isMemory64: Bool = false, shared: Bool = false) {
5858
self.min = min
@@ -69,8 +69,8 @@ public typealias MemoryType = Limits
6969
/// > Note:
7070
/// <https://webassembly.github.io/spec/core/syntax/types.html#table-types>
7171
public struct TableType: Equatable {
72-
public let elementType: ReferenceType
73-
public let limits: Limits
72+
public var elementType: ReferenceType
73+
public var limits: Limits
7474

7575
public init(elementType: ReferenceType, limits: Limits) {
7676
self.elementType = elementType

Tests/WATTests/EncoderTests.swift

Lines changed: 55 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,28 @@ class EncoderTests: XCTestCase {
1111
var failed: Set<String> = []
1212
}
1313

14+
private func checkMalformed(wast: URL, module: ModuleDirective, message: String, recordFail: () -> Void) {
15+
let diagnostic = {
16+
let (line, column) = module.location.computeLineAndColumn()
17+
return "\(wast.path):\(line):\(column) should be malformed: \(message)"
18+
}
19+
switch module.source {
20+
case .text(var wat):
21+
XCTAssertThrowsError(
22+
try {
23+
_ = try wat.encode()
24+
recordFail()
25+
}(), diagnostic())
26+
case .quote(let bytes):
27+
XCTAssertThrowsError(
28+
try {
29+
_ = try wat2wasm(String(decoding: bytes, as: UTF8.self))
30+
recordFail()
31+
}(), diagnostic())
32+
case .binary: break
33+
}
34+
}
35+
1436
func checkWabtCompatibility(
1537
wast: URL, json: URL, stats parentStats: inout CompatibilityTestStats
1638
) throws {
@@ -35,25 +57,7 @@ class EncoderTests: XCTestCase {
3557
case .module(let moduleDirective):
3658
watModules.append(moduleDirective)
3759
case .assertMalformed(let module, let message):
38-
let diagnostic = {
39-
let (line, column) = module.location.computeLineAndColumn()
40-
return "\(wast.path):\(line):\(column) should be malformed: \(message)"
41-
}
42-
switch module.source {
43-
case .text(var wat):
44-
XCTAssertThrowsError(
45-
try {
46-
_ = try wat.encode()
47-
recordFail()
48-
}(), diagnostic())
49-
case .quote(let bytes):
50-
XCTAssertThrowsError(
51-
try {
52-
_ = try wat2wasm(String(decoding: bytes, as: UTF8.self))
53-
recordFail()
54-
}(), diagnostic())
55-
case .binary: break
56-
}
60+
checkMalformed(wast: wast, module: module, message: message, recordFail: recordFail)
5761
default: break
5862
}
5963
}
@@ -141,6 +145,38 @@ class EncoderTests: XCTestCase {
141145
#endif
142146
}
143147

148+
func testFunctionReferencesProposal() throws {
149+
// NOTE: Perform smoke check for function-references proposal here without
150+
// bit-to-bit compatibility check with wabt as wabt does not support
151+
// function-references proposal yet.
152+
for wastFile in Spectest.wastFiles(
153+
path: [
154+
Spectest.testsuitePath.appendingPathComponent("proposals/function-references")
155+
], include: [], exclude: []
156+
) {
157+
print("Checking \(wastFile.path)")
158+
var parser = WastParser(
159+
try String(contentsOf: wastFile),
160+
features: Spectest.deriveFeatureSet(wast: wastFile)
161+
)
162+
while let directive = try parser.nextDirective() {
163+
switch directive {
164+
case .module(let directive):
165+
guard case var .text(wat) = directive.source else {
166+
continue
167+
}
168+
_ = wat = wat
169+
// TODO: Enable smoke check for encoding
170+
// _ = try wat.encode()
171+
case .assertMalformed(let module, let message):
172+
checkMalformed(wast: wastFile, module: module, message: message, recordFail: {})
173+
default:
174+
break
175+
}
176+
}
177+
}
178+
}
179+
144180
func testEncodeNameSection() throws {
145181
let bytes = try wat2wasm(
146182
"""

Tests/WATTests/Spectest.swift

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,19 @@ enum Spectest {
1616
testsuitePath.appendingPathComponent(file)
1717
}
1818

19-
static func wastFiles(include: [String] = [], exclude: [String] = ["annotations.wast"]) -> AnyIterator<URL> {
20-
var allFiles = [
21-
testsuitePath,
22-
testsuitePath.appendingPathComponent("proposals/memory64"),
23-
testsuitePath.appendingPathComponent("proposals/tail-call"),
24-
rootDirectory.appendingPathComponent("Tests/WasmKitTests/ExtraSuite"),
25-
].flatMap {
19+
static let defaultSuites = [
20+
testsuitePath,
21+
testsuitePath.appendingPathComponent("proposals/memory64"),
22+
testsuitePath.appendingPathComponent("proposals/tail-call"),
23+
rootDirectory.appendingPathComponent("Tests/WasmKitTests/ExtraSuite"),
24+
]
25+
26+
static func wastFiles(
27+
path: [URL] = defaultSuites,
28+
include: [String] = [],
29+
exclude: [String] = ["annotations.wast"]
30+
) -> AnyIterator<URL> {
31+
var allFiles = path.flatMap {
2632
try! FileManager.default.contentsOfDirectory(at: $0, includingPropertiesForKeys: nil)
2733
}.makeIterator()
2834

Tests/WasmKitTests/Spectest/TestCase.swift

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -401,15 +401,15 @@ extension WastRunContext {
401401
extension Value {
402402
func isTestEquivalent(to value: WastExpectValue) -> Bool {
403403
switch (self, value) {
404-
case let (.i32(lhs), .value(.i32(rhs))):
404+
case let (.i32(lhs), .i32(rhs)):
405405
return lhs == rhs
406-
case let (.i64(lhs), .value(.i64(rhs))):
406+
case let (.i64(lhs), .i64(rhs)):
407407
return lhs == rhs
408-
case let (.f32(lhs), .value(.f32(rhs))):
408+
case let (.f32(lhs), .f32(rhs)):
409409
let lhs = Float32(bitPattern: lhs)
410410
let rhs = Float32(bitPattern: rhs)
411411
return lhs.isNaN && rhs.isNaN || lhs == rhs
412-
case let (.f64(lhs), .value(.f64(rhs))):
412+
case let (.f64(lhs), .f64(rhs)):
413413
let lhs = Float64(bitPattern: lhs)
414414
let rhs = Float64(bitPattern: rhs)
415415
return lhs.isNaN && rhs.isNaN || lhs == rhs
@@ -419,12 +419,12 @@ extension Value {
419419
case let (.f32(lhs), .f32ArithmeticNaN),
420420
let (.f32(lhs), .f32CanonicalNaN):
421421
return Float32(bitPattern: lhs).isNaN
422-
case let (.ref(.extern(lhs?)), .value(.refExtern(rhs))):
423-
return lhs == rhs
424-
case let (.ref(.function(lhs?)), .value(.refFunc(rhs))):
425-
return lhs == rhs
426-
case (.ref(.extern(nil)), .value(.refNull(.abstract(.externRef)))),
427-
(.ref(.function(nil)), .value(.refNull(.abstract(.funcRef)))):
422+
case let (.ref(.extern(lhs?)), .refExtern(rhs)):
423+
return rhs.map { lhs == $0 } ?? true
424+
case let (.ref(.function(lhs?)), .refFunc(rhs)):
425+
return rhs.map { lhs == $0 } ?? true
426+
case (.ref(.extern(nil)), .refNull(.abstract(.externRef))),
427+
(.ref(.function(nil)), .refNull(.abstract(.funcRef))):
428428
return true
429429
default:
430430
return false

0 commit comments

Comments
 (0)