watr 3.3.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/const.js CHANGED
@@ -1,77 +1,163 @@
1
1
  // https://webassembly.github.io/spec/core/appendix/index-instructions.html
2
+ // Format: 'name' or 'name handler'
3
+ // Immediate types: blocktype, labelidx, funcidx, typeidx, tableidx, memoryidx, globalidx, localidx, dataidx, elemidx, multi
4
+ // Value types: i32, i64, f32, f64, v128
2
5
  export const INSTR = [
3
- 'unreachable', 'nop', 'block', 'loop', 'if', 'else', 'then', , , , ,
4
- 'end', 'br', 'br_if', 'br_table', 'return', 'call', 'call_indirect', 'return_call', 'return_call_indirect', 'call_ref', 'return_call_ref', , , , ,
5
- 'drop', 'select', '', , , ,
6
- 'local.get', 'local.set', 'local.tee', 'global.get', 'global.set', 'table.get', 'table.set', ,
7
- 'i32.load', 'i64.load', 'f32.load', 'f64.load',
8
- 'i32.load8_s', 'i32.load8_u', 'i32.load16_s', 'i32.load16_u',
9
- 'i64.load8_s', 'i64.load8_u', 'i64.load16_s', 'i64.load16_u', 'i64.load32_s', 'i64.load32_u',
10
- 'i32.store', 'i64.store', 'f32.store', 'f64.store',
11
- 'i32.store8', 'i32.store16', 'i64.store8', 'i64.store16', 'i64.store32',
12
- 'memory.size', 'memory.grow',
13
- 'i32.const', 'i64.const', 'f32.const', 'f64.const',
6
+ // 0x00-0x0a: control
7
+ 'unreachable', 'nop', 'block block', 'loop block', 'if block', 'else null', 'then null', , 'throw tagidx', , 'throw_ref',
8
+ // 0x0b-0x19: control
9
+ 'end end', 'br labelidx', 'br_if labelidx', 'br_table br_table', 'return', 'call funcidx', 'call_indirect call_indirect', 'return_call funcidx', 'return_call_indirect call_indirect', 'call_ref typeidx', 'return_call_ref typeidx', , , , ,
10
+ // 0x1a-0x1f: parametric
11
+ 'drop', 'select select', '', , , 'try_table try_table',
12
+ // 0x20-0x27: variable
13
+ 'local.get localidx', 'local.set localidx', 'local.tee localidx', 'global.get globalidx', 'global.set globalidx', 'table.get tableidx', 'table.set tableidx', ,
14
+ // 0x28-0x3e: memory
15
+ 'i32.load memarg', 'i64.load memarg', 'f32.load memarg', 'f64.load memarg',
16
+ 'i32.load8_s memarg', 'i32.load8_u memarg', 'i32.load16_s memarg', 'i32.load16_u memarg',
17
+ 'i64.load8_s memarg', 'i64.load8_u memarg', 'i64.load16_s memarg', 'i64.load16_u memarg', 'i64.load32_s memarg', 'i64.load32_u memarg',
18
+ 'i32.store memarg', 'i64.store memarg', 'f32.store memarg', 'f64.store memarg',
19
+ 'i32.store8 memarg', 'i32.store16 memarg', 'i64.store8 memarg', 'i64.store16 memarg', 'i64.store32 memarg',
20
+ // 0x3f-0x40: memory size/grow
21
+ 'memory.size opt_memory', 'memory.grow opt_memory',
22
+ // 0x41-0x44: const
23
+ 'i32.const i32', 'i64.const i64', 'f32.const f32', 'f64.const f64',
24
+ // 0x45-0x4f: i32 comparison
14
25
  'i32.eqz', 'i32.eq', 'i32.ne', 'i32.lt_s', 'i32.lt_u', 'i32.gt_s', 'i32.gt_u', 'i32.le_s', 'i32.le_u', 'i32.ge_s', 'i32.ge_u',
26
+ // 0x50-0x5a: i64 comparison
15
27
  'i64.eqz', 'i64.eq', 'i64.ne', 'i64.lt_s', 'i64.lt_u', 'i64.gt_s', 'i64.gt_u', 'i64.le_s', 'i64.le_u', 'i64.ge_s', 'i64.ge_u',
28
+ // 0x5b-0x60: f32 comparison
16
29
  'f32.eq', 'f32.ne', 'f32.lt', 'f32.gt', 'f32.le', 'f32.ge',
30
+ // 0x61-0x66: f64 comparison
17
31
  'f64.eq', 'f64.ne', 'f64.lt', 'f64.gt', 'f64.le', 'f64.ge',
32
+ // 0x67-0x78: i32 arithmetic
18
33
  'i32.clz', 'i32.ctz', 'i32.popcnt', 'i32.add', 'i32.sub', 'i32.mul', 'i32.div_s', 'i32.div_u', 'i32.rem_s', 'i32.rem_u', 'i32.and', 'i32.or', 'i32.xor', 'i32.shl', 'i32.shr_s', 'i32.shr_u', 'i32.rotl', 'i32.rotr',
34
+ // 0x79-0x8a: i64 arithmetic
19
35
  'i64.clz', 'i64.ctz', 'i64.popcnt', 'i64.add', 'i64.sub', 'i64.mul', 'i64.div_s', 'i64.div_u', 'i64.rem_s', 'i64.rem_u', 'i64.and', 'i64.or', 'i64.xor', 'i64.shl', 'i64.shr_s', 'i64.shr_u', 'i64.rotl', 'i64.rotr',
36
+ // 0x8b-0x98: f32 arithmetic
20
37
  'f32.abs', 'f32.neg', 'f32.ceil', 'f32.floor', 'f32.trunc', 'f32.nearest', 'f32.sqrt', 'f32.add', 'f32.sub', 'f32.mul', 'f32.div', 'f32.min', 'f32.max', 'f32.copysign',
38
+ // 0x99-0xa6: f64 arithmetic
21
39
  'f64.abs', 'f64.neg', 'f64.ceil', 'f64.floor', 'f64.trunc', 'f64.nearest', 'f64.sqrt', 'f64.add', 'f64.sub', 'f64.mul', 'f64.div', 'f64.min', 'f64.max', 'f64.copysign',
40
+ // 0xa7-0xc4: conversions (no immediates)
22
41
  'i32.wrap_i64',
23
42
  'i32.trunc_f32_s', 'i32.trunc_f32_u', 'i32.trunc_f64_s', 'i32.trunc_f64_u', 'i64.extend_i32_s', 'i64.extend_i32_u',
24
43
  'i64.trunc_f32_s', 'i64.trunc_f32_u', 'i64.trunc_f64_s', 'i64.trunc_f64_u',
25
44
  'f32.convert_i32_s', 'f32.convert_i32_u', 'f32.convert_i64_s', 'f32.convert_i64_u', 'f32.demote_f64',
26
45
  'f64.convert_i32_s', 'f64.convert_i32_u', 'f64.convert_i64_s', 'f64.convert_i64_u', 'f64.promote_f32',
27
46
  'i32.reinterpret_f32', 'i64.reinterpret_f64', 'f32.reinterpret_i32', 'f64.reinterpret_i64',
47
+ // 0xc0-0xc4: sign extension
28
48
  'i32.extend8_s', 'i32.extend16_s', 'i64.extend8_s', 'i64.extend16_s', 'i64.extend32_s', , , , , , , , , , , ,
29
- 'ref.null', 'ref.is_null', 'ref.func', 'ref.eq', 'ref.as_non_null', 'br_on_null', 'br_on_non_null', , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ,
49
+ // 0xd0-0xd6: reference
50
+ 'ref.null ref_null', 'ref.is_null', 'ref.func funcidx', 'ref.eq', 'ref.as_non_null', 'br_on_null labelidx', 'br_on_non_null labelidx',
51
+ // 0xd7-0xfa: padding to 0xfb (36 empty slots)
52
+ , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ,
53
+ // 0xfb: GC instructions (nested array for multi-byte opcodes)
54
+ [
55
+ 'struct.new typeidx', 'struct.new_default typeidx', 'struct.get typeidx_field', 'struct.get_s typeidx_field', 'struct.get_u typeidx_field', 'struct.set typeidx_field',
56
+ 'array.new typeidx', 'array.new_default typeidx', 'array.new_fixed typeidx_multi', 'array.new_data typeidx_dataidx', 'array.new_elem typeidx_elemidx',
57
+ 'array.get typeidx', 'array.get_s typeidx', 'array.get_u typeidx', 'array.set typeidx', 'array.len', 'array.fill typeidx', 'array.copy typeidx_typeidx',
58
+ 'array.init_data typeidx_dataidx', 'array.init_elem typeidx_elemidx', 'ref.test reftype', '', 'ref.cast reftype', '', 'br_on_cast reftype2', 'br_on_cast_fail reftype2',
59
+ 'any.convert_extern', 'extern.convert_any', 'ref.i31', 'i31.get_s', 'i31.get_u'
60
+ ],
30
61
 
31
- // 0xFB 0xNN (0xFB shift)
32
- 'struct.new', 'struct.new_default', 'struct.get', 'struct.get_s', 'struct.get_u', 'struct.set', 'array.new', 'array.new_default', 'array.new_fixed', 'array.new_data', 'array.new_elem', 'array.get', 'array.get_s', 'array.get_u', 'array.set', 'array.len', 'array.fill', 'array.copy', 'array.init_data', 'array.init_elem', 'ref.test', '', 'ref.cast', '', 'br_on_cast', 'br_on_cast_fail', 'any.convert_extern', 'extern.convert_any', 'ref.i31', 'i31.get_s', 'i31.get_u', ,
62
+ // 0xfc: Bulk memory/table operations (nested array)
63
+ [
64
+ 'i32.trunc_sat_f32_s', 'i32.trunc_sat_f32_u', 'i32.trunc_sat_f64_s', 'i32.trunc_sat_f64_u',
65
+ 'i64.trunc_sat_f32_s', 'i64.trunc_sat_f32_u', 'i64.trunc_sat_f64_s', 'i64.trunc_sat_f64_u',
66
+ 'memory.init dataidx_memoryidx', 'data.drop dataidx', 'memory.copy memoryidx_memoryidx', 'memory.fill memoryidx?',
67
+ 'table.init reversed', 'elem.drop elemidx', 'table.copy tableidx_tableidx', 'table.grow tableidx', 'table.size tableidx', 'table.fill tableidx', ,
68
+ 'i64.add128', 'i64.sub128', 'i64.mul_wide_s', 'i64.mul_wide_u'
69
+ ],
33
70
 
34
- // 0xFC 0xNN (0x11b shift)
35
- 'i32.trunc_sat_f32_s', 'i32.trunc_sat_f32_u', 'i32.trunc_sat_f64_s', 'i32.trunc_sat_f64_u', 'i64.trunc_sat_f32_s', 'i64.trunc_sat_f32_u', 'i64.trunc_sat_f64_s', 'i64.trunc_sat_f64_u',
36
- 'memory.init', 'data.drop', 'memory.copy', 'memory.fill', 'table.init', 'elem.drop', 'table.copy', 'table.grow', 'table.size', 'table.fill', ,
37
- 'i64.add128', 'i64.sub128', 'i64.mul_wide_s', 'i64.mul_wide_u', ,
71
+ // 0xfd: SIMD instructions (nested array)
72
+ [
73
+ 'v128.load memarg', 'v128.load8x8_s memarg', 'v128.load8x8_u memarg', 'v128.load16x4_s memarg', 'v128.load16x4_u memarg',
74
+ 'v128.load32x2_s memarg', 'v128.load32x2_u memarg', 'v128.load8_splat memarg', 'v128.load16_splat memarg', 'v128.load32_splat memarg',
75
+ 'v128.load64_splat memarg', 'v128.store memarg', 'v128.const v128const', 'i8x16.shuffle shuffle',
76
+ 'i8x16.swizzle', 'i8x16.splat', 'i16x8.splat', 'i32x4.splat', 'i64x2.splat', 'f32x4.splat', 'f64x2.splat',
77
+ 'i8x16.extract_lane_s laneidx', 'i8x16.extract_lane_u laneidx', 'i8x16.replace_lane laneidx',
78
+ 'i16x8.extract_lane_s laneidx', 'i16x8.extract_lane_u laneidx', 'i16x8.replace_lane laneidx',
79
+ 'i32x4.extract_lane laneidx', 'i32x4.replace_lane laneidx', 'i64x2.extract_lane laneidx', 'i64x2.replace_lane laneidx',
80
+ 'f32x4.extract_lane laneidx', 'f32x4.replace_lane laneidx', 'f64x2.extract_lane laneidx', 'f64x2.replace_lane laneidx',
81
+ 'i8x16.eq', 'i8x16.ne', 'i8x16.lt_s', 'i8x16.lt_u', 'i8x16.gt_s', 'i8x16.gt_u', 'i8x16.le_s', 'i8x16.le_u', 'i8x16.ge_s', 'i8x16.ge_u',
82
+ 'i16x8.eq', 'i16x8.ne', 'i16x8.lt_s', 'i16x8.lt_u', 'i16x8.gt_s', 'i16x8.gt_u', 'i16x8.le_s', 'i16x8.le_u', 'i16x8.ge_s', 'i16x8.ge_u',
83
+ 'i32x4.eq', 'i32x4.ne', 'i32x4.lt_s', 'i32x4.lt_u', 'i32x4.gt_s', 'i32x4.gt_u', 'i32x4.le_s', 'i32x4.le_u', 'i32x4.ge_s', 'i32x4.ge_u',
84
+ 'f32x4.eq', 'f32x4.ne', 'f32x4.lt', 'f32x4.gt', 'f32x4.le', 'f32x4.ge', 'f64x2.eq', 'f64x2.ne', 'f64x2.lt', 'f64x2.gt', 'f64x2.le', 'f64x2.ge',
85
+ 'v128.not', 'v128.and', 'v128.andnot', 'v128.or', 'v128.xor', 'v128.bitselect', 'v128.any_true',
86
+ 'v128.load8_lane memlane', 'v128.load16_lane memlane', 'v128.load32_lane memlane', 'v128.load64_lane memlane',
87
+ 'v128.store8_lane memlane', 'v128.store16_lane memlane', 'v128.store32_lane memlane', 'v128.store64_lane memlane',
88
+ 'v128.load32_zero memarg', 'v128.load64_zero memarg', 'f32x4.demote_f64x2_zero', 'f64x2.promote_low_f32x4',
89
+ 'i8x16.abs', 'i8x16.neg', 'i8x16.popcnt', 'i8x16.all_true', 'i8x16.bitmask', 'i8x16.narrow_i16x8_s', 'i8x16.narrow_i16x8_u',
90
+ 'f32x4.ceil', 'f32x4.floor', 'f32x4.trunc', 'f32x4.nearest', 'i8x16.shl', 'i8x16.shr_s', 'i8x16.shr_u',
91
+ 'i8x16.add', 'i8x16.add_sat_s', 'i8x16.add_sat_u', 'i8x16.sub', 'i8x16.sub_sat_s', 'i8x16.sub_sat_u',
92
+ 'f64x2.ceil', 'f64x2.floor', 'i8x16.min_s', 'i8x16.min_u', 'i8x16.max_s', 'i8x16.max_u', 'f64x2.trunc', 'i8x16.avgr_u',
93
+ 'i16x8.extadd_pairwise_i8x16_s', 'i16x8.extadd_pairwise_i8x16_u', 'i32x4.extadd_pairwise_i16x8_s', 'i32x4.extadd_pairwise_i16x8_u',
94
+ 'i16x8.abs', 'i16x8.neg', 'i16x8.q15mulr_sat_s', 'i16x8.all_true', 'i16x8.bitmask', 'i16x8.narrow_i32x4_s', 'i16x8.narrow_i32x4_u',
95
+ 'i16x8.extend_low_i8x16_s', 'i16x8.extend_high_i8x16_s', 'i16x8.extend_low_i8x16_u', 'i16x8.extend_high_i8x16_u',
96
+ 'i16x8.shl', 'i16x8.shr_s', 'i16x8.shr_u', 'i16x8.add', 'i16x8.add_sat_s', 'i16x8.add_sat_u', 'i16x8.sub', 'i16x8.sub_sat_s', 'i16x8.sub_sat_u',
97
+ 'f64x2.nearest', 'i16x8.mul', 'i16x8.min_s', 'i16x8.min_u', 'i16x8.max_s', 'i16x8.max_u', , 'i16x8.avgr_u',
98
+ 'i16x8.extmul_low_i8x16_s', 'i16x8.extmul_high_i8x16_s', 'i16x8.extmul_low_i8x16_u', 'i16x8.extmul_high_i8x16_u',
99
+ 'i32x4.abs', 'i32x4.neg', , 'i32x4.all_true', 'i32x4.bitmask', , , 'i32x4.extend_low_i16x8_s', 'i32x4.extend_high_i16x8_s',
100
+ 'i32x4.extend_low_i16x8_u', 'i32x4.extend_high_i16x8_u', 'i32x4.shl', 'i32x4.shr_s', 'i32x4.shr_u', 'i32x4.add', , , 'i32x4.sub', , , ,
101
+ 'i32x4.mul', 'i32x4.min_s', 'i32x4.min_u', 'i32x4.max_s', 'i32x4.max_u', 'i32x4.dot_i16x8_s', ,
102
+ 'i32x4.extmul_low_i16x8_s', 'i32x4.extmul_high_i16x8_s', 'i32x4.extmul_low_i16x8_u', 'i32x4.extmul_high_i16x8_u',
103
+ 'i64x2.abs', 'i64x2.neg', , 'i64x2.all_true', 'i64x2.bitmask', , , 'i64x2.extend_low_i32x4_s', 'i64x2.extend_high_i32x4_s',
104
+ 'i64x2.extend_low_i32x4_u', 'i64x2.extend_high_i32x4_u', 'i64x2.shl', 'i64x2.shr_s', 'i64x2.shr_u', 'i64x2.add', , , 'i64x2.sub', , , ,
105
+ 'i64x2.mul', 'i64x2.eq', 'i64x2.ne', 'i64x2.lt_s', 'i64x2.gt_s', 'i64x2.le_s', 'i64x2.ge_s',
106
+ 'i64x2.extmul_low_i32x4_s', 'i64x2.extmul_high_i32x4_s', 'i64x2.extmul_low_i32x4_u', 'i64x2.extmul_high_i32x4_u',
107
+ 'f32x4.abs', 'f32x4.neg', , 'f32x4.sqrt', 'f32x4.add', 'f32x4.sub', 'f32x4.mul', 'f32x4.div', 'f32x4.min', 'f32x4.max', 'f32x4.pmin', 'f32x4.pmax',
108
+ 'f64x2.abs', 'f64x2.neg', , 'f64x2.sqrt', 'f64x2.add', 'f64x2.sub', 'f64x2.mul', 'f64x2.div', 'f64x2.min', 'f64x2.max', 'f64x2.pmin', 'f64x2.pmax',
109
+ 'i32x4.trunc_sat_f32x4_s', 'i32x4.trunc_sat_f32x4_u', 'f32x4.convert_i32x4_s', 'f32x4.convert_i32x4_u',
110
+ 'i32x4.trunc_sat_f64x2_s_zero', 'i32x4.trunc_sat_f64x2_u_zero', 'f64x2.convert_low_i32x4_s', 'f64x2.convert_low_i32x4_u',
111
+ 'i8x16.relaxed_swizzle', 'i32x4.relaxed_trunc_f32x4_s', 'i32x4.relaxed_trunc_f32x4_u', 'i32x4.relaxed_trunc_f64x2_s_zero',
112
+ 'i32x4.relaxed_trunc_f64x2_u_zero', 'f32x4.relaxed_madd', 'f32x4.relaxed_nmadd', 'f64x2.relaxed_madd', 'f64x2.relaxed_nmadd',
113
+ 'i8x16.relaxed_laneselect', 'i16x8.relaxed_laneselect', 'i32x4.relaxed_laneselect', 'i64x2.relaxed_laneselect',
114
+ 'f32x4.relaxed_min', 'f32x4.relaxed_max', 'f64x2.relaxed_min', 'f64x2.relaxed_max',
115
+ 'i16x8.relaxed_q15mulr_s', 'i16x8.relaxed_dot_i8x16_i7x16_s', 'i32x4.relaxed_dot_i8x16_i7x16_add_s'
116
+ ],
117
+ // 0xfe: atomic/thread instructions
118
+ [
119
+ 'memory.atomic.notify memarg', 'memory.atomic.wait32 memarg', 'memory.atomic.wait64 memarg', 'atomic.fence opt_memory',
120
+ ,,,,,,,,,,,,
121
+ 'i32.atomic.load memarg', 'i64.atomic.load memarg', 'i32.atomic.load8_u memarg', 'i32.atomic.load16_u memarg',
122
+ 'i64.atomic.load8_u memarg', 'i64.atomic.load16_u memarg', 'i64.atomic.load32_u memarg', 'i32.atomic.store memarg',
123
+ 'i64.atomic.store memarg', 'i32.atomic.store8 memarg', 'i32.atomic.store16 memarg', 'i64.atomic.store8 memarg',
124
+ 'i64.atomic.store16 memarg', 'i64.atomic.store32 memarg', 'i32.atomic.rmw.add memarg', 'i64.atomic.rmw.add memarg',
125
+ 'i32.atomic.rmw8.add_u memarg', 'i32.atomic.rmw16.add_u memarg', 'i64.atomic.rmw8.add_u memarg', 'i64.atomic.rmw16.add_u memarg',
126
+ 'i64.atomic.rmw32.add_u memarg', 'i32.atomic.rmw.sub memarg', 'i64.atomic.rmw.sub memarg', 'i32.atomic.rmw8.sub_u memarg',
127
+ 'i32.atomic.rmw16.sub_u memarg', 'i64.atomic.rmw8.sub_u memarg', 'i64.atomic.rmw16.sub_u memarg', 'i64.atomic.rmw32.sub_u memarg',
128
+ 'i32.atomic.rmw.and memarg', 'i64.atomic.rmw.and memarg', 'i32.atomic.rmw8.and_u memarg', 'i32.atomic.rmw16.and_u memarg',
129
+ 'i64.atomic.rmw8.and_u memarg', 'i64.atomic.rmw16.and_u memarg', 'i64.atomic.rmw32.and_u memarg', 'i32.atomic.rmw.or memarg',
130
+ 'i64.atomic.rmw.or memarg', 'i32.atomic.rmw8.or_u memarg', 'i32.atomic.rmw16.or_u memarg', 'i64.atomic.rmw8.or_u memarg',
131
+ 'i64.atomic.rmw16.or_u memarg', 'i64.atomic.rmw32.or_u memarg', 'i32.atomic.rmw.xor memarg', 'i64.atomic.rmw.xor memarg',
132
+ 'i32.atomic.rmw8.xor_u memarg', 'i32.atomic.rmw16.xor_u memarg', 'i64.atomic.rmw8.xor_u memarg', 'i64.atomic.rmw16.xor_u memarg',
133
+ 'i64.atomic.rmw32.xor_u memarg', 'i32.atomic.rmw.xchg memarg', 'i64.atomic.rmw.xchg memarg', 'i32.atomic.rmw8.xchg_u memarg',
134
+ 'i32.atomic.rmw16.xchg_u memarg', 'i64.atomic.rmw8.xchg_u memarg', 'i64.atomic.rmw16.xchg_u memarg', 'i64.atomic.rmw32.xchg_u memarg',
135
+ 'i32.atomic.rmw.cmpxchg memarg', 'i64.atomic.rmw.cmpxchg memarg', 'i32.atomic.rmw8.cmpxchg_u memarg', 'i32.atomic.rmw16.cmpxchg_u memarg',
136
+ 'i64.atomic.rmw8.cmpxchg_u memarg', 'i64.atomic.rmw16.cmpxchg_u memarg', 'i64.atomic.rmw32.cmpxchg_u memarg'
137
+ ]
138
+ ]
38
139
 
