watr 0.0.0 → 1.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.
@@ -0,0 +1,1090 @@
1
+ import t, { is, ok, same, throws } from 'tst'
2
+ import compile from '../src/compile.js'
3
+ import parse from '../src/parse.js'
4
+ import Wabt from '../lib/wabt.js'
5
+ import watCompiler from '../lib/wat-compiler.js'
6
+
7
+
8
+ // examples from https://ontouchstart.pages.dev/chapter_wasm_binary
9
+
10
+ t('compile: empty', t => {
11
+ is(compile(['module']), hex`00 61 73 6d 01 00 00 00`)
12
+ })
13
+
14
+ t('compile: (module (func))', t => {
15
+ let buffer
16
+ is(compile(['module', ['func']]), buffer = hex`
17
+ 00 61 73 6d 01 00 00 00
18
+ 01 04 01 60 00 00 ; type section
19
+ 03 02 01 00 ; func section
20
+ 0a 04 01 02 00 0b ; code section
21
+ `)
22
+
23
+ new WebAssembly.Module(buffer)
24
+ })
25
+
26
+ t('compile: (module (memory 1) (func))', t => {
27
+ let buffer = hex`
28
+ 00 61 73 6d 01 00 00 00
29
+ 01 04 01 60 00 00 ; type
30
+ 03 02 01 00 ; func
31
+ 05 03 01 00 01 ; memory
32
+ 0a 04 01 02 00 0b ; code
33
+ `
34
+ new WebAssembly.Module(buffer)
35
+
36
+ is(compile(['module', ['memory', 1], ['func']]), buffer )
37
+ })
38
+
39
+ t('compile: (module (memory (import "js" "mem") 1) (func))', t => {
40
+ let buffer = hex`
41
+ 00 61 73 6d 01 00 00 00
42
+ 01 04 01 60 00 00 ; type
43
+ 02 0b 01 02 6a 73 03 6d 65 6d 02 00 01 ; import
44
+ 03 02 01 00 ; func
45
+ 0a 04 01 02 00 0b ; code
46
+ `
47
+ new WebAssembly.Module(buffer)
48
+ is(compile(['module', ['memory', ['import', 'js', 'mem'], 1], ['func']]), buffer)
49
+ })
50
+
51
+ t('compile: export mem/func', t => {
52
+ let buffer = hex`
53
+ 00 61 73 6d 01 00 00 00
54
+ 01 07 01 60 02 7f 7f 01 7f ; type
55
+ 03 02 01 00 ; function
56
+ 05 03 01 00 01 ; memory
57
+ 07 09 02 01 6d 02 00 01 66 00 00 ; export
58
+ 0a 0d 01 0b 00 20 00 20 01 36 02 00 20 01 0b ; code
59
+ `
60
+ is(compile(['module', // (module
61
+ ['memory', 1], // (memory 1)
62
+ ['func', ['param', 'i32', 'i32'], ['result', 'i32'], // (func (param i32 i32) (result i32)
63
+ // ['local.get', 0], // local.get 0
64
+ // ['local.get', 1], // local.get 1
65
+ // ['i32.store', ['align','4']], // i32.store
66
+ ['i32.store', ['align','4'], ['local.get', 0], ['local.get', 1]],
67
+ ['local.get', 1] // local.get 1
68
+ ], // )
69
+ ['export', '"m"', ['memory', 0]], // (export "m" (memory 0 ))
70
+ ['export', '"f"', ['func', 0]], // (export "f" (func 0 ))
71
+ ]), // )
72
+ buffer)
73
+
74
+ new WebAssembly.Module(buffer)
75
+ })
76
+
77
+ t('compiler: reexport', () => {
78
+ let src = `
79
+ (export "f0" (func 0))
80
+ (export "f1" (func 1))
81
+ (import "math" "add" (func (param i32 i32) (result i32)))
82
+ (func (param i32 i32) (result i32)
83
+ (i32.sub (local.get 0) (local.get 1))
84
+ )
85
+ `
86
+
87
+ let {f0, f1} = run(src, {math:{add(a,b){return a+b}}}).exports
88
+ is(f0(3,1), 4)
89
+ is(f1(3,1), 2)
90
+ })
91
+
92
+ t('compiler: memory $foo (import "a" "b" ) 1 2 shared', () => {
93
+ let src = `(memory $foo (import "env" "mem") 1 2 shared)`
94
+ run(src, {env:{mem: new WebAssembly.Memory({initial:1, maximum: 1, shared: 1})}})
95
+ })
96
+
97
+ t('compiler: stacked syntax should not be supported', () => {
98
+ throws(t => {
99
+ compile(parse(`
100
+ (func (export "answer") (param i32 i32) (result i32)
101
+ (local.get 0)
102
+ (local.get 1)
103
+ (i32.add)
104
+ )
105
+ `))
106
+ })
107
+ })
108
+
109
+ t('compiler: inline syntax is not supported', () => {
110
+ throws(t => {
111
+ compile(parse(`
112
+ (func $f1 (result i32)
113
+ i32.const 42)
114
+ `))
115
+ })
116
+ })
117
+
118
+
119
+ // wat-compiler
120
+ t('wat-compiler: minimal function', t => {
121
+ run('(module (func (export "answer") (result i32) (i32.const 42)))')
122
+ })
123
+
124
+ t('wat-compiler: function with 1 param', t => {
125
+ run('(func (export "answer") (param i32) (result i32) (local.get 0))')
126
+ })
127
+
128
+ t('wat-compiler: function with 1 param', () => {
129
+ let {answer} = run(`
130
+ (func (export "answer") (param i32) (result i32) (local.get 0))
131
+ `).exports
132
+ is(answer(42), 42)
133
+ })
134
+
135
+ t('wat-compiler: function with 2 params', () => {
136
+ let {answer} = run(`
137
+ (func (export "answer") (param i32 i32) (result i32)
138
+ (i32.add (local.get 0) (local.get 1))
139
+ )
140
+ `).exports
141
+ is(answer(20,22), 42)
142
+ })
143
+
144
+ t('wat-compiler: function with 2 params 2 results', () => {
145
+ let {answer} = run(`
146
+ (func (export "answer") (param i32 i32) (result i32 i32)
147
+ (i32.add (local.get 0) (local.get 1))
148
+ (i32.const 666)
149
+ )
150
+ `).exports
151
+
152
+ is(answer(20,22), [42,666])
153
+ })
154
+
155
+ t('wat-compiler: named function named param', () => {
156
+ let {dbl} = run(`
157
+ (func $dbl (export "dbl") (param $a i32) (result i32)
158
+ (i32.add (local.get $a) (local.get $a))
159
+ )
160
+ `).exports
161
+
162
+ is(dbl(21), 42)
163
+ })
164
+
165
+
166
+ t('wat-compiler: call function direct', () => {
167
+ let {call_function_direct} = run(`
168
+ (func $dbl (param $a i32) (result i32)
169
+ (i32.add (local.get $a) (local.get $a))
170
+ )
171
+ (func (export "call_function_direct") (param $a i32) (result i32)
172
+ (call $dbl (local.get $a))
173
+ )
174
+ `).exports
175
+ is(call_function_direct(333), 666)
176
+ })
177
+
178
+ t('wat-compiler: function param + local', () => {
179
+ let {add} = run(`
180
+ (func (export "add") (param $a i32) (result i32)
181
+ (local $b i32)
182
+ (i32.add (local.get $a) (local.tee $b (i32.const 20)))
183
+ )
184
+ `).exports
185
+
186
+ is(add(22), 42)
187
+ })
188
+
189
+ t('wat-compiler: call function indirect (table)', () => {
190
+ let {call_function_indirect} = run(`
191
+ (type $return_i32 (func (result i32)))
192
+ (table 2 funcref)
193
+ (elem (i32.const 0) $f1 $f2)
194
+ (func $f1 (result i32)
195
+ (i32.const 42))
196
+ (func $f2 (result i32)
197
+ (i32.const 13))
198
+ (func (export "call_function_indirect") (param $a i32) (result i32)
199
+ (call_indirect (type $return_i32) (local.get $a))
200
+ )
201
+ `).exports
202
+
203
+ is(call_function_indirect(0), 42)
204
+ is(call_function_indirect(1), 13)
205
+ })
206
+
207
+ t('wat-compiler: call function indirect (table) non zero indexed ref types', () => {
208
+ let {call_function_indirect} = run(`
209
+ (type $return_i32 (func (result i32)))
210
+ (type $return_i64 (func (result i64)))
211
+ (table 2 funcref)
212
+ (elem (i32.const 0) $f1 $f2)
213
+ (func $xx (result i32)
214
+ (i32.const 42))
215
+ (func $f1 (result i32)
216
+ (i32.const 42))
217
+ (func $f2 (result i32)
218
+ (i32.const 13))
219
+ (func (export "call_function_indirect") (param $a i32) (result i32)
220
+ (call_indirect (type $return_i32) (local.get $a))
221
+ )
222
+ `).exports
223
+
224
+ is(call_function_indirect(0), 42)
225
+ is(call_function_indirect(1), 13)
226
+ })
227
+
228
+ t('wat-compiler: 1 global const (immutable)', () => {
229
+ let {get} = run(`
230
+ (global $answer i32 (i32.const 42))
231
+ (func (export "get") (result i32)
232
+ (global.get $answer)
233
+ )
234
+ `).exports
235
+
236
+ is(get(), 42)
237
+ })
238
+
239
+ t('wat-compiler: 1 global var (mut)', () => {
240
+ let {get} = run(`
241
+ (global $answer (mut i32) (i32.const 42))
242
+ (func (export "get") (result i32)
243
+ (global.get $answer)
244
+ )
245
+ `).exports
246
+
247
+ is(get(), 42)
248
+ })
249
+
250
+ t('wat-compiler: 1 global var (mut) + mutate', () => {
251
+ let {get} = run(`
252
+ (global $answer (mut i32) (i32.const 42))
253
+ (func (export "get") (result i32)
254
+ (global.set $answer (i32.const 777))
255
+ (global.get $answer)
256
+ )
257
+ `).exports
258
+
259
+ is(get(), 777)
260
+ })
261
+
262
+ t('wat-compiler: memory.grow', () => {
263
+ run(`
264
+ (memory 1)
265
+ (func (export "main") (result i32)
266
+ (memory.grow (i32.const 2))
267
+ )
268
+ `)
269
+ })
270
+
271
+ t('wat-compiler: local memory page min 1 - data 1 offset 0 i32', () => {
272
+ let {get} = run(String.raw`
273
+ (memory 1)
274
+ (data (i32.const 0) "\2a")
275
+ (func (export "get") (result i32)
276
+ (i32.load (i32.const 0))
277
+ )
278
+ `).exports
279
+
280
+ is(get(), 42)
281
+ })
282
+
283
+ t('wat-compiler: local memory page min 1 max 2 - data 1 offset 0 i32', () => {
284
+ let {get} = run(String.raw`
285
+ (memory 1 2)
286
+ (data (i32.const 0) "\2a")
287
+ (func (export "get") (result i32)
288
+ (drop (i32.const 1))
289
+ (drop (i32.const 2))
290
+ (i32.load offset=0 align=4 (i32.const 0))
291
+ )
292
+ `).exports
293
+
294
+ is(get(), 42)
295
+ })
296
+
297
+ t('wat-compiler: import function', () => {
298
+ let src = `
299
+ (import "math" "add" (func $add (param i32 i32) (result i32)))
300
+ (func (export "call_imported_function") (result i32)
301
+ (call $add (i32.const 20) (i32.const 22))
302
+ )
303
+ `
304
+ let {call_imported_function} = run(src, {math:{ add: (a, b) => a + b }}).exports
305
+
306
+ is(call_imported_function(), 42)
307
+ })
308
+
309
+ t('wat-compiler: import memory 1', () => {
310
+ run(`
311
+ (import "env" "mem" (memory 1))
312
+ `, {env: {mem: new WebAssembly.Memory({initial:1})}})
313
+ })
314
+
315
+ t('wat-compiler: import memory 1 2', () => {
316
+ run(`
317
+ (import "env" "mem" (memory 1 2))
318
+ `, {env: {mem: new WebAssembly.Memory({initial:1, maximum:1})}})
319
+ })
320
+
321
+ t('wat-compiler: import memory 1 2 shared', () => {
322
+ run(`
323
+ (import "env" "mem" (memory 1 2 shared))
324
+ `, {env: {mem: new WebAssembly.Memory({initial:1, maximum:1, shared:1})}})
325
+ })
326
+
327
+ t('wat-compiler: import memory $foo 1 2 shared', () => run(`
328
+ (import "env" "mem" (memory $foo 1 2 shared))
329
+ `, {env:{mem: new WebAssembly.Memory({initial:1, maximum: 1, shared: 1})}}))
330
+
331
+
332
+ t('wat-compiler: set a start function', () => {
333
+ let src = `
334
+ (global $answer (mut i32) (i32.const 42))
335
+ (start $main)
336
+ (func $main
337
+ (global.set $answer (i32.const 666))
338
+ )
339
+ (func (export "get") (result i32)
340
+ (global.get $answer)
341
+ )
342
+ `
343
+ let {get} = run(src).exports
344
+
345
+ is(get(), 666)
346
+ })
347
+
348
+ t('wat-compiler: if else', () => {
349
+ let src1 = `
350
+ (func $dummy)
351
+ (func (export "foo") (param i32) (result i32)
352
+ (if (result i32) (local.get 0)
353
+ (then (call $dummy) (i32.const 1))
354
+ (else (call $dummy) (i32.const 0))
355
+ )
356
+ )
357
+ `, src2 = `
358
+ (func $dummy)
359
+ (func (export "foo") (param i32) (result i32)
360
+ (local.get 0)
361
+ if (result i32)
362
+ (call $dummy)
363
+ (i32.const 1)
364
+ else
365
+ (call $dummy)
366
+ (i32.const 0)
367
+ end
368
+ )
369
+ `
370
+ // these are identical parts, but we restrict only lisp syntax src1
371
+ // console.log(wat(src1))
372
+ is(wat(src1).buffer, wat(src2).buffer)
373
+ let {foo} = run(src1).exports
374
+ is(foo(0), 0)
375
+ is(foo(1), 1)
376
+ })
377
+
378
+ t.skip('bench: if', () => {
379
+ let src = `(func $dummy)
380
+ (func (export "foo") (param i32) (result i32)
381
+ (if (result i32) (local.get 0)
382
+ (then (call $dummy) (i32.const 1))
383
+ (else (call $dummy) (i32.const 0))
384
+ )
385
+ )`
386
+ is(compile(parse(src)), watCompiler(src))
387
+ let N = 5000
388
+
389
+ console.time('watr')
390
+ for (let i = 0; i < N; i++) compile(parse(src))
391
+ console.timeEnd('watr')
392
+
393
+ console.time('wat-compiler')
394
+ for (let i = 0; i < N; i++) watCompiler(src, {metrics: false})
395
+ console.timeEnd('wat-compiler')
396
+
397
+ console.time('wabt')
398
+ for (let i = 0; i < N; i++) wat(src, {metrics: false})
399
+ console.timeEnd('wabt')
400
+ })
401
+
402
+ t('wat-compiler: block', () => {
403
+ let src = `
404
+ (func (export "answer") (result i32)
405
+ (block (nop))
406
+ (block (result i32) (i32.const 42))
407
+ )
408
+ `
409
+ let {answer} = run(src).exports
410
+ is(answer(), 42)
411
+ is(answer(), 42)
412
+ })
413
+
414
+ t('wat-compiler: block multi', () => {
415
+ let src = `
416
+ (func $dummy)
417
+ (func (export "multi") (result i32)
418
+ (block (call $dummy) (call $dummy) (call $dummy) (call $dummy))
419
+ (block (result i32) (call $dummy) (call $dummy) (call $dummy) (i32.const 8))
420
+ )
421
+ `
422
+ let {multi} = run(src).exports
423
+ is(multi(), 8)
424
+ is(multi(), 8)
425
+
426
+ })
427
+
428
+ t('wat-compiler: br', () => {
429
+ let src = `
430
+ (global $answer (mut i32) (i32.const 42))
431
+ (func $set
432
+ (global.set $answer (i32.const 666))
433
+ )
434
+ (func (export "main") (result i32)
435
+ (block (br 0) (call $set))
436
+ (global.get $answer)
437
+ )
438
+ `
439
+
440
+ let {main} = run(src).exports
441
+ is(main(), 42)
442
+ is(main(), 42)
443
+ })
444
+
445
+ t('wat-compiler: br mid', () => {
446
+ let src = `
447
+ (global $answer (mut i32) (i32.const 42))
448
+ (func $set
449
+ (global.set $answer (i32.const 666))
450
+ )
451
+ (func (export "main") (result i32)
452
+ (block (call $set) (br 0) (global.set $answer (i32.const 0)))
453
+ (global.get $answer)
454
+ )
455
+ `
456
+ let {main} = run(src).exports
457
+ is(main(), 666)
458
+ is(main(), 666)
459
+ })
460
+
461
+ t('wat-compiler: block named + br', () => {
462
+ let src = `
463
+ (global $answer (mut i32) (i32.const 42))
464
+ (func $set
465
+ (global.set $answer (i32.const 666))
466
+ )
467
+ (func (export "main") (result i32)
468
+ (block $outerer ;; 3
469
+ (block $outer ;; 2
470
+ (block $inner ;; 1
471
+ (block $innerer ;; 0
472
+ (call $set)
473
+ (br 1)
474
+ )
475
+ )
476
+ (global.set $answer (i32.const 0))
477
+ )
478
+ )
479
+ (global.get $answer)
480
+ )
481
+ `
482
+ // console.log(wat(src))
483
+ let {main} = run(src).exports
484
+ is(main(), 0)
485
+ is(main(), 0)
486
+ })
487
+
488
+ t('wat-compiler: block named 2 + br', () => {
489
+ let src = `
490
+ (global $answer (mut i32) (i32.const 42))
491
+ (func $set
492
+ (global.set $answer (i32.const 666))
493
+ )
494
+ (func (export "main") (result i32)
495
+ (block $outer
496
+ (block $inner
497
+ (call $set)
498
+ (br $inner)
499
+ )
500
+ (block $inner2
501
+ (global.set $answer (i32.const 444))
502
+ (br $inner2)
503
+ )
504
+ (global.set $answer (i32.const 0))
505
+ )
506
+ (global.get $answer)
507
+ )
508
+ `
509
+ let {main} = run(src).exports
510
+
511
+ is(main(), 0)
512
+ is(main(), 0)
513
+
514
+ })
515
+
516
+ t('wat-compiler: br_table', () => {
517
+ let src = `
518
+ (func (export "main") (param i32) (result i32)
519
+ (block
520
+ (block
521
+ (br_table 1 0 (local.get 0))
522
+ (return (i32.const 21))
523
+ )
524
+ (return (i32.const 20))
525
+ )
526
+ (i32.const 22)
527
+ )
528
+ `
529
+ // console.log(wat(src))
530
+ let {main} = run(src).exports
531
+ is(main(0), 22)
532
+ is(main(1), 20)
533
+ })
534
+
535
+ t('wat-compiler: br_table multiple', () => {
536
+ let src = `
537
+ (func (export "main") (param i32) (result i32)
538
+ (block
539
+ (block
540
+ (block
541
+ (block
542
+ (block
543
+ (br_table 3 2 1 0 4 (local.get 0))
544
+ (return (i32.const 99))
545
+ )
546
+ (return (i32.const 100))
547
+ )
548
+ (return (i32.const 101))
549
+ )
550
+ (return (i32.const 102))
551
+ )
552
+ (return (i32.const 103))
553
+ )
554
+ (i32.const 104)
555
+ )
556
+ `
557
+ let {main} = run(src).exports
558
+
559
+ is(main(0), 103)
560
+ is(main(1), 102)
561
+ is(main(2), 101)
562
+ is(main(3), 100)
563
+ is(main(4), 104)
564
+ })
565
+
566
+ t('wat-compiler: loop', () => {
567
+ let src = `
568
+ (func (export "main") (result i32)
569
+ (loop (nop))
570
+ (loop (result i32) (i32.const 42))
571
+ )
572
+ `
573
+ let {main} = run(src).exports
574
+ is(main(), 42)
575
+ })
576
+
577
+ t('wat-compiler: break-value', () => {
578
+ let src = `
579
+ (func (export "main") (result i32)
580
+ (block (result i32)
581
+ (loop (result i32) (br 1 (i32.const 18)) (br 0) (i32.const 19))
582
+ )
583
+ )
584
+ `
585
+ // console.log(wat(src))
586
+ let {main} = run(src).exports
587
+ is(main(), 18)
588
+ is(main(), 18)
589
+ })
590
+
591
+ t('wat-compiler: br_if', () => {
592
+ let src = `
593
+ (func (export "main") (result i32)
594
+ (block (result i32)
595
+ (loop (result i32)
596
+ (br 1 (i32.const 18))
597
+ (br 1 (i32.const 19))
598
+ (drop (br_if 1 (i32.const 20) (i32.const 0)))
599
+ (drop (br_if 1 (i32.const 20) (i32.const 1)))
600
+ (br 1 (i32.const 21))
601
+ (br_table 1 (i32.const 22) (i32.const 0))
602
+ (br_table 1 1 1 (i32.const 23) (i32.const 1))
603
+ (i32.const 21)
604
+ )
605
+ )
606
+ )
607
+ `
608
+ let {main} = run(src).exports
609
+ is(main(), 18)
610
+ })
611
+
612
+ t('wat-compiler: while', () => {
613
+ let src = `
614
+ (func (export "main") (param i32) (result i32)
615
+ (local i32)
616
+ (local.set 1 (i32.const 1))
617
+ (block
618
+ (loop
619
+ (br_if 1 (i32.eqz (local.get 0)))
620
+ (local.set 1 (i32.mul (local.get 0) (local.get 1)))
621
+ (local.set 0 (i32.sub (local.get 0) (i32.const 1)))
622
+ (br 0)
623
+ )
624
+ )
625
+ (local.get 1)
626
+ )
627
+ `
628
+ let {main} = run(src).exports
629
+ is(main(), 1)
630
+ })
631
+
632
+ t('wat-compiler: select', () => {
633
+ let src = `
634
+ (func (export "main") (result i32)
635
+ (select (loop (result i32) (i32.const 1)) (i32.const 2) (i32.const 3))
636
+ )
637
+ `
638
+ let {main} = run(src).exports
639
+ is(main(), 1)
640
+ })
641
+
642
+ t('wat-compiler: select mid', () => {
643
+ let src = `
644
+ (func (export "main") (result i32)
645
+ (select (i32.const 2) (loop (result i32) (i32.const 1)) (i32.const 3))
646
+ )
647
+ `
648
+ let {main} = run(src).exports
649
+ is(main(), 2)
650
+ })
651
+
652
+ t('wat-compiler: block labels', () => {
653
+ let src = `
654
+ (func (export "main") (result i32)
655
+ (block $exit (result i32)
656
+ (br $exit (i32.const 1))
657
+ (i32.const 0)
658
+ )
659
+ )
660
+ `
661
+ let {main} = run(src).exports
662
+ is(main(), 1)
663
+ })
664
+
665
+ t('wat-compiler: loop labels', () => {
666
+ let src = `
667
+ (func (export "main") (result i32)
668
+ (local $i i32)
669
+ (local.set $i (i32.const 0))
670
+ (block $exit (result i32)
671
+ (loop $cont (result i32)
672
+ (local.set $i (i32.add (local.get $i) (i32.const 1)))
673
+ (if (i32.eq (local.get $i) (i32.const 5))
674
+ (then (br $exit (local.get $i)))
675
+ )
676
+ (br $cont)
677
+ )
678
+ )
679
+ )
680
+ `
681
+ // console.log(wat(src))
682
+ let {main} = run(src).exports
683
+ is(main(), 5)
684
+ })
685
+
686
+ t('wat-compiler: loop labels 2', () => {
687
+ let src = `
688
+ (func (export "main") (result i32)
689
+ (local $i i32)
690
+ (local.set $i (i32.const 0))
691
+ (block $exit (result i32)
692
+ (loop $cont (result i32)
693
+ (local.set $i (i32.add (local.get $i) (i32.const 1)))
694
+ (if (i32.eq (local.get $i) (i32.const 5))
695
+ (then (br $cont))
696
+ )
697
+ (if (i32.eq (local.get $i) (i32.const 8))
698
+ (then (br $exit (local.get $i)))
699
+ )
700
+ (local.set $i (i32.add (local.get $i) (i32.const 1)))
701
+ (br $cont)
702
+ )
703
+ )
704
+ )
705
+ `
706
+ let {main} = run(src).exports
707
+ is(main(), 8)
708
+ })
709
+
710
+ t('wat-compiler: switch', () => {
711
+ let src = `
712
+ (func (export "main") (param i32) (result i32)
713
+ (block $ret (result i32)
714
+ (i32.mul (i32.const 10)
715
+ (block $exit (result i32)
716
+ (block $0
717
+ (block $default
718
+ (block $3
719
+ (block $2
720
+ (block $1
721
+ (br_table $0 $1 $2 $3 $default (local.get 0))
722
+ ) ;; 1
723
+ ) ;; 2
724
+ (br $exit (i32.const 2))
725
+ ) ;; 3
726
+ (br $ret (i32.const 3))
727
+ ) ;; default
728
+ ) ;; 0
729
+ (i32.const 5)
730
+ )
731
+ )
732
+ )
733
+ )
734
+ `
735
+ // console.log(wat(src))
736
+ let {main} = run(src).exports
737
+ is(main(0), 50)
738
+ is(main(1), 20)
739
+ is(main(3), 3)
740
+ })
741
+
742
+ t('wat-compiler: label redefinition', () => {
743
+ let src = `
744
+ (func (export "main") (result i32)
745
+ (block $l1 (result i32)
746
+ (i32.add
747
+ (block $l1 (result i32) (i32.const 2))
748
+ (block $l1 (result i32) (br $l1 (i32.const 3)))
749
+ )
750
+ )
751
+ )
752
+ `
753
+ let {main} = run(src).exports
754
+ is(main(), 5)
755
+ })
756
+
757
+ t('wat-compiler: address', () => {
758
+ let src = `
759
+ (memory 1)
760
+ (data (i32.const 0) "abcdefghijklmnopqrstuvwxyz")
761
+ (func (export "a") (param $i i32) (result i32)
762
+ (i32.load8_u offset=0 (local.get $i)) ;; 97 'a'
763
+ )
764
+ (func (export "b") (param $i i32) (result i32)
765
+ (i32.load8_u offset=1 align=1 (local.get $i)) ;; 98 'b'
766
+ )
767
+ (func (export "ab") (param $i i32) (result i32)
768
+ (i32.load16_s offset=0 (local.get $i)) ;; 25185 'ab'
769
+ )
770
+ (func (export "cd") (param $i i32) (result i32)
771
+ (i32.load16_u offset=2 align=2 (local.get $i)) ;; 25699 'cd'
772
+ )
773
+ (func (export "z") (param $i i32) (result i32)
774
+ (i32.load8_s offset=25 align=1 (local.get $i)) ;; 122 'z'
775
+ )
776
+ `
777
+ // console.log(wat(src))
778
+ let {a,b,ab,cd,z}=run(src).exports
779
+ is(a(), 97)
780
+ is(b(), 98)
781
+ is(ab(), 25185)
782
+ is(cd(), 25699)
783
+ is(z(), 122)
784
+ })
785
+
786
+ t('wat-compiler: int literals', () => {
787
+ let src = `
788
+ (func (export "i32.test") (result i32) (return (i32.const 0x0bAdD00D)))
789
+ (func (export "i32.umax") (result i32) (return (i32.const 0xffffffff)))
790
+ (func (export "i32.smax") (result i32) (return (i32.const 0x7fffffff)))
791
+ (func (export "i32.neg_smax") (result i32) (return (i32.const -0x7fffffff)))
792
+ (func (export "i32.smin") (result i32) (return (i32.const -0x80000000)))
793
+ (func (export "i32.alt_smin") (result i32) (return (i32.const 0x80000000)))
794
+ (func (export "i32.inc_smin") (result i32) (return (i32.add (i32.const -0x80000000) (i32.const 1))))
795
+ (func (export "i32.neg_zero") (result i32) (return (i32.const -0x0)))
796
+ (func (export "i32.not_octal") (result i32) (return (i32.const 010)))
797
+ (func (export "i32.plus_sign") (result i32) (return (i32.const +42)))
798
+ (func (export "i32.unsigned_decimal") (result i32) (return (i32.const 4294967295)))
799
+ (func (export "i64.neg_zero") (result i64) (return (i64.const -0x0)))
800
+ (func (export "i64.not_octal") (result i64) (return (i64.const 010)))
801
+ (func (export "i64.plus_sign") (result i64) (return (i64.const +42)))
802
+ (;func (export "i64.test") (result i64) (return (i64.const 0x0CABBA6E0ba66a6e)))
803
+ (func (export "i64.umax") (result i64) (return (i64.const 0xffffffffffffffff)))
804
+ (func (export "i32-dec-sep1") (result i32) (i32.const 1_000_000))
805
+ (func (export "i32-dec-sep2") (result i32) (i32.const 1_0_0_0))
806
+ (func (export "i32-hex-sep1") (result i32) (i32.const 0xa_0f_00_99))
807
+ (func (export "i32-hex-sep2") (result i32) (i32.const 0x1_a_A_0_f))
808
+ (func (export "i64-dec-sep1") (result i64) (i64.const 1_000_000))
809
+ (func (export "i64-dec-sep2") (result i64) (i64.const 1_0_0_0))
810
+ (func (export "i64-hex-sep2") (result i64) (i64.const 0x1_a_A_0_f))
811
+ (func (export "i64.smax") (result i64) (return (i64.const 0x7fffffffffffffff)))
812
+ (func (export "i64.neg_smax") (result i64) (return (i64.const -0x7fffffffffffffff)))
813
+ (func (export "i64.smin") (result i64) (return (i64.const -0x8000000000000000)))
814
+ (func (export "i64.alt_smin") (result i64) (return (i64.const 0x8000000000000000)))
815
+ (func (export "i64.inc_smin") (result i64) (return (i64.add (i64.const -0x8000000000000000) (i64.const 1))))
816
+ (func (export "i64.unsigned_decimal") (result i64) (return (i64.const 18446744073709551615)))
817
+ (func (export "i64-hex-sep1") (result i64) (i64.const 0xa_f00f_0000_9999);)
818
+ `
819
+ run(src)
820
+ })
821
+
822
+ t('wat-compiler: float literals', () => {
823
+ let src = `
824
+ ;; f32 in decimal format
825
+ (func (export "f32_dec.zero") (result i32) (i32.reinterpret_f32 (f32.const 0.0e0)))
826
+ (func (export "f32_dec.positive_zero") (result i32) (i32.reinterpret_f32 (f32.const +0.0e0)))
827
+ (func (export "f32_dec.negative_zero") (result i32) (i32.reinterpret_f32 (f32.const -0.0e0)))
828
+ (func (export "f32_dec.misc") (result i32) (i32.reinterpret_f32 (f32.const 6.28318548202514648)))
829
+ (func (export "f32_dec.min_positive") (result i32) (i32.reinterpret_f32 (f32.const 1.4013e-45)))
830
+ (func (export "f32_dec.min_normal") (result i32) (i32.reinterpret_f32 (f32.const 1.1754944e-38)))
831
+ (func (export "f32_dec.max_subnormal") (result i32) (i32.reinterpret_f32 (f32.const 1.1754942e-38)))
832
+ (func (export "f32_dec.max_finite") (result i32) (i32.reinterpret_f32 (f32.const 3.4028234e+38)))
833
+ (func (export "f32_dec.trailing_dot") (result i32) (i32.reinterpret_f32 (f32.const 1.e10)))
834
+ ;; https://twitter.com/Archivd/status/994637336506912768
835
+ (func (export "f32_dec.root_beer_float") (result i32) (i32.reinterpret_f32 (f32.const 1.000000119)))
836
+ ;; f64 numbers in decimal format
837
+ (func (export "f64_dec.zero") (result i64) (i64.reinterpret_f64 (f64.const 0.0e0)))
838
+ (func (export "f64_dec.positive_zero") (result i64) (i64.reinterpret_f64 (f64.const +0.0e0)))
839
+ (func (export "f64_dec.negative_zero") (result i64) (i64.reinterpret_f64 (f64.const -0.0e0)))
840
+ (func (export "f64_dec.misc") (result i64) (i64.reinterpret_f64 (f64.const 6.28318530717958623)))
841
+ (func (export "f64_dec.min_positive") (result i64) (i64.reinterpret_f64 (f64.const 4.94066e-324)))
842
+ (func (export "f64_dec.min_normal") (result i64) (i64.reinterpret_f64 (f64.const 2.2250738585072012e-308)))
843
+ (func (export "f64_dec.max_subnormal") (result i64) (i64.reinterpret_f64 (f64.const 2.2250738585072011e-308)))
844
+ (func (export "f64_dec.max_finite") (result i64) (i64.reinterpret_f64 (f64.const 1.7976931348623157e+308)))
845
+ (func (export "f64_dec.trailing_dot") (result i64) (i64.reinterpret_f64 (f64.const 1.e100)))
846
+ ;; https://twitter.com/Archivd/status/994637336506912768
847
+ (func (export "f64_dec.root_beer_float") (result i64) (i64.reinterpret_f64 (f64.const 1.000000119)))
848
+ `
849
+ run(src)
850
+
851
+ let special = `
852
+ ;; f32 special values
853
+ (func (export "f32.nan") (result i32) (i32.reinterpret_f32 (f32.const nan)))
854
+ (func (export "f32.positive_nan") (result i32) (i32.reinterpret_f32 (f32.const +nan)))
855
+ (func (export "f32.negative_nan") (result i32) (i32.reinterpret_f32 (f32.const -nan)))
856
+ (func (export "f32.plain_nan") (result i32) (i32.reinterpret_f32 (f32.const nan:0x400000)))
857
+ (func (export "f32.informally_known_as_plain_snan") (result i32) (i32.reinterpret_f32 (f32.const nan:0x200000)))
858
+ (func (export "f32.all_ones_nan") (result i32) (i32.reinterpret_f32 (f32.const -nan:0x7fffff)))
859
+ (func (export "f32.misc_nan") (result i32) (i32.reinterpret_f32 (f32.const nan:0x012345)))
860
+ (func (export "f32.misc_positive_nan") (result i32) (i32.reinterpret_f32 (f32.const +nan:0x304050)))
861
+ (func (export "f32.misc_negative_nan") (result i32) (i32.reinterpret_f32 (f32.const -nan:0x2abcde)))
862
+ (func (export "f32.infinity") (result i32) (i32.reinterpret_f32 (f32.const inf)))
863
+ (func (export "f32.positive_infinity") (result i32) (i32.reinterpret_f32 (f32.const +inf)))
864
+ (func (export "f32.negative_infinity") (result i32) (i32.reinterpret_f32 (f32.const -inf)))
865
+ ;; f32 numbers
866
+ (;func (export "f32.zero") (result i32) (i32.reinterpret_f32 (f32.const 0x0.0p0)))
867
+ (func (export "f32.positive_zero") (result i32) (i32.reinterpret_f32 (f32.const +0x0.0p0)))
868
+ (func (export "f32.negative_zero") (result i32) (i32.reinterpret_f32 (f32.const -0x0.0p0)))
869
+ (func (export "f32.misc") (result i32) (i32.reinterpret_f32 (f32.const 0x1.921fb6p+2)))
870
+ (func (export "f32.min_positive") (result i32) (i32.reinterpret_f32 (f32.const 0x1p-149)))
871
+ (func (export "f32.min_normal") (result i32) (i32.reinterpret_f32 (f32.const 0x1p-126)))
872
+ (func (export "f32.max_finite") (result i32) (i32.reinterpret_f32 (f32.const 0x1.fffffep+127)))
873
+ (func (export "f32.max_subnormal") (result i32) (i32.reinterpret_f32 (f32.const 0x1.fffffcp-127)))
874
+ (func (export "f32.trailing_dot") (result i32) (i32.reinterpret_f32 (f32.const 0x1.p10));)
875
+
876
+ ;; f64 special values
877
+ (func (export "f64.nan") (result i64) (i64.reinterpret_f64 (f64.const nan)))
878
+ (func (export "f64.positive_nan") (result i64) (i64.reinterpret_f64 (f64.const +nan)))
879
+ (func (export "f64.negative_nan") (result i64) (i64.reinterpret_f64 (f64.const -nan)))
880
+ (func (export "f64.plain_nan") (result i64) (i64.reinterpret_f64 (f64.const nan:0x8000000000000)))
881
+ (func (export "f64.informally_known_as_plain_snan") (result i64) (i64.reinterpret_f64 (f64.const nan:0x4000000000000)))
882
+ (func (export "f64.all_ones_nan") (result i64) (i64.reinterpret_f64 (f64.const -nan:0xfffffffffffff)))
883
+ (func (export "f64.misc_nan") (result i64) (i64.reinterpret_f64 (f64.const nan:0x0123456789abc)))
884
+ (func (export "f64.misc_positive_nan") (result i64) (i64.reinterpret_f64 (f64.const +nan:0x3040506070809)))
885
+ (func (export "f64.misc_negative_nan") (result i64) (i64.reinterpret_f64 (f64.const -nan:0x2abcdef012345)))
886
+ (func (export "f64.infinity") (result i64) (i64.reinterpret_f64 (f64.const inf)))
887
+ (func (export "f64.positive_infinity") (result i64) (i64.reinterpret_f64 (f64.const +inf)))
888
+ (func (export "f64.negative_infinity") (result i64) (i64.reinterpret_f64 (f64.const -inf)))
889
+ ;; f64 numbers
890
+ (;func (export "f64.zero") (result i64) (i64.reinterpret_f64 (f64.const 0x0.0p0)))
891
+ (func (export "f64.positive_zero") (result i64) (i64.reinterpret_f64 (f64.const +0x0.0p0)))
892
+ (func (export "f64.negative_zero") (result i64) (i64.reinterpret_f64 (f64.const -0x0.0p0)))
893
+ (func (export "f64.misc") (result i64) (i64.reinterpret_f64 (f64.const 0x1.921fb54442d18p+2)))
894
+ (func (export "f64.min_positive") (result i64) (i64.reinterpret_f64 (f64.const 0x0.0000000000001p-1022)))
895
+ (func (export "f64.min_normal") (result i64) (i64.reinterpret_f64 (f64.const 0x1p-1022)))
896
+ (func (export "f64.max_subnormal") (result i64) (i64.reinterpret_f64 (f64.const 0x0.fffffffffffffp-1022)))
897
+ (func (export "f64.max_finite") (result i64) (i64.reinterpret_f64 (f64.const 0x1.fffffffffffffp+1023)))
898
+ (func (export "f64.trailing_dot") (result i64) (i64.reinterpret_f64 (f64.const 0x1.p100));)
899
+
900
+ (;func (export "f32-dec-sep1") (result f32) (f32.const 1_000_000))
901
+ (func (export "f32-dec-sep2") (result f32) (f32.const 1_0_0_0))
902
+ (func (export "f32-dec-sep3") (result f32) (f32.const 100_3.141_592))
903
+ (func (export "f32-dec-sep4") (result f32) (f32.const 99e+1_3))
904
+ (func (export "f32-dec-sep5") (result f32) (f32.const 122_000.11_3_54E0_2_3))
905
+ (func (export "f32-hex-sep1") (result f32) (f32.const 0xa_0f_00_99))
906
+ (func (export "f32-hex-sep2") (result f32) (f32.const 0x1_a_A_0_f))
907
+ (func (export "f32-hex-sep3") (result f32) (f32.const 0xa0_ff.f141_a59a))
908
+ (func (export "f32-hex-sep4") (result f32) (f32.const 0xf0P+1_3))
909
+ (func (export "f32-hex-sep5") (result f32) (f32.const 0x2a_f00a.1f_3_eep2_3))
910
+ (func (export "f64-dec-sep1") (result f64) (f64.const 1_000_000))
911
+ (func (export "f64-dec-sep2") (result f64) (f64.const 1_0_0_0))
912
+ (func (export "f64-dec-sep3") (result f64) (f64.const 100_3.141_592))
913
+ (func (export "f64-dec-sep4") (result f64) (f64.const 99e-1_23))
914
+ (func (export "f64-dec-sep5") (result f64) (f64.const 122_000.11_3_54e0_2_3))
915
+ (func (export "f64-hex-sep1") (result f64) (f64.const 0xa_f00f_0000_9999))
916
+ (func (export "f64-hex-sep2") (result f64) (f64.const 0x1_a_A_0_f))
917
+ (func (export "f64-hex-sep3") (result f64) (f64.const 0xa0_ff.f141_a59a))
918
+ (func (export "f64-hex-sep4") (result f64) (f64.const 0xf0P+1_3))
919
+ (func (export "f64-hex-sep5") (result f64) (f64.const 0x2a_f00a.1f_3_eep2_3);)`
920
+
921
+ run(special)
922
+ })
923
+
924
+ t(`export 2 funcs`, () => run(`
925
+ (func (export "value") (result i32)
926
+ (i32.const 42)
927
+ )
928
+ (func (export "another") (result i32)
929
+ (i32.const 666)
930
+ )
931
+ `))
932
+
933
+ t(`exported & unexported function`, () => {
934
+ run(`
935
+ (func (export "value") (result i32)
936
+ (i32.const 42)
937
+ )
938
+ (func (result i32)
939
+ (i32.const 666)
940
+ )
941
+ `)
942
+ })
943
+
944
+ t(`2 different locals`, () => {
945
+ let src = `
946
+ (func (export "value") (result i32)
947
+ (local i32)
948
+ (local i64)
949
+ (i32.const 42)
950
+ )
951
+ `
952
+ let {value} = run(src).exports
953
+ is(value(), 42)
954
+ })
955
+
956
+ t(`3 locals [i32, i64, i32] (disjointed)`, () => {
957
+ let src = `
958
+ (func (export "value") (result i32)
959
+ (local i32)
960
+ (local i64)
961
+ (local i32)
962
+ (i32.const 42)
963
+ )
964
+ `
965
+ let {value} = run(src).exports
966
+ is(value(), 42)
967
+ })
968
+
969
+ t(`3 locals [i32, i32, i64] (joined)`, () => {
970
+ let src = `
971
+ (func (export "value") (result i32)
972
+ (local i32)
973
+ (local i32)
974
+ (local i64)
975
+ (i32.const 42)
976
+ )
977
+ `
978
+ let {value} = run(src).exports
979
+ is(value(), 42)
980
+ })
981
+
982
+ t('call function indirect (table)', () => {
983
+ let src = `
984
+ (type $return_i32 (func (result i32)))
985
+ (table 2 funcref)
986
+ (elem (i32.const 0) $f1 $f2)
987
+ (func $f1 (result i32)
988
+ (i32.const 42))
989
+ (func $f2 (result i32)
990
+ (i32.const 13))
991
+ (func (export "call_function_indirect") (param $a i32) (result i32)
992
+ (call_indirect (type $return_i32) (local.get $a))
993
+ )
994
+ `
995
+ let {call_function_indirect} = run(src).exports
996
+
997
+ is(call_function_indirect(0), 42)
998
+ is(call_function_indirect(1), 13)
999
+ })
1000
+
1001
+ t('call function indirect (table) non zero indexed ref types', () => {
1002
+ let src = `
1003
+ (type $return_i64 (func (result i64)))
1004
+ (type $return_i32 (func (result i32)))
1005
+ (table 2 funcref)
1006
+ (elem (i32.const 0) $f1 $f2)
1007
+ (func $xx (result i64)
1008
+ (i64.const 42))
1009
+ (func $f1 (result i32)
1010
+ (i32.const 42))
1011
+ (func $f2 (result i32)
1012
+ (i32.const 13))
1013
+ (func (export "call_function_indirect") (param $a i32) (result i32)
1014
+ (call_indirect (type $return_i32) (local.get $a))
1015
+ )
1016
+ `
1017
+ let {call_function_indirect} = run(src).exports
1018
+
1019
+ is(call_function_indirect(0), 42)
1020
+ is(call_function_indirect(1), 13)
1021
+ })
1022
+
1023
+
1024
+
1025
+ // e2e
1026
+ // TODO: examples
1027
+ const e2e = [
1028
+ 'malloc',
1029
+ 'brownian',
1030
+ 'containers',
1031
+ 'quine',
1032
+ 'fire',
1033
+ 'metaball',
1034
+ 'raytrace',
1035
+ 'snake',
1036
+ 'maze',
1037
+ 'dino',
1038
+ 'raycast',
1039
+ ]
1040
+
1041
+ e2e.forEach(name => {
1042
+ t.todo('wat-compiler: e2e: '+name, () =>
1043
+ fetch('/test/fixtures/e2e/'+name+'.wat')
1044
+ .then(res => res.text())
1045
+ .then(text => buffers(text))
1046
+ .then(([exp,act]) => hexAssertEqual(exp,act)))
1047
+ })
1048
+
1049
+
1050
+
1051
+
1052
+ let wabt = await Wabt()
1053
+
1054
+ const hex = (str, ...fields) =>
1055
+ new Uint8Array(
1056
+ String.raw.call(null, str, fields)
1057
+ .trim()
1058
+ .replace(/;[^\n]*/g,'')
1059
+ .split(/[\s\n]+/)
1060
+ .filter(n => n !== '')
1061
+ .map(n => parseInt(n, 16))
1062
+ )
1063
+
1064
+ function wat (code, config) {
1065
+ let metrics = config ? config.metrics : true
1066
+ const parsed = wabt.parseWat('inline', code, {})
1067
+ metrics && console.time('wabt build')
1068
+ const binary = parsed.toBinary({
1069
+ log: true,
1070
+ canonicalize_lebs: true,
1071
+ relocatable: false,
1072
+ write_debug_names: false,
1073
+ })
1074
+ parsed.destroy()
1075
+ metrics && console.timeEnd('wabt build')
1076
+
1077
+ return binary
1078
+ }
1079
+
1080
+ // run test case against wabt, return instance
1081
+ // FIXME: rename to something more meaningful? testCase?
1082
+ const run = (src, importObj) => {
1083
+ let tree = parse(src)
1084
+ const freeze = node => Array.isArray(node) && (Object.freeze(node), node.forEach(freeze))
1085
+ freeze(tree)
1086
+ let buffer = compile(tree)
1087
+ is(buffer, wat(src).buffer)
1088
+ const mod = new WebAssembly.Module(buffer)
1089
+ return new WebAssembly.Instance(mod, importObj)
1090
+ }