porffor 0.0.0-8c0bdaa → 0.0.0-a2afb57
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/README.md +49 -15
- package/c +0 -0
- package/c.exe +0 -0
- package/compiler/2c.js +354 -0
- package/compiler/builtins.js +0 -1
- package/compiler/codeGen.js +710 -159
- package/compiler/decompile.js +3 -3
- package/compiler/embedding.js +9 -5
- package/compiler/encoding.js +4 -2
- package/compiler/index.js +55 -4
- package/compiler/opt.js +49 -23
- package/compiler/parse.js +1 -0
- package/compiler/prototype.js +172 -34
- package/compiler/sections.js +47 -6
- package/compiler/wrap.js +12 -1
- package/cool.exe +0 -0
- package/g +0 -0
- package/g.exe +0 -0
- package/hi.c +37 -0
- package/out +0 -0
- package/out.exe +0 -0
- package/package.json +1 -1
- package/publish.js +3 -1
- package/r.js +39 -0
- package/rhemyn/README.md +37 -0
- package/rhemyn/compile.js +214 -0
- package/rhemyn/parse.js +321 -0
- package/rhemyn/test/parse.js +59 -0
- package/runner/index.js +54 -33
- package/runner/info.js +37 -2
- package/runner/repl.js +8 -13
- package/runner/transform.js +5 -26
- package/runner/version.js +10 -0
- package/t.js +31 -0
- package/tmp.c +61 -0
package/compiler/codeGen.js
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
import { Blocktype, Opcodes, Valtype, PageSize, ValtypeSize } from "./wasmSpec.js";
|
2
|
-
import { signedLEB128, unsignedLEB128 } from "./encoding.js";
|
2
|
+
import { ieee754_binary64, signedLEB128, unsignedLEB128 } from "./encoding.js";
|
3
3
|
import { operatorOpcode } from "./expression.js";
|
4
4
|
import { BuiltinFuncs, BuiltinVars, importedFuncs, NULL, UNDEFINED } from "./builtins.js";
|
5
5
|
import { PrototypeFuncs } from "./prototype.js";
|
6
|
-
import { number, i32x4 } from "./embedding.js";
|
6
|
+
import { number, i32x4, enforceOneByte, enforceTwoBytes, enforceFourBytes, enforceEightBytes } from "./embedding.js";
|
7
7
|
import parse from "./parse.js";
|
8
|
+
import * as Rhemyn from "../rhemyn/compile.js";
|
8
9
|
|
9
10
|
let globals = {};
|
10
11
|
let globalInd = 0;
|
@@ -35,7 +36,14 @@ const debug = str => {
|
|
35
36
|
};
|
36
37
|
|
37
38
|
const todo = msg => {
|
38
|
-
|
39
|
+
class TodoError extends Error {
|
40
|
+
constructor(message) {
|
41
|
+
super(message);
|
42
|
+
this.name = 'TodoError';
|
43
|
+
}
|
44
|
+
}
|
45
|
+
|
46
|
+
throw new TodoError(`todo: ${msg}`);
|
39
47
|
|
40
48
|
const code = [];
|
41
49
|
|
@@ -101,6 +109,9 @@ const generate = (scope, decl, global = false, name = undefined) => {
|
|
101
109
|
case 'WhileStatement':
|
102
110
|
return generateWhile(scope, decl);
|
103
111
|
|
112
|
+
case 'ForOfStatement':
|
113
|
+
return generateForOf(scope, decl);
|
114
|
+
|
104
115
|
case 'BreakStatement':
|
105
116
|
return generateBreak(scope, decl);
|
106
117
|
|
@@ -141,45 +152,65 @@ const generate = (scope, decl, global = false, name = undefined) => {
|
|
141
152
|
|
142
153
|
return [];
|
143
154
|
|
144
|
-
case 'TaggedTemplateExpression':
|
145
|
-
|
146
|
-
|
155
|
+
case 'TaggedTemplateExpression': {
|
156
|
+
const funcs = {
|
157
|
+
asm: str => {
|
158
|
+
let out = [];
|
147
159
|
|
148
|
-
|
149
|
-
|
160
|
+
for (const line of str.split('\n')) {
|
161
|
+
const asm = line.trim().split(';;')[0].split(' ');
|
162
|
+
if (asm[0] === '') continue; // blank
|
150
163
|
|
151
|
-
|
152
|
-
|
153
|
-
|
164
|
+
if (asm[0] === 'local') {
|
165
|
+
const [ name, idx, type ] = asm.slice(1);
|
166
|
+
scope.locals[name] = { idx: parseInt(idx), type: Valtype[type] };
|
167
|
+
continue;
|
168
|
+
}
|
154
169
|
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
}
|
170
|
+
if (asm[0] === 'returns') {
|
171
|
+
scope.returns = asm.slice(1).map(x => Valtype[x]);
|
172
|
+
continue;
|
173
|
+
}
|
160
174
|
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
175
|
+
if (asm[0] === 'memory') {
|
176
|
+
allocPage('asm instrinsic');
|
177
|
+
// todo: add to store/load offset insts
|
178
|
+
continue;
|
179
|
+
}
|
165
180
|
|
166
|
-
|
167
|
-
|
168
|
-
allocPage('asm instrinsic');
|
169
|
-
// todo: add to store/load offset insts
|
170
|
-
continue;
|
171
|
-
}
|
181
|
+
let inst = Opcodes[asm[0].replace('.', '_')];
|
182
|
+
if (!inst) throw new Error(`inline asm: inst ${asm[0]} not found`);
|
172
183
|
|
173
|
-
|
174
|
-
|
184
|
+
if (!Array.isArray(inst)) inst = [ inst ];
|
185
|
+
const immediates = asm.slice(1).map(x => parseInt(x));
|
186
|
+
|
187
|
+
out.push([ ...inst, ...immediates ]);
|
188
|
+
}
|
175
189
|
|
176
|
-
|
177
|
-
|
190
|
+
return out;
|
191
|
+
},
|
178
192
|
|
179
|
-
|
193
|
+
__internal_print_type: str => {
|
194
|
+
const type = getType(scope, str) - TYPES.number;
|
195
|
+
|
196
|
+
return [
|
197
|
+
...number(type),
|
198
|
+
[ Opcodes.call, importedFuncs.print ],
|
199
|
+
|
200
|
+
// newline
|
201
|
+
...number(10),
|
202
|
+
[ Opcodes.call, importedFuncs.printChar ]
|
203
|
+
];
|
204
|
+
}
|
180
205
|
}
|
181
206
|
|
182
|
-
|
207
|
+
const name = decl.tag.name;
|
208
|
+
// hack for inline asm
|
209
|
+
if (!funcs[name]) return todo('tagged template expressions not implemented');
|
210
|
+
|
211
|
+
const str = decl.quasi.quasis[0].value.raw;
|
212
|
+
return funcs[name](str);
|
213
|
+
}
|
183
214
|
|
184
215
|
default:
|
185
216
|
return todo(`no generation for ${decl.type}!`);
|
@@ -278,7 +309,7 @@ const generateReturn = (scope, decl) => {
|
|
278
309
|
];
|
279
310
|
}
|
280
311
|
|
281
|
-
|
312
|
+
scope.returnType = getNodeType(scope, decl.argument);
|
282
313
|
|
283
314
|
return [
|
284
315
|
...generate(scope, decl.argument),
|
@@ -295,11 +326,13 @@ const localTmp = (scope, name, type = valtypeBinary) => {
|
|
295
326
|
return idx;
|
296
327
|
};
|
297
328
|
|
298
|
-
const
|
329
|
+
const isIntOp = op => op[0] >= 0xb7 && op[0] <= 0xba;
|
330
|
+
|
331
|
+
const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
299
332
|
const checks = {
|
300
|
-
'||':
|
301
|
-
'&&':
|
302
|
-
|
333
|
+
'||': falsy,
|
334
|
+
'&&': truthy,
|
335
|
+
'??': nullish
|
303
336
|
};
|
304
337
|
|
305
338
|
if (!checks[op]) return todo(`logic operator ${op} not implemented yet`);
|
@@ -307,10 +340,37 @@ const performLogicOp = (scope, op, left, right) => {
|
|
307
340
|
// generic structure for {a} OP {b}
|
308
341
|
// -->
|
309
342
|
// _ = {a}; if (OP_CHECK) {b} else _
|
343
|
+
|
344
|
+
// if we can, use int tmp and convert at the end to help prevent unneeded conversions
|
345
|
+
// (like if we are in an if condition - very common)
|
346
|
+
const leftIsInt = isIntOp(left[left.length - 1]);
|
347
|
+
const rightIsInt = isIntOp(right[right.length - 1]);
|
348
|
+
|
349
|
+
const canInt = leftIsInt && rightIsInt;
|
350
|
+
|
351
|
+
if (canInt) {
|
352
|
+
// remove int -> float conversions from left and right
|
353
|
+
left.pop();
|
354
|
+
right.pop();
|
355
|
+
|
356
|
+
return [
|
357
|
+
...left,
|
358
|
+
[ Opcodes.local_tee, localTmp(scope, 'logictmpi', Valtype.i32) ],
|
359
|
+
...checks[op](scope, [], leftType, true),
|
360
|
+
[ Opcodes.if, Valtype.i32 ],
|
361
|
+
...right,
|
362
|
+
[ Opcodes.else ],
|
363
|
+
[ Opcodes.local_get, localTmp(scope, 'logictmpi', Valtype.i32) ],
|
364
|
+
[ Opcodes.end ],
|
365
|
+
Opcodes.i32_from
|
366
|
+
];
|
367
|
+
}
|
368
|
+
|
310
369
|
return [
|
311
370
|
...left,
|
312
371
|
[ Opcodes.local_tee, localTmp(scope, 'logictmp') ],
|
313
|
-
...checks[op],
|
372
|
+
...checks[op](scope, [], leftType),
|
373
|
+
Opcodes.i32_to,
|
314
374
|
[ Opcodes.if, valtypeBinary ],
|
315
375
|
...right,
|
316
376
|
[ Opcodes.else ],
|
@@ -325,15 +385,16 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
325
385
|
// todo: optimize by looking up names in arrays and using that if exists?
|
326
386
|
// todo: optimize this if using literals/known lengths?
|
327
387
|
|
328
|
-
scope.memory = true;
|
329
|
-
|
330
|
-
const pointer = arrays.get(name ?? '$undeclared');
|
331
|
-
|
332
388
|
const rightPointer = localTmp(scope, 'concat_right_pointer', Valtype.i32);
|
333
389
|
const rightLength = localTmp(scope, 'concat_right_length', Valtype.i32);
|
334
390
|
const leftLength = localTmp(scope, 'concat_left_length', Valtype.i32);
|
335
391
|
|
392
|
+
const aotWFA = process.argv.includes('-aot-well-formed-string-approximation');
|
393
|
+
if (aotWFA) addVarMeta(name, { wellFormed: undefined });
|
394
|
+
|
336
395
|
if (assign) {
|
396
|
+
const pointer = arrays.get(name ?? '$undeclared');
|
397
|
+
|
337
398
|
return [
|
338
399
|
// setup right
|
339
400
|
...right,
|
@@ -384,15 +445,12 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
384
445
|
|
385
446
|
const leftPointer = localTmp(scope, 'concat_left_pointer', Valtype.i32);
|
386
447
|
|
387
|
-
|
448
|
+
// alloc/assign array
|
449
|
+
const [ , pointer ] = makeArray(scope, {
|
388
450
|
rawElements: new Array(0)
|
389
451
|
}, global, name, true, 'i16');
|
390
452
|
|
391
453
|
return [
|
392
|
-
// setup new/out array
|
393
|
-
...newOut,
|
394
|
-
[ Opcodes.drop ],
|
395
|
-
|
396
454
|
// setup left
|
397
455
|
...left,
|
398
456
|
Opcodes.i32_to_u,
|
@@ -458,89 +516,259 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
458
516
|
];
|
459
517
|
};
|
460
518
|
|
461
|
-
const
|
519
|
+
const compareStrings = (scope, left, right) => {
|
520
|
+
// todo: this should be rewritten into a built-in/func: String.prototype.concat
|
521
|
+
// todo: convert left and right to strings if not
|
522
|
+
// todo: optimize by looking up names in arrays and using that if exists?
|
523
|
+
// todo: optimize this if using literals/known lengths?
|
524
|
+
|
525
|
+
const leftPointer = localTmp(scope, 'compare_left_pointer', Valtype.i32);
|
526
|
+
const leftLength = localTmp(scope, 'compare_left_length', Valtype.i32);
|
527
|
+
const rightPointer = localTmp(scope, 'compare_right_pointer', Valtype.i32);
|
528
|
+
const rightLength = localTmp(scope, 'compare_right_length', Valtype.i32);
|
529
|
+
|
530
|
+
const index = localTmp(scope, 'compare_index', Valtype.i32);
|
531
|
+
const indexEnd = localTmp(scope, 'compare_index_end', Valtype.i32);
|
532
|
+
|
533
|
+
return [
|
534
|
+
// setup left
|
535
|
+
...left,
|
536
|
+
Opcodes.i32_to_u,
|
537
|
+
[ Opcodes.local_tee, leftPointer ],
|
538
|
+
|
539
|
+
// setup right
|
540
|
+
...right,
|
541
|
+
Opcodes.i32_to_u,
|
542
|
+
[ Opcodes.local_tee, rightPointer ],
|
543
|
+
|
544
|
+
// fast path: check leftPointer == rightPointer
|
545
|
+
// use if (block) for everything after to "return" a value early
|
546
|
+
[ Opcodes.i32_ne ],
|
547
|
+
[ Opcodes.if, Valtype.i32 ],
|
548
|
+
|
549
|
+
// get lengths
|
550
|
+
[ Opcodes.local_get, leftPointer ],
|
551
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128(0) ],
|
552
|
+
[ Opcodes.local_tee, leftLength ],
|
553
|
+
|
554
|
+
[ Opcodes.local_get, rightPointer ],
|
555
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128(0) ],
|
556
|
+
[ Opcodes.local_tee, rightLength ],
|
557
|
+
|
558
|
+
// fast path: check leftLength != rightLength
|
559
|
+
[ Opcodes.i32_ne ],
|
560
|
+
[ Opcodes.if, Blocktype.void ],
|
561
|
+
...number(0, Valtype.i32),
|
562
|
+
[ Opcodes.br, 1 ],
|
563
|
+
[ Opcodes.end ],
|
564
|
+
|
565
|
+
// no fast path for length = 0 as it would probably be slower for most of the time?
|
566
|
+
|
567
|
+
// setup index end as length * sizeof i16 (2)
|
568
|
+
// we do this instead of having to do mul/div each iter for perf™
|
569
|
+
[ Opcodes.local_get, leftLength ],
|
570
|
+
...number(ValtypeSize.i16, Valtype.i32),
|
571
|
+
[ Opcodes.i32_mul ],
|
572
|
+
[ Opcodes.local_set, indexEnd ],
|
573
|
+
|
574
|
+
// iterate over each char and check if eq
|
575
|
+
[ Opcodes.loop, Blocktype.void ],
|
576
|
+
|
577
|
+
// fetch left
|
578
|
+
[ Opcodes.local_get, index ],
|
579
|
+
[ Opcodes.local_get, leftPointer ],
|
580
|
+
[ Opcodes.i32_add ],
|
581
|
+
[ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(ValtypeSize.i32) ],
|
582
|
+
|
583
|
+
// fetch right
|
584
|
+
[ Opcodes.local_get, index ],
|
585
|
+
[ Opcodes.local_get, rightPointer ],
|
586
|
+
[ Opcodes.i32_add ],
|
587
|
+
[ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(ValtypeSize.i32) ],
|
588
|
+
|
589
|
+
// not equal, "return" false
|
590
|
+
[ Opcodes.i32_ne ],
|
591
|
+
[ Opcodes.if, Blocktype.void ],
|
592
|
+
...number(0, Valtype.i32),
|
593
|
+
[ Opcodes.br, 2 ],
|
594
|
+
[ Opcodes.end ],
|
595
|
+
|
596
|
+
// index += sizeof i16 (2)
|
597
|
+
[ Opcodes.local_get, index ],
|
598
|
+
...number(ValtypeSize.i16, Valtype.i32),
|
599
|
+
[ Opcodes.i32_add ],
|
600
|
+
[ Opcodes.local_tee, index ],
|
601
|
+
|
602
|
+
// if index != index end (length * sizeof 16), loop
|
603
|
+
[ Opcodes.local_get, indexEnd ],
|
604
|
+
[ Opcodes.i32_ne ],
|
605
|
+
[ Opcodes.br_if, 0 ],
|
606
|
+
[ Opcodes.end ],
|
607
|
+
|
608
|
+
// no failed checks, so true!
|
609
|
+
...number(1, Valtype.i32),
|
610
|
+
|
611
|
+
// pointers match, so true
|
612
|
+
[ Opcodes.else ],
|
613
|
+
...number(1, Valtype.i32),
|
614
|
+
[ Opcodes.end ],
|
615
|
+
|
616
|
+
// convert i32 result to valtype
|
617
|
+
// do not do as automatically added by binary exp gen for equality ops
|
618
|
+
// Opcodes.i32_from_u
|
619
|
+
];
|
620
|
+
};
|
621
|
+
|
622
|
+
const truthy = (scope, wasm, type, int = false) => {
|
462
623
|
// arrays are always truthy
|
463
624
|
if (type === TYPES._array) return [
|
464
625
|
...wasm,
|
465
626
|
[ Opcodes.drop ],
|
466
|
-
number(
|
627
|
+
...number(1, int ? Valtype.i32 : valtypeBinary)
|
467
628
|
];
|
468
629
|
|
469
630
|
if (type === TYPES.string) {
|
470
|
-
// if "" (length = 0)
|
631
|
+
// if not "" (length = 0)
|
471
632
|
return [
|
472
633
|
// pointer
|
473
634
|
...wasm,
|
635
|
+
Opcodes.i32_to_u,
|
474
636
|
|
475
637
|
// get length
|
476
638
|
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
|
477
639
|
|
478
|
-
// if length
|
479
|
-
[ Opcodes.i32_eqz ],
|
480
|
-
Opcodes.
|
481
|
-
|
640
|
+
// if length != 0
|
641
|
+
/* [ Opcodes.i32_eqz ],
|
642
|
+
[ Opcodes.i32_eqz ], */
|
643
|
+
...(int ? [] : [ Opcodes.i32_from_u ])
|
644
|
+
];
|
482
645
|
}
|
483
646
|
|
484
|
-
// if
|
647
|
+
// if != 0
|
485
648
|
return [
|
486
649
|
...wasm,
|
487
650
|
|
488
|
-
|
489
|
-
Opcodes.
|
651
|
+
/* Opcodes.eqz,
|
652
|
+
[ Opcodes.i32_eqz ],
|
653
|
+
Opcodes.i32_from */
|
490
654
|
];
|
491
655
|
};
|
492
656
|
|
493
|
-
const
|
657
|
+
const falsy = (scope, wasm, type, int = false) => {
|
494
658
|
// arrays are always truthy
|
495
659
|
if (type === TYPES._array) return [
|
496
660
|
...wasm,
|
497
661
|
[ Opcodes.drop ],
|
498
|
-
number(
|
662
|
+
...number(0, int ? Valtype.i32 : valtypeBinary)
|
499
663
|
];
|
500
664
|
|
501
665
|
if (type === TYPES.string) {
|
502
|
-
// if
|
666
|
+
// if "" (length = 0)
|
503
667
|
return [
|
504
668
|
// pointer
|
505
669
|
...wasm,
|
670
|
+
Opcodes.i32_to_u,
|
506
671
|
|
507
672
|
// get length
|
508
673
|
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
|
509
674
|
|
510
|
-
// if length
|
511
|
-
|
512
|
-
[ Opcodes.
|
513
|
-
Opcodes.i32_from_u
|
675
|
+
// if length == 0
|
676
|
+
[ Opcodes.i32_eqz ],
|
677
|
+
...(int ? [] : [ Opcodes.i32_from_u ])
|
514
678
|
]
|
515
679
|
}
|
516
680
|
|
517
|
-
// if
|
681
|
+
// if = 0
|
518
682
|
return [
|
519
683
|
...wasm,
|
520
684
|
|
521
|
-
|
522
|
-
|
523
|
-
|
685
|
+
...(int ? [ [ Opcodes.i32_eqz ] ] : [ ...Opcodes.eqz, Opcodes.i32_from_u ])
|
686
|
+
];
|
687
|
+
};
|
688
|
+
|
689
|
+
const nullish = (scope, wasm, type, int = false) => {
|
690
|
+
// undefined
|
691
|
+
if (type === TYPES.undefined) return [
|
692
|
+
...wasm,
|
693
|
+
[ Opcodes.drop ],
|
694
|
+
...number(1, int ? Valtype.i32 : valtypeBinary)
|
695
|
+
];
|
696
|
+
|
697
|
+
// null (if object and = "0")
|
698
|
+
if (type === TYPES.object) return [
|
699
|
+
...wasm,
|
700
|
+
...(int ? [ [ Opcodes.i32_eqz ] ] : [ ...Opcodes.eqz, Opcodes.i32_from_u ])
|
701
|
+
];
|
702
|
+
|
703
|
+
// not
|
704
|
+
return [
|
705
|
+
...wasm,
|
706
|
+
[ Opcodes.drop ],
|
707
|
+
...number(0, int ? Valtype.i32 : valtypeBinary)
|
524
708
|
];
|
525
709
|
};
|
526
710
|
|
527
|
-
const performOp = (scope, op, left, right, leftType, rightType, _global = false, _name = '$
|
711
|
+
const performOp = (scope, op, left, right, leftType, rightType, _global = false, _name = '$undeclared', assign = false) => {
|
528
712
|
if (op === '||' || op === '&&' || op === '??') {
|
529
|
-
return performLogicOp(scope, op, left, right);
|
713
|
+
return performLogicOp(scope, op, left, right, leftType, rightType);
|
530
714
|
}
|
531
715
|
|
716
|
+
if (codeLog && (!leftType || !rightType)) log('codegen', 'untracked type in op', op, _name, '\n' + new Error().stack.split('\n').slice(1).join('\n'));
|
717
|
+
|
718
|
+
const eqOp = ['==', '===', '!=', '!==', '>', '>=', '<', '<='].includes(op);
|
719
|
+
|
720
|
+
if (leftType && rightType && (
|
721
|
+
// if strict (in)equal and known types mismatch, return false (===)/true (!==)
|
722
|
+
((op === '===' || op === '!==') && leftType !== rightType) ||
|
723
|
+
|
724
|
+
// if equality op and an operand is undefined, return false
|
725
|
+
(eqOp && leftType === TYPES.undefined ^ rightType === TYPES.undefined)
|
726
|
+
)) {
|
727
|
+
// undefined == null
|
728
|
+
if (((leftType === TYPES.undefined && rightType === TYPES.object) || (leftType === TYPES.object && rightType === TYPES.undefined)) && (op === '==' || op === '!=')) return [
|
729
|
+
...(leftType === TYPES.object ? left : right),
|
730
|
+
...Opcodes.eqz,
|
731
|
+
...(op === '!=' ? [ [ Opcodes.i32_eqz ] ] : [])
|
732
|
+
];
|
733
|
+
|
734
|
+
return [
|
735
|
+
...left,
|
736
|
+
[ Opcodes.drop ],
|
737
|
+
|
738
|
+
...right,
|
739
|
+
[ Opcodes.drop ],
|
740
|
+
|
741
|
+
// return true (!=/!==) or false (else)
|
742
|
+
...number(op === '!=' || op === '!==' ? 1 : 0, Valtype.i32)
|
743
|
+
];
|
744
|
+
}
|
745
|
+
|
746
|
+
// todo: niche null hell with 0
|
747
|
+
|
532
748
|
if (leftType === TYPES.string || rightType === TYPES.string) {
|
533
749
|
if (op === '+') {
|
534
750
|
// string concat (a + b)
|
535
751
|
return concatStrings(scope, left, right, _global, _name, assign);
|
536
752
|
}
|
537
753
|
|
538
|
-
//
|
539
|
-
if (!
|
754
|
+
// not an equality op, NaN
|
755
|
+
if (!eqOp) return number(NaN);
|
540
756
|
|
541
757
|
// else leave bool ops
|
542
|
-
// todo: convert string to number if string and number
|
543
|
-
// todo: string
|
758
|
+
// todo: convert string to number if string and number/bool
|
759
|
+
// todo: string (>|>=|<|<=) string
|
760
|
+
|
761
|
+
// string comparison
|
762
|
+
if (op === '===' || op === '==') {
|
763
|
+
return compareStrings(scope, left, right);
|
764
|
+
}
|
765
|
+
|
766
|
+
if (op === '!==' || op === '!=') {
|
767
|
+
return [
|
768
|
+
...compareStrings(scope, left, right),
|
769
|
+
[ Opcodes.i32_eqz ]
|
770
|
+
];
|
771
|
+
}
|
544
772
|
}
|
545
773
|
|
546
774
|
let ops = operatorOpcode[valtype][op];
|
@@ -570,16 +798,14 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
570
798
|
};
|
571
799
|
|
572
800
|
const generateBinaryExp = (scope, decl, _global, _name) => {
|
573
|
-
const out =
|
574
|
-
...performOp(scope, decl.operator, generate(scope, decl.left), generate(scope, decl.right), getNodeType(scope, decl.left), getNodeType(scope, decl.right), _global, _name)
|
575
|
-
];
|
801
|
+
const out = performOp(scope, decl.operator, generate(scope, decl.left), generate(scope, decl.right), getNodeType(scope, decl.left), getNodeType(scope, decl.right), _global, _name);
|
576
802
|
|
577
803
|
if (valtype !== 'i32' && ['==', '===', '!=', '!==', '>', '>=', '<', '<='].includes(decl.operator)) out.push(Opcodes.i32_from_u);
|
578
804
|
|
579
805
|
return out;
|
580
806
|
};
|
581
807
|
|
582
|
-
const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits, returns, returnType,
|
808
|
+
const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits, returns, returnType, localNames = [], globalNames = [] }) => {
|
583
809
|
const existing = funcs.find(x => x.name === name);
|
584
810
|
if (existing) return existing;
|
585
811
|
|
@@ -615,7 +841,6 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
|
|
615
841
|
returns,
|
616
842
|
returnType: TYPES[returnType ?? 'number'],
|
617
843
|
wasm,
|
618
|
-
memory,
|
619
844
|
internal: true,
|
620
845
|
index: currentFuncIndex++
|
621
846
|
};
|
@@ -634,7 +859,7 @@ const includeBuiltin = (scope, builtin) => {
|
|
634
859
|
};
|
635
860
|
|
636
861
|
const generateLogicExp = (scope, decl) => {
|
637
|
-
return performLogicOp(scope, decl.operator, generate(scope, decl.left), generate(scope, decl.right));
|
862
|
+
return performLogicOp(scope, decl.operator, generate(scope, decl.left), generate(scope, decl.right), getNodeType(scope, decl.left), getNodeType(scope, decl.right));
|
638
863
|
};
|
639
864
|
|
640
865
|
const TYPES = {
|
@@ -648,7 +873,8 @@ const TYPES = {
|
|
648
873
|
bigint: 0xffffffffffff7,
|
649
874
|
|
650
875
|
// these are not "typeof" types but tracked internally
|
651
|
-
_array:
|
876
|
+
_array: 0xfffffffffff0f,
|
877
|
+
_regexp: 0xfffffffffff1f
|
652
878
|
};
|
653
879
|
|
654
880
|
const TYPE_NAMES = {
|
@@ -682,6 +908,9 @@ const getType = (scope, _name) => {
|
|
682
908
|
|
683
909
|
const getNodeType = (scope, node) => {
|
684
910
|
if (node.type === 'Literal') {
|
911
|
+
if (['number', 'boolean', 'string', 'undefined', 'object', 'function', 'symbol', 'bigint'].includes(node.value)) return TYPES.number;
|
912
|
+
if (node.regex) return TYPES._regexp;
|
913
|
+
|
685
914
|
return TYPES[typeof node.value];
|
686
915
|
}
|
687
916
|
|
@@ -696,7 +925,7 @@ const getNodeType = (scope, node) => {
|
|
696
925
|
if (node.type === 'CallExpression' || node.type === 'NewExpression') {
|
697
926
|
const name = node.callee.name;
|
698
927
|
const func = funcs.find(x => x.name === name);
|
699
|
-
if (func) return func.returnType
|
928
|
+
if (func) return func.returnType;
|
700
929
|
|
701
930
|
if (builtinFuncs[name]) return TYPES[builtinFuncs[name].returnType ?? 'number'];
|
702
931
|
if (internalConstrs[name]) return internalConstrs[name].type;
|
@@ -715,15 +944,18 @@ const getNodeType = (scope, node) => {
|
|
715
944
|
|
716
945
|
// literal.func()
|
717
946
|
if (!name && node.callee.type === 'MemberExpression') {
|
947
|
+
if (node.callee.object.regex) {
|
948
|
+
const funcName = node.callee.property.name;
|
949
|
+
return Rhemyn[funcName] ? TYPES.boolean : TYPES.undefined;
|
950
|
+
}
|
951
|
+
|
718
952
|
const baseType = getNodeType(scope, node.callee.object);
|
719
953
|
|
720
954
|
const func = node.callee.property.name;
|
721
955
|
protoFunc = prototypeFuncs[baseType]?.[func];
|
722
956
|
}
|
723
957
|
|
724
|
-
if (protoFunc) return protoFunc.returnType
|
725
|
-
|
726
|
-
return TYPES.number;
|
958
|
+
if (protoFunc) return protoFunc.returnType;
|
727
959
|
}
|
728
960
|
|
729
961
|
if (node.type === 'ExpressionStatement') {
|
@@ -755,14 +987,16 @@ const getNodeType = (scope, node) => {
|
|
755
987
|
|
756
988
|
if (objectType === TYPES.string && node.computed) return TYPES.string;
|
757
989
|
}
|
758
|
-
|
759
|
-
// default to number
|
760
|
-
return TYPES.number;
|
761
990
|
};
|
762
991
|
|
763
992
|
const generateLiteral = (scope, decl, global, name) => {
|
764
993
|
if (decl.value === null) return number(NULL);
|
765
994
|
|
995
|
+
if (decl.regex) {
|
996
|
+
scope.regex[name] = decl.regex;
|
997
|
+
return number(1);
|
998
|
+
}
|
999
|
+
|
766
1000
|
switch (typeof decl.value) {
|
767
1001
|
case 'number':
|
768
1002
|
return number(decl.value);
|
@@ -784,15 +1018,41 @@ const generateLiteral = (scope, decl, global, name) => {
|
|
784
1018
|
case 'bigint': return number(TYPES.bigint);
|
785
1019
|
}
|
786
1020
|
|
1021
|
+
const aotWFA = process.argv.includes('-aot-well-formed-string-approximation');
|
1022
|
+
let wellFormed = aotWFA ? true : undefined;
|
1023
|
+
|
787
1024
|
const str = decl.value;
|
788
1025
|
const rawElements = new Array(str.length);
|
1026
|
+
let j = 0;
|
789
1027
|
for (let i = 0; i < str.length; i++) {
|
790
1028
|
rawElements[i] = str.charCodeAt(i);
|
1029
|
+
|
1030
|
+
if (wellFormed) {
|
1031
|
+
// check if surrogate
|
1032
|
+
if ((str.charCodeAt(j) & 0xF800) === 0xD800) {
|
1033
|
+
// unpaired trailing surrogate
|
1034
|
+
if (str.charCodeAt(j) >= 0xDC00) {
|
1035
|
+
wellFormed = false;
|
1036
|
+
}
|
1037
|
+
|
1038
|
+
// unpaired leading surrogate
|
1039
|
+
// if (++j >= str.length || (str.charCodeAt(j) & 0xFC00) != 0xDC00) {
|
1040
|
+
if ((str.charCodeAt(++j) & 0xFC00) != 0xDC00) {
|
1041
|
+
wellFormed = false;
|
1042
|
+
}
|
1043
|
+
}
|
1044
|
+
|
1045
|
+
j++;
|
1046
|
+
}
|
791
1047
|
}
|
792
1048
|
|
1049
|
+
// console.log(wellFormed, str);
|
1050
|
+
|
1051
|
+
if (aotWFA) addVarMeta(name, { wellFormed });
|
1052
|
+
|
793
1053
|
return makeArray(scope, {
|
794
1054
|
rawElements
|
795
|
-
}, global, name, false, 'i16');
|
1055
|
+
}, global, name, false, 'i16')[0];
|
796
1056
|
|
797
1057
|
default:
|
798
1058
|
return todo(`cannot generate literal of type ${typeof decl.value}`);
|
@@ -802,7 +1062,8 @@ const generateLiteral = (scope, decl, global, name) => {
|
|
802
1062
|
const countLeftover = wasm => {
|
803
1063
|
let count = 0, depth = 0;
|
804
1064
|
|
805
|
-
for (
|
1065
|
+
for (let i = 0; i < wasm.length; i++) {
|
1066
|
+
const inst = wasm[i];
|
806
1067
|
if (depth === 0 && (inst[0] === Opcodes.if || inst[0] === Opcodes.block || inst[0] === Opcodes.loop)) {
|
807
1068
|
if (inst[0] === Opcodes.if) count--;
|
808
1069
|
if (inst[1] !== Blocktype.void) count++;
|
@@ -823,6 +1084,8 @@ const countLeftover = wasm => {
|
|
823
1084
|
} else count--;
|
824
1085
|
if (func) count += func.returns.length;
|
825
1086
|
} else count--;
|
1087
|
+
|
1088
|
+
// console.log(count, decompile([ inst ]).slice(0, -1));
|
826
1089
|
}
|
827
1090
|
|
828
1091
|
return count;
|
@@ -906,7 +1169,7 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
906
1169
|
}
|
907
1170
|
|
908
1171
|
let out = [];
|
909
|
-
let protoFunc, protoName, baseType, baseName
|
1172
|
+
let protoFunc, protoName, baseType, baseName;
|
910
1173
|
// ident.func()
|
911
1174
|
if (name && name.startsWith('__')) {
|
912
1175
|
const spl = name.slice(2).split('_');
|
@@ -921,6 +1184,25 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
921
1184
|
|
922
1185
|
// literal.func()
|
923
1186
|
if (!name && decl.callee.type === 'MemberExpression') {
|
1187
|
+
// megahack for /regex/.func()
|
1188
|
+
if (decl.callee.object.regex) {
|
1189
|
+
const funcName = decl.callee.property.name;
|
1190
|
+
const func = Rhemyn[funcName](decl.callee.object.regex.pattern, currentFuncIndex++);
|
1191
|
+
|
1192
|
+
funcIndex[func.name] = func.index;
|
1193
|
+
funcs.push(func);
|
1194
|
+
|
1195
|
+
return [
|
1196
|
+
// make string arg
|
1197
|
+
...generate(scope, decl.arguments[0]),
|
1198
|
+
|
1199
|
+
// call regex func
|
1200
|
+
Opcodes.i32_to_u,
|
1201
|
+
[ Opcodes.call, func.index ],
|
1202
|
+
Opcodes.i32_from
|
1203
|
+
];
|
1204
|
+
}
|
1205
|
+
|
924
1206
|
baseType = getNodeType(scope, decl.callee.object);
|
925
1207
|
|
926
1208
|
const func = decl.callee.property.name;
|
@@ -929,11 +1211,36 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
929
1211
|
|
930
1212
|
out = generate(scope, decl.callee.object);
|
931
1213
|
out.push([ Opcodes.drop ]);
|
1214
|
+
|
1215
|
+
baseName = [...arrays.keys()].pop();
|
932
1216
|
}
|
933
1217
|
|
934
|
-
if (
|
935
|
-
|
1218
|
+
if (protoName && baseType === TYPES.string && Rhemyn[protoName]) {
|
1219
|
+
const func = Rhemyn[protoName](decl.arguments[0].regex.pattern, currentFuncIndex++);
|
1220
|
+
|
1221
|
+
funcIndex[func.name] = func.index;
|
1222
|
+
funcs.push(func);
|
1223
|
+
|
1224
|
+
const pointer = arrays.get(baseName);
|
1225
|
+
const [ local, isGlobal ] = lookupName(scope, baseName);
|
1226
|
+
|
1227
|
+
return [
|
1228
|
+
...out,
|
936
1229
|
|
1230
|
+
...(pointer == null ? [
|
1231
|
+
[ isGlobal ? Opcodes.global_get : Opcodes.local_get, local.idx ],
|
1232
|
+
Opcodes.i32_to_u,
|
1233
|
+
] : [
|
1234
|
+
...number(pointer, Valtype.i32)
|
1235
|
+
]),
|
1236
|
+
|
1237
|
+
// call regex func
|
1238
|
+
[ Opcodes.call, func.index ],
|
1239
|
+
Opcodes.i32_from
|
1240
|
+
];
|
1241
|
+
}
|
1242
|
+
|
1243
|
+
if (protoFunc) {
|
937
1244
|
let pointer = arrays.get(baseName);
|
938
1245
|
|
939
1246
|
if (pointer == null) {
|
@@ -941,10 +1248,9 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
941
1248
|
if (codeLog) log('codegen', 'cloning unknown dynamic pointer');
|
942
1249
|
|
943
1250
|
// register array
|
944
|
-
makeArray(scope, {
|
1251
|
+
0, [ , pointer ] = makeArray(scope, {
|
945
1252
|
rawElements: new Array(0)
|
946
1253
|
}, _global, baseName, true, baseType === TYPES.string ? 'i16' : valtype);
|
947
|
-
pointer = arrays.get(baseName);
|
948
1254
|
|
949
1255
|
const [ local, isGlobal ] = lookupName(scope, baseName);
|
950
1256
|
|
@@ -962,29 +1268,39 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
962
1268
|
if (protoFunc.noArgRetLength && decl.arguments.length === 0) return arrayUtil.getLength(pointer)
|
963
1269
|
|
964
1270
|
let protoLocal = protoFunc.local ? localTmp(scope, `__${TYPE_NAMES[baseType]}_${protoName}_tmp`, protoFunc.local) : -1;
|
1271
|
+
let protoLocal2 = protoFunc.local2 ? localTmp(scope, `__${TYPE_NAMES[baseType]}_${protoName}_tmp2`, protoFunc.local2) : -1;
|
965
1272
|
|
966
1273
|
// use local for cached i32 length as commonly used
|
967
1274
|
let lengthLocal = localTmp(scope, '__proto_length_cache', Valtype.i32);
|
968
1275
|
|
1276
|
+
let lengthI32CacheUsed = false;
|
1277
|
+
|
1278
|
+
const protoOut = protoFunc(pointer, {
|
1279
|
+
getCachedI32: () => {
|
1280
|
+
lengthI32CacheUsed = true;
|
1281
|
+
return [ [ Opcodes.local_get, lengthLocal ] ]
|
1282
|
+
},
|
1283
|
+
setCachedI32: () => [ [ Opcodes.local_set, lengthLocal ] ],
|
1284
|
+
get: () => arrayUtil.getLength(pointer),
|
1285
|
+
getI32: () => arrayUtil.getLengthI32(pointer),
|
1286
|
+
set: value => arrayUtil.setLength(pointer, value),
|
1287
|
+
setI32: value => arrayUtil.setLengthI32(pointer, value)
|
1288
|
+
}, generate(scope, decl.arguments[0] ?? DEFAULT_VALUE), protoLocal, protoLocal2, (length, itemType) => {
|
1289
|
+
return makeArray(scope, {
|
1290
|
+
rawElements: new Array(length)
|
1291
|
+
}, _global, _name, true, itemType);
|
1292
|
+
}, varMetadata.get(baseName));
|
1293
|
+
|
969
1294
|
return [
|
970
1295
|
...out,
|
971
1296
|
|
972
|
-
...
|
973
|
-
|
1297
|
+
...(!lengthI32CacheUsed ? [] : [
|
1298
|
+
...arrayUtil.getLengthI32(pointer),
|
1299
|
+
[ Opcodes.local_set, lengthLocal ],
|
1300
|
+
]),
|
974
1301
|
|
975
1302
|
[ Opcodes.block, valtypeBinary ],
|
976
|
-
...
|
977
|
-
cachedI32: [ [ Opcodes.local_get, lengthLocal ] ],
|
978
|
-
get: arrayUtil.getLength(pointer),
|
979
|
-
getI32: arrayUtil.getLengthI32(pointer),
|
980
|
-
set: value => arrayUtil.setLength(pointer, value),
|
981
|
-
setI32: value => arrayUtil.setLengthI32(pointer, value)
|
982
|
-
}, generate(scope, decl.arguments[0] ?? DEFAULT_VALUE), protoLocal, (length, itemType) => {
|
983
|
-
const out = makeArray(scope, {
|
984
|
-
rawElements: new Array(length)
|
985
|
-
}, _global, _name, true, itemType);
|
986
|
-
return [ out, arrays.get(_name ?? '$undeclared') ];
|
987
|
-
}),
|
1303
|
+
...protoOut,
|
988
1304
|
[ Opcodes.end ]
|
989
1305
|
];
|
990
1306
|
}
|
@@ -1041,11 +1357,10 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1041
1357
|
args = args.slice(0, func.params.length);
|
1042
1358
|
}
|
1043
1359
|
|
1044
|
-
if (func && func.memory) scope.memory = true;
|
1045
1360
|
if (func && func.throws) scope.throws = true;
|
1046
1361
|
|
1047
1362
|
for (const arg of args) {
|
1048
|
-
out.
|
1363
|
+
out = out.concat(generate(scope, arg));
|
1049
1364
|
}
|
1050
1365
|
|
1051
1366
|
out.push([ Opcodes.call, idx ]);
|
@@ -1056,8 +1371,8 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1056
1371
|
const generateNew = (scope, decl, _global, _name) => {
|
1057
1372
|
// hack: basically treat this as a normal call for builtins for now
|
1058
1373
|
const name = mapName(decl.callee.name);
|
1059
|
-
if (internalConstrs[name]) return internalConstrs[name].generate(scope, decl, _global, _name);
|
1060
|
-
if (!builtinFuncs[name]) return todo(`new statement is not supported yet (new ${unhackName(name)})`);
|
1374
|
+
if (internalConstrs[name] && !internalConstrs[name].notConstr) return internalConstrs[name].generate(scope, decl, _global, _name);
|
1375
|
+
if (!builtinFuncs[name]) return todo(`new statement is not supported yet`); // return todo(`new statement is not supported yet (new ${unhackName(name)})`);
|
1061
1376
|
|
1062
1377
|
return generateCall(scope, decl, _global, _name);
|
1063
1378
|
};
|
@@ -1074,12 +1389,12 @@ const unhackName = name => {
|
|
1074
1389
|
};
|
1075
1390
|
|
1076
1391
|
const generateVar = (scope, decl) => {
|
1077
|
-
|
1392
|
+
let out = [];
|
1078
1393
|
|
1079
1394
|
const topLevel = scope.name === 'main';
|
1080
1395
|
|
1081
1396
|
// global variable if in top scope (main) and var ..., or if wanted
|
1082
|
-
const global = decl.kind === 'var';
|
1397
|
+
const global = topLevel || decl._bare; // decl.kind === 'var';
|
1083
1398
|
const target = global ? globals : scope.locals;
|
1084
1399
|
|
1085
1400
|
for (const x of decl.declarations) {
|
@@ -1116,7 +1431,7 @@ const generateVar = (scope, decl) => {
|
|
1116
1431
|
|
1117
1432
|
// x.init ??= DEFAULT_VALUE;
|
1118
1433
|
if (x.init) {
|
1119
|
-
out.
|
1434
|
+
out = out.concat(generate(scope, x.init, global, name));
|
1120
1435
|
|
1121
1436
|
// if our value is the result of a function, infer the type from that func's return value
|
1122
1437
|
if (out[out.length - 1][0] === Opcodes.call) {
|
@@ -1165,8 +1480,6 @@ const generateAssign = (scope, decl) => {
|
|
1165
1480
|
const name = decl.left.object.name;
|
1166
1481
|
const pointer = arrays.get(name);
|
1167
1482
|
|
1168
|
-
scope.memory = true;
|
1169
|
-
|
1170
1483
|
const aotPointer = pointer != null;
|
1171
1484
|
|
1172
1485
|
const newValueTmp = localTmp(scope, '__length_setter_tmp');
|
@@ -1187,13 +1500,60 @@ const generateAssign = (scope, decl) => {
|
|
1187
1500
|
];
|
1188
1501
|
}
|
1189
1502
|
|
1503
|
+
const op = decl.operator.slice(0, -1) || '=';
|
1504
|
+
|
1505
|
+
// arr[i] | str[i]
|
1506
|
+
if (decl.left.type === 'MemberExpression' && decl.left.computed) {
|
1507
|
+
const name = decl.left.object.name;
|
1508
|
+
const pointer = arrays.get(name);
|
1509
|
+
|
1510
|
+
const aotPointer = pointer != null;
|
1511
|
+
|
1512
|
+
const newValueTmp = localTmp(scope, '__member_setter_val_tmp');
|
1513
|
+
const pointerTmp = op === '=' ? -1 : localTmp(scope, '__member_setter_ptr_tmp', Valtype.i32);
|
1514
|
+
|
1515
|
+
const parentType = getNodeType(scope, decl.left.object);
|
1516
|
+
|
1517
|
+
return [
|
1518
|
+
...(aotPointer ? [] : [
|
1519
|
+
...generate(scope, decl.left.object),
|
1520
|
+
Opcodes.i32_to_u
|
1521
|
+
]),
|
1522
|
+
|
1523
|
+
// get index as valtype
|
1524
|
+
...generate(scope, decl.left.property),
|
1525
|
+
|
1526
|
+
// convert to i32 and turn into byte offset by * valtypeSize (4 for i32, 8 for i64/f64)
|
1527
|
+
Opcodes.i32_to_u,
|
1528
|
+
...number(ValtypeSize[valtype], Valtype.i32),
|
1529
|
+
[ Opcodes.i32_mul ],
|
1530
|
+
...(aotPointer ? [] : [ [ Opcodes.i32_add ] ]),
|
1531
|
+
...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
|
1532
|
+
|
1533
|
+
...(op === '=' ? generate(scope, decl.right, false, name) : performOp(scope, op, [
|
1534
|
+
[ Opcodes.local_get, pointerTmp ],
|
1535
|
+
[ Opcodes.load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
|
1536
|
+
], generate(scope, decl.right), parentType === TYPES._array ? TYPES.number : TYPES.string, getNodeType(scope, decl.right), false, name, true)),
|
1537
|
+
[ Opcodes.local_tee, newValueTmp ],
|
1538
|
+
|
1539
|
+
...(parentType === TYPES._array ? [
|
1540
|
+
[ Opcodes.store, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
|
1541
|
+
] : [
|
1542
|
+
Opcodes.i32_to_u,
|
1543
|
+
[ StoreOps.i16, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
|
1544
|
+
]),
|
1545
|
+
|
1546
|
+
[ Opcodes.local_get, newValueTmp ]
|
1547
|
+
];
|
1548
|
+
}
|
1549
|
+
|
1190
1550
|
const [ local, isGlobal ] = lookupName(scope, name);
|
1191
1551
|
|
1192
1552
|
if (local === undefined) {
|
1193
|
-
// todo: this should be a
|
1553
|
+
// todo: this should be a sloppy mode only thing
|
1194
1554
|
|
1195
1555
|
// only allow = for this
|
1196
|
-
if (
|
1556
|
+
if (op !== '=') return internalThrow(scope, 'ReferenceError', `${unhackName(name)} is not defined`);
|
1197
1557
|
|
1198
1558
|
if (builtinVars[name]) {
|
1199
1559
|
// just return rhs (eg `NaN = 2`)
|
@@ -1202,13 +1562,15 @@ const generateAssign = (scope, decl) => {
|
|
1202
1562
|
|
1203
1563
|
// set global and return (eg a = 2)
|
1204
1564
|
return [
|
1205
|
-
...generateVar(scope, { kind: 'var', declarations: [ { id: { name }, init: decl.right } ] }),
|
1565
|
+
...generateVar(scope, { kind: 'var', _bare: true, declarations: [ { id: { name }, init: decl.right } ] }),
|
1206
1566
|
[ Opcodes.global_get, globals[name].idx ]
|
1207
1567
|
];
|
1208
1568
|
}
|
1209
1569
|
|
1210
|
-
|
1211
|
-
|
1570
|
+
typeStates[name] = getNodeType(scope, decl.right);
|
1571
|
+
|
1572
|
+
if (op === '=') {
|
1573
|
+
// typeStates[name] = getNodeType(scope, decl.right);
|
1212
1574
|
|
1213
1575
|
return [
|
1214
1576
|
...generate(scope, decl.right, isGlobal, name),
|
@@ -1217,8 +1579,26 @@ const generateAssign = (scope, decl) => {
|
|
1217
1579
|
];
|
1218
1580
|
}
|
1219
1581
|
|
1582
|
+
if (op === '||' || op === '&&' || op === '??') {
|
1583
|
+
// todo: is this needed?
|
1584
|
+
// for logical assignment ops, it is not left @= right ~= left = left @ right
|
1585
|
+
// instead, left @ (left = right)
|
1586
|
+
// eg, x &&= y ~= x && (x = y)
|
1587
|
+
|
1588
|
+
return [
|
1589
|
+
...performOp(scope, op, [
|
1590
|
+
[ isGlobal ? Opcodes.global_get : Opcodes.local_get, local.idx ]
|
1591
|
+
], [
|
1592
|
+
...generate(scope, decl.right),
|
1593
|
+
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
|
1594
|
+
[ isGlobal ? Opcodes.global_get : Opcodes.local_get, local.idx ]
|
1595
|
+
], getType(scope, name), getNodeType(scope, decl.right), isGlobal, name, true),
|
1596
|
+
[ isGlobal ? Opcodes.global_get : Opcodes.local_get, local.idx ]
|
1597
|
+
];
|
1598
|
+
}
|
1599
|
+
|
1220
1600
|
return [
|
1221
|
-
...performOp(scope,
|
1601
|
+
...performOp(scope, op, [ [ isGlobal ? Opcodes.global_get : Opcodes.local_get, local.idx ] ], generate(scope, decl.right), getType(scope, name), getNodeType(scope, decl.right), isGlobal, name, true),
|
1222
1602
|
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
|
1223
1603
|
[ isGlobal ? Opcodes.global_get : Opcodes.local_get, local.idx ]
|
1224
1604
|
];
|
@@ -1245,13 +1625,14 @@ const generateUnary = (scope, decl) => {
|
|
1245
1625
|
|
1246
1626
|
case '!':
|
1247
1627
|
// !=
|
1248
|
-
return falsy(scope, generate(scope, decl.argument));
|
1628
|
+
return falsy(scope, generate(scope, decl.argument), getNodeType(scope, decl.argument));
|
1249
1629
|
|
1250
1630
|
case '~':
|
1631
|
+
// todo: does not handle Infinity properly (should convert to 0) (but opt const converting saves us sometimes)
|
1251
1632
|
return [
|
1252
1633
|
...generate(scope, decl.argument),
|
1253
1634
|
Opcodes.i32_to,
|
1254
|
-
[ Opcodes.i32_const, signedLEB128(-1) ],
|
1635
|
+
[ Opcodes.i32_const, ...signedLEB128(-1) ],
|
1255
1636
|
[ Opcodes.i32_xor ],
|
1256
1637
|
Opcodes.i32_from
|
1257
1638
|
];
|
@@ -1289,7 +1670,7 @@ const generateUnary = (scope, decl) => {
|
|
1289
1670
|
return out;
|
1290
1671
|
|
1291
1672
|
case 'typeof':
|
1292
|
-
const type = getNodeType(scope, decl.argument);
|
1673
|
+
const type = getNodeType(scope, decl.argument) ?? TYPES.number;
|
1293
1674
|
|
1294
1675
|
// for custom types, just return object
|
1295
1676
|
if (type > 0xffffffffffff7) return number(TYPES.object);
|
@@ -1332,7 +1713,7 @@ const generateUpdate = (scope, decl) => {
|
|
1332
1713
|
};
|
1333
1714
|
|
1334
1715
|
const generateIf = (scope, decl) => {
|
1335
|
-
const out = truthy(scope, generate(scope, decl.test), decl.test);
|
1716
|
+
const out = truthy(scope, generate(scope, decl.test), getNodeType(scope, decl.test));
|
1336
1717
|
|
1337
1718
|
out.push(Opcodes.i32_to, [ Opcodes.if, Blocktype.void ]);
|
1338
1719
|
depth.push('if');
|
@@ -1422,9 +1803,116 @@ const generateWhile = (scope, decl) => {
|
|
1422
1803
|
return out;
|
1423
1804
|
};
|
1424
1805
|
|
1806
|
+
const generateForOf = (scope, decl) => {
|
1807
|
+
const out = [];
|
1808
|
+
|
1809
|
+
const rightType = getNodeType(scope, decl.right);
|
1810
|
+
const valtypeSize = rightType === TYPES._array ? ValtypeSize[valtype] : ValtypeSize.i16; // presume array (:()
|
1811
|
+
|
1812
|
+
// todo: for of inside for of might fuck up?
|
1813
|
+
const pointer = localTmp(scope, 'forof_base_pointer', Valtype.i32);
|
1814
|
+
const length = localTmp(scope, 'forof_length', Valtype.i32);
|
1815
|
+
const counter = localTmp(scope, 'forof_counter', Valtype.i32);
|
1816
|
+
|
1817
|
+
out.push(
|
1818
|
+
// set pointer as right
|
1819
|
+
...generate(scope, decl.right),
|
1820
|
+
Opcodes.i32_to_u,
|
1821
|
+
[ Opcodes.local_set, pointer ],
|
1822
|
+
|
1823
|
+
// get length
|
1824
|
+
[ Opcodes.local_get, pointer ],
|
1825
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
|
1826
|
+
[ Opcodes.local_set, length ]
|
1827
|
+
);
|
1828
|
+
|
1829
|
+
out.push([ Opcodes.loop, Blocktype.void ]);
|
1830
|
+
depth.push('forof');
|
1831
|
+
|
1832
|
+
// setup local for left
|
1833
|
+
generate(scope, decl.left);
|
1834
|
+
|
1835
|
+
const leftName = decl.left.declarations[0].id.name;
|
1836
|
+
|
1837
|
+
// set type for local
|
1838
|
+
typeStates[leftName] = rightType === TYPES._array ? TYPES.number : TYPES.string;
|
1839
|
+
|
1840
|
+
const [ local, isGlobal ] = lookupName(scope, leftName);
|
1841
|
+
|
1842
|
+
if (rightType === TYPES._array) { // array
|
1843
|
+
out.push(
|
1844
|
+
[ Opcodes.local_get, pointer ],
|
1845
|
+
[ Opcodes.load, Math.log2(valtypeSize) - 1, ...unsignedLEB128(ValtypeSize.i32) ]
|
1846
|
+
);
|
1847
|
+
} else { // string
|
1848
|
+
const [ newOut, newPointer ] = makeArray(scope, {
|
1849
|
+
rawElements: new Array(1)
|
1850
|
+
}, isGlobal, leftName, true, 'i16');
|
1851
|
+
|
1852
|
+
out.push(
|
1853
|
+
// setup new/out array
|
1854
|
+
...newOut,
|
1855
|
+
[ Opcodes.drop ],
|
1856
|
+
|
1857
|
+
...number(0, Valtype.i32), // base 0 for store after
|
1858
|
+
|
1859
|
+
// load current string ind {arg}
|
1860
|
+
[ Opcodes.local_get, pointer ],
|
1861
|
+
[ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(ValtypeSize.i32) ],
|
1862
|
+
|
1863
|
+
// store to new string ind 0
|
1864
|
+
[ Opcodes.i32_store16, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
|
1865
|
+
|
1866
|
+
// return new string (page)
|
1867
|
+
...number(newPointer)
|
1868
|
+
);
|
1869
|
+
}
|
1870
|
+
|
1871
|
+
// set left value
|
1872
|
+
out.push([ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ]);
|
1873
|
+
|
1874
|
+
out.push(
|
1875
|
+
[ Opcodes.block, Blocktype.void ],
|
1876
|
+
[ Opcodes.block, Blocktype.void ]
|
1877
|
+
);
|
1878
|
+
depth.push('block');
|
1879
|
+
depth.push('block');
|
1880
|
+
|
1881
|
+
out.push(
|
1882
|
+
...generate(scope, decl.body),
|
1883
|
+
[ Opcodes.end ]
|
1884
|
+
);
|
1885
|
+
depth.pop();
|
1886
|
+
|
1887
|
+
out.push(
|
1888
|
+
// increment iter pointer by valtype size
|
1889
|
+
[ Opcodes.local_get, pointer ],
|
1890
|
+
...number(valtypeSize, Valtype.i32),
|
1891
|
+
[ Opcodes.i32_add ],
|
1892
|
+
[ Opcodes.local_set, pointer ],
|
1893
|
+
|
1894
|
+
// increment counter by 1
|
1895
|
+
[ Opcodes.local_get, counter ],
|
1896
|
+
...number(1, Valtype.i32),
|
1897
|
+
[ Opcodes.i32_add ],
|
1898
|
+
[ Opcodes.local_tee, counter ],
|
1899
|
+
|
1900
|
+
// loop if counter != length
|
1901
|
+
[ Opcodes.local_get, length ],
|
1902
|
+
[ Opcodes.i32_ne ],
|
1903
|
+
[ Opcodes.br_if, 1 ],
|
1904
|
+
|
1905
|
+
[ Opcodes.end ], [ Opcodes.end ]
|
1906
|
+
);
|
1907
|
+
depth.pop();
|
1908
|
+
depth.pop();
|
1909
|
+
|
1910
|
+
return out;
|
1911
|
+
};
|
1912
|
+
|
1425
1913
|
const getNearestLoop = () => {
|
1426
1914
|
for (let i = depth.length - 1; i >= 0; i--) {
|
1427
|
-
if (depth[i] === 'while' || depth[i] === 'for') return i;
|
1915
|
+
if (depth[i] === 'while' || depth[i] === 'for' || depth[i] === 'forof') return i;
|
1428
1916
|
}
|
1429
1917
|
|
1430
1918
|
return -1;
|
@@ -1508,13 +1996,22 @@ const generateAssignPat = (scope, decl) => {
|
|
1508
1996
|
};
|
1509
1997
|
|
1510
1998
|
let pages = new Map();
|
1511
|
-
const allocPage = reason => {
|
1512
|
-
if (pages.has(reason)) return pages.get(reason);
|
1999
|
+
const allocPage = (reason, type) => {
|
2000
|
+
if (pages.has(reason)) return pages.get(reason).ind;
|
2001
|
+
|
2002
|
+
const ind = pages.size;
|
2003
|
+
pages.set(reason, { ind, type });
|
2004
|
+
|
2005
|
+
if (allocLog) log('alloc', `allocated new page of memory (${ind}) | ${reason} (type: ${type})`);
|
2006
|
+
|
2007
|
+
return ind;
|
2008
|
+
};
|
1513
2009
|
|
1514
|
-
|
1515
|
-
pages.
|
2010
|
+
const freePage = reason => {
|
2011
|
+
const { ind } = pages.get(reason);
|
2012
|
+
pages.delete(reason);
|
1516
2013
|
|
1517
|
-
if (
|
2014
|
+
if (allocLog) log('alloc', `freed page of memory (${ind}) | ${reason}`);
|
1518
2015
|
|
1519
2016
|
return ind;
|
1520
2017
|
};
|
@@ -1528,7 +2025,7 @@ const itemTypeToValtype = {
|
|
1528
2025
|
i16: 'i32'
|
1529
2026
|
};
|
1530
2027
|
|
1531
|
-
const
|
2028
|
+
const StoreOps = {
|
1532
2029
|
i32: Opcodes.i32_store,
|
1533
2030
|
i64: Opcodes.i64_store,
|
1534
2031
|
f64: Opcodes.f64_store,
|
@@ -1537,13 +2034,31 @@ const storeOps = {
|
|
1537
2034
|
i16: Opcodes.i32_store16
|
1538
2035
|
};
|
1539
2036
|
|
2037
|
+
let data = [];
|
2038
|
+
|
2039
|
+
const compileBytes = (val, itemType, signed = true) => {
|
2040
|
+
switch (itemType) {
|
2041
|
+
case 'i8': return [ val % 256 ];
|
2042
|
+
case 'i16': return [ val % 256, Math.floor(val / 256) ];
|
2043
|
+
|
2044
|
+
case 'i32':
|
2045
|
+
case 'i64':
|
2046
|
+
return enforceFourBytes(signedLEB128(val));
|
2047
|
+
|
2048
|
+
case 'f64': return ieee754_binary64(val);
|
2049
|
+
}
|
2050
|
+
};
|
2051
|
+
|
1540
2052
|
const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false, itemType = valtype) => {
|
1541
2053
|
const out = [];
|
1542
2054
|
|
2055
|
+
let firstAssign = false;
|
1543
2056
|
if (!arrays.has(name) || name === '$undeclared') {
|
2057
|
+
firstAssign = true;
|
2058
|
+
|
1544
2059
|
// todo: can we just have 1 undeclared array? probably not? but this is not really memory efficient
|
1545
2060
|
const uniqueName = name === '$undeclared' ? name + Math.random().toString().slice(2) : name;
|
1546
|
-
arrays.set(name, allocPage(`${itemType === 'i16' ? 'string' : 'array'}: ${uniqueName}
|
2061
|
+
arrays.set(name, allocPage(`${itemType === 'i16' ? 'string' : 'array'}: ${uniqueName}`, itemType) * pageSize);
|
1547
2062
|
}
|
1548
2063
|
|
1549
2064
|
const pointer = arrays.get(name);
|
@@ -1551,8 +2066,29 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
1551
2066
|
const useRawElements = !!decl.rawElements;
|
1552
2067
|
const elements = useRawElements ? decl.rawElements : decl.elements;
|
1553
2068
|
|
2069
|
+
const valtype = itemTypeToValtype[itemType];
|
1554
2070
|
const length = elements.length;
|
1555
2071
|
|
2072
|
+
if (firstAssign && useRawElements) {
|
2073
|
+
let bytes = compileBytes(length, 'i32');
|
2074
|
+
|
2075
|
+
if (!initEmpty) for (let i = 0; i < length; i++) {
|
2076
|
+
if (elements[i] == null) continue;
|
2077
|
+
|
2078
|
+
bytes.push(...compileBytes(elements[i], itemType));
|
2079
|
+
}
|
2080
|
+
|
2081
|
+
data.push({
|
2082
|
+
offset: pointer,
|
2083
|
+
bytes
|
2084
|
+
});
|
2085
|
+
|
2086
|
+
// local value as pointer
|
2087
|
+
out.push(...number(pointer));
|
2088
|
+
|
2089
|
+
return [ out, pointer ];
|
2090
|
+
}
|
2091
|
+
|
1556
2092
|
// store length as 0th array
|
1557
2093
|
out.push(
|
1558
2094
|
...number(0, Valtype.i32),
|
@@ -1560,8 +2096,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
1560
2096
|
[ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(pointer) ]
|
1561
2097
|
);
|
1562
2098
|
|
1563
|
-
const storeOp =
|
1564
|
-
const valtype = itemTypeToValtype[itemType];
|
2099
|
+
const storeOp = StoreOps[itemType];
|
1565
2100
|
|
1566
2101
|
if (!initEmpty) for (let i = 0; i < length; i++) {
|
1567
2102
|
if (elements[i] == null) continue;
|
@@ -1576,14 +2111,23 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
1576
2111
|
// local value as pointer
|
1577
2112
|
out.push(...number(pointer));
|
1578
2113
|
|
1579
|
-
|
1580
|
-
|
1581
|
-
return out;
|
2114
|
+
return [ out, pointer ];
|
1582
2115
|
};
|
1583
2116
|
|
1584
2117
|
let arrays = new Map();
|
1585
2118
|
const generateArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false) => {
|
1586
|
-
return makeArray(scope, decl, global, name, initEmpty, valtype);
|
2119
|
+
return makeArray(scope, decl, global, name, initEmpty, valtype)[0];
|
2120
|
+
};
|
2121
|
+
|
2122
|
+
let varMetadata = new Map();
|
2123
|
+
const addVarMeta = (_name, obj) => {
|
2124
|
+
const name = _name ?? '$undeclared';
|
2125
|
+
if (!varMetadata.has(name)) varMetadata.set(name, {});
|
2126
|
+
|
2127
|
+
const meta = varMetadata.get(name);
|
2128
|
+
for (const k in obj) {
|
2129
|
+
meta[k] = obj[k];
|
2130
|
+
}
|
1587
2131
|
};
|
1588
2132
|
|
1589
2133
|
export const generateMember = (scope, decl, _global, _name) => {
|
@@ -1596,8 +2140,6 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
1596
2140
|
const name = decl.object.name;
|
1597
2141
|
const pointer = arrays.get(name);
|
1598
2142
|
|
1599
|
-
scope.memory = true;
|
1600
|
-
|
1601
2143
|
const aotPointer = pointer != null;
|
1602
2144
|
|
1603
2145
|
return [
|
@@ -1617,8 +2159,6 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
1617
2159
|
const name = decl.object.name;
|
1618
2160
|
const pointer = arrays.get(name);
|
1619
2161
|
|
1620
|
-
scope.memory = true;
|
1621
|
-
|
1622
2162
|
const aotPointer = pointer != null;
|
1623
2163
|
|
1624
2164
|
if (type === TYPES._array) {
|
@@ -1644,10 +2184,9 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
1644
2184
|
|
1645
2185
|
// string
|
1646
2186
|
|
1647
|
-
const newOut = makeArray(scope, {
|
2187
|
+
const [ newOut, newPointer ] = makeArray(scope, {
|
1648
2188
|
rawElements: new Array(1)
|
1649
2189
|
}, _global, _name, true, 'i16');
|
1650
|
-
const newPointer = arrays.get(_name ?? '$undeclared');
|
1651
2190
|
|
1652
2191
|
return [
|
1653
2192
|
// setup new/out array
|
@@ -1729,7 +2268,7 @@ const generateFunc = (scope, decl) => {
|
|
1729
2268
|
locals: {},
|
1730
2269
|
localInd: 0,
|
1731
2270
|
returns: [ valtypeBinary ],
|
1732
|
-
|
2271
|
+
returnType: null,
|
1733
2272
|
throws: false,
|
1734
2273
|
name
|
1735
2274
|
};
|
@@ -1753,9 +2292,8 @@ const generateFunc = (scope, decl) => {
|
|
1753
2292
|
name,
|
1754
2293
|
params: Object.values(innerScope.locals).slice(0, params.length).map(x => x.type),
|
1755
2294
|
returns: innerScope.returns,
|
1756
|
-
returnType: innerScope.returnType
|
2295
|
+
returnType: innerScope.returnType,
|
1757
2296
|
locals: innerScope.locals,
|
1758
|
-
memory: innerScope.memory,
|
1759
2297
|
throws: innerScope.throws,
|
1760
2298
|
index: currentFuncIndex++
|
1761
2299
|
};
|
@@ -1770,6 +2308,8 @@ const generateFunc = (scope, decl) => {
|
|
1770
2308
|
|
1771
2309
|
if (name !== 'main' && func.returns.length !== 0 && wasm[wasm.length - 1]?.[0] !== Opcodes.return && countLeftover(wasm) === 0) {
|
1772
2310
|
wasm.push(...number(0), [ Opcodes.return ]);
|
2311
|
+
|
2312
|
+
if (func.returnType === null) func.returnType = TYPES.undefined;
|
1773
2313
|
}
|
1774
2314
|
|
1775
2315
|
// change v128 params into many <type> (i32x4 -> i32/etc) instead as unsupported param valtype
|
@@ -1780,9 +2320,7 @@ const generateFunc = (scope, decl) => {
|
|
1780
2320
|
if (local.type === Valtype.v128) {
|
1781
2321
|
vecParams++;
|
1782
2322
|
|
1783
|
-
/*
|
1784
|
-
|
1785
|
-
wasm.unshift( // add v128 load for param
|
2323
|
+
/* wasm.unshift( // add v128 load for param
|
1786
2324
|
[ Opcodes.i32_const, 0 ],
|
1787
2325
|
[ ...Opcodes.v128_load, 0, i * 16 ],
|
1788
2326
|
[ Opcodes.local_set, local.idx ]
|
@@ -1893,10 +2431,10 @@ const generateFunc = (scope, decl) => {
|
|
1893
2431
|
};
|
1894
2432
|
|
1895
2433
|
const generateCode = (scope, decl) => {
|
1896
|
-
|
2434
|
+
let out = [];
|
1897
2435
|
|
1898
2436
|
for (const x of decl.body) {
|
1899
|
-
out.
|
2437
|
+
out = out.concat(generate(scope, x));
|
1900
2438
|
}
|
1901
2439
|
|
1902
2440
|
return out;
|
@@ -1912,10 +2450,9 @@ const internalConstrs = {
|
|
1912
2450
|
|
1913
2451
|
// new Array(n)
|
1914
2452
|
|
1915
|
-
makeArray(scope, {
|
2453
|
+
const [ , pointer ] = makeArray(scope, {
|
1916
2454
|
rawElements: new Array(0)
|
1917
2455
|
}, global, name, true);
|
1918
|
-
const pointer = arrays.get(name ?? '$undeclared');
|
1919
2456
|
|
1920
2457
|
const arg = decl.arguments[0] ?? DEFAULT_VALUE;
|
1921
2458
|
|
@@ -1933,6 +2470,18 @@ const internalConstrs = {
|
|
1933
2470
|
];
|
1934
2471
|
},
|
1935
2472
|
type: TYPES._array
|
2473
|
+
},
|
2474
|
+
|
2475
|
+
__Array_of: {
|
2476
|
+
// this is not a constructor but best fits internal structure here
|
2477
|
+
generate: (scope, decl, global, name) => {
|
2478
|
+
// Array.of(i0, i1, ...)
|
2479
|
+
return generateArray(scope, {
|
2480
|
+
elements: decl.arguments
|
2481
|
+
}, global, name);
|
2482
|
+
},
|
2483
|
+
type: TYPES._array,
|
2484
|
+
notConstr: true
|
1936
2485
|
}
|
1937
2486
|
};
|
1938
2487
|
|
@@ -1946,7 +2495,9 @@ export default program => {
|
|
1946
2495
|
depth = [];
|
1947
2496
|
typeStates = {};
|
1948
2497
|
arrays = new Map();
|
2498
|
+
varMetadata = new Map();
|
1949
2499
|
pages = new Map();
|
2500
|
+
data = [];
|
1950
2501
|
currentFuncIndex = importedFuncs.length;
|
1951
2502
|
|
1952
2503
|
globalThis.valtype = 'f64';
|
@@ -2023,5 +2574,5 @@ export default program => {
|
|
2023
2574
|
// if blank main func and other exports, remove it
|
2024
2575
|
if (main.wasm.length === 0 && funcs.reduce((acc, x) => acc + (x.export ? 1 : 0), 0) > 1) funcs.splice(funcs.length - 1, 1);
|
2025
2576
|
|
2026
|
-
return { funcs, globals, tags, exceptions, pages };
|
2577
|
+
return { funcs, globals, tags, exceptions, pages, data };
|
2027
2578
|
};
|