39
- // 0xFD 0xNN (0x133 shift)
40
- 'v128.load', 'v128.load8x8_s', 'v128.load8x8_u', 'v128.load16x4_s', 'v128.load16x4_u', 'v128.load32x2_s', 'v128.load32x2_u', 'v128.load8_splat', 'v128.load16_splat', 'v128.load32_splat', 'v128.load64_splat', 'v128.store', 'v128.const', 'i8x16.shuffle',
41
- 'i8x16.swizzle', 'i8x16.splat', 'i16x8.splat', 'i32x4.splat', 'i64x2.splat', 'f32x4.splat', 'f64x2.splat',
42
- 'i8x16.extract_lane_s', 'i8x16.extract_lane_u', 'i8x16.replace_lane', 'i16x8.extract_lane_s', 'i16x8.extract_lane_u', 'i16x8.replace_lane', 'i32x4.extract_lane', 'i32x4.replace_lane', 'i64x2.extract_lane', 'i64x2.replace_lane', 'f32x4.extract_lane', 'f32x4.replace_lane', 'f64x2.extract_lane', 'f64x2.replace_lane',
43
- 'i8x16.eq', 'i8x16.ne', 'i8x16.lt_s', 'i8x16.lt_u', 'i8x16.gt_s', 'i8x16.gt_u', 'i8x16.le_s', 'i8x16.le_u', 'i8x16.ge_s', 'i8x16.ge_u', 'i16x8.eq', 'i16x8.ne', 'i16x8.lt_s', 'i16x8.lt_u', 'i16x8.gt_s', 'i16x8.gt_u', 'i16x8.le_s', 'i16x8.le_u', 'i16x8.ge_s', 'i16x8.ge_u', 'i32x4.eq', 'i32x4.ne', 'i32x4.lt_s', 'i32x4.lt_u', 'i32x4.gt_s', 'i32x4.gt_u', 'i32x4.le_s', 'i32x4.le_u', 'i32x4.ge_s', 'i32x4.ge_u', 'f32x4.eq', 'f32x4.ne', 'f32x4.lt', 'f32x4.gt', 'f32x4.le', 'f32x4.ge', 'f64x2.eq', 'f64x2.ne', 'f64x2.lt', 'f64x2.gt', 'f64x2.le', 'f64x2.ge', 'v128.not', 'v128.and', 'v128.andnot', 'v128.or', 'v128.xor', 'v128.bitselect', 'v128.any_true',
44
- 'v128.load8_lane', 'v128.load16_lane', 'v128.load32_lane', 'v128.load64_lane', 'v128.store8_lane', 'v128.store16_lane', 'v128.store32_lane', 'v128.store64_lane', 'v128.load32_zero', 'v128.load64_zero', 'f32x4.demote_f64x2_zero', 'f64x2.promote_low_f32x4',
45
- 'i8x16.abs', 'i8x16.neg', 'i8x16.popcnt', 'i8x16.all_true', 'i8x16.bitmask', 'i8x16.narrow_i16x8_s', 'i8x16.narrow_i16x8_u', 'f32x4.ceil', 'f32x4.floor', 'f32x4.trunc', 'f32x4.nearest', 'i8x16.shl', 'i8x16.shr_s', 'i8x16.shr_u', 'i8x16.add', 'i8x16.add_sat_s', 'i8x16.add_sat_u', 'i8x16.sub', 'i8x16.sub_sat_s', 'i8x16.sub_sat_u', 'f64x2.ceil', 'f64x2.floor', 'i8x16.min_s', 'i8x16.min_u', 'i8x16.max_s', 'i8x16.max_u', 'f64x2.trunc', 'i8x16.avgr_u',
46
- 'i16x8.extadd_pairwise_i8x16_s', 'i16x8.extadd_pairwise_i8x16_u', 'i32x4.extadd_pairwise_i16x8_s', 'i32x4.extadd_pairwise_i16x8_u', 'i16x8.abs', 'i16x8.neg', 'i16x8.q15mulr_sat_s', 'i16x8.all_true', 'i16x8.bitmask', 'i16x8.narrow_i32x4_s', 'i16x8.narrow_i32x4_u', 'i16x8.extend_low_i8x16_s', 'i16x8.extend_high_i8x16_s', 'i16x8.extend_low_i8x16_u', 'i16x8.extend_high_i8x16_u',
47
- 'i16x8.shl', 'i16x8.shr_s', 'i16x8.shr_u', 'i16x8.add', 'i16x8.add_sat_s', 'i16x8.add_sat_u', 'i16x8.sub', 'i16x8.sub_sat_s', 'i16x8.sub_sat_u', 'f64x2.nearest', 'i16x8.mul', 'i16x8.min_s', 'i16x8.min_u', 'i16x8.max_s', 'i16x8.max_u', , 'i16x8.avgr_u',
48
- 'i16x8.extmul_low_i8x16_s', 'i16x8.extmul_high_i8x16_s', 'i16x8.extmul_low_i8x16_u', 'i16x8.extmul_high_i8x16_u', 'i32x4.abs', 'i32x4.neg', , 'i32x4.all_true', 'i32x4.bitmask', , , 'i32x4.extend_low_i16x8_s', 'i32x4.extend_high_i16x8_s', 'i32x4.extend_low_i16x8_u', 'i32x4.extend_high_i16x8_u', 'i32x4.shl', 'i32x4.shr_s', 'i32x4.shr_u', 'i32x4.add', , , 'i32x4.sub', , , , 'i32x4.mul', 'i32x4.min_s', 'i32x4.min_u', 'i32x4.max_s', 'i32x4.max_u', 'i32x4.dot_i16x8_s', , 'i32x4.extmul_low_i16x8_s', 'i32x4.extmul_high_i16x8_s', 'i32x4.extmul_low_i16x8_u', 'i32x4.extmul_high_i16x8_u', 'i64x2.abs', 'i64x2.neg', , 'i64x2.all_true', 'i64x2.bitmask', , , 'i64x2.extend_low_i32x4_s', 'i64x2.extend_high_i32x4_s', 'i64x2.extend_low_i32x4_u', 'i64x2.extend_high_i32x4_u', 'i64x2.shl', 'i64x2.shr_s', 'i64x2.shr_u', 'i64x2.add', , , 'i64x2.sub', , , , 'i64x2.mul', 'i64x2.eq', 'i64x2.ne', 'i64x2.lt_s', 'i64x2.gt_s', 'i64x2.le_s', 'i64x2.ge_s', 'i64x2.extmul_low_i32x4_s', 'i64x2.extmul_high_i32x4_s', 'i64x2.extmul_low_i32x4_u', 'i64x2.extmul_high_i32x4_u', 'f32x4.abs', 'f32x4.neg', , 'f32x4.sqrt', 'f32x4.add', 'f32x4.sub', 'f32x4.mul', 'f32x4.div', 'f32x4.min', 'f32x4.max', 'f32x4.pmin', 'f32x4.pmax', 'f64x2.abs', 'f64x2.neg', , 'f64x2.sqrt', 'f64x2.add', 'f64x2.sub', 'f64x2.mul', 'f64x2.div', 'f64x2.min', 'f64x2.max', 'f64x2.pmin', 'f64x2.pmax', 'i32x4.trunc_sat_f32x4_s', 'i32x4.trunc_sat_f32x4_u', 'f32x4.convert_i32x4_s', 'f32x4.convert_i32x4_u', 'i32x4.trunc_sat_f64x2_s_zero', 'i32x4.trunc_sat_f64x2_u_zero', 'f64x2.convert_low_i32x4_s', 'f64x2.convert_low_i32x4_u',
49
- // relaxed SIMD instructions
50
- 'i8x16.relaxed_swizzle', 'i32x4.relaxed_trunc_f32x4_s', 'i32x4.relaxed_trunc_f32x4_u', 'i32x4.relaxed_trunc_f64x2_s_zero', 'i32x4.relaxed_trunc_f64x2_u_zero', 'f32x4.relaxed_madd', 'f32x4.relaxed_nmadd', 'f64x2.relaxed_madd', 'f64x2.relaxed_nmadd', 'i8x16.relaxed_laneselect', 'i16x8.relaxed_laneselect', 'i32x4.relaxed_laneselect', 'i64x2.relaxed_laneselect', 'f32x4.relaxed_min', 'f32x4.relaxed_max', 'f64x2.relaxed_min', 'f64x2.relaxed_max', 'i16x8.relaxed_q15mulr_s', 'i16x8.relaxed_dot_i8x16_i7x16_s', 'i32x4.relaxed_dot_i8x16_i7x16_add_s'
51
- ],
52
- SECTION = { custom: 0, type: 1, import: 2, func: 3, table: 4, memory: 5, global: 6, tag: 13, export: 7, start: 8, elem: 9, datacount: 12, code: 10, data: 11 },
53
- RECTYPE = { sub: 0x50, subfinal: 0x4F, rec: 0x4E },
54
- DEFTYPE = { func: 0x60, struct: 0x5F, array: 0x5E, ...RECTYPE },
55
- HEAPTYPE = { nofunc: 0x73, noextern: 0x72, noexn: 0x74, none: 0x71, func: 0x70, extern: 0x6F, exn: 0x75, any: 0x6E, eq: 0x6D, i31: 0x6C, struct: 0x6B, array: 0x6A },
56
- REFTYPE = {
57
- // absheaptype abbrs
58
- nullfuncref: HEAPTYPE.nofunc,
59
- nullexternref: HEAPTYPE.noextern,
60
- nullexnref: HEAPTYPE.noexn,
61
- nullref: HEAPTYPE.none,
62
- funcref: HEAPTYPE.func,
63
- externref: HEAPTYPE.extern,
64
- exnref: HEAPTYPE.exn,
65
- anyref: HEAPTYPE.any,
66
- eqref: HEAPTYPE.eq,
67
- i31ref: HEAPTYPE.i31,
68
- structref: HEAPTYPE.struct,
69
- arrayref: HEAPTYPE.array,
140
+ // Binary section type codes
141
+ export const SECTION = { custom: 0, type: 1, import: 2, func: 3, table: 4, memory: 5, tag: 13, global: 6, export: 7, start: 8, elem: 9, datacount: 12, code: 10, data: 11 }
70
142
 
71
- // ref, refnull
72
- ref: 0x64 /* -0x1c */, refnull: 0x63 /* -0x1d */
73
- },
74
- TYPE = { i8: 0x78, i16: 0x77, i32: 0x7f, i64: 0x7e, f32: 0x7d, f64: 0x7c, void: 0x40, v128: 0x7B, ...HEAPTYPE, ...REFTYPE },
75
- KIND = { func: 0, table: 1, memory: 2, global: 3, tag: 4 },
76
- // WAT escape codes: https://webassembly.github.io/spec/core/text/values.html#strings
77
- ESCAPE = { n: 10, r: 13, t: 9, v: 11, '"': 34, "'": 39, '\\': 92 }
143
+ // Type codes
144
+ export const TYPE = {
145
+ // Value types
146
+ i8: 0x78, i16: 0x77, i32: 0x7f, i64: 0x7e, f32: 0x7d, f64: 0x7c, void: 0x40, v128: 0x7B,
147
+ // Heap types
148
+ exn: 0x69, noexn: 0x74, nofunc: 0x73, noextern: 0x72, none: 0x71, func: 0x70, extern: 0x6F, any: 0x6E, eq: 0x6D, i31: 0x6C, struct: 0x6B, array: 0x6A,
149
+ // Reference type abbreviations (absheaptype abbrs)
150
+ nullfuncref: 0x73, nullexternref: 0x72, nullexnref: 0x74, nullref: 0x71,
151
+ funcref: 0x70, externref: 0x6F, exnref: 0x69, anyref: 0x6E, eqref: 0x6D, i31ref: 0x6C, structref: 0x6B, arrayref: 0x6A,
152
+ // ref, refnull
153
+ ref: 0x64, // -0x1c
154
+ refnull: 0x63, // -0x1d
155
+ // Recursion group / type definition opcodes
156
+ sub: 0x50, subfinal: 0x4F, rec: 0x4E
157
+ }
158
+
159
+ // Type definition codes (different from heap types - func is 0x60 not 0x70)
160
+ export const DEFTYPE = { func: 0x60, struct: 0x5F, array: 0x5E, sub: 0x50, subfinal: 0x4F, rec: 0x4E }
161
+
162
+ // Import/export kind codes
163
+ export const KIND = { func: 0, table: 1, memory: 2, global: 3, tag: 4 }
package/src/encode.js CHANGED
@@ -1,40 +1,75 @@
1
- import { err, intRE, sepRE } from './util.js'
1
+ /**
2
+ * Binary encoding utilities for WebAssembly.
3
+ * @module encode
4
+ * @see https://webassembly.github.io/spec/core/binary/values.html
5
+ */
2
6
 
