Skip to content

Commit

Permalink
Merge pull request bytecodealliance#11 from dhil/wasmfx-merge
Browse files Browse the repository at this point in the history
Merge with upstream
  • Loading branch information
dhil authored Sep 25, 2023
2 parents 9a01dae + 1095b0d commit 096b512
Show file tree
Hide file tree
Showing 13 changed files with 133 additions and 83 deletions.
2 changes: 1 addition & 1 deletion crates/wasm-compose/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ of composing WebAssembly components together.
## License

This project is licensed under the Apache 2.0 license with the LLVM exception.
See [LICENSE](LICENSE) for more details.
See [LICENSE](../../LICENSE) for more details.

### Contribution

Expand Down
2 changes: 1 addition & 1 deletion crates/wasm-mutate/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ wasm-tools mutate original.wasm --seed 0 -o out.wasm --preserve-semantics
# License

This project is licensed under the Apache 2.0 license with the LLVM exception.
See [LICENSE](LICENSE) for more details.
See [LICENSE](../../LICENSE) for more details.

### Contribution

Expand Down
3 changes: 1 addition & 2 deletions crates/wasm-smith/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
[![](https://docs.rs/wasm-smith/badge.svg)](https://docs.rs/wasm-smith/)
[![](https://img.shields.io/crates/v/wasm-smith.svg)](https://crates.io/crates/wasm-smith)
[![](https://img.shields.io/crates/d/wasm-smith.svg)](https://crates.io/crates/wasm-smith)
![Rust](https://github.com/fitzgen/wasm-smith/workflows/Rust/badge.svg)

* [Features](#features)
* [Usage](#usage)
Expand Down Expand Up @@ -76,7 +75,7 @@ $ cargo fuzz run my_wasm_smith_fuzz_target
```

> **Note:** Also check out [the `validate` fuzz
> target](https://github.com/bytecodealliance/wasm-tools/blob/main/fuzz/fuzz_targets/validate.rs)
> target](https://github.com/bytecodealliance/wasm-tools/blob/main/fuzz/src/validate.rs)
> defined in this repository. Using the `wasmparser` crate, it checks that every
> module generated by `wasm-smith` validates successfully.
Expand Down
102 changes: 38 additions & 64 deletions crates/wast/src/core/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,18 +98,18 @@ enum Level<'a> {
TryArm,
}

/// Possible states of "what should be parsed next?" in an `if` expression.
/// Possible states of "what is currently being parsed?" in an `if` expression.
enum If<'a> {
/// Only the `if` has been parsed, next thing to parse is the clause, if
/// any, of the `if` instruction.
/// Only the `if` instructoin has been parsed, next thing to parse is the
/// clause, if any, of the `if` instruction.
///
/// This parse ends when `(then ...)` is encountered.
Clause(Instruction<'a>),
/// Next thing to parse is the `then` block
Then(Instruction<'a>),
/// Next thing to parse is the `else` block
/// Currently parsing the `then` block, and afterwards a closing paren is
/// required or an `(else ...)` expression.
Then,
/// Parsing the `else` expression, nothing can come after.
Else,
/// This `if` statement has finished parsing and if anything remains it's a
/// syntax error.
End,
}

/// Possible state of "what should be parsed next?" in a `try` expression.
Expand Down Expand Up @@ -217,9 +217,6 @@ impl<'a> ExpressionParser<'a> {
// items in the `if` statement. Otherwise we're just careful
// to terminate with an `end` instruction.
Level::If(If::Clause(_)) => {
return Err(parser.error("previous `if` had no clause"));
}
Level::If(If::Then(_)) => {
return Err(parser.error("previous `if` had no `then`"));
}
Level::If(_) => {
Expand Down Expand Up @@ -282,23 +279,16 @@ impl<'a> ExpressionParser<'a> {
})
}

/// Handles all parsing of an `if` statement.
/// State transitions with parsing an `if` statement.
///
/// The syntactical form of an `if` stament looks like:
///
/// ```wat
/// (if $clause (then $then) (else $else))
/// (if ($clause)... (then $then) (else $else))
/// ```
///
/// but it turns out we practically see a few things in the wild:
///
/// * inside the `(if ...)` every sub-thing is surrounded by parens
/// * The `then` and `else` keywords are optional
/// * The `$then` and `$else` blocks don't need to be surrounded by parens
///
/// That's all attempted to be handled here. The part about all sub-parts
/// being surrounded by `(` and `)` means that we hook into the `LParen`
/// parsing above to call this method there unconditionally.
/// THis method is called after a `(` is parsed within the `(if ...` block.
/// This determines what to do next.
///
/// Returns `true` if the rest of the arm above should be skipped, or
/// `false` if we should parse the next item as an instruction (because we
Expand All @@ -310,53 +300,37 @@ impl<'a> ExpressionParser<'a> {
_ => return Ok(false),
};

// The first thing parsed in an `if` statement is the clause. If the
// clause starts with `then`, however, then we know to skip the clause
// and fall through to below.
if let If::Clause(if_instr) = i {
let instr = mem::replace(if_instr, Instruction::End(None));
*i = If::Then(instr);
if !parser.peek::<kw::then>()? {
return Ok(false);
}
}

// All `if` statements are required to have a `then`. This is either the
// second s-expr (with or without a leading `then`) or the first s-expr
// with a leading `then`. The optionality of `then` isn't strictly what
// the text spec says but it matches wabt for now.
//
// Note that when we see the `then`, that's when we actually add the
// original `if` instruction to the stream.
if let If::Then(if_instr) = i {
let instr = mem::replace(if_instr, Instruction::End(None));
self.instrs.push(instr);
*i = If::Else;
if parser.parse::<Option<kw::then>>()?.is_some() {
match i {
// If the clause is still being parsed then interpret this `(` as a
// folded instruction unless it starts with `then`, in which case
// this transitions to the `Then` state and a new level has been
// reached.
If::Clause(if_instr) => {
if !parser.peek::<kw::then>()? {
return Ok(false);
}
parser.parse::<kw::then>()?;
let instr = mem::replace(if_instr, Instruction::End(None));
self.instrs.push(instr);
*i = If::Then;
self.stack.push(Level::IfArm);
return Ok(true);
Ok(true)
}
return Ok(false);
}

// effectively the same as the `then` parsing above
if let If::Else = i {
self.instrs.push(Instruction::Else(None));
if parser.parse::<Option<kw::r#else>>()?.is_some() {
if parser.is_empty() {
self.instrs.pop();
}
// Previously we were parsing the `(then ...)` clause so this next
// `(` must be followed by `else`.
If::Then => {
parser.parse::<kw::r#else>()?;
self.instrs.push(Instruction::Else(None));
*i = If::Else;
self.stack.push(Level::IfArm);
return Ok(true);
Ok(true)
}
*i = If::End;
return Ok(false);
}

// If we made it this far then we're at `If::End` which means that there
// were too many s-expressions inside the `(if)` and we don't want to
// parse anything else.
Err(parser.error("unexpected token: too many payloads inside of `(if)`"))
// If after a `(else ...` clause is parsed there's another `(` then
// that's not syntactically allowed.
If::Else => Err(parser.error("unexpected token: too many payloads inside of `(if)`")),
}
}

/// Handles parsing of a `try` statement. A `try` statement is simpler
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

;; `wit-component` should have injected a call to a function that allocates
;; the stack and sets $allocation_state to 2
(if (i32.ne (global.get $allocation_state) (i32.const 2)) (unreachable))
(if (i32.ne (global.get $allocation_state) (i32.const 2)) (then (unreachable)))

;; First, allocate a page using $cabi_realloc and write to it. This tests
;; that we can use the main module's allocator if present (or else a
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

;; `wit-component` should have injected a call to a function that allocates
;; the stack and sets $allocation_state to 2
(if (i32.ne (global.get $allocation_state) (i32.const 2)) (unreachable))
(if (i32.ne (global.get $allocation_state) (i32.const 2)) (then (unreachable)))

;; First, allocate a page using $cabi_realloc and write to it. This tests
;; that we can use the main module's allocator if present (or else a
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

;; `wit-component` should have injected a call to a function that allocates
;; the stack and sets $allocation_state to 2
(if (i32.ne (global.get $allocation_state) (i32.const 2)) (unreachable))
(if (i32.ne (global.get $allocation_state) (i32.const 2)) (then (unreachable)))

;; Allocate 8 bytes of stack space for the two u32 return values. The
;; original stack pointer is saved in local 0 and the stack frame for this
Expand Down
24 changes: 12 additions & 12 deletions tests/local/fuzz1.wat
Original file line number Diff line number Diff line change
Expand Up @@ -260,9 +260,9 @@
(i32.eqz
(global.get $hangLimit)
)
(return
(then (return
(i64.const 4293531749)
)
))
)
(global.set $hangLimit
(i32.sub
Expand All @@ -279,8 +279,8 @@
(local.tee $0
(if (result i32)
(i32.const -2147483647)
(local.get $0)
(block $label$4 (result i32)
(then (local.get $0))
(else (block $label$4 (result i32)
(br_if $label$4
(if (result i32)
(block $label$5 (result i32)
Expand All @@ -293,9 +293,9 @@
(i32.eqz
(global.get $hangLimit)
)
(return
(then (return
(i64.const -32766)
)
))
)
(global.set $hangLimit
(i32.sub
Expand All @@ -315,22 +315,22 @@
)
)
)
(block $label$7 (result f32)
(then (block $label$7 (result f32)
(f32.const -nan:0x7fffda)
)
(f32.const 4294967296)
))
(else (f32.const 4294967296))
)
)
(i32.const -2147483648)
)
(i32.const -1073741824)
(local.get $0)
(then (i32.const -1073741824))
(else (local.get $0))
)
(i32.eqz
(global.get $global$0)
)
)
)
))
)
)
)
Expand Down
42 changes: 42 additions & 0 deletions tests/local/if-else-parsing.wast
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
(module
(func $a1
i32.const 0
(if (then) (else))
)
(func $a2
(if (i32.const 1) (i32.eqz) (then) (else))
)
(func $a3
(if (i32.const 1) (i32.eqz) (then))
)
(func $a4
(if (i32.const 1) (i32.eqz) (then nop))
)
)
(assert_invalid
(module quote
"(func (if))"
)
"no `then`")

(assert_invalid
(module quote
"(func (if (else)))"
)
"no `then`")

(assert_invalid
(module quote
"(func (if nop (else)))"
)
"expected `(`")
(assert_invalid
(module quote
"(func (if (nop) (else)))"
)
"no `then`")
(assert_invalid
(module quote
"(func (if (nop) nop (then)))"
)
"expected `(`")
29 changes: 29 additions & 0 deletions tests/snapshots/local/if-else-parsing.wast/0.print
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
(module
(type (;0;) (func))
(func $a1 (;0;) (type 0)
i32.const 0
if ;; label = @1
else
end
)
(func $a2 (;1;) (type 0)
i32.const 1
i32.eqz
if ;; label = @1
else
end
)
(func $a3 (;2;) (type 0)
i32.const 1
i32.eqz
if ;; label = @1
end
)
(func $a4 (;3;) (type 0)
i32.const 1
i32.eqz
if ;; label = @1
nop
end
)
)
2 changes: 2 additions & 0 deletions tests/snapshots/testsuite/if.wast/0.print
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@
end
local.get 0
if ;; label = @1
else
end
local.get 0
if $l ;; label = @1
end
local.get 0
if $l ;; label = @1
else
end
)
(func (;2;) (type 5) (param i32) (result i32)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@
end
local.get 0
if ;; label = @1
else
end
local.get 0
if $l ;; label = @1
end
local.get 0
if $l ;; label = @1
else
end
)
(func (;2;) (type 5) (param i32) (result i32)
Expand Down
2 changes: 2 additions & 0 deletions tests/snapshots/testsuite/proposals/gc/if.wast/0.print
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@
end
local.get 0
if ;; label = @1
else
end
local.get 0
if $l ;; label = @1
end
local.get 0
if $l ;; label = @1
else
end
)
(func (;2;) (type 5) (param i32) (result i32)
Expand Down

0 comments on commit 096b512

Please sign in to comment.