watr 1.4.1 → 2.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.
Files changed (4) hide show
  1. package/package.json +14 -3
  2. package/readme.md +34 -78
  3. package/src/compile.js +108 -141
  4. package/src/const.js +19 -18
package/package.json CHANGED
@@ -1,8 +1,14 @@
1
1
  {
2
2
  "name": "watr",
3
- "version": "1.4.1",
3
+ "version": "2.0.0",
4
4
  "description": "Ligth & fast WAT compiler",
5
5
  "main": "watr.js",
6
+ "exports": {
7
+ ".": "./watr.js",
8
+ "./parse": "./src/parse.js",
9
+ "./print": "./src/print.js",
10
+ "./compile": "./src/compile.js"
11
+ },
6
12
  "type": "module",
7
13
  "scripts": {
8
14
  "test": "node test"
@@ -16,11 +22,16 @@
16
22
  ],
17
23
  "keywords": [
18
24
  "wat",
25
+ "wasm",
26
+ "wat2wasm",
19
27
  "compiler",
20
- "wabt"
28
+ "wabt",
29
+ "pretty-print",
30
+ "webassembly",
31
+ "wasm-text"
21
32
  ],
22
33
  "author": "Dmitry Iv",
23
- "license": "ISC",
34
+ "license": "MIT",
24
35
  "bugs": {
25
36
  "url": "https://github.com/audio-lab/watr/issues"
26
37
  },
package/readme.md CHANGED
@@ -1,25 +1,16 @@
1
- # watr [![Test](https://github.com/audio-lab/watr/actions/workflows/test.js.yml/badge.svg)](https://github.com/audio-lab/watr/actions/workflows/test.js.yml)
1
+ # watr [![test](https://github.com/audio-lab/watr/actions/workflows/test.js.yml/badge.svg)](https://github.com/audio-lab/watr/actions/workflows/test.js.yml) [![npm bundle size](https://img.shields.io/bundlephobia/minzip/watr/latest?color=brightgreen&label=gzip)](https://bundlephobia.com/package/watr)
2
2
 
3
- > Light & fast WAT compiler.
3
+ > Bare minimum wasm text compiler & formatter.
4
4
 
5
- Provides bare minimum WAT to WASM compilation.<br/>
6
- Useful as WASM API layer, eg. for hi-level languages or for dynamic (in-browser?) compilation.
7
- <!--, eg. [sonl](https://github.com/audio-lab/sonl). -->
5
+ Light & fast alternative for [wat2wasm](https://github.com/AssemblyScript/wabt.js), useful for hi-level languages or dynamic (in-browser) compilation.<br>
8
6
 
9
7
  <!-- See [REPL](https://audio-lab.github.io/watr/repl.html).-->
10
8
 
11
- <!--
12
- &nbsp; | watr | wat-compiler | wabt
13
- ---|---|---|---
14
- Size (gzipped) | 2.8kb | 6kb | 300kb
15
- Performance (op/s) | 45000 | 2500 | 3100
16
- -->
17
-
18
9
  &nbsp; | Size (gzipped) | Performance (op/s)
19
10
  ---|---|---
20
- watr | 3.8 kb | 1900
21
- [wat-compiler](https://github.com/stagas/wat-compiler) | 6 kb | 135
22
- [wabt](https://github.com/AssemblyScript/wabt.js) | 300 kb | 250
11
+ watr | 3.8 kb | 5950
12
+ [wat-compiler](https://github.com/stagas/wat-compiler) | 6 kb | 348
13
+ [wabt](https://github.com/AssemblyScript/wabt.js) | 300 kb | 574
23
14
 
24
15
  ## Usage
25
16
 
@@ -43,32 +34,17 @@ double(108) // 216
43
34
 
44
35
  ## API
45
36
 
46
- ### Parse
47
-
48
- Parser converts input Wasm text string to syntax tree.
49
-
50
- ```js
51
- import { parse } from 'watr
52
-
53
- parse(`(func (export "double") (param f64) (result f64) (f64.mul (local.get 0) (f64.const 2)))`)
54
-
55
- // [
56
- // 'func', ['export', '"double"'], ['param', 'f64'], ['result', 'f64'],
57
- // ['f64.mul', ['local.get', 0], ['f64.const', 2]]
58
- // ]
59
- ```
60
-
61
37
  ### Compile
62
38
 
63
- Compiles Wasm tree or text into wasm binary. Lightweight alternative to [wabt/wat2wasm](https://github.com/WebAssembly/wabt).
39
+ Compiles wasm text or syntax tree into wasm binary.
64
40
 
65
41
  ```js
66
42
  import { compile } from 'watr'
67
43
 
68
- const buffer = compile([
69
- 'func', ['export', '"double"'], ['param', 'f64'], ['result', 'f64'],
70
- ['f64.mul', ['local.get', 0], ['f64.const', 2]]
71
- ])
44
+ const buffer = compile(`(func (export "double")
45
+ (param f64) (result f64)
46
+ (f64.mul (local.get 0) (f64.const 2))
47
+ )`)
72
48
  const module = new WebAssembly.Module(buffer)
73
49
  const instance = new WebAssembly.Instance(module)
74
50
  const {double} = instance.exports
@@ -78,20 +54,20 @@ double(108) // 216
78
54
 
79
55
  ### Print
80
56
 
81
- Format input Wasm text string or tree into minified or pretty form.
57
+ Format input wasm text or syntax tree into minified or pretty form.
82
58
 
83
59
  ```js
84
60
  import { print } from 'watr'
85
61
 
86
- const tree = [
87
- 'func', ['export', '"double"'], ['param', 'f64'], ['result', 'f64'],
88
- ['f64.mul', ['local.get', 0], ['f64.const', 2]]
89
- ]
62
+ const src = `(func (export "double")
63
+ (param f64) (result f64)
64
+ (f64.mul (local.get 0) (f64.const 2))
65
+ )`
90
66
 
91
67
  // pretty-print (default)
92
- const str = print(tree, {
93
- indent: ' ', // indentation chars
94
- newline: '\n', // new line chars
68
+ print(src, {
69
+ indent: ' ',
70
+ newline: '\n',
95
71
  })
96
72
  // (func (export "double")
97
73
  // (param f64) (result f64)
@@ -100,53 +76,34 @@ const str = print(tree, {
100
76
  // (f64.const 2)))
101
77
 
102
78
  // minify
103
- const str = print(tree, {
79
+ print(src, {
104
80
  indent: false,
105
81
  newline: false
106
82
  })
107
83
  // (func (export "double")(param f64)(result f64)(f64.mul (local.get 0)(f64.const 2)))
108
84
  ```
109
85
 
110
- <!--
111
- ## Limitations
112
-
113
- It may miss some edge cases and nice error messages.
114
- For better REPL/dev experience use [wabt](https://github.com/AssemblyScript/wabt.js).
115
-
86
+ ### Parse
116
87
 
117
- Ambiguous syntax is prohibited in favor of explicit lispy notation. Each instruction must have prefix signature with parenthesized immediates and arguments.
88
+ Parse input wasm text into syntax tree.
118
89
 
119
- ```wast
120
- (func (result i32)
121
- i32.const 1 ;; ✘ stacked arguments
122
- drop
123
- i32.const 0
124
- i32.load offset=0 align=4 ;; ✘ ungrouped immediates
125
- )
90
+ ```js
91
+ import { parse } from 'watr'
126
92
 
127
- (func (result i32)
128
- (drop (i32.const 1)) ;; ✔ nested arguments
129
- (i32.load offset=0 align=4 (i32.const 0)) ;; grouped immediates
130
- )
93
+ parse(`(func (export "double") (param f64) (result f64) (f64.mul (local.get 0) (f64.const 2)))`)
94
+ // [
95
+ // 'func', ['export', '"double"'], ['param', 'f64'], ['result', 'f64'],
96
+ // ['f64.mul', ['local.get', 0], ['f64.const', 2]]
97
+ // ]
131
98
  ```
132
99
 
133
- ```wast
134
- (local.get 0) ;; ✘ stacked argument
135
- if (result i32) ;; ✘ inline instruction
136
- (i32.const 1)
137
- end
138
-
139
- (if (result i32) (local.get 0) ;; ✔ explicit signature
140
- (i32.const 1)
141
- )
142
- ```
100
+ ## Limitations
143
101
 
144
- ```wast
145
- (f32.const 0x1.fffffep+127) ;; ✘ floating HEX - not supported
146
- ```
147
- -->
102
+ No floating HEX support, eg. `(f32.const 0x1.fffffep+127)`.
148
103
 
104
+ ## Projects using watr
149
105
 
106
+ * [auro](https://github.com/audio-lab/auro) – audio processing language
150
107
 
151
108
  ## Useful links
152
109
 
@@ -162,20 +119,19 @@ end
162
119
  * [wabt source search](https://github.com/WebAssembly/wabt/search?p=5&q=then)
163
120
  * [wat control flow](https://developer.mozilla.org/en-US/docs/WebAssembly/Reference/Control_flow)
164
121
  * [ontouchstart wasm book](https://ontouchstart.pages.dev/chapter_wasm_binary)
165
- * [wat-compiler](https://github.com/stagas/wat-compiler/)
166
122
  * [hackernoon](https://web.archive.org/web/20210215171830/https://hackernoon.com/webassembly-binary-format-explained-part-2-hj1t33yp?source=rss)
167
123
  * [webassemblyjs](https://github.com/xtuc/webassemblyjs)
168
124
  * [chasm](https://github.com/ColinEberhardt/chasm/blob/master/src/encoding.ts)
169
125
  * [WebBS](https://github.com/j-s-n/WebBS)
170
126
  * [leb128a](https://github.com/minhducsun2002/leb128/blob/master/src/index.ts)
171
127
  * [leb128b](https://github.com/shmishtopher/wasm-LEB128/tree/master/esm)
172
-
173
128
  -->
174
129
 
175
130
  ## Alternatives
176
131
 
177
132
  * [wabt](https://www.npmjs.com/package/wabt) − port of WABT for the web, de-facto standard.
178
133
  * [wat-compiler](https://www.npmjs.com/package/wat-compiler) − compact alternative for WABT, older brother of _watr_.
134
+ * [wassemble](https://github.com/wingo/wassemble)
179
135
  * [web49](https://github.com/FastVM/Web49)
180
136
 
181
137
  <p align=center><a href="https://github.com/krsnzd/license/">🕉</a></p>
package/src/compile.js CHANGED
@@ -2,8 +2,7 @@ import { uleb, leb, bigleb, f64, f32 } from './util.js'
2
2
  import { OP, SECTION, ALIGN, TYPE, KIND } from './const.js'
3
3
  import parse from './parse.js'
4
4
 
5
- // some inlinable instructions
6
- const INLINE = { loop: 1, block: 1, if: 1, end: -1, return: -1 }
5
+ const OP_END = 0xb, OP_I32_CONST = 0x41, OP_I64_CONST = 0x42, OP_F32_CONST = 0x43, OP_F64_CONST = 0x44
7
6
 
8
7
  // convert wat tree to wasm binary
9
8
  export default (nodes) => {
@@ -43,14 +42,12 @@ export default (nodes) => {
43
42
  for (let node of nodes) {
44
43
  node[0] === name ? postcall.push(build[name](node, sections)) : remaining.push(node)
45
44
  }
46
-
47
45
  nodes = remaining
48
46
  }
49
47
 
50
48
  // code must be compiled after all definitions
51
49
  for (let cb of postcall) cb && cb.call && cb()
52
50
 
53
-
54
51
  // 3. build binary
55
52
  for (let name in sections) {
56
53
  let items = sections[name]
@@ -99,7 +96,7 @@ const build = {
99
96
  // (func $name? ...params result ...body)
100
97
  func([, ...body], ctx) {
101
98
  let locals = [], // list of local variables
102
- callstack = []
99
+ blocks = [] // control instructions / blocks stack
103
100
 
104
101
  // fn name
105
102
  if (body[0]?.[0] === '$') ctx.func[body.shift()] = ctx.func.length
@@ -125,164 +122,134 @@ const build = {
125
122
  // squash local types
126
123
  let locTypes = locals.reduce((a, type) => (type == a[a.length - 1] ? a[a.length - 2]++ : a.push(1, type), a), [])
127
124
 
128
- // map code instruction into bytes: [args, opCode, immediates]
129
- const instr = (group) => {
130
- let [op, ...nodes] = group
131
- let opCode = OP[op], argc = 0, before = [], after = [], id
132
-
133
- // NOTE: we could reorganize ops by groups and detect signature as `op in STORE`
134
- // but numeric comparison is faster than generic hash lookup
135
- // FIXME: we often use OP.end or alike: what if we had list of global constants?
136
-
137
- // binary/unary
138
- if (opCode >= 69) {
139
- argc = opCode >= 167 ||
140
- (opCode <= 159 && opCode >= 153) ||
141
- (opCode <= 145 && opCode >= 139) ||
142
- (opCode <= 123 && opCode >= 121) ||
143
- (opCode <= 105 && opCode >= 103) ||
144
- opCode == 80 || opCode == 69 ? 1 : 2
145
- }
146
- // instruction
147
- else {
148
- // (i32.store align=n offset=m at value)
149
- if (opCode >= 40 && opCode <= 62) {
150
- // FIXME: figure out point in Math.log2 aligns
151
- let o = { align: ALIGN[op], offset: 0 }, p
152
- while (nodes[0]?.includes('=')) p = nodes.shift().split('='), o[p[0]] = Number(p[1])
153
- after = [Math.log2(o.align), ...uleb(o.offset)]
154
- argc = opCode >= 54 ? 2 : 1
155
- }
156
-
157
- // (i32.const 123)
158
- else if (opCode >= 65 && opCode <= 68) {
159
- after = (opCode == 65 ? leb : opCode == 66 ? bigleb : opCode == 67 ? f32 : f64)(nodes.shift())
160
- }
125
+ // convert sequence of instructions from input nodes to out bytes
126
+ const consume = (nodes, out = []) => {
127
+ if (!nodes?.length) return out
161
128
 
162
- // (local.get $id), (local.tee $id x)
163
- else if (opCode >= 32 && opCode <= 34) {
164
- after = uleb(nodes[0]?.[0] === '$' ? params[id = nodes.shift()] || locals[id] : nodes.shift())
165
- if (opCode > 32) argc = 1
166
- }
129
+ let op = nodes.shift(), opCode, args = nodes, immed, id, group
167
130
 
168
- // (global.get id), (global.set id)
169
- else if (opCode == 35 || opCode == 36) {
170
- after = uleb(nodes[0]?.[0] === '$' ? ctx.global[nodes.shift()] : nodes.shift())
171
- if (opCode > 35) argc = 1
172
- }
131
+ // groups are flattened, eg. (cmd z w) -> z w cmd
132
+ if (group = Array.isArray(op)) {
133
+ args = [...op] // op is immutable
134
+ opCode = OP.indexOf(op = args.shift())
135
+ }
136
+ else opCode = OP.indexOf(op)
173
137
 
174
- // (call id ...nodes)
175
- else if (opCode == 16) {
176
- let fnName = nodes.shift()
177
- after = uleb(id = fnName[0] === '$' ? ctx.func[fnName] ?? err('Unknown function `' + fnName + '`') : fnName);
178
- // FIXME: how to get signature of imported function
179
- [, argc] = ctx.type[ctx.func[id][0]]
180
- }
138
+ // NOTE: numeric comparison is faster than generic hash lookup
181
139
 
182
- // (call_indirect (type $typeName) (idx) ...nodes)
183
- else if (opCode == 17) {
184
- let typeId = nodes.shift()[1];
185
- [, argc] = ctx.type[typeId = typeId[0] === '$' ? ctx.type[typeId] : typeId]
186
- argc++
187
- after = uleb(typeId), after.push(0) // extra afterediate indicates table idx (reserved)
188
- }
140
+ // binary/unary - just consume immed
141
+ if (opCode >= 69) { }
189
142
 
190
- // FIXME (memory.grow $idx?)
191
- else if (opCode == 63 || opCode == 64) {
192
- after = [0]
193
- argc = 1
194
- }
143
+ // (i32.store align=n offset=m at value)
144
+ else if (opCode >= 40 && opCode <= 62) {
145
+ // FIXME: figure out point in Math.log2 aligns
146
+ let o = { align: ALIGN[op], offset: 0 }, param
147
+ while (args[0]?.includes('=')) param = args.shift().split('='), o[param[0]] = Number(param[1])
148
+ immed = [Math.log2(o.align), ...uleb(o.offset)]
149
+ }
195
150
 
196
- // (if (result i32)? (local.get 0) (then a b) (else a b)?)
197
- else if (opCode == 4) {
198
- callstack.push(opCode)
199
- let [, type] = nodes[0][0] === 'result' ? nodes.shift() : [, 'void']
200
- after = [TYPE[type]]
201
-
202
- argc = 0, before.push(...instr(nodes.shift()))
203
- let body
204
- if (nodes[0]?.[0] === 'then') [, ...body] = nodes.shift(); else body = nodes
205
- after.push(...consume(body))
206
-
207
- callstack.pop(), callstack.push(OP.else)
208
- if (nodes[0]?.[0] === 'else') {
209
- [, ...body] = nodes.shift()
210
- if (body.length) after.push(OP.else, ...consume(body))
211
- }
212
- callstack.pop()
213
- after.push(OP.end)
214
- }
151
+ // (i32.const 123)
152
+ else if (opCode >= 65 && opCode <= 68) {
153
+ immed = (opCode == 65 ? leb : opCode == 66 ? bigleb : opCode == 67 ? f32 : f64)(args.shift())
154
+ }
215
155
 
216
- // (drop arg?), (return arg?)
217
- else if (opCode == 0x1a || opCode == 0x0f) { argc = nodes.length ? 1 : 0 }
156
+ // (local.get $id), (local.tee $id x)
157
+ else if (opCode >= 32 && opCode <= 34) {
158
+ immed = uleb(args[0]?.[0] === '$' ? params[id = args.shift()] || locals[id] : args.shift())
159
+ }
218
160
 
219
- // (select a b cond)
220
- else if (opCode == 0x1b) { argc = 3 }
161
+ // (global.get id), (global.set id)
162
+ else if (opCode == 35 || opCode == 36) {
163
+ immed = uleb(args[0]?.[0] === '$' ? ctx.global[args.shift()] : args.shift())
164
+ }
221
165
 
222
- // (block ...), (loop ...)
223
- else if (opCode == 2 || opCode == 3) {
224
- callstack.push(opCode)
225
- if (nodes[0]?.[0] === '$') (callstack[nodes.shift()] = callstack.length)
226
- let [, type] = nodes[0]?.[0] === 'result' ? nodes.shift() : [, 'void']
227
- after = [TYPE[type], ...consume(nodes)]
166
+ // (call id ...nodes)
167
+ else if (opCode == 16) {
168
+ let fnName = args.shift()
169
+ immed = uleb(id = fnName[0] === '$' ? ctx.func[fnName] ?? err('Unknown function `' + fnName + '`') : fnName);
170
+ // FIXME: how to get signature of imported function
171
+ }
228
172
 
229
- if (!group.inline) callstack.pop(), after.push(OP.end) // inline loop/block expects end to be separately provided
230
- }
173
+ // (call_indirect (type $typeName) (idx) ...nodes)
174
+ else if (opCode == 17) {
175
+ let typeId = args.shift()[1];
176
+ typeId = typeId[0] === '$' ? ctx.type[typeId] : typeId
177
+ immed = uleb(typeId), immed.push(0) // extra immediate indicates table idx (reserved)
178
+ }
231
179
 
232
- // (end)
233
- else if (opCode == 0x0b) callstack.pop()
180
+ // FIXME (memory.grow $idx?)
181
+ else if (opCode == 63 || opCode == 64) {
182
+ immed = [0]
183
+ }
234
184
 
235
- // (br $label result?)
236
- // (br_if $label cond result?)
237
- else if (opCode == 0x0c || opCode == 0x0d) {
238
- // br index indicates how many callstack items to pop
239
- after = uleb(nodes[0]?.[0] === '$' ? callstack.length - callstack[nodes.shift()] : nodes.shift())
240
- argc = (opCode == 0x0d ? 1 + (nodes.length > 1) : !!nodes.length)
185
+ // (if (result i32)? (local.get 0) (then a b) (argse a b)?)
186
+ else if (opCode == 4) {
187
+ blocks.push(opCode)
188
+ let [, type] = args[0][0] === 'result' ? args.shift() : [, 'void']
189
+ immed = [TYPE[type]] // FIXME: what if that's multiple values return?
190
+
191
+ // (if ... (then) (else)) -> `... if ... else ... end`
192
+ if (group) {
193
+ nodes.unshift('end')
194
+ if (args[args.length - 1][0] === 'else') nodes.unshift(args.pop())
195
+ if (args[args.length - 1][0] === 'then') nodes.unshift(args.pop())
241
196
  }
197
+ }
198
+ // (else)
199
+ else if (opCode === 5) {
200
+ // ignore empty else
201
+ if (!args.length && nodes[0] === 'end') return
202
+ // (else xxx) -> else xxx
203
+ if (group) while (args.length) nodes.unshift(args.pop())
204
+ }
242
205
 
243
- // (br_table 1 2 3 4 0 selector result?)
244
- else if (opCode == 0x0e) {
245
- after = []
246
- while (!Array.isArray(nodes[0])) id = nodes.shift(), after.push(...uleb(id[0][0] === '$' ? callstack.length - callstack[id] : id))
247
- after.unshift(...uleb(after.length - 1))
248
- argc = 1 + (nodes.length > 1)
206
+ // (block ...), (loop ...)
207
+ else if (opCode == 2 || opCode == 3) {
208
+ blocks.push(opCode)
209
+ if (args[0]?.[0] === '$') (blocks[args.shift()] = blocks.length)
210
+ let [, type] = args[0]?.[0] === 'result' ? args.shift() : [, 'void']
211
+ immed = [TYPE[type]]
212
+ // (block xxx) -> block xxx end
213
+ if (group) {
214
+ nodes.unshift('end')
215
+ while (args.length) nodes.unshift(args.pop())
249
216
  }
250
-
251
- else if (opCode == null) err(`Unknown instruction \`${op}\``)
252
217
  }
253
218
 
254
- // consume arguments
255
- if (nodes.length < argc) err(`Stack arguments are not supported at \`${op}\``)
256
- while (argc--) before.push(...instr(nodes.shift()))
257
- if (nodes.length) err(`Too many arguments for \`${op}\`.`)
219
+ // (end)
220
+ else if (opCode == 0x0b) blocks.pop()
258
221
 
259
- return [...before, opCode, ...after]
260
- }
222
+ // (br $label result?)
223
+ // (br_if $label cond result?)
224
+ else if (opCode == 0x0c || opCode == 0x0d) {
225
+ // br index indicates how many block items to pop
226
+ immed = uleb(args[0]?.[0] === '$' ? blocks.length - blocks[args.shift()] : args.shift())
227
+ }
261
228
 
262
- // consume sequence of nodes
263
- const consume = nodes => {
264
- let result = []
265
- while (nodes.length) {
266
- let node = nodes.shift(), c
267
-
268
- if (typeof node === 'string') {
269
- // permit some inline instructions: loop $label ... end, br $label, arg return
270
- if (c = INLINE[node]) {
271
- node = [node], node.inline = true
272
- if (c > 0) nodes[0]?.[0] === '$' && node.push(nodes.shift())
273
- }
274
- else err(`Inline instruction \`${node}\` is not supported`)
275
- }
229
+ // (br_table 1 2 3 4 0 selector result?)
230
+ else if (opCode == 0x0e) {
231
+ immed = []
232
+ while (!Array.isArray(args[0])) id = args.shift(), immed.push(...uleb(id[0][0] === '$' ? blocks.length - blocks[id] : id))
233
+ immed.unshift(...uleb(immed.length - 1))
234
+ }
276
235
 
277
- node && result.push(...instr(node))
236
+ // if group (cmd im1 im2 arg1 arg2) - insert any remaining args first: arg1 arg2
237
+ // because inline case has them in stack already
238
+ if (group) {
239
+ while (args.length) consume(args, out)
278
240
  }
279
- return result
241
+
242
+ // ignore (then) and other unknown instructions
243
+ if (opCode >= 0) out.push(opCode)
244
+ if (immed) out.push(...immed)
280
245
  }
281
246
 
282
- // evaluates after all definitions
247
+ // evaluates after all definitions (need globals, elements, data etc.)
248
+ // FIXME: get rid of this postcall
283
249
  return () => {
284
- let code = consume(body)
285
- ctx.code.push([...uleb(code.length + 2 + locTypes.length), ...uleb(locTypes.length >> 1), ...locTypes, ...code, OP.end])
250
+ const bytes = []
251
+ while (body.length) consume(body, bytes)
252
+ ctx.code.push([...uleb(bytes.length + 2 + locTypes.length), ...uleb(locTypes.length >> 1), ...locTypes, ...bytes, OP_END])
286
253
  }
287
254
  },
288
255
 
@@ -372,8 +339,8 @@ const build = {
372
339
 
373
340
  // (i32.const 0) - instantiation time initializer
374
341
  const iinit = ([op, literal], ctx) => op[0] === 'f' ?
375
- [OP[op], ...(op[1] === '3' ? f32 : f64)(literal), OP.end] :
376
- [OP[op], ...(op[1] === '3' ? leb : bigleb)(literal[0] === '$' ? ctx.global[literal] : literal), OP.end]
342
+ [op[1] === '3' ? OP_F32_CONST : OP_F64_CONST, ...(op[1] === '3' ? f32 : f64)(literal), OP_END] :
343
+ [op[1] === '3' ? OP_I32_CONST : OP_I64_CONST, ...(op[1] === '3' ? leb : bigleb)(literal[0] === '$' ? ctx.global[literal] : literal), OP_END]
377
344
 
378
345
  const escape = { n: 10, r: 13, t: 9, v: 1 }
379
346
 
package/src/const.js CHANGED
@@ -1,10 +1,10 @@
1
1
  // ref: https://github.com/stagas/wat-compiler/blob/main/lib/const.js
2
2
  // NOTE: squashing into a string doesn't save up gzipped size
3
3
  export const OP = [
4
- 'unreachable', 'nop', 'block', 'loop', 'if', 'else', ,,,,,
5
- 'end', 'br', 'br_if', 'br_table', 'return', 'call', 'call_indirect', ,,,,,,,,
6
- 'drop', 'select', ,,,,
7
- 'local.get', 'local.set', 'local.tee', 'global.get', 'global.set', ,,,
4
+ 'unreachable', 'nop', 'block', 'loop', 'if', 'else', , , , , ,
5
+ 'end', 'br', 'br_if', 'br_table', 'return', 'call', 'call_indirect', , , , , , , , ,
6
+ 'drop', 'select', , , , ,
7
+ 'local.get', 'local.set', 'local.tee', 'global.get', 'global.set', , , ,
8
8
  'i32.load', 'i64.load', 'f32.load', 'f64.load',
9
9
  'i32.load8_s', 'i32.load8_u', 'i32.load16_s', 'i32.load16_u',
10
10
  'i64.load8_s', 'i64.load8_u', 'i64.load16_s', 'i64.load16_u', 'i64.load32_s', 'i64.load32_u',
@@ -14,8 +14,8 @@ export const OP = [
14
14
  'i32.const', 'i64.const', 'f32.const', 'f64.const',
15
15
  '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',
16
16
  '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',
17
- 'f32.eq', 'f32.ne', 'f32.lt', 'f32.gt', 'f32.le', 'f32.ge',
18
- 'f64.eq', 'f64.ne', 'f64.lt', 'f64.gt', 'f64.le', 'f64.ge',
17
+ 'f32.eq', 'f32.ne', 'f32.lt', 'f32.gt', 'f32.le', 'f32.ge',
18
+ 'f64.eq', 'f64.ne', 'f64.lt', 'f64.gt', 'f64.le', 'f64.ge',
19
19
  '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',
20
20
  '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',
21
21
  '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',
@@ -27,15 +27,16 @@ export const OP = [
27
27
  'f64.convert_i32_s', 'f64.convert_i32_u', 'f64.convert_i64_s', 'f64.convert_i64_u', 'f64.promote_f32',
28
28
  'i32.reinterpret_f32', 'i64.reinterpret_f64', 'f32.reinterpret_i32', 'f64.reinterpret_i64',
29
29
  ],
30
- SECTION = { type:1, import:2, func:3, table:4, memory:5, global:6, export:7, start:8, elem:9, code:10, data:11 },
31
- TYPE = { i32:0x7f, i64:0x7e, f32:0x7d, f64:0x7c, void:0x40, func:0x60, funcref:0x70 },
32
- KIND = { func: 0, table: 1, memory: 2, global: 3 },
33
- ALIGN = {
34
- 'i32.load': 4, 'i64.load': 8, 'f32.load': 4, 'f64.load': 8,
35
- 'i32.load8_s': 1, 'i32.load8_u': 1, 'i32.load16_s': 2, 'i32.load16_u': 2,
36
- 'i64.load8_s': 1, 'i64.load8_u': 1, 'i64.load16_s': 2, 'i64.load16_u': 2, 'i64.load32_s': 4, 'i64.load32_u': 4, 'i32.store': 4,
37
- 'i64.store': 8, 'f32.store': 4, 'f64.store': 8,
38
- 'i32.store8': 1, 'i32.store16': 2, 'i64.store8': 1, 'i64.store16': 2, 'i64.store32': 4,
39
- }
40
-
41
- OP.map((op,i)=>OP[op]=i) // init op names
30
+ SECTION = { type: 1, import: 2, func: 3, table: 4, memory: 5, global: 6, export: 7, start: 8, elem: 9, code: 10, data: 11 },
31
+ TYPE = { i32: 0x7f, i64: 0x7e, f32: 0x7d, f64: 0x7c, void: 0x40, func: 0x60, funcref: 0x70 },
32
+ KIND = { func: 0, table: 1, memory: 2, global: 3 },
33
+ ALIGN = {
34
+ 'i32.load': 4, 'i64.load': 8, 'f32.load': 4, 'f64.load': 8,
35
+ 'i32.load8_s': 1, 'i32.load8_u': 1, 'i32.load16_s': 2, 'i32.load16_u': 2,
36
+ 'i64.load8_s': 1, 'i64.load8_u': 1, 'i64.load16_s': 2, 'i64.load16_u': 2, 'i64.load32_s': 4, 'i64.load32_u': 4, 'i32.store': 4,
37
+ 'i64.store': 8, 'f32.store': 4, 'f64.store': 8,
38
+ 'i32.store8': 1, 'i32.store16': 2, 'i64.store8': 1, 'i64.store16': 2, 'i64.store32': 4,
39
+ },
40
+ BLOCK = {
41
+ loop: 1, block: 1, if: 1, end: -1, return: -1
42
+ }