3
- // encoding ref: https://github.com/j-s-n/WebBS/blob/master/compiler/byteCode.js
7
+ import { err, intRE, sepRE } from './util.js'
4
8
 
5
- // uleb
9
+ /**
10
+ * Encode unsigned LEB128. Handles both 32-bit numbers and 64-bit BigInts.
11
+ *
12
+ * @param {number|bigint|string|null} n - Value to encode
13
+ * @param {number[]} [buffer=[]] - Output buffer
14
+ * @returns {number[]} Encoded bytes
15
+ */
6
16
  export const uleb = (n, buffer = []) => {
7
17
  if (n == null) return buffer
8
- if (typeof n === 'string') n = i32.parse(n)
18
+ if (typeof n === 'string') n = /[_x]/i.test(n) ? BigInt(n.replaceAll('_', '')) : i32.parse(n)
19
+
20
+ // Handle BigInt for 64-bit values
21
+ if (typeof n === 'bigint') {
22
+ while (true) {
23
+ const byte = Number(n & 0x7Fn)
24
+ n >>= 7n
25
+ if (n === 0n) {
26
+ buffer.push(byte)
27
+ break
28
+ }
29
+ buffer.push(byte | 0x80)
30
+ }
31
+ return buffer
32
+ }
9
33
 
10
- let byte = n & 0b01111111;
11
- n = n >>> 7;
34
+ // Handle regular numbers for 32-bit values
35
+ let byte = n & 0x7f
36
+ n >>>= 7
12
37
 
13
38
  if (n === 0) {
14
- buffer.push(byte);
15
- return buffer;
16
- } else {
17
- buffer.push(byte | 0b10000000);
18
- return uleb(n, buffer);
39
+ buffer.push(byte)
40
+ return buffer
41
+ }
42
+ buffer.push(byte | 0x80)
43
+ return uleb(n, buffer)
44
+ }
45
+
46
+ /**
47
+ * Encode as fixed-width 5-byte ULEB128 (canonical form).
48
+ * Used by some tools for predictable binary layout.
49
+ *
50
+ * @param {number} value - 32-bit unsigned value
51
+ * @returns {number[]} 5-byte array
52
+ */
53
+ export function uleb5(value) {
54
+ const result = [];
55
+ for (let i = 0; i < 5; i++) {
56
+ let byte = value & 0x7f;
57
+ value >>>= 7;
58
+ if (i < 4) {
59
+ byte |= 0x80; // Set continuation bit for first 4 bytes
60
+ }
61
+ result.push(byte);
19
62
  }
63
+ return result;
20
64
  }
