///|
/// WAST arbitrary generation keeps local text-shape opcode pickers instead of calling typed `gen_valid`; keep duplicated coverage aligned with `docs/wiki/fuzzing/wast-arbitrary-parity-plan.md`.
fn rand_bounded(max_exclusive : UInt32, st : RandomState) -> UInt32 {
if max_exclusive == 0 {
0
} else {
st.next_uint() % max_exclusive
}
}
///|
fn bytes_of(s : String) -> Bytes {
let bytes = @buffer.new(size_hint=s.length())
for i = 0; i < s.length(); i = i + 1 {
bytes.write_byte(s[i].to_int().to_byte())
}
bytes.to_bytes()
}
// Exact text-surface counters for WAST arbitrary's FZG mirror prelude.
// These are parser/printer counters, not typed GenValid validity proof.
///|
pub struct WastArbitraryFeatureStats {
gc_type_fields : Int32
exported_tags : Int32
descriptor_ref_ops : Int32
bulk_memory_ops : Int32
bulk_table_ops : Int32
tail_call_ops : Int32
exception_ops : Int32
simd_const_ops : Int32
control_ops : Int32
parametric_ops : Int32
variable_ops : Int32
table_ops : Int32
memory_ops : Int32
numeric_ops : Int32
reference_ops : Int32
simd_ops : Int32
} derive(Eq, Debug)
///|
fn wast_arbitrary_empty_feature_stats() -> WastArbitraryFeatureStats {
{
gc_type_fields: 0,
exported_tags: 0,
descriptor_ref_ops: 0,
bulk_memory_ops: 0,
bulk_table_ops: 0,
tail_call_ops: 0,
exception_ops: 0,
simd_const_ops: 0,
control_ops: 0,
parametric_ops: 0,
variable_ops: 0,
table_ops: 0,
memory_ops: 0,
numeric_ops: 0,
reference_ops: 0,
simd_ops: 0,
}
}
///|
fn wast_arbitrary_add_feature_stats(
a : WastArbitraryFeatureStats,
b : WastArbitraryFeatureStats,
) -> WastArbitraryFeatureStats {
{
gc_type_fields: a.gc_type_fields + b.gc_type_fields,
exported_tags: a.exported_tags + b.exported_tags,
descriptor_ref_ops: a.descriptor_ref_ops + b.descriptor_ref_ops,
bulk_memory_ops: a.bulk_memory_ops + b.bulk_memory_ops,
bulk_table_ops: a.bulk_table_ops + b.bulk_table_ops,
tail_call_ops: a.tail_call_ops + b.tail_call_ops,
exception_ops: a.exception_ops + b.exception_ops,
simd_const_ops: a.simd_const_ops + b.simd_const_ops,
control_ops: a.control_ops + b.control_ops,
parametric_ops: a.parametric_ops + b.parametric_ops,
variable_ops: a.variable_ops + b.variable_ops,
table_ops: a.table_ops + b.table_ops,
memory_ops: a.memory_ops + b.memory_ops,
numeric_ops: a.numeric_ops + b.numeric_ops,
reference_ops: a.reference_ops + b.reference_ops,
simd_ops: a.simd_ops + b.simd_ops,
}
}
///|
fn wast_arbitrary_count_instructions(
instrs : Array[Instruction],
) -> WastArbitraryFeatureStats {
let mut stats = wast_arbitrary_empty_feature_stats()
for instr in instrs {
let current = match instr {
Unreachable
| Nop
| Block(_, _)
| Loop(_, _)
| If(_, _, _)
| Br(_)
| BrIf(_)
| BrOnNull(_)
| BrOnNonNull(_)
| BrOnCastDescEq(_, _, _)
| BrOnCastDescEqFail(_, _, _)
| BrTable(_, _)
| Return
| Call(_)
| CallIndirect(_, _)
| CallRef(_) =>
{ ..wast_arbitrary_empty_feature_stats(), control_ops: 1, }
ReturnCall(_) | ReturnCallIndirect(_, _) | ReturnCallRef(_) =>
{
..wast_arbitrary_empty_feature_stats(),
control_ops: 1,
tail_call_ops: 1,
}
Throw(_)
| ThrowRef
| Rethrow(_)
| TryTable(_, _, _)
| TryLegacy(_, _, _, _) =>
{
..wast_arbitrary_empty_feature_stats(),
control_ops: 1,
exception_ops: 1,
}
Drop | Select(_) =>
{ ..wast_arbitrary_empty_feature_stats(), parametric_ops: 1, }
LocalGet(_) | LocalSet(_) | LocalTee(_) | GlobalGet(_) | GlobalSet(_) =>
{ ..wast_arbitrary_empty_feature_stats(), variable_ops: 1, }
TableGet(_) | TableSet(_) | TableSize(_) | TableGrow(_) =>
{ ..wast_arbitrary_empty_feature_stats(), table_ops: 1, }
TableFill(_) | TableCopy(_, _) | TableInit(_, _) | ElemDrop(_) =>
{
..wast_arbitrary_empty_feature_stats(),
table_ops: 1,
bulk_table_ops: 1,
}
ArrayLoad(op, _, _) | ArrayStore(op, _, _) =>
{
..wast_arbitrary_empty_feature_stats(),
reference_ops: 1,
memory_ops: 1,
simd_ops: if op is V128Load || op is V128Store {
1
} else {
0
},
}
MemoryAtomic(_, _, _, _)
| AtomicFence(_)
| Load(_, _)
| Store(_, _)
| MemorySize
| MemoryGrow => { ..wast_arbitrary_empty_feature_stats(), memory_ops: 1, }
MemoryFill | MemoryCopy | MemoryInit(_) | DataDrop(_) =>
{
..wast_arbitrary_empty_feature_stats(),
memory_ops: 1,
bulk_memory_ops: 1,
}
Const(_, _) | Unary(_) | Binary(_) | Test(_) | Compare(_) | Convert(_) =>
{ ..wast_arbitrary_empty_feature_stats(), numeric_ops: 1, }
RefNull(_)
| RefIsNull
| RefFunc(_)
| StringConst(_)
| StringMeasureWtf16
| StringConcat
| StringEq
| StringAsWtf16
| StringViewWtf16GetCodeunit
| StringViewWtf16Slice
| StringNewUtf8Array
| StringNewWtf16Array
| StringEncodeUtf8Array
| StringEncodeWtf16Array
| StringNewLossyUtf8Array
| StringNewWtf8Array
| StringEncodeLossyUtf8Array
| StringEncodeWtf8Array
| StructNew(_)
| StructNewDefault(_)
| ArrayNew(_)
| ArrayNewDefault(_)
| ArrayNewFixed(_, _)
| ArrayNewData(_, _)
| ArrayNewElem(_, _)
| StructGet(_, _)
| StructGetS(_, _)
| StructGetU(_, _)
| WaitqueueNew
| WaitqueueNotify
| StructWait(_, _)
| Publish
| StructAtomicSet(_, _, _)
| ArrayAtomicSet(_, _)
| StructAtomicGet(_, _, _)
| StructAtomicGetS(_, _, _)
| StructAtomicGetU(_, _, _)
| StructAtomicRmw(_, _, _, _)
| StructAtomicCmpxchg(_, _, _)
| ArrayAtomicGet(_, _)
| ArrayAtomicGetS(_, _)
| ArrayAtomicGetU(_, _)
| ArrayAtomicRmw(_, _, _)
| ArrayAtomicCmpxchg(_, _)
| StructNewDesc(_)
| StructNewDefaultDesc(_)
| RefGetDesc(_) =>
{ ..wast_arbitrary_empty_feature_stats(), reference_ops: 1, }
RefTestDesc(_, _) | RefCastDescEq(_, _) =>
{
..wast_arbitrary_empty_feature_stats(),
reference_ops: 1,
descriptor_ref_ops: 1,
}
V128Const(_) =>
{
..wast_arbitrary_empty_feature_stats(),
simd_ops: 1,
simd_const_ops: 1,
}
V128Load(_)
| V128Load8x8S(_)
| V128Load8x8U(_)
| V128Load16x4S(_)
| V128Load16x4U(_)
| V128Load32x2S(_)
| V128Load32x2U(_)
| V128Load8Splat(_)
| V128Load16Splat(_)
| V128Load32Splat(_)
| V128Load64Splat(_)
| V128Load32Zero(_)
| V128Load64Zero(_)
| V128Store(_)
| V128Load8Lane(_, _)
| V128Load16Lane(_, _)
| V128Load32Lane(_, _)
| V128Load64Lane(_, _)
| V128Store8Lane(_, _)
| V128Store16Lane(_, _)
| V128Store32Lane(_, _)
| V128Store64Lane(_, _)
| I8x16Shuffle(_)
| I8x16Swizzle
| I8x16Splat
| I16x8Splat
| I32x4Splat
| I64x2Splat
| F32x4Splat
| F64x2Splat
| I8x16ExtractLaneS(_)
| I8x16ExtractLaneU(_)
| I8x16ReplaceLane(_)
| I16x8ExtractLaneS(_)
| I16x8ExtractLaneU(_)
| I16x8ReplaceLane(_)
| I32x4ExtractLane(_)
| I32x4ReplaceLane(_)
| I64x2ExtractLane(_)
| I64x2ReplaceLane(_)
| F32x4ExtractLane(_)
| F32x4ReplaceLane(_)
| F64x2ExtractLane(_)
| F64x2ReplaceLane(_)
| SimdUnary(_)
| SimdBinary(_)
| SimdTernary(_)
| SimdShift(_)
| SimdTest(_)
| SimdCompare(_)
| SimdConvert(_)
| SimdRelaxed(_) =>
{ ..wast_arbitrary_empty_feature_stats(), simd_ops: 1, }
}
stats = wast_arbitrary_add_feature_stats(stats, current)
let nested = match instr {
Block(_, body) | Loop(_, body) => wast_arbitrary_count_instructions(body)
If(_, then_body, else_body) =>
wast_arbitrary_add_feature_stats(
wast_arbitrary_count_instructions(then_body),
wast_arbitrary_count_instructions(else_body),
)
TryTable(_, _, body) => wast_arbitrary_count_instructions(body)
_ => wast_arbitrary_empty_feature_stats()
}
stats = wast_arbitrary_add_feature_stats(stats, nested)
}
stats
}
///|
pub fn wast_arbitrary_feature_stats(mod : Module) -> WastArbitraryFeatureStats {
let mut stats = wast_arbitrary_empty_feature_stats()
for field in mod.fields {
let current = match field {
TypeField(t) =>
match t.body {
Struct(_) | Array(_) | Shared(_) =>
{ ..wast_arbitrary_empty_feature_stats(), gc_type_fields: 1, }
_ => wast_arbitrary_empty_feature_stats()
}
TagField(t) =>
{
..wast_arbitrary_empty_feature_stats(),
exported_tags: if t.exports.length() > 0 {
1
} else {
0
},
}
FuncField(f) => wast_arbitrary_count_instructions(f.body)
_ => wast_arbitrary_empty_feature_stats()
}
stats = wast_arbitrary_add_feature_stats(stats, current)
}
stats
}
///|
fn rand_len(size : Int32, st : RandomState, max_len : UInt32) -> Int32 {
let base : UInt32 = if size <= 0 { 1 } else { size.reinterpret_as_uint() + 1 }
let cap = if max_len < base { max_len } else { base }
rand_bounded(cap + 1, st).reinterpret_as_int()
}
///|
fn rand_ident(st : RandomState, prefix : String) -> String {
"\{prefix}\{st.next_uint() % 64}"
}
///|
fn rand_index(st : RandomState, bound : UInt32) -> Index {
Num(rand_bounded(bound, st))
}
///|
fn pick_value_type(st : RandomState) -> ValueType {
match st.next_uint() % 6 {
0 => I32
1 => I64
2 => F32
3 => F64
4 => FuncRef
_ => ExternRef
}
}
///|
fn pick_numeric_value_type(st : RandomState) -> ValueType {
match st.next_uint() % 4 {
0 => I32
1 => I64
2 => F32
_ => F64
}
}
///|
fn pick_numeric_opcode(st : RandomState) -> Opcode {
match st.next_uint() % 14 {
0 => I32Const
1 => I64Const
2 => F32Const
3 => F64Const
4 => I32Add
5 => I32Sub
6 => I64Add
7 => I64Sub
8 => F32Add
9 => F32Sub
10 => F64Add
11 => F64Sub
12 => I32Eqz
_ => I64Eqz
}
}
///|
fn pick_unary_opcode(st : RandomState) -> Opcode {
match st.next_uint() % 12 {
0 => I32Clz
1 => I32Ctz
2 => I32Popcnt
3 => I64Clz
4 => I64Ctz
5 => I64Popcnt
6 => F32Abs
7 => F32Neg
8 => F64Abs
9 => F64Neg
10 => I32Extend8S
_ => I64Extend32S
}
}
///|
fn pick_binary_opcode(st : RandomState) -> Opcode {
match st.next_uint() % 20 {
0 => I32Add
1 => I32Sub
2 => I32Mul
3 => I32And
4 => I32Or
5 => I32Xor
6 => I64Add
7 => I64Sub
8 => I64Mul
9 => I64And
10 => I64Or
11 => I64Xor
12 => F32Add
13 => F32Sub
14 => F32Mul
15 => F32Div
16 => F64Add
17 => F64Sub
18 => F64Mul
_ => F64Div
}
}
///|
fn pick_compare_opcode(st : RandomState) -> Opcode {
match st.next_uint() % 12 {
0 => I32Eq
1 => I32Ne
2 => I32LtS
3 => I32GtS
4 => I64Eq
5 => I64Ne
6 => I64LtS
7 => I64GtS
8 => F32Eq
9 => F32Lt
10 => F64Eq
_ => F64Lt
}
}
///|
fn pick_convert_opcode(st : RandomState) -> Opcode {
match st.next_uint() % 16 {
0 => I32WrapI64
1 => I32TruncF32S
2 => I32TruncF64S
3 => I64ExtendI32S
4 => I64TruncF32S
5 => I64TruncF64S
6 => F32ConvertI32S
7 => F32ConvertI64S
8 => F64ConvertI32S
9 => F64ConvertI64S
10 => F32DemoteF64
11 => F64PromoteF32
12 => I32ReinterpretF32
13 => I64ReinterpretF64
14 => F32ReinterpretI32
_ => F64ReinterpretI64
}
}
///|
fn pick_const_text(op : Opcode, st : RandomState) -> String {
match op {
I32Const => "\{(st.next_uint() % 200).reinterpret_as_int() - 100}"
I64Const =>
"\{(st.next_uint() % 200).reinterpret_as_int().to_int64() - 100L}"
F32Const | F64Const =>
match st.next_uint() % 8 {
0 => "0.0"
1 => "1.0"
2 => "-1.0"
3 => "3.5"
4 => "-2.25"
5 => "inf"
6 => "-inf"
_ => "nan"
}
_ => "0"
}
}
///|
fn gen_memarg(st : RandomState, natural_align : UInt64) -> MemArg {
let align = if (st.next_uint() & 1) == 1 {
natural_align
} else {
match st.next_uint() % 4 {
0 => 1UL
1 => 2UL
2 => 4UL
_ => natural_align
}
}
{ align, offset: (st.next_uint() % 64).to_uint64(), }
}
///|
fn gen_instruction(
size : Int32,
st : RandomState,
func_bound : UInt32,
) -> Instruction {
if size <= 0 {
match st.next_uint() % 12 {
0 => Nop
1 => Drop
2 => LocalGet(Num(0))
3 => LocalSet(Num(0))
4 => GlobalGet(Num(0))
5 => MemorySize
6 => TableSize(Num(0))
7 => Const(I32Const, pick_const_text(I32Const, st))
8 => Const(I64Const, pick_const_text(I64Const, st))
9 => Const(F32Const, pick_const_text(F32Const, st))
10 => Const(F64Const, pick_const_text(F64Const, st))
_ => RefNull(FuncRef)
}
} else {
let next = size / 2
match st.next_uint() % 20 {
0 => {
let body : Array[Instruction] = []
let n = rand_len(next, st, 3)
for _ in 0.. {
let body : Array[Instruction] = []
let n = rand_len(next, st, 3)
for _ in 0.. {
let then_body : Array[Instruction] = []
let else_body : Array[Instruction] = []
let nt = rand_len(next, st, 2)
let ne = rand_len(next, st, 2)
for _ in 0.. Br(Num(st.next_uint() % 2))
4 => BrIf(Num(st.next_uint() % 2))
5 => BrTable([Num(0), Num(1)], Num(0))
6 => Return
7 => Call(Num(rand_bounded(func_bound, st)))
8 =>
CallIndirect(Num(0), {
type_index: Some(Num(0)),
func_type: { params: [], results: [], },
param_ids: [],
})
9 => Load(I32Load, gen_memarg(st, 4))
10 => Store(I32Store, gen_memarg(st, 4))
11 => Unary(pick_unary_opcode(st))
12 => Binary(pick_binary_opcode(st))
13 => Test(I32Eqz)
14 => Compare(pick_compare_opcode(st))
15 => Convert(pick_convert_opcode(st))
16 => RefFunc(Num(rand_bounded(func_bound, st)))
17 => Select(Some([pick_numeric_value_type(st)]))
18 => TableGet(Num(0))
_ => {
let op = pick_numeric_opcode(st)
Const(op, pick_const_text(op, st))
}
}
}
}
///|
fn gen_func_type(st : RandomState) -> FuncType {
let params : Array[ValueType] = []
let results : Array[ValueType] = []
let pc = st.next_uint() % 3
let rc = st.next_uint() % 2
for _ in 0.. TypeUse {
if type_count > 0 && (st.next_uint() & 1) == 1 {
{
type_index: Some(Num(st.next_uint() % type_count)),
func_type: { params: [], results: [], },
param_ids: [],
}
} else {
let func_type = gen_func_type(st)
let param_ids : Array[String?] = []
for _ in func_type.params {
param_ids.push(None)
}
{ type_index: None, func_type, param_ids, }
}
}
///|
fn append_wide_wast_surface(fields : Array[ModuleField]) -> Unit {
fields.push(
TypeField({
id: Some("$gc_struct"),
is_sub: false,
final_: false,
supers: [],
metadata: { describes: None, descriptor: None, },
body: TypeDefBody::Struct([
{ storage: FieldStorage::Value(I32), mutable_: true, },
{ storage: FieldStorage::Value(TypedFuncRef(Struct)), mutable_: false, },
]),
}),
)
fields.push(
TagField({
id: Some("$tag0"),
exports: [Export("tag0")],
type_use: {
type_index: None,
func_type: { params: [I32], results: [], },
param_ids: [None],
},
}),
)
fields.push(
FuncField({
id: Some("$wide_wast_surface"),
annotations: [],
exports: [Export("wide_wast_surface")],
type_use: {
type_index: None,
func_type: { params: [], results: [], },
param_ids: [],
},
locals: [],
body: [
RefNull(TypedFuncRef(Struct)),
RefTestDesc(false, Num(0)),
Drop,
RefNull(TypedFuncRef(Struct)),
RefCastDescEq(true, Num(0)),
Drop,
Const(I32Const, "0"),
Const(I32Const, "0"),
Const(I32Const, "0"),
MemoryFill,
Const(I32Const, "0"),
Const(I32Const, "0"),
Const(I32Const, "0"),
MemoryCopy,
Const(I32Const, "0"),
Const(I32Const, "0"),
Const(I32Const, "0"),
MemoryInit(Num(0)),
DataDrop(Num(0)),
Const(I32Const, "0"),
Const(I32Const, "0"),
Const(I32Const, "0"),
TableFill(Num(0)),
Const(I32Const, "0"),
Const(I32Const, "0"),
Const(I32Const, "0"),
TableCopy(Num(0), Num(0)),
Const(I32Const, "0"),
Const(I32Const, "0"),
Const(I32Const, "0"),
TableInit(Num(0), Num(0)),
ElemDrop(Num(0)),
V128Const({ shape: I32x4, values: ["0", "1", "2", "3"], }),
Drop,
ReturnCall(Num(0)),
ReturnCallIndirect(Num(0), {
type_index: Some(Num(0)),
func_type: { params: [], results: [], },
param_ids: [],
}),
ReturnCallRef({
type_index: Some(Num(0)),
func_type: { params: [], results: [], },
param_ids: [],
}),
Const(I32Const, "0"),
Throw(Num(0)),
TryTable(
{ label: None, type_use: None, },
[CatchClause(Num(0), Num(0))],
[Nop],
),
],
}),
)
}
///|
fn gen_module(size : Int32, st : RandomState) -> Module {
let fields : Array[ModuleField] = []
append_wide_wast_surface(fields)
// Always provide at least one func type for call_indirect / signature references.
let type_count : UInt32 = st.next_uint() % 2 + 1
for _ in 0.. 0 && (st.next_uint() & 1) == 1 {
let funcs : Array[Index] = [Num(0)]
if func_count > 1 {
funcs.push(Num(1))
}
fields.push(
ElemField({
id: None,
declarative: false,
table_index: Num(0),
offset: [Const(I32Const, "0")],
func_indices: funcs,
elem_exprs: [],
elem_type: None,
}),
)
}
if (st.next_uint() & 1) == 1 {
fields.push(
DataField({
id: None,
memory_index: Num(0),
offset: [Const(I32Const, "0")],
data: bytes_of("fuzz"),
}),
)
}
// Optional export/start.
if func_count > 0 {
fields.push(ExportField({ name: "main", desc: ExportDesc::Func(Num(0)), }))
if (st.next_uint() & 1) == 1 {
fields.push(StartField({ func_index: Num(0), }))
}
}
{ id: None, fields, }
}
///|
test "wast arbitrary widened prelude reports exact FZG mirror counters" {
let fields : Array[ModuleField] = []
append_wide_wast_surface(fields)
let stats = wast_arbitrary_feature_stats({ id: None, fields, })
@debug.assert_eq(stats.gc_type_fields, 1)
@debug.assert_eq(stats.exported_tags, 1)
@debug.assert_eq(stats.descriptor_ref_ops, 2)
@debug.assert_eq(stats.bulk_memory_ops, 4)
@debug.assert_eq(stats.bulk_table_ops, 4)
@debug.assert_eq(stats.tail_call_ops, 3)
@debug.assert_eq(stats.exception_ops, 2)
@debug.assert_eq(stats.simd_const_ops, 1)
@debug.assert_eq(stats.control_ops, 6)
@debug.assert_eq(stats.parametric_ops, 3)
@debug.assert_eq(stats.variable_ops, 0)
@debug.assert_eq(stats.table_ops, 4)
@debug.assert_eq(stats.memory_ops, 4)
@debug.assert_eq(stats.numeric_ops, 19)
@debug.assert_eq(stats.reference_ops, 4)
@debug.assert_eq(stats.simd_ops, 1)
}
///|
test "wast arbitrary module includes widened surface prelude" {
let st = @splitmix.new(seed=27)
let mod : Module = Arbitrary::arbitrary(4, st)
let text = match module_to_wast(mod) {
Ok(t) => t
Err(e) => fail("unexpected module_to_wast error: \{e}")
}
assert_true(text.contains("(type $gc_struct (struct"))
assert_true(text.contains("(tag $tag0"))
assert_true(text.contains("return_call"))
assert_true(text.contains("return_call_indirect"))
assert_true(text.contains("return_call_ref"))
assert_true(text.contains("try_table"))
assert_true(text.contains("throw 0"))
assert_true(text.contains("memory.fill"))
assert_true(text.contains("memory.copy"))
assert_true(text.contains("memory.init"))
assert_true(text.contains("table.fill"))
assert_true(text.contains("table.copy"))
assert_true(text.contains("table.init"))
assert_true(text.contains("ref.test_desc"))
assert_true(text.contains("ref.cast_desc_eq_null"))
assert_true(text.contains("v128.const i32x4"))
match wast_to_module(text, filename="wide-wast-arbitrary.wat") {
Ok(_) => ()
Err(e) => fail("widened arbitrary WAST should parse back: \{e}")
}
}
///|
pub impl Arbitrary for ValueType with fn arbitrary(_, st) {
pick_value_type(st)
}
///|
pub impl Arbitrary for Index with fn arbitrary(size, st) {
if (st.next_uint() & 1) == 1 {
Num(rand_bounded((size + 1).reinterpret_as_uint(), st))
} else {
let suffix = rand_ident(st, "id")
Id("$\{suffix}")
}
}
///|
pub impl Arbitrary for Limits with fn arbitrary(_, st) {
let min = st.next_uint() % 5
let max : UInt32? = if (st.next_uint() & 1) == 1 {
Some(min + st.next_uint() % 3)
} else {
None
}
{ min, max, }
}
///|
pub impl Arbitrary for FuncType with fn arbitrary(_, st) {
gen_func_type(st)
}
///|
pub impl Arbitrary for TypeUse with fn arbitrary(size, st) {
gen_type_use(st, (size + 1).reinterpret_as_uint())
}
///|
pub impl Arbitrary for InlineExport with fn arbitrary(_, st) {
Export("ex_\{st.next_uint() % 32}")
}
///|
pub impl Arbitrary for TableType with fn arbitrary(_, st) {
{
limits: Arbitrary::arbitrary(2, st),
elem_type: if (st.next_uint() & 1) == 1 {
FuncRef
} else {
ExternRef
},
}
}
///|
pub impl Arbitrary for MemoryType with fn arbitrary(_, st) {
let limits : Limits = Arbitrary::arbitrary(2, st)
MemoryType::new(
limits.min.to_uint64(),
max=limits.max.map(fn(value) { value.to_uint64() }),
)
}
///|
pub impl Arbitrary for GlobalType with fn arbitrary(_, st) {
{
val_type: pick_numeric_value_type(st),
mutable_: (st.next_uint() & 1) == 1,
}
}
///|
pub impl Arbitrary for Local with fn arbitrary(_, st) {
{
id: if (st.next_uint() & 1) == 1 {
Some("$l\{st.next_uint() % 32}")
} else {
None
},
val_type: pick_numeric_value_type(st),
}
}
///|
pub impl Arbitrary for ImportDesc with fn arbitrary(size, st) {
match st.next_uint() % 4 {
0 => Func(Arbitrary::arbitrary(size, st))
1 => Table(Arbitrary::arbitrary(size, st))
2 => Memory(Arbitrary::arbitrary(size, st))
_ => Global(Arbitrary::arbitrary(size, st))
}
}
///|
pub impl Arbitrary for Import with fn arbitrary(size, st) {
{
module_name: "env",
field_name: "f\{st.next_uint() % 64}",
desc: Arbitrary::arbitrary(size, st),
id: if (st.next_uint() & 1) == 1 {
Some("$imp\{st.next_uint() % 64}")
} else {
None
},
annotations: [],
}
}
///|
pub impl Arbitrary for ExportDesc with fn arbitrary(size, st) {
match st.next_uint() % 4 {
0 => Func(rand_index(st, (size + 1).reinterpret_as_uint()))
1 => Table(rand_index(st, 1))
2 => Memory(rand_index(st, 1))
_ => Global(rand_index(st, 1))
}
}
///|
pub impl Arbitrary for Export with fn arbitrary(size, st) {
{ name: "ex\{st.next_uint() % 64}", desc: Arbitrary::arbitrary(size, st), }
}
///|
pub impl Arbitrary for Start with fn arbitrary(size, st) {
{ func_index: rand_index(st, (size + 1).reinterpret_as_uint()), }
}
///|
pub impl Arbitrary for MemArg with fn arbitrary(_, st) {
gen_memarg(st, 4)
}
///|
pub impl Arbitrary for BlockType with fn arbitrary(size, st) {
{
label: if (st.next_uint() & 1) == 1 {
Some("$b\{st.next_uint() % 32}")
} else {
None
},
type_use: if (st.next_uint() & 1) == 1 {
Some(Arbitrary::arbitrary(size, st))
} else {
None
},
}
}
///|
pub impl Arbitrary for SimdShape with fn arbitrary(_, st) {
match st.next_uint() % 6 {
0 => I8x16
1 => I16x8
2 => I32x4
3 => I64x2
4 => F32x4
_ => F64x2
}
}
///|
pub impl Arbitrary for V128Const with fn arbitrary(_, st) {
let shape : SimdShape = Arbitrary::arbitrary(0, st)
let values : Array[String] = []
let count = match shape {
I8x16 => 16
I16x8 => 8
I32x4 => 4
I64x2 => 2
F32x4 => 4
F64x2 => 2
}
for _ in 0.. pick_binary_opcode(st)
1 => pick_unary_opcode(st)
2 => pick_compare_opcode(st)
3 => pick_convert_opcode(st)
4 => I32Const
5 => I64Const
6 => F32Const
_ => F64Const
}
}
///|
pub impl Arbitrary for Instruction with fn arbitrary(size, st) {
gen_instruction(size, st, (size + 2).reinterpret_as_uint())
}
///|
pub impl Arbitrary for Func with fn arbitrary(size, st) {
let locals : Array[Local] = []
let local_n = rand_len(size, st, 3)
for _ in 0.. TypeField(Arbitrary::arbitrary(size, st))
1 => {
let types : Array[TypeDef] = []
let n = rand_len(size, st, 3)
for _ in 0.. FuncField(Arbitrary::arbitrary(size, st))
3 => TableField(Arbitrary::arbitrary(size, st))
4 => MemoryField(Arbitrary::arbitrary(size, st))
5 => GlobalField(Arbitrary::arbitrary(size, st))
6 => ImportField(Arbitrary::arbitrary(size, st))
7 => ExportField(Arbitrary::arbitrary(size, st))
8 => StartField(Arbitrary::arbitrary(size, st))
9 => ElemField(Arbitrary::arbitrary(size, st))
_ => DataField(Arbitrary::arbitrary(size, st))
}
}
///|
pub impl Arbitrary for Module with fn arbitrary(size, st) {
gen_module(size, st)
}
///|
pub impl Arbitrary for WastValue with fn arbitrary(_, st) {
match st.next_uint() % 6 {
0 => I32("\{(st.next_uint() % 200).reinterpret_as_int() - 100}")
1 => I64("\{(st.next_uint() % 200).reinterpret_as_int().to_int64() - 100L}")
2 => F32("0.0")
3 => F64("0.0")
4 => RefNull(FuncRef)
_ => RefFunc
}
}
///|
pub impl Arbitrary for WastResult with fn arbitrary(size, st) {
match st.next_uint() % 7 {
0 => Value(Arbitrary::arbitrary(size, st))
1 => F32NanCanonical
2 => F32NanArithmetic
3 => F64NanCanonical
4 => F64NanArithmetic
5 => RefNull(Some(FuncRef))
_ => RefFunc
}
}
///|
pub impl Arbitrary for WastAction with fn arbitrary(size, st) {
let args : Array[WastValue] = []
let n = rand_len(size, st, 4)
for _ in 0.. Inline(Arbitrary::arbitrary(size, st))
1 => Binary(b"\x00asm")
_ => Quote(["(module)"])
}
}
///|
pub impl Arbitrary for WastCommand with fn arbitrary(size, st) {
match st.next_uint() % 9 {
0 => Module(Arbitrary::arbitrary(size, st))
1 => Register("m\{st.next_uint() % 8}", None)
2 => Action(Arbitrary::arbitrary(size, st))
3 =>
AssertReturn(Arbitrary::arbitrary(size, st), [
Arbitrary::arbitrary(size, st),
])
4 => AssertTrap(Arbitrary::arbitrary(size, st), "trap")
5 => AssertException(Arbitrary::arbitrary(size, st))
6 => AssertExhaustion(Arbitrary::arbitrary(size, st), "exhaust")
7 => AssertMalformed(Arbitrary::arbitrary(size, st), "malformed")
_ => AssertInvalid(Arbitrary::arbitrary(size, st), "invalid")
}
}
///|
pub impl Arbitrary for WastScript with fn arbitrary(size, st) {
let commands : Array[WastCommand] = []
let n = rand_len(size, st, 6)
for _ in 0..