21
65
 
22
- // fixed-width uleb
23
- // export function full_uleb(value) {
24
- // const result = [];
25
- // for (let i = 0; i < 5; i++) {
26
- // let byte = value & 0x7f;
27
- // value >>>= 7;
28
- // if (i < 4) {
29
- // byte |= 0x80; // Set continuation bit for first 4 bytes
30
- // }
31
- // result.push(byte);
32
- // }
33
- // return result;
34
- // }
35
-
36
-
37
- // leb
66
+ /**
67
+ * Encode signed LEB128 for i32 values.
68
+ *
69
+ * @param {number|string} n - Signed 32-bit value
70
+ * @param {number[]} [buffer=[]] - Output buffer
71
+ * @returns {number[]} Encoded bytes
72
+ */
38
73
  export function i32(n, buffer = []) {
39
74
  if (typeof n === 'string') n = i32.parse(n)
40
75
 
@@ -56,9 +91,19 @@ const cleanInt = (v) => (!sepRE.test(v) && intRE.test(v=v.replaceAll('_',''))) ?
56
91
  // alias
57
92
  export const i8 = i32, i16 = i32
58
93
 
59
- i32.parse = n => parseInt(cleanInt(n))
94
+ i32.parse = n => {
95
+ n = parseInt(cleanInt(n))
96
+ if (n < -0x80000000 || n > 0xffffffff) err(`i32 constant out of range`)
97
+ return n
98
+ }
60
99
 
61
- // bigleb
100
+ /**
101
+ * Encode signed LEB128 for i64 values (BigInt).
102
+ *
103
+ * @param {bigint|string} n - Signed 64-bit value
104
+ * @param {number[]} [buffer=[]] - Output buffer
105
+ * @returns {number[]} Encoded bytes
106
+ */
62
107
  export function i64(n, buffer = []) {
63
108
  if (typeof n === 'string') n = i64.parse(n)
64
109
 
@@ -76,6 +121,7 @@ export function i64(n, buffer = []) {
76
121
  i64.parse = n => {
77
122
  n = cleanInt(n)
78
123
  n = n[0] === '-' ? -BigInt(n.slice(1)) : BigInt(n) // can be -0x123
124
+ if (n < -0x8000000000000000n || n > 0xffffffffffffffffn) err(`i64 constant out of range`)
79
125
  byteView.setBigInt64(0, n)
80
126
  return byteView.getBigInt64(0)
81
127
  }
@@ -84,7 +130,7 @@ const byteView = new DataView(new Float64Array(1).buffer)
84
130
 
85
131
  const F32_SIGN = 0x80000000, F32_NAN = 0x7f800000
86
132
  export function f32(input, value, idx) {
87
- if (~(idx = input.indexOf('nan:'))) {
133
+ if (typeof input === 'string' && ~(idx = input.indexOf('nan:'))) {
88
134
  value = i32.parse(input.slice(idx + 4))
89
135
  value |= F32_NAN
90
136
  if (input[0] === '-') value |= F32_SIGN
@@ -105,7 +151,7 @@ export function f32(input, value, idx) {
105
151
 
106
152
  const F64_SIGN = 0x8000000000000000n, F64_NAN = 0x7ff0000000000000n
107
153
  export function f64(input, value, idx) {
108
- if (~(idx = input.indexOf('nan:'))) {
154
+ if (typeof input === 'string' && ~(idx = input.indexOf('nan:'))) {
109
155
  value = i64.parse(input.slice(idx + 4))
110
156
  value |= F64_NAN
111
157
  if (input[0] === '-') value |= F64_SIGN
@@ -141,12 +187,20 @@ f64.parse = (input, max=Number.MAX_VALUE) => {
141
187
  let [int, fract=''] = sig.split('.'); // integer and fractional parts
142
188
  let flen = fract.length ?? 0;
143
189
 
144
- // FIXME: this is not accurate, it must be byte-perfect
145
- sig = parseInt(int + fract); // 0x is included in int
146
- exp = parseInt(exp, 10);
190
+ // Parse integer part
191
+ let intVal = parseInt(int); // 0x is included in int
192
+ isNaN(intVal) && err()
147
193
 
148
194
  // 0x10a.fbc = 0x10afbc * 16⁻³ = 266.9833984375
149
- let value = sign * sig * (2 ** (exp - 4 * flen));
195
+ // Parse fractional part: fract / 16^flen
196
+ // For better precision, parse as (int + fract) / 16^flen then multiply by 16^flen for int part
197
+ // Equivalent to: intVal + parseInt('0x' + fract) / 16^flen
198
+ let fractVal = fract ? parseInt('0x' + fract) / (16 ** flen) : 0;
199
+
200
+ exp = parseInt(exp, 10);
201
+
202
+ // Combine: (int + fract) * 2^exp
203
+ let value = sign * (intVal + fractVal) * (2 ** exp);
150
204
 
151
205
  // make sure it is not Infinity
152
206
  value = Math.max(-max, Math.min(max, value))
package/src/parse.js CHANGED
@@ -1,51 +1,60 @@
1
- import { unescape } from "./util.js"
2
-
3
- const OPAREN = 40, CPAREN = 41, OBRACK = 91, CBRACK = 93, SPACE = 32, DQUOTE = 34, PERIOD = 46,
4
- _0 = 48, _9 = 57, SEMIC = 59, NEWLINE = 32, PLUS = 43, MINUS = 45, COLON = 58, BACKSLASH = 92, AT = 64
5
-
1
+ import { err } from "./util.js"
6
2
 
7
3
  /**
8
4
  * Parses a wasm text string and constructs a nested array structure (AST).
5
+ * Each array node has `.i` property with source offset for error reporting.
9
6
  *
10
7
  * @param {string} str - The input string with WAT code to parse.
11
- * @param {object} options - Parse options, like comments, etc.
12
8
  * @returns {Array} An array representing the nested syntax tree (AST).
13
9
  */
14
- export default (str, o={ comments: false }) => {
15
- let i = 0, level = [], buf = '', comment = ''
16
-
17
- const commit = () => buf && (
18
- level.push(buf),
19
- buf = ''
20
- )
10
+ export default (str) => {
11
+ let i = 0, level = [], buf = '', q = 0, depth = 0
21
12
 
22
- const parseLevel = () => {
23
- for (let c, root, q, id; i < str.length;) {
13
+ const commit = () => buf && (level.push(buf), buf = '')
24
14
 
15
+ const parseLevel = (pos) => {
16
+ level.i = pos // store start position for error reporting
17
+ for (let c, root, p; i < str.length;) {
25
18
  c = str.charCodeAt(i)
26
- if (q) {
19
+
20
+ // inside "..." or $"..."
21
+ if (q === 34) (buf += str[i++], c === 92 ? buf += str[i++] : c === 34 && (commit(), q = 0))
22
+ // inside (; ... ;) with nesting support (q=60 means depth 1, q=61 means depth 2, etc)
23
+ else if (q > 59) (
24
+ c === 40 && str.charCodeAt(i + 1) === 59 ? (q++, buf += str[i++] + str[i++]) : // nested (;
25
+ c === 59 && str.charCodeAt(i + 1) === 41 ? (buf += str[i++] + str[i++], --q === 59 && (commit(), q = 0)) : // ;)
27
26
  buf += str[i++]
28
- if (c === BACKSLASH) buf += str[i++]
29
- else if (c === DQUOTE) id && (buf = '$' + unescape(buf)), commit(), q = id = 0
30
- }
31
- else if (c === DQUOTE) {
32
- q = c, id = buf == '$', !id && commit(), buf = '"', i++
33
- }
34
- else if (c === OPAREN) {
35
- if (str.charCodeAt(i + 1) === SEMIC) comment = str.slice(i, i = str.indexOf(';)', i) + 2), o.comments && level.push(comment) // (; ... ;)
36
- else if (str.charCodeAt(i + 1) === AT) commit(), i += 2, buf = '@', (root = level).push(level = []), parseLevel(), level = root // (@annotid ...)
37
- else commit(), i++, (root = level).push(level = []), parseLevel(), level = root
38
- }
39
- else if (c === SEMIC && str.charCodeAt(i + 1) === SEMIC) comment = str.slice(i, i = str.indexOf('\n', i) + 1 || str.length), o.comments && level.push(comment) // ;; ...
40
- else if (c <= SPACE) commit(), i++
41
- else if (c === CPAREN) return commit(), i++
27
+ )
28
+ // inside ;; ...\n
29
+ else if (q < 0) (c === 10 || c === 13 ? (buf += str[i++], commit(), q = 0) : buf += str[i++])
30
+ // start "
31
+ else if (c === 34) (buf !== '$' && commit(), q = 34, buf += str[i++])
32
+ // start (;
33
+ else if (c === 40 && str.charCodeAt(i + 1) === 59) (commit(), q = 60, buf = str[i++] + str[i++])
34
+ // start ;;
35
+ else if (c === 59 && str.charCodeAt(i + 1) === 59) (commit(), q = -1, buf = str[i++] + str[i++])
36
+ // start (@
37
+ else if (c === 40 && str.charCodeAt(i + 1) === 64) (commit(), p = i, i += 2, buf = '@', depth++, (root = level).push(level = []), parseLevel(p), level = root)
38
+ // start (
39
+ else if (c === 40) (commit(), p = i++, depth++, (root = level).push(level = []), parseLevel(p), level = root)
40
+ // end )
41
+ else if (c === 41) return commit(), i++, depth--
42
+ // whitespace
43
+ else if (c <= 32) (commit(), i++)
44
+ // other
42
45
  else buf += str[i++]
43
46
  }
44
47
 
48
+ q < 0 && commit() // trailing line comment
45
49
  commit()
46
50
  }
47
51
 
48
- parseLevel()
52
+ parseLevel(0)
53
+
54
+ if (q === 34) err(`Unclosed quote`, i)
55
+ if (q > 59) err(`Unclosed block comment`, i)
56
+ if (depth > 0) err(`Unclosed parenthesis`, i)
57
+ if (i < str.length) err(`Unexpected closing parenthesis`, i)
49
58
 
50
- return level.length > 1 ? level : level[0]
59
+ return level.length > 1 ? level : level[0] || []
51
60
  }
package/src/print.js CHANGED
@@ -8,44 +8,105 @@ import parse from './parse.js';
8
8
  * @param {Object} [options={}] - Optional settings for printing.
9
9
  * @param {string} [options.indent=' '] - The string used for one level of indentation. Defaults to two spaces.
10
10
  * @param {string} [options.newline='\n'] - The string used for line breaks. Defaults to a newline character.
11
+ * @param {boolean} [options.comments=false] - Whether to include comments in the output. Defaults to false.
11
12
  * @returns {string} The formatted WAT string.
12
13
  */
13
14
  export default function print(tree, options = {}) {
14
15
  if (typeof tree === 'string') tree = parse(tree);
15
16
 
16
- let { indent=' ', newline='\n' } = options;
17
+ let { indent=' ', newline='\n', comments=true } = options;
17
18
  indent ||= '', newline ||= ''; // false -> str
18
19
 
19
- return typeof tree[0] === 'string' ? printNode(tree) : tree.map(node => printNode(node)).join(newline)
20
+ // If tree[0] is a string but NOT starting with `;` (comment), it's a keyword like `module` - print as single node
21
+ // Otherwise it's multiple nodes (comments + module) - print each separately
22
+ if (typeof tree[0] === 'string' && tree[0][0] !== ';') return printNode(tree)
23
+
24
+ // Multiple top-level nodes - filter out comments if comments option is false
25
+ return tree
26
+ .filter(node => comments || !isComment(node))
27
+ .map(node => printNode(node))
28
+ .join(newline)
29
+
30
+ function isComment(node) {
31
+ return typeof node === 'string' && node[1] === ';'
32
+ }
20
33
 
21
34
  function printNode(node, level = 0) {
22
35
  if (!Array.isArray(node)) return node
23
36
 
24
37
  let content = node[0]
38
+ if (!content) return ''
39
+ let afterLineComment = false // track if we just printed a line comment
40
+
41
+ // Special handling for try_table: keep catch clauses inline
42
+ if (content === 'try_table') {
43
+ let i = 1
44
+ // Add label if present
45
+ if (typeof node[i] === 'string' && node[i][0] === '$') content += ' ' + node[i++]
46
+ // Add blocktype if present
47
+ if (Array.isArray(node[i]) && (node[i][0] === 'result' || node[i][0] === 'type')) content += ' ' + printNode(node[i++], level)
48
+ // Add catch clauses inline
49
+ while (Array.isArray(node[i]) && /^catch/.test(node[i][0])) content += ' ' + printNode(node[i++], level).trim()
50
+ // Rest is body - print normally
51
+ for (; i < node.length; i++) content += Array.isArray(node[i]) ? newline + indent.repeat(level + 1) + printNode(node[i], level + 1) : ' ' + node[i]
52
+ return `(${content + newline + indent.repeat(level)})`
53
+ }
25
54
 
26
55
  // flat node (no deep subnodes), eg. (i32.const 1), (module (export "") 1)
27
- let flat = !!newline && node.length < 4
56
+ // not flat if contains line comments (they need their own line)
57
+ let flat = !!newline && node.length < 4 && !node.some(n => typeof n === 'string' && n[0] === ';' && n[1] === ';')
28
58
  let curIndent = indent.repeat(level + 1)
29
59
 
30
60
  for (let i = 1; i < node.length; i++) {
31
- // (<keyword> ...)
32
- if (Array.isArray(node[i])) {
33
- // check if it's still flat
34
- if (flat) flat = node[i].every(subnode => !Array.isArray(subnode))
61
+ const sub = node[i].valueOf() // "\00abc\ff" strings are stored as arrays but have ._ with original value
35
62
 
36
- // new line
37
- content += newline + curIndent + printNode(node[i], level + 1)
63
+ // comments - skip if not enabled
64
+ if (typeof sub === 'string' && sub[1] === ';') {
65
+ if (!comments) continue
66
+ // line comments (;;) - MUST end with newline to avoid consuming following elements
67
+ if (sub[0] === ';') {
68
+ if (newline) {
69
+ // prettified: own line with indent, next element adds its own newline
70
+ content += newline + curIndent + sub.trimEnd()
71
+ afterLineComment = true
72
+ } else {
73
+ // minified: keep inline but must have newline after
74
+ const last = content[content.length - 1]
75
+ if (last && last !== ' ' && last !== '(') content += ' '
76
+ content += sub.trimEnd() + '\n'
77
+ }
78
+ }
79
+ // block comments ((;...;)) can stay inline
80
+ else {
81
+ const last = content[content.length - 1]
82
+ if (last && last !== ' ' && last !== '(') content += ' '
83
+ content += sub.trimEnd()
84
+ }
85
+ }
86
+ // (<keyword> ...)
87
+ else if (Array.isArray(sub)) {
88
+ if (flat) flat = sub.every(sub => !Array.isArray(sub))
89
+ content += newline + curIndent + printNode(sub, level + 1)
90
+ afterLineComment = false
38
91
  }
39
92
  // data chunks "\00..."
40
93
  else if (node[0] === 'data') {
41
94
  flat = false;
42
95
  if (newline || content[content.length-1] !== ')') content += newline || ' '
43
- content += curIndent + node[i]
96
+ content += curIndent + sub
97
+ afterLineComment = false
44
98
  }
45
99
  // inline nodes
46
100
  else {
47
- if (newline || content[content.length-1] !== ')') content += ' '
48
- content += node[i]
101
+ const last = content[content.length - 1]
102
+ // after line comment in prettified mode, need newline + indent
103
+ if (afterLineComment && newline) content += newline + curIndent
104
+ // after newline from line comment (minified), add indent
105
+ else if (last === '\n') content += ''
106
+ else if (last && last !== ')' && last !== ' ') content += ' '
107
+ else if (newline || last === ')') content += ' '
108
+ content += sub
109
+ afterLineComment = false
49
110
  }
50
111
  }
51
112