porffor 0.14.0-f67c123a1 → 0.16.0-08983b6b1
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/CONTRIBUTING.md +16 -10
- package/README.md +14 -30
- package/asur/index.js +1 -1
- package/compiler/2c.js +172 -59
- package/compiler/allocators.js +128 -0
- package/compiler/assemble.js +37 -5
- package/compiler/builtins/annexb_string.ts +1 -0
- package/compiler/builtins/array.ts +154 -7
- package/compiler/builtins/base64.ts +1 -0
- package/compiler/builtins/boolean.ts +3 -1
- package/compiler/builtins/console.ts +6 -0
- package/compiler/builtins/crypto.ts +1 -0
- package/compiler/builtins/date.ts +5 -30
- package/compiler/builtins/error.js +22 -0
- package/compiler/builtins/escape.ts +1 -2
- package/compiler/builtins/function.ts +2 -0
- package/compiler/builtins/int.ts +2 -0
- package/compiler/builtins/math.ts +410 -0
- package/compiler/builtins/number.ts +12 -21
- package/compiler/builtins/object.ts +2 -0
- package/compiler/builtins/porffor.d.ts +18 -0
- package/compiler/builtins/set.ts +22 -12
- package/compiler/builtins/string.ts +1 -0
- package/compiler/builtins/string_f64.ts +10 -0
- package/compiler/builtins/symbol.ts +62 -0
- package/compiler/builtins/z_ecma262.ts +62 -0
- package/compiler/builtins.js +50 -12
- package/compiler/codegen.js +826 -552
- package/compiler/cyclone.js +535 -0
- package/compiler/decompile.js +7 -1
- package/compiler/generated_builtins.js +635 -84
- package/compiler/havoc.js +93 -0
- package/compiler/index.js +108 -15
- package/compiler/opt.js +10 -44
- package/compiler/parse.js +3 -9
- package/compiler/pgo.js +212 -0
- package/compiler/precompile.js +17 -11
- package/compiler/prefs.js +13 -5
- package/compiler/prototype.js +200 -186
- package/compiler/wasmSpec.js +2 -2
- package/compiler/wrap.js +128 -44
- package/package.json +3 -5
- package/runner/index.js +31 -15
- package/runner/repl.js +18 -2
- /package/runner/{profiler.js → profile.js} +0 -0
package/compiler/codegen.js
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
import { Blocktype, Opcodes, Valtype,
|
2
|
-
import { ieee754_binary64, signedLEB128, unsignedLEB128, encodeVector } from './encoding.js';
|
1
|
+
import { Blocktype, Opcodes, Valtype, ValtypeSize } from './wasmSpec.js';
|
2
|
+
import { ieee754_binary64, signedLEB128, unsignedLEB128, encodeVector, read_signedLEB128 } 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';
|
@@ -9,15 +9,16 @@ import * as Rhemyn from '../rhemyn/compile.js';
|
|
9
9
|
import parse from './parse.js';
|
10
10
|
import { log } from './log.js';
|
11
11
|
import Prefs from './prefs.js';
|
12
|
+
import makeAllocator from './allocators.js';
|
12
13
|
|
13
14
|
let globals = {};
|
14
|
-
let globalInd = 0;
|
15
15
|
let tags = [];
|
16
16
|
let funcs = [];
|
17
17
|
let exceptions = [];
|
18
18
|
let funcIndex = {};
|
19
19
|
let currentFuncIndex = importedFuncs.length;
|
20
20
|
let builtinFuncs = {}, builtinVars = {}, prototypeFuncs = {};
|
21
|
+
let allocator;
|
21
22
|
|
22
23
|
class TodoError extends Error {
|
23
24
|
constructor(message) {
|
@@ -32,19 +33,14 @@ const todo = (scope, msg, expectsValue = undefined) => {
|
|
32
33
|
|
33
34
|
case 'runtime':
|
34
35
|
return internalThrow(scope, 'TodoError', msg, expectsValue);
|
35
|
-
|
36
|
-
// return [
|
37
|
-
// ...debug(`todo! ${msg}`),
|
38
|
-
// [ Opcodes.unreachable ]
|
39
|
-
// ];
|
40
36
|
}
|
41
37
|
};
|
42
38
|
|
43
|
-
const isFuncType = type =>
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
39
|
+
const isFuncType = type =>
|
40
|
+
type === 'FunctionDeclaration' || type === 'FunctionExpression' || type === 'ArrowFunctionExpression';
|
41
|
+
const hasFuncWithName = name =>
|
42
|
+
funcIndex[name] != null || builtinFuncs[name] != null || importedFuncs[name] != null || internalConstrs[name] != null;
|
43
|
+
|
48
44
|
const generate = (scope, decl, global = false, name = undefined, valueUnused = false) => {
|
49
45
|
switch (decl.type) {
|
50
46
|
case 'BinaryExpression':
|
@@ -140,18 +136,23 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
140
136
|
case 'ArrayExpression':
|
141
137
|
return generateArray(scope, decl, global, name);
|
142
138
|
|
139
|
+
case 'ObjectExpression':
|
140
|
+
return generateObject(scope, decl, global, name);
|
141
|
+
|
143
142
|
case 'MemberExpression':
|
144
143
|
return generateMember(scope, decl, global, name);
|
145
144
|
|
146
145
|
case 'ExportNamedDeclaration':
|
147
|
-
|
148
|
-
const funcsBefore = funcs.length;
|
146
|
+
const funcsBefore = funcs.map(x => x.name);
|
149
147
|
generate(scope, decl.declaration);
|
150
148
|
|
151
|
-
|
152
|
-
|
153
|
-
const
|
154
|
-
|
149
|
+
// set new funcs as exported
|
150
|
+
if (funcsBefore.length !== funcs.length) {
|
151
|
+
const newFuncs = funcs.filter(x => !funcsBefore.includes(x.name)).filter(x => !x.internal);
|
152
|
+
|
153
|
+
for (const x of newFuncs) {
|
154
|
+
x.export = true;
|
155
|
+
}
|
155
156
|
}
|
156
157
|
|
157
158
|
return [];
|
@@ -176,12 +177,6 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
176
177
|
continue;
|
177
178
|
}
|
178
179
|
|
179
|
-
if (asm[0] === 'memory') {
|
180
|
-
allocPage(scope, 'asm instrinsic');
|
181
|
-
// todo: add to store/load offset insts
|
182
|
-
continue;
|
183
|
-
}
|
184
|
-
|
185
180
|
let inst = Opcodes[asm[0].replace('.', '_')];
|
186
181
|
if (inst == null) throw new Error(`inline asm: inst ${asm[0]} not found`);
|
187
182
|
|
@@ -200,19 +195,11 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
200
195
|
|
201
196
|
__Porffor_bs: str => [
|
202
197
|
...makeString(scope, str, global, name, true),
|
203
|
-
|
204
|
-
...(name ? setType(scope, name, TYPES.bytestring) : [
|
205
|
-
...number(TYPES.bytestring, Valtype.i32),
|
206
|
-
...setLastType(scope)
|
207
|
-
])
|
198
|
+
...(name ? setType(scope, name, TYPES.bytestring) : setLastType(scope, TYPES.bytestring))
|
208
199
|
],
|
209
200
|
__Porffor_s: str => [
|
210
201
|
...makeString(scope, str, global, name, false),
|
211
|
-
|
212
|
-
...(name ? setType(scope, name, TYPES.string) : [
|
213
|
-
...number(TYPES.string, Valtype.i32),
|
214
|
-
...setLastType(scope)
|
215
|
-
])
|
202
|
+
...(name ? setType(scope, name, TYPES.string) : setLastType(scope, TYPES.string))
|
216
203
|
],
|
217
204
|
};
|
218
205
|
|
@@ -351,9 +338,7 @@ const generateReturn = (scope, decl) => {
|
|
351
338
|
|
352
339
|
return [
|
353
340
|
...generate(scope, decl.argument),
|
354
|
-
...(scope.returnType != null ? [] :
|
355
|
-
...getNodeType(scope, decl.argument)
|
356
|
-
]),
|
341
|
+
...(scope.returnType != null ? [] : getNodeType(scope, decl.argument)),
|
357
342
|
[ Opcodes.return ]
|
358
343
|
];
|
359
344
|
};
|
@@ -368,7 +353,7 @@ const localTmp = (scope, name, type = valtypeBinary) => {
|
|
368
353
|
};
|
369
354
|
|
370
355
|
const isIntOp = op => op && ((op[0] >= 0x45 && op[0] <= 0x4f) || (op[0] >= 0x67 && op[0] <= 0x78) || op[0] === 0x41);
|
371
|
-
const
|
356
|
+
const isIntToFloatOp = op => op && (op[0] >= 0xb7 && op[0] <= 0xba);
|
372
357
|
|
373
358
|
const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
374
359
|
const checks = {
|
@@ -385,10 +370,10 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
385
370
|
|
386
371
|
// if we can, use int tmp and convert at the end to help prevent unneeded conversions
|
387
372
|
// (like if we are in an if condition - very common)
|
388
|
-
const
|
389
|
-
const
|
373
|
+
const leftWasInt = isIntToFloatOp(left[left.length - 1]);
|
374
|
+
const rightWasInt = isIntToFloatOp(right[right.length - 1]);
|
390
375
|
|
391
|
-
const canInt =
|
376
|
+
const canInt = leftWasInt && rightWasInt;
|
392
377
|
|
393
378
|
if (canInt) {
|
394
379
|
// remove int -> float conversions from left and right
|
@@ -402,13 +387,11 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
402
387
|
[ Opcodes.if, Valtype.i32 ],
|
403
388
|
...right,
|
404
389
|
// note type
|
405
|
-
...rightType,
|
406
|
-
...setLastType(scope),
|
390
|
+
...setLastType(scope, rightType),
|
407
391
|
[ Opcodes.else ],
|
408
392
|
[ Opcodes.local_get, localTmp(scope, 'logictmpi', Valtype.i32) ],
|
409
393
|
// note type
|
410
|
-
...leftType,
|
411
|
-
...setLastType(scope),
|
394
|
+
...setLastType(scope, leftType),
|
412
395
|
[ Opcodes.end ],
|
413
396
|
Opcodes.i32_from
|
414
397
|
];
|
@@ -421,13 +404,11 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
421
404
|
[ Opcodes.if, valtypeBinary ],
|
422
405
|
...right,
|
423
406
|
// note type
|
424
|
-
...rightType,
|
425
|
-
...setLastType(scope),
|
407
|
+
...setLastType(scope, rightType),
|
426
408
|
[ Opcodes.else ],
|
427
409
|
[ Opcodes.local_get, localTmp(scope, 'logictmp') ],
|
428
410
|
// note type
|
429
|
-
...leftType,
|
430
|
-
...setLastType(scope),
|
411
|
+
...setLastType(scope, leftType),
|
431
412
|
[ Opcodes.end ]
|
432
413
|
];
|
433
414
|
};
|
@@ -442,63 +423,12 @@ const concatStrings = (scope, left, right, global, name, assign = false, bytestr
|
|
442
423
|
const rightLength = localTmp(scope, 'concat_right_length', Valtype.i32);
|
443
424
|
const leftLength = localTmp(scope, 'concat_left_length', Valtype.i32);
|
444
425
|
|
445
|
-
if (assign && Prefs.aotPointerOpt) {
|
446
|
-
const pointer = scope.arrays?.get(name ?? '$undeclared');
|
447
|
-
|
448
|
-
return [
|
449
|
-
// setup right
|
450
|
-
...right,
|
451
|
-
Opcodes.i32_to_u,
|
452
|
-
[ Opcodes.local_set, rightPointer ],
|
453
|
-
|
454
|
-
// calculate length
|
455
|
-
...number(0, Valtype.i32), // base 0 for store later
|
456
|
-
|
457
|
-
...number(pointer, Valtype.i32),
|
458
|
-
[ Opcodes.i32_load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128(0) ],
|
459
|
-
[ Opcodes.local_tee, leftLength ],
|
460
|
-
|
461
|
-
[ Opcodes.local_get, rightPointer ],
|
462
|
-
[ Opcodes.i32_load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128(0) ],
|
463
|
-
[ Opcodes.local_tee, rightLength ],
|
464
|
-
|
465
|
-
[ Opcodes.i32_add ],
|
466
|
-
|
467
|
-
// store length
|
468
|
-
[ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(pointer) ],
|
469
|
-
|
470
|
-
// copy right
|
471
|
-
// dst = out pointer + length size + current length * sizeof valtype
|
472
|
-
...number(pointer + ValtypeSize.i32, Valtype.i32),
|
473
|
-
|
474
|
-
[ Opcodes.local_get, leftLength ],
|
475
|
-
...number(bytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
|
476
|
-
[ Opcodes.i32_mul ],
|
477
|
-
[ Opcodes.i32_add ],
|
478
|
-
|
479
|
-
// src = right pointer + length size
|
480
|
-
[ Opcodes.local_get, rightPointer ],
|
481
|
-
...number(ValtypeSize.i32, Valtype.i32),
|
482
|
-
[ Opcodes.i32_add ],
|
483
|
-
|
484
|
-
// size = right length * sizeof valtype
|
485
|
-
[ Opcodes.local_get, rightLength ],
|
486
|
-
...number(bytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
|
487
|
-
[ Opcodes.i32_mul ],
|
488
|
-
|
489
|
-
[ ...Opcodes.memory_copy, 0x00, 0x00 ],
|
490
|
-
|
491
|
-
// return new string (page)
|
492
|
-
...number(pointer)
|
493
|
-
];
|
494
|
-
}
|
495
|
-
|
496
426
|
const leftPointer = localTmp(scope, 'concat_left_pointer', Valtype.i32);
|
497
427
|
|
498
428
|
// alloc/assign array
|
499
|
-
const [ , pointer ] = makeArray(scope, {
|
429
|
+
const [ out, pointer ] = makeArray(scope, {
|
500
430
|
rawElements: new Array(0)
|
501
|
-
}, global, name, true, 'i16');
|
431
|
+
}, assign ? false : global, assign ? undefined : name, true, 'i16', true);
|
502
432
|
|
503
433
|
return [
|
504
434
|
// setup left
|
@@ -512,24 +442,26 @@ const concatStrings = (scope, left, right, global, name, assign = false, bytestr
|
|
512
442
|
[ Opcodes.local_set, rightPointer ],
|
513
443
|
|
514
444
|
// calculate length
|
515
|
-
...
|
445
|
+
...out,
|
516
446
|
|
517
447
|
[ Opcodes.local_get, leftPointer ],
|
518
|
-
[ Opcodes.i32_load,
|
448
|
+
[ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
|
519
449
|
[ Opcodes.local_tee, leftLength ],
|
520
450
|
|
521
451
|
[ Opcodes.local_get, rightPointer ],
|
522
|
-
[ Opcodes.i32_load,
|
452
|
+
[ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
|
523
453
|
[ Opcodes.local_tee, rightLength ],
|
524
454
|
|
525
455
|
[ Opcodes.i32_add ],
|
526
456
|
|
527
457
|
// store length
|
528
|
-
[ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1,
|
458
|
+
[ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, 0 ],
|
529
459
|
|
530
460
|
// copy left
|
531
461
|
// dst = out pointer + length size
|
532
|
-
...
|
462
|
+
...pointer,
|
463
|
+
...number(ValtypeSize.i32, Valtype.i32),
|
464
|
+
[ Opcodes.i32_add ],
|
533
465
|
|
534
466
|
// src = left pointer + length size
|
535
467
|
[ Opcodes.local_get, leftPointer ],
|
@@ -542,7 +474,9 @@ const concatStrings = (scope, left, right, global, name, assign = false, bytestr
|
|
542
474
|
|
543
475
|
// copy right
|
544
476
|
// dst = out pointer + length size + left length * sizeof valtype
|
545
|
-
...
|
477
|
+
...pointer,
|
478
|
+
...number(ValtypeSize.i32, Valtype.i32),
|
479
|
+
[ Opcodes.i32_add ],
|
546
480
|
|
547
481
|
[ Opcodes.local_get, leftLength ],
|
548
482
|
...number(bytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
|
@@ -562,7 +496,8 @@ const concatStrings = (scope, left, right, global, name, assign = false, bytestr
|
|
562
496
|
[ ...Opcodes.memory_copy, 0x00, 0x00 ],
|
563
497
|
|
564
498
|
// return new string (page)
|
565
|
-
...
|
499
|
+
...pointer,
|
500
|
+
Opcodes.i32_from_u
|
566
501
|
];
|
567
502
|
};
|
568
503
|
|
@@ -597,11 +532,11 @@ const compareStrings = (scope, left, right, bytestrings = false) => {
|
|
597
532
|
|
598
533
|
// get lengths
|
599
534
|
[ Opcodes.local_get, leftPointer ],
|
600
|
-
[ Opcodes.i32_load,
|
535
|
+
[ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
|
601
536
|
[ Opcodes.local_tee, leftLength ],
|
602
537
|
|
603
538
|
[ Opcodes.local_get, rightPointer ],
|
604
|
-
[ Opcodes.i32_load,
|
539
|
+
[ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
|
605
540
|
|
606
541
|
// fast path: check leftLength != rightLength
|
607
542
|
[ Opcodes.i32_ne ],
|
@@ -657,9 +592,9 @@ const compareStrings = (scope, left, right, bytestrings = false) => {
|
|
657
592
|
[ Opcodes.i32_add ],
|
658
593
|
[ Opcodes.local_tee, index ],
|
659
594
|
|
660
|
-
// if index
|
595
|
+
// if index < index end (length * sizeof valtype), loop
|
661
596
|
[ Opcodes.local_get, indexEnd ],
|
662
|
-
[ Opcodes.
|
597
|
+
[ Opcodes.i32_lt_s ],
|
663
598
|
[ Opcodes.br_if, 0 ],
|
664
599
|
[ Opcodes.end ],
|
665
600
|
|
@@ -677,38 +612,50 @@ const compareStrings = (scope, left, right, bytestrings = false) => {
|
|
677
612
|
];
|
678
613
|
};
|
679
614
|
|
680
|
-
const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
681
|
-
if (
|
682
|
-
|
683
|
-
|
684
|
-
];
|
615
|
+
const truthy = (scope, wasm, type, intIn = false, intOut = false, forceTruthyMode = undefined) => {
|
616
|
+
// if (isIntToFloatOp(wasm[wasm.length - 1])) return [
|
617
|
+
// ...wasm,
|
618
|
+
// ...(!intIn && intOut ? [ Opcodes.i32_to_u ] : [])
|
619
|
+
// ];
|
685
620
|
// if (isIntOp(wasm[wasm.length - 1])) return [ ...wasm ];
|
686
621
|
|
622
|
+
// todo/perf: use knownType and custom bytecode here instead of typeSwitch
|
623
|
+
|
687
624
|
const useTmp = knownType(scope, type) == null;
|
688
625
|
const tmp = useTmp && localTmp(scope, `#logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
|
689
626
|
|
690
|
-
const def =
|
691
|
-
|
692
|
-
|
627
|
+
const def = (truthyMode => {
|
628
|
+
if (truthyMode === 'full') return [
|
629
|
+
// if value != 0 or NaN
|
630
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
631
|
+
...(intIn ? [ ] : [ Opcodes.i32_to ]),
|
693
632
|
|
694
|
-
|
695
|
-
|
633
|
+
[ Opcodes.i32_eqz ],
|
634
|
+
[ Opcodes.i32_eqz ],
|
696
635
|
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
636
|
+
...(intOut ? [] : [ Opcodes.i32_from ]),
|
637
|
+
];
|
638
|
+
|
639
|
+
if (truthyMode === 'no_negative') return [
|
640
|
+
// if value != 0 or NaN, non-binary output. negative numbers not truthy :/
|
641
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
642
|
+
...(intIn ? [] : [ Opcodes.i32_to ]),
|
643
|
+
...(intOut ? [] : [ Opcodes.i32_from ])
|
644
|
+
];
|
645
|
+
|
646
|
+
if (truthyMode === 'no_nan_negative') return [
|
647
|
+
// simpler and faster but makes NaN truthy and negative numbers not truthy,
|
648
|
+
// plus non-binary output
|
649
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
650
|
+
...(!intOut || (intIn && intOut) ? [] : [ Opcodes.i32_to_u ])
|
651
|
+
];
|
652
|
+
})(forceTruthyMode ?? Prefs.truthy ?? 'full');
|
701
653
|
|
702
654
|
return [
|
703
655
|
...wasm,
|
704
656
|
...(!useTmp ? [] : [ [ Opcodes.local_set, tmp ] ]),
|
705
657
|
|
706
658
|
...typeSwitch(scope, type, {
|
707
|
-
// [TYPES.number]: def,
|
708
|
-
[TYPES.array]: [
|
709
|
-
// arrays are always truthy
|
710
|
-
...number(1, intOut ? Valtype.i32 : valtypeBinary)
|
711
|
-
],
|
712
659
|
[TYPES.string]: [
|
713
660
|
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
714
661
|
...(intIn ? [] : [ Opcodes.i32_to_u ]),
|
@@ -744,10 +691,6 @@ const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
744
691
|
...(!useTmp ? [] : [ [ Opcodes.local_set, tmp ] ]),
|
745
692
|
|
746
693
|
...typeSwitch(scope, type, {
|
747
|
-
[TYPES.array]: [
|
748
|
-
// arrays are always truthy
|
749
|
-
...number(0, intOut ? Valtype.i32 : valtypeBinary)
|
750
|
-
],
|
751
694
|
[TYPES.string]: [
|
752
695
|
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
753
696
|
...(intIn ? [] : [ Opcodes.i32_to_u ]),
|
@@ -991,7 +934,7 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
991
934
|
// if both are true
|
992
935
|
[ Opcodes.i32_and ],
|
993
936
|
[ Opcodes.if, Blocktype.void ],
|
994
|
-
...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ]),
|
937
|
+
...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ], false),
|
995
938
|
...(op === '!==' || op === '!=' ? [ [ Opcodes.i32_eqz ] ] : []),
|
996
939
|
[ Opcodes.br, 1 ],
|
997
940
|
[ Opcodes.end ],
|
@@ -1015,12 +958,9 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
1015
958
|
[ Opcodes.end ],
|
1016
959
|
]));
|
1017
960
|
|
1018
|
-
//
|
1019
|
-
|
1020
|
-
|
1021
|
-
// endOut.push(stringOnly([ Opcodes.end ]));
|
1022
|
-
endOut.unshift(stringOnly([ Opcodes.end ]));
|
1023
|
-
// }
|
961
|
+
// add a surrounding block
|
962
|
+
startOut.push(stringOnly([ Opcodes.block, Valtype.i32 ]));
|
963
|
+
endOut.unshift(stringOnly([ Opcodes.end ]));
|
1024
964
|
}
|
1025
965
|
|
1026
966
|
return finalize([
|
@@ -1040,14 +980,14 @@ const generateBinaryExp = (scope, decl, _global, _name) => {
|
|
1040
980
|
return out;
|
1041
981
|
};
|
1042
982
|
|
1043
|
-
const asmFuncToAsm = (func,
|
1044
|
-
return func(
|
983
|
+
const asmFuncToAsm = (func, scope) => {
|
984
|
+
return func(scope, {
|
1045
985
|
TYPES, TYPE_NAMES, typeSwitch, makeArray, makeString, allocPage, internalThrow,
|
1046
|
-
builtin:
|
1047
|
-
let idx = funcIndex[
|
1048
|
-
if (idx
|
1049
|
-
includeBuiltin(null,
|
1050
|
-
idx = funcIndex[
|
986
|
+
builtin: n => {
|
987
|
+
let idx = funcIndex[n] ?? importedFuncs[n];
|
988
|
+
if (idx == null && builtinFuncs[n]) {
|
989
|
+
includeBuiltin(null, n);
|
990
|
+
idx = funcIndex[n];
|
1051
991
|
}
|
1052
992
|
|
1053
993
|
return idx;
|
@@ -1055,7 +995,7 @@ const asmFuncToAsm = (func, { name = '#unknown_asm_func', params = [], locals =
|
|
1055
995
|
});
|
1056
996
|
};
|
1057
997
|
|
1058
|
-
const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits, returns, returnType, localNames = [], globalNames = [], data: _data = [] }) => {
|
998
|
+
const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits = [], returns, returnType, localNames = [], globalNames = [], data: _data = [], table = false }) => {
|
1059
999
|
const existing = funcs.find(x => x.name === name);
|
1060
1000
|
if (existing) return existing;
|
1061
1001
|
|
@@ -1069,17 +1009,32 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
|
|
1069
1009
|
|
1070
1010
|
for (const x of _data) {
|
1071
1011
|
const copy = { ...x };
|
1072
|
-
copy.offset += pages.size * pageSize;
|
1012
|
+
if (copy.offset != null) copy.offset += pages.size * pageSize;
|
1073
1013
|
data.push(copy);
|
1074
1014
|
}
|
1075
1015
|
|
1076
|
-
|
1016
|
+
const func = {
|
1017
|
+
name,
|
1018
|
+
params,
|
1019
|
+
locals,
|
1020
|
+
localInd: allLocals.length,
|
1021
|
+
returns,
|
1022
|
+
returnType,
|
1023
|
+
internal: true,
|
1024
|
+
index: currentFuncIndex++,
|
1025
|
+
table
|
1026
|
+
};
|
1027
|
+
|
1028
|
+
funcs.push(func);
|
1029
|
+
funcIndex[name] = func.index;
|
1030
|
+
|
1031
|
+
if (typeof wasm === 'function') wasm = asmFuncToAsm(wasm, func);
|
1077
1032
|
|
1078
1033
|
let baseGlobalIdx, i = 0;
|
1079
1034
|
for (const type of globalTypes) {
|
1080
|
-
if (baseGlobalIdx === undefined) baseGlobalIdx =
|
1035
|
+
if (baseGlobalIdx === undefined) baseGlobalIdx = globals['#ind'];
|
1081
1036
|
|
1082
|
-
globals[globalNames[i] ?? `${name}_global_${i}`] = { idx:
|
1037
|
+
globals[globalNames[i] ?? `${name}_global_${i}`] = { idx: globals['#ind']++, type, init: globalInits[i] ?? 0 };
|
1083
1038
|
i++;
|
1084
1039
|
}
|
1085
1040
|
|
@@ -1092,19 +1047,18 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
|
|
1092
1047
|
}
|
1093
1048
|
}
|
1094
1049
|
|
1095
|
-
|
1096
|
-
|
1097
|
-
|
1098
|
-
|
1099
|
-
|
1100
|
-
|
1101
|
-
|
1102
|
-
internal: true,
|
1103
|
-
index: currentFuncIndex++
|
1104
|
-
};
|
1050
|
+
if (table) {
|
1051
|
+
for (const inst of wasm) {
|
1052
|
+
if (inst[0] === Opcodes.i32_load16_u && inst.at(-1) === 'read_argc') {
|
1053
|
+
inst.splice(2, 99);
|
1054
|
+
inst.push(...unsignedLEB128(allocPage({}, 'func argc lut') * pageSize));
|
1055
|
+
}
|
1056
|
+
}
|
1105
1057
|
|
1106
|
-
|
1107
|
-
|
1058
|
+
funcs.table = true;
|
1059
|
+
}
|
1060
|
+
|
1061
|
+
func.wasm = wasm;
|
1108
1062
|
|
1109
1063
|
return func;
|
1110
1064
|
};
|
@@ -1196,9 +1150,10 @@ const getLastType = scope => {
|
|
1196
1150
|
return [ [ Opcodes.local_get, localTmp(scope, '#last_type', Valtype.i32) ] ];
|
1197
1151
|
};
|
1198
1152
|
|
1199
|
-
const setLastType = scope =>
|
1200
|
-
|
1201
|
-
|
1153
|
+
const setLastType = (scope, type = []) => [
|
1154
|
+
...(typeof type === 'number' ? number(type, Valtype.i32) : type),
|
1155
|
+
[ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ]
|
1156
|
+
];
|
1202
1157
|
|
1203
1158
|
const getNodeType = (scope, node) => {
|
1204
1159
|
const ret = (() => {
|
@@ -1244,7 +1199,7 @@ const getNodeType = (scope, node) => {
|
|
1244
1199
|
const func = funcs.find(x => x.name === name);
|
1245
1200
|
|
1246
1201
|
if (func) {
|
1247
|
-
if (func.returnType) return func.returnType;
|
1202
|
+
if (func.returnType != null) return func.returnType;
|
1248
1203
|
}
|
1249
1204
|
|
1250
1205
|
if (builtinFuncs[name] && !builtinFuncs[name].typedReturns) return builtinFuncs[name].returnType ?? TYPES.number;
|
@@ -1259,7 +1214,9 @@ const getNodeType = (scope, node) => {
|
|
1259
1214
|
|
1260
1215
|
const func = spl[spl.length - 1];
|
1261
1216
|
const protoFuncs = Object.keys(prototypeFuncs).filter(x => x != TYPES.bytestring && prototypeFuncs[x][func] != null);
|
1262
|
-
if (protoFuncs.length === 1)
|
1217
|
+
if (protoFuncs.length === 1) {
|
1218
|
+
if (protoFuncs[0].returnType != null) return protoFuncs[0].returnType;
|
1219
|
+
}
|
1263
1220
|
}
|
1264
1221
|
|
1265
1222
|
if (name.startsWith('__Porffor_wasm_')) {
|
@@ -1306,7 +1263,15 @@ const getNodeType = (scope, node) => {
|
|
1306
1263
|
}
|
1307
1264
|
|
1308
1265
|
if (node.type === 'AssignmentExpression') {
|
1309
|
-
|
1266
|
+
const op = node.operator.slice(0, -1) || '=';
|
1267
|
+
if (op === '=') return getNodeType(scope, node.right);
|
1268
|
+
|
1269
|
+
return getNodeType(scope, {
|
1270
|
+
type: ['||', '&&', '??'].includes(op) ? 'LogicalExpression' : 'BinaryExpression',
|
1271
|
+
left: node.left,
|
1272
|
+
right: node.right,
|
1273
|
+
operator: op
|
1274
|
+
});
|
1310
1275
|
}
|
1311
1276
|
|
1312
1277
|
if (node.type === 'ArrayExpression') {
|
@@ -1325,23 +1290,6 @@ const getNodeType = (scope, node) => {
|
|
1325
1290
|
if (knownLeft === TYPES.bytestring || knownRight === TYPES.bytestring) return TYPES.bytestring;
|
1326
1291
|
|
1327
1292
|
return TYPES.number;
|
1328
|
-
|
1329
|
-
// todo: string concat types
|
1330
|
-
// if (node.operator !== '+') return TYPES.number;
|
1331
|
-
// else return [
|
1332
|
-
// // if left is string
|
1333
|
-
// ...getNodeType(scope, node.left),
|
1334
|
-
// ...number(TYPES.string, Valtype.i32),
|
1335
|
-
// [ Opcodes.i32_eq ],
|
1336
|
-
|
1337
|
-
// // if right is string
|
1338
|
-
// ...getNodeType(scope, node.right),
|
1339
|
-
// ...number(TYPES.string, Valtype.i32),
|
1340
|
-
// [ Opcodes.i32_eq ],
|
1341
|
-
|
1342
|
-
// // if either are true
|
1343
|
-
// [ Opcodes.i32_or ],
|
1344
|
-
// ];
|
1345
1293
|
}
|
1346
1294
|
|
1347
1295
|
if (node.type === 'UnaryExpression') {
|
@@ -1354,22 +1302,25 @@ const getNodeType = (scope, node) => {
|
|
1354
1302
|
}
|
1355
1303
|
|
1356
1304
|
if (node.type === 'MemberExpression') {
|
1357
|
-
|
1358
|
-
|
1359
|
-
|
1360
|
-
|
1361
|
-
|
1362
|
-
return TYPES.undefined;
|
1363
|
-
}
|
1305
|
+
const name = node.property.name;
|
1306
|
+
|
1307
|
+
if (name === 'length') {
|
1308
|
+
if (hasFuncWithName(node.object.name)) return TYPES.number;
|
1309
|
+
if (Prefs.fastLength) return TYPES.number;
|
1364
1310
|
}
|
1365
1311
|
|
1366
|
-
|
1367
|
-
if (
|
1312
|
+
const objectKnownType = knownType(scope, getNodeType(scope, node.object));
|
1313
|
+
if (objectKnownType != null) {
|
1314
|
+
if (name === 'length') {
|
1315
|
+
if ([ TYPES.string, TYPES.bytestring, TYPES.array ].includes(objectKnownType)) return TYPES.number;
|
1316
|
+
else return TYPES.undefined;
|
1317
|
+
}
|
1368
1318
|
|
1369
|
-
|
1370
|
-
|
1371
|
-
|
1372
|
-
|
1319
|
+
if (node.computed) {
|
1320
|
+
if (objectKnownType === TYPES.string) return TYPES.string;
|
1321
|
+
if (objectKnownType === TYPES.bytestring) return TYPES.bytestring;
|
1322
|
+
}
|
1323
|
+
}
|
1373
1324
|
|
1374
1325
|
if (scope.locals['#last_type']) return getLastType(scope);
|
1375
1326
|
|
@@ -1433,24 +1384,23 @@ const countLeftover = wasm => {
|
|
1433
1384
|
|
1434
1385
|
if (depth === 0)
|
1435
1386
|
if ([Opcodes.throw, Opcodes.drop, Opcodes.local_set, Opcodes.global_set].includes(inst[0])) count--;
|
1436
|
-
else if ([null, Opcodes.i32_eqz, Opcodes.i64_eqz, Opcodes.f64_ceil, Opcodes.f64_floor, Opcodes.f64_trunc, Opcodes.f64_nearest, Opcodes.f64_sqrt, Opcodes.local_tee, Opcodes.i32_wrap_i64, Opcodes.i64_extend_i32_s, Opcodes.i64_extend_i32_u, Opcodes.f32_demote_f64, Opcodes.f64_promote_f32, Opcodes.f64_convert_i32_s, Opcodes.f64_convert_i32_u, Opcodes.i32_clz, Opcodes.i32_ctz, Opcodes.i32_popcnt, Opcodes.f64_neg, Opcodes.end, Opcodes.i32_trunc_sat_f64_s[0], Opcodes.i32x4_extract_lane, Opcodes.i16x8_extract_lane, Opcodes.i32_load, Opcodes.i64_load, Opcodes.f64_load, Opcodes.v128_load, Opcodes.i32_load16_u, Opcodes.i32_load16_s, Opcodes.i32_load8_u, Opcodes.i32_load8_s, Opcodes.memory_grow].includes(inst[0]) && (inst[0] !== 0xfc || inst[1] <
|
1387
|
+
else if ([null, Opcodes.i32_eqz, Opcodes.i64_eqz, Opcodes.f64_ceil, Opcodes.f64_floor, Opcodes.f64_trunc, Opcodes.f64_nearest, Opcodes.f64_sqrt, Opcodes.local_tee, Opcodes.i32_wrap_i64, Opcodes.i64_extend_i32_s, Opcodes.i64_extend_i32_u, Opcodes.f32_demote_f64, Opcodes.f64_promote_f32, Opcodes.f64_convert_i32_s, Opcodes.f64_convert_i32_u, Opcodes.i32_clz, Opcodes.i32_ctz, Opcodes.i32_popcnt, Opcodes.f64_neg, Opcodes.end, Opcodes.i32_trunc_sat_f64_s[0], Opcodes.i32x4_extract_lane, Opcodes.i16x8_extract_lane, Opcodes.i32_load, Opcodes.i64_load, Opcodes.f64_load, Opcodes.v128_load, Opcodes.i32_load16_u, Opcodes.i32_load16_s, Opcodes.i32_load8_u, Opcodes.i32_load8_s, Opcodes.memory_grow].includes(inst[0]) && (inst[0] !== 0xfc || inst[1] < 0x04)) {}
|
1437
1388
|
else if ([Opcodes.local_get, Opcodes.global_get, Opcodes.f64_const, Opcodes.i32_const, Opcodes.i64_const, Opcodes.v128_const, Opcodes.memory_size].includes(inst[0])) count++;
|
1438
1389
|
else if ([Opcodes.i32_store, Opcodes.i64_store, Opcodes.f64_store, Opcodes.i32_store16, Opcodes.i32_store8].includes(inst[0])) count -= 2;
|
1439
|
-
else if (Opcodes.memory_copy[0]
|
1390
|
+
else if (inst[0] === Opcodes.memory_copy[0] && (inst[1] === Opcodes.memory_copy[1] || inst[1] === Opcodes.memory_init[1])) count -= 3;
|
1440
1391
|
else if (inst[0] === Opcodes.return) count = 0;
|
1441
1392
|
else if (inst[0] === Opcodes.call) {
|
1442
1393
|
let func = funcs.find(x => x.index === inst[1]);
|
1443
|
-
if (inst[1]
|
1444
|
-
|
1445
|
-
|
1446
|
-
count -= importedFuncs[inst[1]].params;
|
1447
|
-
count += importedFuncs[inst[1]].returns;
|
1394
|
+
if (inst[1] < importedFuncs.length) {
|
1395
|
+
func = importedFuncs[inst[1]];
|
1396
|
+
count = count - func.params + func.returns;
|
1448
1397
|
} else {
|
1449
|
-
|
1450
|
-
count -= func.params.length;
|
1451
|
-
} else count--;
|
1452
|
-
if (func) count += func.returns.length;
|
1398
|
+
count = count - func.params.length + func.returns.length;
|
1453
1399
|
}
|
1400
|
+
} else if (inst[0] === Opcodes.call_indirect) {
|
1401
|
+
count--; // funcidx
|
1402
|
+
count -= inst[1] * 2; // params * 2 (typed)
|
1403
|
+
count += 2; // fixed return (value, type)
|
1454
1404
|
} else count--;
|
1455
1405
|
|
1456
1406
|
// console.log(count, decompile([ inst ]).slice(0, -1));
|
@@ -1468,7 +1418,7 @@ const disposeLeftover = wasm => {
|
|
1468
1418
|
const generateExp = (scope, decl) => {
|
1469
1419
|
const expression = decl.expression;
|
1470
1420
|
|
1471
|
-
const out = generate(scope, expression, undefined, undefined,
|
1421
|
+
const out = generate(scope, expression, undefined, undefined, Prefs.optUnused);
|
1472
1422
|
disposeLeftover(out);
|
1473
1423
|
|
1474
1424
|
return out;
|
@@ -1559,16 +1509,10 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1559
1509
|
out.splice(out.length - 1, 1);
|
1560
1510
|
|
1561
1511
|
const finalStatement = parsed.body[parsed.body.length - 1];
|
1562
|
-
out.push(
|
1563
|
-
...getNodeType(scope, finalStatement),
|
1564
|
-
...setLastType(scope)
|
1565
|
-
);
|
1512
|
+
out.push(...setLastType(scope, getNodeType(scope, finalStatement)));
|
1566
1513
|
} else if (countLeftover(out) === 0) {
|
1567
1514
|
out.push(...number(UNDEFINED));
|
1568
|
-
out.push(
|
1569
|
-
...number(TYPES.undefined, Valtype.i32),
|
1570
|
-
...setLastType(scope)
|
1571
|
-
);
|
1515
|
+
out.push(...setLastType(scope, TYPES.undefined));
|
1572
1516
|
}
|
1573
1517
|
|
1574
1518
|
// if (lastInst && lastInst[0] === Opcodes.drop) {
|
@@ -1604,6 +1548,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1604
1548
|
|
1605
1549
|
if (!funcIndex[rhemynName]) {
|
1606
1550
|
const func = Rhemyn[funcName](regex, currentFuncIndex++, rhemynName);
|
1551
|
+
func.internal = true;
|
1607
1552
|
|
1608
1553
|
funcIndex[func.name] = func.index;
|
1609
1554
|
funcs.push(func);
|
@@ -1620,8 +1565,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1620
1565
|
[ Opcodes.call, idx ],
|
1621
1566
|
Opcodes.i32_from_u,
|
1622
1567
|
|
1623
|
-
...
|
1624
|
-
...setLastType(scope)
|
1568
|
+
...setLastType(scope, TYPES.boolean)
|
1625
1569
|
];
|
1626
1570
|
}
|
1627
1571
|
|
@@ -1693,9 +1637,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1693
1637
|
if (protoFunc.noArgRetLength && decl.arguments.length === 0) {
|
1694
1638
|
protoBC[x] = [
|
1695
1639
|
...RTArrayUtil.getLength(getPointer),
|
1696
|
-
|
1697
|
-
...number(TYPES.number, Valtype.i32),
|
1698
|
-
...setLastType(scope)
|
1640
|
+
...setLastType(scope, TYPES.number)
|
1699
1641
|
];
|
1700
1642
|
continue;
|
1701
1643
|
}
|
@@ -1714,10 +1656,10 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1714
1656
|
getI32: () => RTArrayUtil.getLengthI32(getPointer),
|
1715
1657
|
set: value => RTArrayUtil.setLength(getPointer, value),
|
1716
1658
|
setI32: value => RTArrayUtil.setLengthI32(getPointer, value)
|
1717
|
-
}, generate(scope, decl.arguments[0] ?? DEFAULT_VALUE), protoLocal, protoLocal2, (length, itemType) => {
|
1659
|
+
}, generate(scope, decl.arguments[0] ?? DEFAULT_VALUE), getNodeType(scope, decl.arguments[0] ?? DEFAULT_VALUE), protoLocal, protoLocal2, (length, itemType) => {
|
1718
1660
|
return makeArray(scope, {
|
1719
1661
|
rawElements: new Array(length)
|
1720
|
-
}, _global, _name, true, itemType);
|
1662
|
+
}, _global, _name, true, itemType, true);
|
1721
1663
|
}, () => {
|
1722
1664
|
optUnused = true;
|
1723
1665
|
return unusedValue;
|
@@ -1728,9 +1670,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1728
1670
|
protoBC[x] = [
|
1729
1671
|
[ Opcodes.block, unusedValue && optUnused ? Blocktype.void : valtypeBinary ],
|
1730
1672
|
...protoOut,
|
1731
|
-
|
1732
|
-
...number(protoFunc.returnType ?? TYPES.number, Valtype.i32),
|
1733
|
-
...setLastType(scope),
|
1673
|
+
...(unusedValue && optUnused ? [] : (protoFunc.returnType != null ? setLastType(scope, protoFunc.returnType) : setLastType(scope))),
|
1734
1674
|
[ Opcodes.end ]
|
1735
1675
|
];
|
1736
1676
|
}
|
@@ -1780,11 +1720,6 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1780
1720
|
|
1781
1721
|
if (idx === undefined && internalConstrs[name]) return internalConstrs[name].generate(scope, decl, _global, _name);
|
1782
1722
|
|
1783
|
-
if (idx === undefined && name === scope.name) {
|
1784
|
-
// hack: calling self, func generator will fix later
|
1785
|
-
idx = -1;
|
1786
|
-
}
|
1787
|
-
|
1788
1723
|
if (idx === undefined && name.startsWith('__Porffor_wasm_')) {
|
1789
1724
|
const wasmOps = {
|
1790
1725
|
// pointer, align, offset
|
@@ -1806,7 +1741,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1806
1741
|
f64_store: { imms: 2, args: [ true, false ], returns: 0 },
|
1807
1742
|
|
1808
1743
|
// value
|
1809
|
-
i32_const: { imms: 1, args: [], returns:
|
1744
|
+
i32_const: { imms: 1, args: [], returns: 0 },
|
1810
1745
|
};
|
1811
1746
|
|
1812
1747
|
const opName = name.slice('__Porffor_wasm_'.length);
|
@@ -1833,30 +1768,143 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1833
1768
|
|
1834
1769
|
if (idx === undefined) {
|
1835
1770
|
if (scope.locals[name] !== undefined || globals[name] !== undefined || builtinVars[name] !== undefined) {
|
1836
|
-
if (!Prefs.indirectCalls) return internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, true);
|
1837
|
-
|
1838
1771
|
const [ local, global ] = lookupName(scope, name);
|
1772
|
+
if (!Prefs.indirectCalls || local == null) return internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, true);
|
1773
|
+
|
1774
|
+
// todo: only works when function uses typedParams and typedReturns
|
1775
|
+
|
1776
|
+
const indirectMode = Prefs.indirectCallMode ?? 'vararg';
|
1777
|
+
// options: vararg, strict
|
1778
|
+
// - strict: simpler, smaller size usage, no func argc lut needed.
|
1779
|
+
// ONLY works when arg count of call == arg count of function being called
|
1780
|
+
// - vararg: large size usage, cursed.
|
1781
|
+
// works when arg count of call != arg count of function being called*
|
1782
|
+
// * most of the time, some edgecases
|
1783
|
+
|
1839
1784
|
funcs.table = true;
|
1785
|
+
scope.table = true;
|
1786
|
+
|
1787
|
+
let args = decl.arguments;
|
1788
|
+
let out = [];
|
1789
|
+
|
1790
|
+
let locals = [];
|
1791
|
+
|
1792
|
+
if (indirectMode === 'vararg') {
|
1793
|
+
const minArgc = Prefs.indirectCallMinArgc ?? 3;
|
1794
|
+
|
1795
|
+
if (args.length < minArgc) {
|
1796
|
+
args = args.concat(new Array(minArgc - args.length).fill(DEFAULT_VALUE));
|
1797
|
+
}
|
1798
|
+
}
|
1799
|
+
|
1800
|
+
for (let i = 0; i < args.length; i++) {
|
1801
|
+
const arg = args[i];
|
1802
|
+
out = out.concat(generate(scope, arg));
|
1803
|
+
|
1804
|
+
if (valtypeBinary !== Valtype.i32 && (
|
1805
|
+
(builtinFuncs[name] && builtinFuncs[name].params[i * (typedParams ? 2 : 1)] === Valtype.i32) ||
|
1806
|
+
(importedFuncs[name] && name.startsWith('profile'))
|
1807
|
+
)) {
|
1808
|
+
out.push(Opcodes.i32_to);
|
1809
|
+
}
|
1810
|
+
|
1811
|
+
out = out.concat(getNodeType(scope, arg));
|
1812
|
+
|
1813
|
+
if (indirectMode === 'vararg') {
|
1814
|
+
const typeLocal = localTmp(scope, `#indirect_arg${i}_type`, Valtype.i32);
|
1815
|
+
const valLocal = localTmp(scope, `#indirect_arg${i}_val`);
|
1816
|
+
|
1817
|
+
locals.push([valLocal, typeLocal]);
|
1818
|
+
|
1819
|
+
out.push(
|
1820
|
+
[ Opcodes.local_set, typeLocal ],
|
1821
|
+
[ Opcodes.local_set, valLocal ]
|
1822
|
+
);
|
1823
|
+
}
|
1824
|
+
}
|
1825
|
+
|
1826
|
+
if (indirectMode === 'strict') {
|
1827
|
+
return typeSwitch(scope, getNodeType(scope, decl.callee), {
|
1828
|
+
[TYPES.function]: [
|
1829
|
+
...out,
|
1830
|
+
[ global ? Opcodes.global_get : Opcodes.local_get, local.idx ],
|
1831
|
+
Opcodes.i32_to_u,
|
1832
|
+
[ Opcodes.call_indirect, args.length, 0 ],
|
1833
|
+
...setLastType(scope)
|
1834
|
+
],
|
1835
|
+
default: internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, true)
|
1836
|
+
});
|
1837
|
+
}
|
1840
1838
|
|
1841
|
-
//
|
1842
|
-
//
|
1843
|
-
//
|
1844
|
-
|
1839
|
+
// hi, I will now explain how vararg mode works:
|
1840
|
+
// wasm's indirect_call instruction requires you know the func type at compile-time
|
1841
|
+
// since we have varargs (variable argument count), we do not know it.
|
1842
|
+
// we could just store args in memory and not use wasm func args,
|
1843
|
+
// but that is slow (probably) and breaks js exports.
|
1844
|
+
// instead, we generate every* possibility of argc and use different indirect_call
|
1845
|
+
// ops for each one, with type depending on argc for that branch.
|
1846
|
+
// then we load the argc for the wanted function from a memory lut,
|
1847
|
+
// and call the branch with the matching argc we require.
|
1848
|
+
// sorry, yes it is very cursed (and size inefficient), but indirect calls
|
1849
|
+
// are kind of rare anyway (mostly callbacks) so I am not concerned atm.
|
1850
|
+
// *for argc 0-3, in future (todo:) the max number should be
|
1851
|
+
// dynamically changed to the max argc of any func in the js file.
|
1852
|
+
|
1853
|
+
const funcLocal = localTmp(scope, `#indirect_func`, Valtype.i32);
|
1854
|
+
|
1855
|
+
const gen = argc => {
|
1856
|
+
const out = [];
|
1857
|
+
for (let i = 0; i < argc; i++) {
|
1858
|
+
out.push(
|
1859
|
+
[ Opcodes.local_get, locals[i][0] ],
|
1860
|
+
[ Opcodes.local_get, locals[i][1] ]
|
1861
|
+
);
|
1862
|
+
}
|
1863
|
+
|
1864
|
+
out.push(
|
1865
|
+
[ Opcodes.local_get, funcLocal ],
|
1866
|
+
[ Opcodes.call_indirect, argc, 0 ],
|
1867
|
+
...setLastType(scope)
|
1868
|
+
)
|
1869
|
+
|
1870
|
+
return out;
|
1871
|
+
};
|
1872
|
+
|
1873
|
+
const tableBc = {};
|
1874
|
+
for (let i = 0; i <= args.length; i++) {
|
1875
|
+
tableBc[i] = gen(i);
|
1876
|
+
}
|
1877
|
+
|
1878
|
+
// todo/perf: check if we should use br_table here or just generate our own big if..elses
|
1879
|
+
|
1880
|
+
return typeSwitch(scope, getNodeType(scope, decl.callee), {
|
1845
1881
|
[TYPES.function]: [
|
1882
|
+
...out,
|
1883
|
+
|
1846
1884
|
[ global ? Opcodes.global_get : Opcodes.local_get, local.idx ],
|
1847
|
-
|
1885
|
+
Opcodes.i32_to_u,
|
1886
|
+
[ Opcodes.local_set, funcLocal ],
|
1887
|
+
|
1888
|
+
...brTable([
|
1889
|
+
// get argc of func we are calling
|
1890
|
+
[ Opcodes.local_get, funcLocal ],
|
1891
|
+
...number(ValtypeSize.i16, Valtype.i32),
|
1892
|
+
[ Opcodes.i32_mul ],
|
1893
|
+
|
1894
|
+
[ Opcodes.i32_load16_u, 0, ...unsignedLEB128(allocPage(scope, 'func argc lut') * pageSize), 'read_argc' ]
|
1895
|
+
], tableBc, valtypeBinary)
|
1848
1896
|
],
|
1849
1897
|
default: internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, true)
|
1850
1898
|
});
|
1851
1899
|
}
|
1900
|
+
|
1852
1901
|
return internalThrow(scope, 'ReferenceError', `${unhackName(name)} is not defined`, true);
|
1853
1902
|
}
|
1854
1903
|
|
1855
|
-
const func = funcs.find(x => x.index === idx);
|
1856
|
-
|
1857
|
-
const userFunc = (funcIndex[name] && !importedFuncs[name] && !builtinFuncs[name] && !internalConstrs[name]) || idx === -1;
|
1904
|
+
const func = funcs[idx - importedFuncs.length]; // idx === scope.index ? scope : funcs.find(x => x.index === idx);
|
1905
|
+
const userFunc = func && !func.internal;
|
1858
1906
|
const typedParams = userFunc || builtinFuncs[name]?.typedParams;
|
1859
|
-
const typedReturns = (
|
1907
|
+
const typedReturns = (userFunc && func.returnType == null) || builtinFuncs[name]?.typedReturns;
|
1860
1908
|
const paramCount = func && (typedParams ? func.params.length / 2 : func.params.length);
|
1861
1909
|
|
1862
1910
|
let args = decl.arguments;
|
@@ -1877,13 +1925,25 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1877
1925
|
const arg = args[i];
|
1878
1926
|
out = out.concat(generate(scope, arg));
|
1879
1927
|
|
1880
|
-
|
1881
|
-
|
1882
|
-
|
1883
|
-
|
1928
|
+
// todo: this should be used instead of the too many args thing above (by removing that)
|
1929
|
+
if (i >= paramCount) {
|
1930
|
+
// over param count of func, drop arg
|
1931
|
+
out.push([ Opcodes.drop ]);
|
1932
|
+
continue;
|
1933
|
+
}
|
1934
|
+
|
1935
|
+
if (valtypeBinary !== Valtype.i32 &&
|
1936
|
+
(func && func.params[i * (typedParams ? 2 : 1)] === Valtype.i32)
|
1937
|
+
) {
|
1884
1938
|
out.push(Opcodes.i32_to);
|
1885
1939
|
}
|
1886
1940
|
|
1941
|
+
if (valtypeBinary === Valtype.i32 &&
|
1942
|
+
(func && func.params[i * (typedParams ? 2 : 1)] === Valtype.f64)
|
1943
|
+
) {
|
1944
|
+
out.push([ Opcodes.f64_convert_i32_s ]);
|
1945
|
+
}
|
1946
|
+
|
1887
1947
|
if (typedParams) out = out.concat(getNodeType(scope, arg));
|
1888
1948
|
}
|
1889
1949
|
|
@@ -1905,6 +1965,10 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1905
1965
|
out.push(Opcodes.i32_from);
|
1906
1966
|
}
|
1907
1967
|
|
1968
|
+
if (builtinFuncs[name] && builtinFuncs[name].returns?.[0] === Valtype.f64 && valtypeBinary === Valtype.i32) {
|
1969
|
+
out.push(Opcodes.i32_trunc_sat_f64_s);
|
1970
|
+
}
|
1971
|
+
|
1908
1972
|
return out;
|
1909
1973
|
};
|
1910
1974
|
|
@@ -1925,7 +1989,12 @@ const generateNew = (scope, decl, _global, _name) => {
|
|
1925
1989
|
}, _global, _name);
|
1926
1990
|
}
|
1927
1991
|
|
1928
|
-
if (
|
1992
|
+
if (
|
1993
|
+
(builtinFuncs[name] && !builtinFuncs[name].constr) ||
|
1994
|
+
(internalConstrs[name] && builtinFuncs[name].notConstr)
|
1995
|
+
) return internalThrow(scope, 'TypeError', `${name} is not a constructor`, true);
|
1996
|
+
|
1997
|
+
if (!builtinFuncs[name]) return todo(scope, `new statement is not supported yet`, true); // return todo(scope, `new statement is not supported yet (new ${unhackName(name)})`);
|
1929
1998
|
|
1930
1999
|
return generateCall(scope, decl, _global, _name);
|
1931
2000
|
};
|
@@ -1950,8 +2019,11 @@ const knownType = (scope, type) => {
|
|
1950
2019
|
const idx = type[0][1];
|
1951
2020
|
|
1952
2021
|
// type idx = var idx + 1
|
1953
|
-
const
|
1954
|
-
if (
|
2022
|
+
const name = Object.values(scope.locals).find(x => x.idx === idx)?.name;
|
2023
|
+
if (name) {
|
2024
|
+
const local = scope.locals[name];
|
2025
|
+
if (local.metadata?.type != null) return v.metadata.type;
|
2026
|
+
}
|
1955
2027
|
}
|
1956
2028
|
|
1957
2029
|
return null;
|
@@ -1986,16 +2058,16 @@ const brTable = (input, bc, returns) => {
|
|
1986
2058
|
}
|
1987
2059
|
|
1988
2060
|
for (let i = 0; i < count; i++) {
|
1989
|
-
if (i === 0) out.push([ Opcodes.block, returns
|
2061
|
+
if (i === 0) out.push([ Opcodes.block, returns ]);
|
1990
2062
|
else out.push([ Opcodes.block, Blocktype.void ]);
|
1991
2063
|
}
|
1992
2064
|
|
1993
|
-
const nums = keys.filter(x => +x);
|
2065
|
+
const nums = keys.filter(x => +x >= 0);
|
1994
2066
|
const offset = Math.min(...nums);
|
1995
2067
|
const max = Math.max(...nums);
|
1996
2068
|
|
1997
2069
|
const table = [];
|
1998
|
-
let br =
|
2070
|
+
let br = 0;
|
1999
2071
|
|
2000
2072
|
for (let i = offset; i <= max; i++) {
|
2001
2073
|
// if branch for this num, go to that block
|
@@ -2019,10 +2091,8 @@ const brTable = (input, bc, returns) => {
|
|
2019
2091
|
[ Opcodes.br_table, ...encodeVector(table), 0 ]
|
2020
2092
|
);
|
2021
2093
|
|
2022
|
-
//
|
2023
|
-
// (
|
2024
|
-
// dm me and if you are correct and the first person
|
2025
|
-
// I will somehow shout you out or something
|
2094
|
+
// sort the wrong way and then reverse
|
2095
|
+
// so strings ('default') are at the start before any numbers
|
2026
2096
|
const orderedBc = keys.sort((a, b) => b - a).reverse();
|
2027
2097
|
|
2028
2098
|
br = count - 1;
|
@@ -2035,10 +2105,9 @@ const brTable = (input, bc, returns) => {
|
|
2035
2105
|
br--;
|
2036
2106
|
}
|
2037
2107
|
|
2038
|
-
|
2039
|
-
|
2040
|
-
|
2041
|
-
];
|
2108
|
+
out.push([ Opcodes.end ]);
|
2109
|
+
|
2110
|
+
return out;
|
2042
2111
|
};
|
2043
2112
|
|
2044
2113
|
const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
|
@@ -2049,10 +2118,10 @@ const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
|
|
2049
2118
|
return bc[known] ?? bc.default;
|
2050
2119
|
}
|
2051
2120
|
|
2052
|
-
if (Prefs.
|
2121
|
+
if (Prefs.typeswitchBrtable)
|
2053
2122
|
return brTable(type, bc, returns);
|
2054
2123
|
|
2055
|
-
const tmp = localTmp(scope, '#typeswitch_tmp', Valtype.i32);
|
2124
|
+
const tmp = localTmp(scope, '#typeswitch_tmp' + (Prefs.typeswitchUniqueTmp ? randId() : ''), Valtype.i32);
|
2056
2125
|
const out = [
|
2057
2126
|
...type,
|
2058
2127
|
[ Opcodes.local_set, tmp ],
|
@@ -2082,6 +2151,17 @@ const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
|
|
2082
2151
|
return out;
|
2083
2152
|
};
|
2084
2153
|
|
2154
|
+
const typeIsOneOf = (type, types, valtype = Valtype.i32) => {
|
2155
|
+
const out = [];
|
2156
|
+
|
2157
|
+
for (let i = 0; i < types.length; i++) {
|
2158
|
+
out.push(...type, ...number(types[i], valtype), valtype === Valtype.f64 ? [ Opcodes.f64_eq ] : [ Opcodes.i32_eq ]);
|
2159
|
+
if (i !== 0) out.push([ Opcodes.i32_or ]);
|
2160
|
+
}
|
2161
|
+
|
2162
|
+
return out;
|
2163
|
+
};
|
2164
|
+
|
2085
2165
|
const allocVar = (scope, name, global = false, type = true) => {
|
2086
2166
|
const target = global ? globals : scope.locals;
|
2087
2167
|
|
@@ -2093,12 +2173,12 @@ const allocVar = (scope, name, global = false, type = true) => {
|
|
2093
2173
|
return target[name].idx;
|
2094
2174
|
}
|
2095
2175
|
|
2096
|
-
let idx = global ?
|
2176
|
+
let idx = global ? globals['#ind']++ : scope.localInd++;
|
2097
2177
|
target[name] = { idx, type: valtypeBinary };
|
2098
2178
|
|
2099
2179
|
if (type) {
|
2100
|
-
let typeIdx = global ?
|
2101
|
-
target[name + '#type'] = { idx: typeIdx, type: Valtype.i32 };
|
2180
|
+
let typeIdx = global ? globals['#ind']++ : scope.localInd++;
|
2181
|
+
target[name + '#type'] = { idx: typeIdx, type: Valtype.i32, name };
|
2102
2182
|
}
|
2103
2183
|
|
2104
2184
|
return idx;
|
@@ -2190,24 +2270,10 @@ const generateVar = (scope, decl) => {
|
|
2190
2270
|
}
|
2191
2271
|
|
2192
2272
|
if (x.init) {
|
2193
|
-
|
2194
|
-
// // let a = function () { ... }
|
2195
|
-
// x.init.id = { name };
|
2196
|
-
|
2197
|
-
// const func = generateFunc(scope, x.init);
|
2198
|
-
|
2199
|
-
// out.push(
|
2200
|
-
// ...number(func.index - importedFuncs.length),
|
2201
|
-
// [ global ? Opcodes.global_set : Opcodes.local_set, idx ],
|
2202
|
-
|
2203
|
-
// ...setType(scope, name, TYPES.function)
|
2204
|
-
// );
|
2205
|
-
|
2206
|
-
// continue;
|
2207
|
-
// }
|
2273
|
+
const alreadyArray = scope.arrays?.get(name) != null;
|
2208
2274
|
|
2209
2275
|
const generated = generate(scope, x.init, global, name);
|
2210
|
-
if (scope.arrays?.get(name) != null) {
|
2276
|
+
if (!alreadyArray && scope.arrays?.get(name) != null) {
|
2211
2277
|
// hack to set local as pointer before
|
2212
2278
|
out.push(...number(scope.arrays.get(name)), [ global ? Opcodes.global_set : Opcodes.local_set, idx ]);
|
2213
2279
|
if (generated.at(-1) == Opcodes.i32_from_u) generated.pop();
|
@@ -2259,19 +2325,12 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
|
|
2259
2325
|
|
2260
2326
|
// hack: .length setter
|
2261
2327
|
if (decl.left.type === 'MemberExpression' && decl.left.property.name === 'length') {
|
2262
|
-
const name = decl.left.object.name;
|
2263
|
-
const pointer = scope.arrays?.get(name);
|
2264
|
-
|
2265
|
-
const aotPointer = Prefs.aotPointerOpt && pointer != null;
|
2266
|
-
|
2267
2328
|
const newValueTmp = localTmp(scope, '__length_setter_tmp');
|
2268
2329
|
const pointerTmp = op === '=' ? null : localTmp(scope, '__member_setter_ptr_tmp', Valtype.i32);
|
2269
2330
|
|
2270
2331
|
return [
|
2271
|
-
...(
|
2272
|
-
|
2273
|
-
Opcodes.i32_to_u
|
2274
|
-
]),
|
2332
|
+
...generate(scope, decl.left.object),
|
2333
|
+
Opcodes.i32_to_u,
|
2275
2334
|
...(!pointerTmp ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
|
2276
2335
|
|
2277
2336
|
...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
|
@@ -2282,7 +2341,7 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
|
|
2282
2341
|
[ Opcodes.local_tee, newValueTmp ],
|
2283
2342
|
|
2284
2343
|
Opcodes.i32_to_u,
|
2285
|
-
[ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1,
|
2344
|
+
[ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, 0 ],
|
2286
2345
|
|
2287
2346
|
[ Opcodes.local_get, newValueTmp ]
|
2288
2347
|
];
|
@@ -2290,59 +2349,38 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
|
|
2290
2349
|
|
2291
2350
|
// arr[i]
|
2292
2351
|
if (decl.left.type === 'MemberExpression' && decl.left.computed) {
|
2293
|
-
const name = decl.left.object.name;
|
2294
|
-
const pointer = scope.arrays?.get(name);
|
2295
|
-
|
2296
|
-
const aotPointer = Prefs.aotPointerOpt && pointer != null;
|
2297
|
-
|
2298
2352
|
const newValueTmp = localTmp(scope, '__member_setter_val_tmp');
|
2299
2353
|
const pointerTmp = op === '=' ? -1 : localTmp(scope, '__member_setter_ptr_tmp', Valtype.i32);
|
2300
2354
|
|
2301
2355
|
return [
|
2302
2356
|
...typeSwitch(scope, getNodeType(scope, decl.left.object), {
|
2303
2357
|
[TYPES.array]: [
|
2304
|
-
...(
|
2305
|
-
|
2306
|
-
Opcodes.i32_to_u
|
2307
|
-
]),
|
2358
|
+
...generate(scope, decl.left.object),
|
2359
|
+
Opcodes.i32_to_u,
|
2308
2360
|
|
2309
2361
|
// get index as valtype
|
2310
2362
|
...generate(scope, decl.left.property),
|
2311
2363
|
Opcodes.i32_to_u,
|
2312
2364
|
|
2313
2365
|
// turn into byte offset by * valtypeSize (4 for i32, 8 for i64/f64)
|
2314
|
-
...number(ValtypeSize[valtype], Valtype.i32),
|
2366
|
+
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
2315
2367
|
[ Opcodes.i32_mul ],
|
2316
|
-
|
2368
|
+
[ Opcodes.i32_add ],
|
2317
2369
|
...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
|
2318
2370
|
|
2319
2371
|
...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
|
2320
2372
|
[ Opcodes.local_get, pointerTmp ],
|
2321
|
-
[ Opcodes.load,
|
2322
|
-
], generate(scope, decl.right),
|
2373
|
+
[ Opcodes.load, 0, ValtypeSize.i32 ]
|
2374
|
+
], generate(scope, decl.right), [
|
2375
|
+
[ Opcodes.local_get, pointerTmp ],
|
2376
|
+
[ Opcodes.i32_load8_u, 0, ValtypeSize.i32 + ValtypeSize[valtype] ]
|
2377
|
+
], getNodeType(scope, decl.right), false, name, true)),
|
2323
2378
|
[ Opcodes.local_tee, newValueTmp ],
|
2324
2379
|
|
2325
|
-
[ Opcodes.store,
|
2380
|
+
[ Opcodes.store, 0, ValtypeSize.i32 ]
|
2326
2381
|
],
|
2327
2382
|
|
2328
2383
|
default: internalThrow(scope, 'TypeError', `Cannot assign member with non-array`)
|
2329
|
-
|
2330
|
-
// [TYPES.string]: [
|
2331
|
-
// // turn into byte offset by * sizeof i16
|
2332
|
-
// ...number(ValtypeSize.i16, Valtype.i32),
|
2333
|
-
// [ Opcodes.i32_mul ],
|
2334
|
-
// ...(aotPointer ? [] : [ [ Opcodes.i32_add ] ]),
|
2335
|
-
// ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
|
2336
|
-
|
2337
|
-
// ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
|
2338
|
-
// [ Opcodes.local_get, pointerTmp ],
|
2339
|
-
// [ Opcodes.load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
|
2340
|
-
// ], generate(scope, decl.right), number(TYPES.string, Valtype.i32), getNodeType(scope, decl.right))),
|
2341
|
-
// [ Opcodes.local_tee, newValueTmp ],
|
2342
|
-
|
2343
|
-
// Opcodes.i32_to_u,
|
2344
|
-
// [ StoreOps.i16, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
|
2345
|
-
// ]
|
2346
2384
|
}, Blocktype.void),
|
2347
2385
|
|
2348
2386
|
[ Opcodes.local_get, newValueTmp ]
|
@@ -2404,9 +2442,7 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
|
|
2404
2442
|
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
|
2405
2443
|
[ isGlobal ? Opcodes.global_get : Opcodes.local_get, local.idx ],
|
2406
2444
|
|
2407
|
-
|
2408
|
-
|
2409
|
-
...setType(scope, name, TYPES.number)
|
2445
|
+
...setType(scope, name, getNodeType(scope, decl))
|
2410
2446
|
];
|
2411
2447
|
};
|
2412
2448
|
|
@@ -2420,7 +2456,7 @@ const generateUnary = (scope, decl) => {
|
|
2420
2456
|
// * -1
|
2421
2457
|
|
2422
2458
|
if (decl.prefix && decl.argument.type === 'Literal' && typeof decl.argument.value === 'number') {
|
2423
|
-
// if
|
2459
|
+
// if -n, just return that as a const
|
2424
2460
|
return number(-1 * decl.argument.value);
|
2425
2461
|
}
|
2426
2462
|
|
@@ -2430,11 +2466,16 @@ const generateUnary = (scope, decl) => {
|
|
2430
2466
|
];
|
2431
2467
|
|
2432
2468
|
case '!':
|
2469
|
+
const arg = decl.argument;
|
2470
|
+
if (arg.type === 'UnaryExpression' && arg.operator === '!') {
|
2471
|
+
// opt: !!x -> is x truthy
|
2472
|
+
return truthy(scope, generate(scope, arg.argument), getNodeType(scope, arg.argument), false, false);
|
2473
|
+
}
|
2474
|
+
|
2433
2475
|
// !=
|
2434
|
-
return falsy(scope, generate(scope,
|
2476
|
+
return falsy(scope, generate(scope, arg), getNodeType(scope, arg), false, false);
|
2435
2477
|
|
2436
2478
|
case '~':
|
2437
|
-
// todo: does not handle Infinity properly (should convert to 0) (but opt const converting saves us sometimes)
|
2438
2479
|
return [
|
2439
2480
|
...generate(scope, decl.argument),
|
2440
2481
|
Opcodes.i32_to,
|
@@ -2499,6 +2540,7 @@ const generateUnary = (scope, decl) => {
|
|
2499
2540
|
[TYPES.string]: makeString(scope, 'string', false, '#typeof_result'),
|
2500
2541
|
[TYPES.undefined]: makeString(scope, 'undefined', false, '#typeof_result'),
|
2501
2542
|
[TYPES.function]: makeString(scope, 'function', false, '#typeof_result'),
|
2543
|
+
[TYPES.symbol]: makeString(scope, 'symbol', false, '#typeof_result'),
|
2502
2544
|
|
2503
2545
|
[TYPES.bytestring]: makeString(scope, 'string', false, '#typeof_result'),
|
2504
2546
|
|
@@ -2575,21 +2617,16 @@ const generateConditional = (scope, decl) => {
|
|
2575
2617
|
out.push([ Opcodes.if, valtypeBinary ]);
|
2576
2618
|
depth.push('if');
|
2577
2619
|
|
2578
|
-
out.push(...generate(scope, decl.consequent));
|
2579
|
-
|
2580
|
-
// note type
|
2581
2620
|
out.push(
|
2582
|
-
...
|
2583
|
-
...setLastType(scope)
|
2621
|
+
...generate(scope, decl.consequent),
|
2622
|
+
...setLastType(scope, getNodeType(scope, decl.consequent))
|
2584
2623
|
);
|
2585
2624
|
|
2586
2625
|
out.push([ Opcodes.else ]);
|
2587
|
-
out.push(...generate(scope, decl.alternate));
|
2588
2626
|
|
2589
|
-
// note type
|
2590
2627
|
out.push(
|
2591
|
-
...
|
2592
|
-
...setLastType(scope)
|
2628
|
+
...generate(scope, decl.alternate),
|
2629
|
+
...setLastType(scope, getNodeType(scope, decl.alternate))
|
2593
2630
|
);
|
2594
2631
|
|
2595
2632
|
out.push([ Opcodes.end ]);
|
@@ -2725,24 +2762,27 @@ const generateForOf = (scope, decl) => {
|
|
2725
2762
|
|
2726
2763
|
// // todo: we should only do this for strings but we don't know at compile-time :(
|
2727
2764
|
// hack: this is naughty and will break things!
|
2728
|
-
let newOut = number(0, Valtype.
|
2765
|
+
let newOut = number(0, Valtype.i32), newPointer = number(0, Valtype.i32);
|
2729
2766
|
if (pages.hasAnyString) {
|
2730
2767
|
// todo: we use i16 even for bytestrings which should not make a bad thing happen, just be confusing for debugging?
|
2731
2768
|
0, [ newOut, newPointer ] = makeArray(scope, {
|
2732
|
-
rawElements: new Array(
|
2733
|
-
}, isGlobal, leftName, true, 'i16');
|
2769
|
+
rawElements: new Array(0)
|
2770
|
+
}, isGlobal, leftName, true, 'i16', true);
|
2734
2771
|
}
|
2735
2772
|
|
2736
2773
|
// set type for local
|
2737
2774
|
// todo: optimize away counter and use end pointer
|
2738
2775
|
out.push(...typeSwitch(scope, getNodeType(scope, decl.right), {
|
2739
2776
|
[TYPES.array]: [
|
2740
|
-
...setType(scope, leftName, TYPES.number),
|
2741
|
-
|
2742
2777
|
[ Opcodes.loop, Blocktype.void ],
|
2743
2778
|
|
2744
2779
|
[ Opcodes.local_get, pointer ],
|
2745
|
-
[ Opcodes.load,
|
2780
|
+
[ Opcodes.load, 0, ...unsignedLEB128(ValtypeSize.i32) ],
|
2781
|
+
|
2782
|
+
...setType(scope, leftName, [
|
2783
|
+
[ Opcodes.local_get, pointer ],
|
2784
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32 + ValtypeSize[valtype]) ],
|
2785
|
+
]),
|
2746
2786
|
|
2747
2787
|
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
|
2748
2788
|
|
@@ -2751,9 +2791,9 @@ const generateForOf = (scope, decl) => {
|
|
2751
2791
|
...generate(scope, decl.body),
|
2752
2792
|
[ Opcodes.end ],
|
2753
2793
|
|
2754
|
-
// increment iter pointer by valtype size
|
2794
|
+
// increment iter pointer by valtype size + 1
|
2755
2795
|
[ Opcodes.local_get, pointer ],
|
2756
|
-
...number(ValtypeSize[valtype], Valtype.i32),
|
2796
|
+
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
2757
2797
|
[ Opcodes.i32_add ],
|
2758
2798
|
[ Opcodes.local_set, pointer ],
|
2759
2799
|
|
@@ -2774,23 +2814,28 @@ const generateForOf = (scope, decl) => {
|
|
2774
2814
|
[TYPES.string]: [
|
2775
2815
|
...setType(scope, leftName, TYPES.string),
|
2776
2816
|
|
2777
|
-
[ Opcodes.loop, Blocktype.void ],
|
2778
|
-
|
2779
2817
|
// setup new/out array
|
2780
2818
|
...newOut,
|
2781
|
-
[ Opcodes.drop ],
|
2782
2819
|
|
2783
|
-
|
2820
|
+
// set length to 1
|
2821
|
+
...number(1, Valtype.i32),
|
2822
|
+
[ Opcodes.i32_store, 0, 0 ],
|
2823
|
+
|
2824
|
+
[ Opcodes.loop, Blocktype.void ],
|
2825
|
+
|
2826
|
+
// use as pointer for store later
|
2827
|
+
...newPointer,
|
2784
2828
|
|
2785
2829
|
// load current string ind {arg}
|
2786
2830
|
[ Opcodes.local_get, pointer ],
|
2787
|
-
[ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1,
|
2831
|
+
[ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ValtypeSize.i32 ],
|
2788
2832
|
|
2789
2833
|
// store to new string ind 0
|
2790
|
-
[ Opcodes.i32_store16, Math.log2(ValtypeSize.i16) - 1,
|
2834
|
+
[ Opcodes.i32_store16, Math.log2(ValtypeSize.i16) - 1, ValtypeSize.i32 ],
|
2791
2835
|
|
2792
2836
|
// return new string (page)
|
2793
|
-
...
|
2837
|
+
...newPointer,
|
2838
|
+
Opcodes.i32_from_u,
|
2794
2839
|
|
2795
2840
|
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
|
2796
2841
|
|
@@ -2822,25 +2867,30 @@ const generateForOf = (scope, decl) => {
|
|
2822
2867
|
[TYPES.bytestring]: [
|
2823
2868
|
...setType(scope, leftName, TYPES.bytestring),
|
2824
2869
|
|
2825
|
-
[ Opcodes.loop, Blocktype.void ],
|
2826
|
-
|
2827
2870
|
// setup new/out array
|
2828
2871
|
...newOut,
|
2829
|
-
[ Opcodes.drop ],
|
2830
2872
|
|
2831
|
-
|
2873
|
+
// set length to 1
|
2874
|
+
...number(1, Valtype.i32),
|
2875
|
+
[ Opcodes.i32_store, 0, 0 ],
|
2876
|
+
|
2877
|
+
[ Opcodes.loop, Blocktype.void ],
|
2878
|
+
|
2879
|
+
// use as pointer for store later
|
2880
|
+
...newPointer,
|
2832
2881
|
|
2833
2882
|
// load current string ind {arg}
|
2834
2883
|
[ Opcodes.local_get, pointer ],
|
2835
2884
|
[ Opcodes.local_get, counter ],
|
2836
2885
|
[ Opcodes.i32_add ],
|
2837
|
-
[ Opcodes.i32_load8_u, 0,
|
2886
|
+
[ Opcodes.i32_load8_u, 0, ValtypeSize.i32 ],
|
2838
2887
|
|
2839
2888
|
// store to new string ind 0
|
2840
|
-
[ Opcodes.i32_store8, 0,
|
2889
|
+
[ Opcodes.i32_store8, 0, ValtypeSize.i32 ],
|
2841
2890
|
|
2842
2891
|
// return new string (page)
|
2843
|
-
...
|
2892
|
+
...newPointer,
|
2893
|
+
Opcodes.i32_from_u,
|
2844
2894
|
|
2845
2895
|
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
|
2846
2896
|
|
@@ -2869,6 +2919,44 @@ const generateForOf = (scope, decl) => {
|
|
2869
2919
|
[ Opcodes.end ],
|
2870
2920
|
[ Opcodes.end ]
|
2871
2921
|
],
|
2922
|
+
[TYPES.set]: [
|
2923
|
+
[ Opcodes.loop, Blocktype.void ],
|
2924
|
+
|
2925
|
+
[ Opcodes.local_get, pointer ],
|
2926
|
+
[ Opcodes.load, 0, ...unsignedLEB128(ValtypeSize.i32) ],
|
2927
|
+
|
2928
|
+
...setType(scope, leftName, [
|
2929
|
+
[ Opcodes.local_get, pointer ],
|
2930
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32 + ValtypeSize[valtype]) ],
|
2931
|
+
]),
|
2932
|
+
|
2933
|
+
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
|
2934
|
+
|
2935
|
+
[ Opcodes.block, Blocktype.void ],
|
2936
|
+
[ Opcodes.block, Blocktype.void ],
|
2937
|
+
...generate(scope, decl.body),
|
2938
|
+
[ Opcodes.end ],
|
2939
|
+
|
2940
|
+
// increment iter pointer by valtype size + 1
|
2941
|
+
[ Opcodes.local_get, pointer ],
|
2942
|
+
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
2943
|
+
[ Opcodes.i32_add ],
|
2944
|
+
[ Opcodes.local_set, pointer ],
|
2945
|
+
|
2946
|
+
// increment counter by 1
|
2947
|
+
[ Opcodes.local_get, counter ],
|
2948
|
+
...number(1, Valtype.i32),
|
2949
|
+
[ Opcodes.i32_add ],
|
2950
|
+
[ Opcodes.local_tee, counter ],
|
2951
|
+
|
2952
|
+
// loop if counter != length
|
2953
|
+
[ Opcodes.local_get, length ],
|
2954
|
+
[ Opcodes.i32_ne ],
|
2955
|
+
[ Opcodes.br_if, 1 ],
|
2956
|
+
|
2957
|
+
[ Opcodes.end ],
|
2958
|
+
[ Opcodes.end ]
|
2959
|
+
],
|
2872
2960
|
default: internalThrow(scope, 'TypeError', `Tried for..of on non-iterable type`)
|
2873
2961
|
}, Blocktype.void));
|
2874
2962
|
|
@@ -2970,14 +3058,18 @@ const generateThrow = (scope, decl) => {
|
|
2970
3058
|
};
|
2971
3059
|
|
2972
3060
|
const generateTry = (scope, decl) => {
|
2973
|
-
|
3061
|
+
// todo: handle control-flow pre-exit for finally
|
3062
|
+
// "Immediately before a control-flow statement (return, throw, break, continue) is executed in the try block or catch block."
|
2974
3063
|
|
2975
3064
|
const out = [];
|
2976
3065
|
|
3066
|
+
const finalizer = decl.finalizer ? generate(scope, decl.finalizer) : [];
|
3067
|
+
|
2977
3068
|
out.push([ Opcodes.try, Blocktype.void ]);
|
2978
3069
|
depth.push('try');
|
2979
3070
|
|
2980
3071
|
out.push(...generate(scope, decl.block));
|
3072
|
+
out.push(...finalizer);
|
2981
3073
|
|
2982
3074
|
if (decl.handler) {
|
2983
3075
|
depth.pop();
|
@@ -2985,6 +3077,7 @@ const generateTry = (scope, decl) => {
|
|
2985
3077
|
|
2986
3078
|
out.push([ Opcodes.catch_all ]);
|
2987
3079
|
out.push(...generate(scope, decl.handler.body));
|
3080
|
+
out.push(...finalizer);
|
2988
3081
|
}
|
2989
3082
|
|
2990
3083
|
out.push([ Opcodes.end ]);
|
@@ -3012,18 +3105,6 @@ const allocPage = (scope, reason, type) => {
|
|
3012
3105
|
scope.pages ??= new Map();
|
3013
3106
|
scope.pages.set(reason, { ind, type });
|
3014
3107
|
|
3015
|
-
if (Prefs.allocLog) log('alloc', `allocated new page of memory (${ind}) | ${reason} (type: ${type})`);
|
3016
|
-
|
3017
|
-
return ind;
|
3018
|
-
};
|
3019
|
-
|
3020
|
-
// todo: add scope.pages
|
3021
|
-
const freePage = reason => {
|
3022
|
-
const { ind } = pages.get(reason);
|
3023
|
-
pages.delete(reason);
|
3024
|
-
|
3025
|
-
if (Prefs.allocLog) log('alloc', `freed page of memory (${ind}) | ${reason}`);
|
3026
|
-
|
3027
3108
|
return ind;
|
3028
3109
|
};
|
3029
3110
|
|
@@ -3061,34 +3142,58 @@ const compileBytes = (val, itemType) => {
|
|
3061
3142
|
}
|
3062
3143
|
};
|
3063
3144
|
|
3064
|
-
const
|
3065
|
-
|
3066
|
-
|
3067
|
-
|
3145
|
+
const makeData = (scope, elements, offset = null, itemType, initEmpty) => {
|
3146
|
+
const length = elements.length;
|
3147
|
+
|
3148
|
+
// if length is 0 memory/data will just be 0000... anyway
|
3149
|
+
if (length === 0) return false;
|
3150
|
+
|
3151
|
+
let bytes = compileBytes(length, 'i32');
|
3152
|
+
|
3153
|
+
if (!initEmpty) for (let i = 0; i < length; i++) {
|
3154
|
+
if (elements[i] == null) continue;
|
3068
3155
|
|
3069
|
-
|
3156
|
+
bytes.push(...compileBytes(elements[i], itemType));
|
3070
3157
|
}
|
3158
|
+
|
3159
|
+
const obj = { bytes };
|
3160
|
+
if (offset != null) obj.offset = offset;
|
3161
|
+
|
3162
|
+
const idx = data.push(obj) - 1;
|
3163
|
+
|
3164
|
+
scope.data ??= [];
|
3165
|
+
scope.data.push(idx);
|
3166
|
+
|
3167
|
+
return { idx, size: bytes.length };
|
3071
3168
|
};
|
3072
3169
|
|
3073
|
-
const
|
3170
|
+
const printStaticStr = str => {
|
3074
3171
|
const out = [];
|
3075
3172
|
|
3076
|
-
|
3077
|
-
|
3078
|
-
|
3079
|
-
|
3080
|
-
|
3173
|
+
for (let i = 0; i < str.length; i++) {
|
3174
|
+
out.push(
|
3175
|
+
// ...number(str.charCodeAt(i)),
|
3176
|
+
...number(str.charCodeAt(i), Valtype.i32),
|
3177
|
+
Opcodes.i32_from_u,
|
3178
|
+
[ Opcodes.call, importedFuncs.printChar ]
|
3179
|
+
);
|
3180
|
+
}
|
3081
3181
|
|
3082
|
-
|
3083
|
-
|
3182
|
+
return out;
|
3183
|
+
};
|
3084
3184
|
|
3085
|
-
|
3086
|
-
|
3185
|
+
const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false, itemType = valtype, intOut = false, typed = false) => {
|
3186
|
+
if (itemType !== 'i16' && itemType !== 'i8') {
|
3187
|
+
pages.hasArray = true;
|
3188
|
+
} else {
|
3189
|
+
pages.hasAnyString = true;
|
3190
|
+
if (itemType === 'i8') pages.hasByteString = true;
|
3191
|
+
else pages.hasString = true;
|
3087
3192
|
}
|
3088
3193
|
|
3089
|
-
const
|
3194
|
+
const out = [];
|
3090
3195
|
|
3091
|
-
const
|
3196
|
+
const uniqueName = name === '$undeclared' ? name + randId() : name;
|
3092
3197
|
|
3093
3198
|
const useRawElements = !!decl.rawElements;
|
3094
3199
|
const elements = useRawElements ? decl.rawElements : decl.elements;
|
@@ -3096,68 +3201,167 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
3096
3201
|
const valtype = itemTypeToValtype[itemType];
|
3097
3202
|
const length = elements.length;
|
3098
3203
|
|
3099
|
-
|
3100
|
-
|
3101
|
-
|
3102
|
-
|
3204
|
+
const allocated = allocator.alloc({ scope, pages, globals, asmFunc, funcIndex }, uniqueName, { itemType });
|
3205
|
+
|
3206
|
+
let pointer = allocated;
|
3207
|
+
if (allocator.constructor.name !== 'StaticAllocator') {
|
3208
|
+
// const tmp = localTmp(scope, '#makearray_pointer' + uniqueName, Valtype.i32);
|
3209
|
+
const tmp = localTmp(scope, '#makearray_pointer' + name, Valtype.i32);
|
3210
|
+
out.push(
|
3211
|
+
...allocated,
|
3212
|
+
[ Opcodes.local_set, tmp ]
|
3213
|
+
);
|
3214
|
+
|
3215
|
+
if (Prefs.runtimeAllocLog) out.push(
|
3216
|
+
...printStaticStr(`${name}: `),
|
3217
|
+
|
3218
|
+
[ Opcodes.local_get, tmp ],
|
3219
|
+
Opcodes.i32_from_u,
|
3220
|
+
[ Opcodes.call, 0 ],
|
3221
|
+
|
3222
|
+
...number(10),
|
3223
|
+
[ Opcodes.call, 1 ]
|
3224
|
+
);
|
3103
3225
|
|
3104
|
-
|
3105
|
-
if (elements[i] == null) continue;
|
3226
|
+
pointer = [ [ Opcodes.local_get, tmp ] ];
|
3106
3227
|
|
3107
|
-
|
3228
|
+
if (Prefs.data && useRawElements) {
|
3229
|
+
const data = makeData(scope, elements, null, itemType, initEmpty);
|
3230
|
+
if (data) {
|
3231
|
+
// init data
|
3232
|
+
out.push(
|
3233
|
+
...pointer,
|
3234
|
+
...number(0, Valtype.i32),
|
3235
|
+
...number(data.size, Valtype.i32),
|
3236
|
+
[ ...Opcodes.memory_init, ...unsignedLEB128(data.idx), 0 ]
|
3237
|
+
);
|
3108
3238
|
}
|
3109
3239
|
|
3110
|
-
|
3111
|
-
|
3112
|
-
|
3113
|
-
|
3240
|
+
// return pointer in out
|
3241
|
+
out.push(
|
3242
|
+
...pointer,
|
3243
|
+
...(!intOut ? [ Opcodes.i32_from_u ] : [])
|
3244
|
+
);
|
3114
3245
|
|
3115
|
-
|
3116
|
-
scope.data.push(ind);
|
3246
|
+
return [ out, pointer ];
|
3117
3247
|
}
|
3248
|
+
} else {
|
3249
|
+
const rawPtr = read_signedLEB128(pointer[0].slice(1));
|
3118
3250
|
|
3119
|
-
|
3120
|
-
|
3251
|
+
scope.arrays ??= new Map();
|
3252
|
+
const firstAssign = !scope.arrays.has(uniqueName);
|
3253
|
+
if (firstAssign) scope.arrays.set(uniqueName, rawPtr);
|
3121
3254
|
|
3122
|
-
|
3123
|
-
|
3255
|
+
if (Prefs.data && firstAssign && useRawElements) {
|
3256
|
+
makeData(scope, elements, rawPtr, itemType, initEmpty);
|
3124
3257
|
|
3125
|
-
|
3126
|
-
|
3127
|
-
|
3128
|
-
|
3129
|
-
|
3130
|
-
|
3131
|
-
)
|
3258
|
+
// local value as pointer
|
3259
|
+
return [ number(rawPtr, intOut ? Valtype.i32 : valtypeBinary), pointer ];
|
3260
|
+
}
|
3261
|
+
|
3262
|
+
const local = global ? globals[name] : scope.locals[name];
|
3263
|
+
const pointerTmp = local != null ? localTmp(scope, '#makearray_pointer_tmp', Valtype.i32) : null;
|
3264
|
+
if (pointerTmp != null) {
|
3265
|
+
out.push(
|
3266
|
+
[ global ? Opcodes.global_get : Opcodes.local_get, local.idx ],
|
3267
|
+
Opcodes.i32_to_u,
|
3268
|
+
[ Opcodes.local_set, pointerTmp ]
|
3269
|
+
);
|
3270
|
+
|
3271
|
+
pointer = [ [ Opcodes.local_get, pointerTmp ] ];
|
3272
|
+
}
|
3132
3273
|
}
|
3133
3274
|
|
3134
|
-
const pointerWasm = pointerTmp != null ? [ [ Opcodes.local_get, pointerTmp ] ] : number(pointer, Valtype.i32);
|
3135
3275
|
|
3136
|
-
// store length
|
3276
|
+
// store length
|
3137
3277
|
out.push(
|
3138
|
-
...
|
3278
|
+
...pointer,
|
3139
3279
|
...number(length, Valtype.i32),
|
3140
3280
|
[ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, 0 ]
|
3141
3281
|
);
|
3142
3282
|
|
3143
3283
|
const storeOp = StoreOps[itemType];
|
3144
|
-
|
3284
|
+
const sizePerEl = ValtypeSize[itemType] + (typed ? 1 : 0);
|
3145
3285
|
if (!initEmpty) for (let i = 0; i < length; i++) {
|
3146
3286
|
if (elements[i] == null) continue;
|
3147
3287
|
|
3288
|
+
const offset = ValtypeSize.i32 + i * sizePerEl;
|
3148
3289
|
out.push(
|
3149
|
-
...
|
3290
|
+
...pointer,
|
3150
3291
|
...(useRawElements ? number(elements[i], Valtype[valtype]) : generate(scope, elements[i])),
|
3151
|
-
[ storeOp,
|
3292
|
+
[ storeOp, 0, ...unsignedLEB128(offset) ],
|
3293
|
+
...(!typed ? [] : [ // typed presumes !useRawElements
|
3294
|
+
...pointer,
|
3295
|
+
...getNodeType(scope, elements[i]),
|
3296
|
+
[ Opcodes.i32_store8, 0, ...unsignedLEB128(offset + ValtypeSize[itemType]) ]
|
3297
|
+
])
|
3152
3298
|
);
|
3153
3299
|
}
|
3154
3300
|
|
3155
3301
|
// local value as pointer
|
3156
|
-
out.push(...
|
3302
|
+
out.push(...pointer);
|
3303
|
+
if (!intOut) out.push(Opcodes.i32_from_u);
|
3157
3304
|
|
3158
3305
|
return [ out, pointer ];
|
3159
3306
|
};
|
3160
3307
|
|
3308
|
+
const storeArray = (scope, array, index, element) => {
|
3309
|
+
if (!Array.isArray(element)) element = generate(scope, element);
|
3310
|
+
if (typeof index === 'number') index = number(index);
|
3311
|
+
|
3312
|
+
const offset = localTmp(scope, '#storeArray_offset', Valtype.i32);
|
3313
|
+
|
3314
|
+
return [
|
3315
|
+
// calculate offset
|
3316
|
+
...index,
|
3317
|
+
Opcodes.i32_to_u,
|
3318
|
+
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
3319
|
+
[ Opcodes.i32_mul ],
|
3320
|
+
|
3321
|
+
...array,
|
3322
|
+
Opcodes.i32_to_u,
|
3323
|
+
[ Opcodes.i32_add ],
|
3324
|
+
[ Opcodes.local_set, offset ],
|
3325
|
+
|
3326
|
+
// store value
|
3327
|
+
[ Opcodes.local_get, offset ],
|
3328
|
+
...generate(scope, element),
|
3329
|
+
[ Opcodes.store, 0, ValtypeSize.i32 ],
|
3330
|
+
|
3331
|
+
// store type
|
3332
|
+
[ Opcodes.local_get, offset ],
|
3333
|
+
...getNodeType(scope, element),
|
3334
|
+
[ Opcodes.i32_store8, 0, ValtypeSize.i32 + ValtypeSize[valtype] ]
|
3335
|
+
];
|
3336
|
+
};
|
3337
|
+
|
3338
|
+
const loadArray = (scope, array, index) => {
|
3339
|
+
if (typeof index === 'number') index = number(index);
|
3340
|
+
|
3341
|
+
const offset = localTmp(scope, '#loadArray_offset', Valtype.i32);
|
3342
|
+
|
3343
|
+
return [
|
3344
|
+
// calculate offset
|
3345
|
+
...index,
|
3346
|
+
Opcodes.i32_to_u,
|
3347
|
+
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
3348
|
+
[ Opcodes.i32_mul ],
|
3349
|
+
|
3350
|
+
...array,
|
3351
|
+
Opcodes.i32_to_u,
|
3352
|
+
[ Opcodes.i32_add ],
|
3353
|
+
[ Opcodes.local_set, offset ],
|
3354
|
+
|
3355
|
+
// load value
|
3356
|
+
[ Opcodes.local_get, offset ],
|
3357
|
+
[ Opcodes.load, 0, ValtypeSize.i32 ],
|
3358
|
+
|
3359
|
+
// load type
|
3360
|
+
[ Opcodes.local_get, offset ],
|
3361
|
+
[ Opcodes.i32_load8_u, 0, ValtypeSize.i32 + ValtypeSize[valtype] ]
|
3362
|
+
];
|
3363
|
+
};
|
3364
|
+
|
3161
3365
|
const byteStringable = str => {
|
3162
3366
|
if (!Prefs.bytestring) return false;
|
3163
3367
|
|
@@ -3186,14 +3390,25 @@ const makeString = (scope, str, global = false, name = '$undeclared', forceBytes
|
|
3186
3390
|
};
|
3187
3391
|
|
3188
3392
|
const generateArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false) => {
|
3189
|
-
return makeArray(scope, decl, global, name, initEmpty, valtype)[0];
|
3393
|
+
return makeArray(scope, decl, global, name, initEmpty, valtype, false, true)[0];
|
3190
3394
|
};
|
3191
3395
|
|
3192
|
-
|
3193
|
-
|
3194
|
-
const pointer = scope.arrays?.get(name);
|
3396
|
+
const generateObject = (scope, decl, global = false, name = '$undeclared') => {
|
3397
|
+
if (decl.properties.length > 0) return todo(scope, 'objects are not supported yet', true);
|
3195
3398
|
|
3196
|
-
|
3399
|
+
return [
|
3400
|
+
...number(1),
|
3401
|
+
...setLastType(scope, TYPES.object)
|
3402
|
+
];
|
3403
|
+
};
|
3404
|
+
|
3405
|
+
const withType = (scope, wasm, type) => [
|
3406
|
+
...wasm,
|
3407
|
+
...setLastType(scope, type)
|
3408
|
+
];
|
3409
|
+
|
3410
|
+
const generateMember = (scope, decl, _global, _name) => {
|
3411
|
+
const name = decl.object.name;
|
3197
3412
|
|
3198
3413
|
// hack: .name
|
3199
3414
|
if (decl.property.name === 'name') {
|
@@ -3203,9 +3418,9 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3203
3418
|
// eg: __String_prototype_toLowerCase -> toLowerCase
|
3204
3419
|
if (nameProp.startsWith('__')) nameProp = nameProp.split('_').pop();
|
3205
3420
|
|
3206
|
-
return makeString(scope, nameProp, _global, _name, true);
|
3421
|
+
return withType(scope, makeString(scope, nameProp, _global, _name, true), TYPES.bytestring);
|
3207
3422
|
} else {
|
3208
|
-
return
|
3423
|
+
return withType(scope, number(0), TYPES.undefined);
|
3209
3424
|
}
|
3210
3425
|
}
|
3211
3426
|
|
@@ -3213,9 +3428,8 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3213
3428
|
if (decl.property.name === 'length') {
|
3214
3429
|
const func = funcs.find(x => x.name === name);
|
3215
3430
|
if (func) {
|
3216
|
-
const
|
3217
|
-
|
3218
|
-
return number(typedParams ? func.params.length / 2 : func.params.length);
|
3431
|
+
const typedParams = !func.internal || builtinFuncs[name]?.typedParams;
|
3432
|
+
return withType(scope, number(typedParams ? func.params.length / 2 : func.params.length), TYPES.number);
|
3219
3433
|
}
|
3220
3434
|
|
3221
3435
|
if (builtinFuncs[name + '$constructor']) {
|
@@ -3225,65 +3439,110 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3225
3439
|
const constructorFunc = builtinFuncs[name + '$constructor'];
|
3226
3440
|
const constructorParams = constructorFunc.typedParams ? (constructorFunc.params.length / 2) : constructorFunc.params.length;
|
3227
3441
|
|
3228
|
-
return number(Math.max(regularParams, constructorParams));
|
3442
|
+
return withType(scope, number(Math.max(regularParams, constructorParams)), TYPES.number);
|
3229
3443
|
}
|
3230
3444
|
|
3231
|
-
if (builtinFuncs[name]) return number(builtinFuncs[name].typedParams ? (builtinFuncs[name].params.length / 2) : builtinFuncs[name].params.length);
|
3232
|
-
if (importedFuncs[name]) return number(importedFuncs[name].params);
|
3233
|
-
if (internalConstrs[name]) return number(internalConstrs[name].length ?? 0);
|
3445
|
+
if (builtinFuncs[name]) return withType(scope, number(builtinFuncs[name].typedParams ? (builtinFuncs[name].params.length / 2) : builtinFuncs[name].params.length), TYPES.number);
|
3446
|
+
if (importedFuncs[name]) return withType(scope, number(importedFuncs[name].params.length ?? importedFuncs[name].params), TYPES.number);
|
3447
|
+
if (internalConstrs[name]) return withType(scope, number(internalConstrs[name].length ?? 0), TYPES.number);
|
3448
|
+
|
3449
|
+
if (Prefs.fastLength) {
|
3450
|
+
// presume valid length object
|
3451
|
+
return [
|
3452
|
+
...generate(scope, decl.object),
|
3453
|
+
Opcodes.i32_to_u,
|
3454
|
+
|
3455
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
|
3456
|
+
Opcodes.i32_from_u
|
3457
|
+
];
|
3458
|
+
}
|
3459
|
+
|
3460
|
+
const type = getNodeType(scope, decl.object);
|
3461
|
+
const known = knownType(scope, type);
|
3462
|
+
if (known != null) {
|
3463
|
+
if ([ TYPES.string, TYPES.bytestring, TYPES.array ].includes(known)) return [
|
3464
|
+
...generate(scope, decl.object),
|
3465
|
+
Opcodes.i32_to_u,
|
3466
|
+
|
3467
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
|
3468
|
+
Opcodes.i32_from_u
|
3469
|
+
];
|
3470
|
+
|
3471
|
+
return number(0);
|
3472
|
+
}
|
3234
3473
|
|
3235
3474
|
return [
|
3236
|
-
...(
|
3475
|
+
...typeIsOneOf(getNodeType(scope, decl.object), [ TYPES.string, TYPES.bytestring, TYPES.array ]),
|
3476
|
+
[ Opcodes.if, valtypeBinary ],
|
3237
3477
|
...generate(scope, decl.object),
|
3238
|
-
Opcodes.i32_to_u
|
3239
|
-
|
3478
|
+
Opcodes.i32_to_u,
|
3479
|
+
|
3480
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
|
3481
|
+
Opcodes.i32_from_u,
|
3240
3482
|
|
3241
|
-
|
3242
|
-
Opcodes.
|
3483
|
+
...setLastType(scope, TYPES.number),
|
3484
|
+
[ Opcodes.else ],
|
3485
|
+
...number(0),
|
3486
|
+
...setLastType(scope, TYPES.undefined),
|
3487
|
+
[ Opcodes.end ]
|
3243
3488
|
];
|
3244
3489
|
}
|
3245
3490
|
|
3491
|
+
// todo: generate this array procedurally during builtinFuncs creation
|
3492
|
+
if (['size', 'description'].includes(decl.property.name)) {
|
3493
|
+
const bc = {};
|
3494
|
+
const cands = Object.keys(builtinFuncs).filter(x => x.startsWith('__') && x.endsWith('_prototype_' + decl.property.name + '$get'));
|
3495
|
+
|
3496
|
+
if (cands.length > 0) {
|
3497
|
+
for (const x of cands) {
|
3498
|
+
const type = TYPES[x.split('_prototype_')[0].slice(2).toLowerCase()];
|
3499
|
+
if (type == null) continue;
|
3500
|
+
|
3501
|
+
bc[type] = generateCall(scope, {
|
3502
|
+
callee: {
|
3503
|
+
type: 'Identifier',
|
3504
|
+
name: x
|
3505
|
+
},
|
3506
|
+
arguments: [ decl.object ],
|
3507
|
+
_protoInternalCall: true
|
3508
|
+
});
|
3509
|
+
}
|
3510
|
+
}
|
3511
|
+
|
3512
|
+
return typeSwitch(scope, getNodeType(scope, decl.object), {
|
3513
|
+
...bc,
|
3514
|
+
default: withType(scope, number(0), TYPES.undefined)
|
3515
|
+
}, valtypeBinary);
|
3516
|
+
}
|
3517
|
+
|
3246
3518
|
const object = generate(scope, decl.object);
|
3247
3519
|
const property = generate(scope, decl.property);
|
3248
3520
|
|
3249
3521
|
// // todo: we should only do this for strings but we don't know at compile-time :(
|
3250
3522
|
// hack: this is naughty and will break things!
|
3251
|
-
let newOut = number(0,
|
3252
|
-
if (pages.hasAnyString) {
|
3523
|
+
let newOut = number(0, Valtype.i32), newPointer = number(0, Valtype.i32);
|
3524
|
+
if (pages.hasAnyString && knownType(scope, getNodeType(scope, decl.object)) !== TYPES.array) {
|
3525
|
+
// todo: we use i16 even for bytestrings which should not make a bad thing happen, just be confusing for debugging?
|
3253
3526
|
0, [ newOut, newPointer ] = makeArray(scope, {
|
3254
|
-
rawElements: new Array(
|
3255
|
-
}, _global, _name, true, 'i16');
|
3527
|
+
rawElements: new Array(0)
|
3528
|
+
}, _global, _name, true, 'i16', true);
|
3256
3529
|
}
|
3257
3530
|
|
3258
3531
|
return typeSwitch(scope, getNodeType(scope, decl.object), {
|
3259
3532
|
[TYPES.array]: [
|
3260
|
-
|
3261
|
-
...property,
|
3262
|
-
|
3263
|
-
// convert to i32 and turn into byte offset by * valtypeSize (4 for i32, 8 for i64/f64)
|
3264
|
-
Opcodes.i32_to_u,
|
3265
|
-
...number(ValtypeSize[valtype], Valtype.i32),
|
3266
|
-
[ Opcodes.i32_mul ],
|
3267
|
-
|
3268
|
-
...(aotPointer ? [] : [
|
3269
|
-
...object,
|
3270
|
-
Opcodes.i32_to_u,
|
3271
|
-
[ Opcodes.i32_add ]
|
3272
|
-
]),
|
3273
|
-
|
3274
|
-
// read from memory
|
3275
|
-
[ Opcodes.load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
3276
|
-
|
3277
|
-
...number(TYPES.number, Valtype.i32),
|
3533
|
+
...loadArray(scope, object, property),
|
3278
3534
|
...setLastType(scope)
|
3279
3535
|
],
|
3280
|
-
|
3281
3536
|
[TYPES.string]: [
|
3282
3537
|
// setup new/out array
|
3283
3538
|
...newOut,
|
3284
|
-
[ Opcodes.drop ],
|
3285
3539
|
|
3286
|
-
|
3540
|
+
// set length to 1
|
3541
|
+
...number(1, Valtype.i32),
|
3542
|
+
[ Opcodes.i32_store, 0, 0 ],
|
3543
|
+
|
3544
|
+
// use as pointer for store later
|
3545
|
+
...newPointer,
|
3287
3546
|
|
3288
3547
|
...property,
|
3289
3548
|
Opcodes.i32_to_u,
|
@@ -3291,58 +3550,56 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3291
3550
|
...number(ValtypeSize.i16, Valtype.i32),
|
3292
3551
|
[ Opcodes.i32_mul ],
|
3293
3552
|
|
3294
|
-
...
|
3295
|
-
|
3296
|
-
|
3297
|
-
[ Opcodes.i32_add ]
|
3298
|
-
]),
|
3553
|
+
...object,
|
3554
|
+
Opcodes.i32_to_u,
|
3555
|
+
[ Opcodes.i32_add ],
|
3299
3556
|
|
3300
3557
|
// load current string ind {arg}
|
3301
|
-
[ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1,
|
3558
|
+
[ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ValtypeSize.i32 ],
|
3302
3559
|
|
3303
3560
|
// store to new string ind 0
|
3304
|
-
[ Opcodes.i32_store16, Math.log2(ValtypeSize.i16) - 1,
|
3561
|
+
[ Opcodes.i32_store16, Math.log2(ValtypeSize.i16) - 1, ValtypeSize.i32 ],
|
3305
3562
|
|
3306
3563
|
// return new string (page)
|
3307
|
-
...
|
3308
|
-
|
3309
|
-
...
|
3310
|
-
...setLastType(scope)
|
3564
|
+
...newPointer,
|
3565
|
+
Opcodes.i32_from_u,
|
3566
|
+
...setLastType(scope, TYPES.string)
|
3311
3567
|
],
|
3312
3568
|
[TYPES.bytestring]: [
|
3313
3569
|
// setup new/out array
|
3314
3570
|
...newOut,
|
3315
|
-
[ Opcodes.drop ],
|
3316
3571
|
|
3317
|
-
|
3572
|
+
// set length to 1
|
3573
|
+
...number(1, Valtype.i32),
|
3574
|
+
[ Opcodes.i32_store, 0, 0 ],
|
3575
|
+
|
3576
|
+
// use as pointer for store later
|
3577
|
+
...newPointer,
|
3318
3578
|
|
3319
3579
|
...property,
|
3320
3580
|
Opcodes.i32_to_u,
|
3321
3581
|
|
3322
|
-
...
|
3323
|
-
|
3324
|
-
|
3325
|
-
[ Opcodes.i32_add ]
|
3326
|
-
]),
|
3582
|
+
...object,
|
3583
|
+
Opcodes.i32_to_u,
|
3584
|
+
[ Opcodes.i32_add ],
|
3327
3585
|
|
3328
3586
|
// load current string ind {arg}
|
3329
|
-
[ Opcodes.i32_load8_u, 0,
|
3587
|
+
[ Opcodes.i32_load8_u, 0, ValtypeSize.i32 ],
|
3330
3588
|
|
3331
3589
|
// store to new string ind 0
|
3332
|
-
[ Opcodes.i32_store8, 0,
|
3590
|
+
[ Opcodes.i32_store8, 0, ValtypeSize.i32 ],
|
3333
3591
|
|
3334
3592
|
// return new string (page)
|
3335
|
-
...
|
3336
|
-
|
3337
|
-
...
|
3338
|
-
...setLastType(scope)
|
3593
|
+
...newPointer,
|
3594
|
+
Opcodes.i32_from_u,
|
3595
|
+
...setLastType(scope, TYPES.bytestring)
|
3339
3596
|
],
|
3340
3597
|
|
3341
3598
|
default: internalThrow(scope, 'TypeError', 'Member expression is not supported for non-string non-array yet', true)
|
3342
3599
|
});
|
3343
3600
|
};
|
3344
3601
|
|
3345
|
-
const randId = () => Math.random().toString(16).slice(
|
3602
|
+
const randId = () => Math.random().toString(16).slice(1, -2).padEnd(12, '0');
|
3346
3603
|
|
3347
3604
|
const objectHack = node => {
|
3348
3605
|
if (!node) return node;
|
@@ -3360,7 +3617,7 @@ const objectHack = node => {
|
|
3360
3617
|
if (!objectName) objectName = objectHack(node.object)?.name?.slice?.(2);
|
3361
3618
|
|
3362
3619
|
// if .name or .length, give up (hack within a hack!)
|
3363
|
-
if (['name', 'length'].includes(node.property.name)) {
|
3620
|
+
if (['name', 'length', 'size', 'description'].includes(node.property.name)) {
|
3364
3621
|
node.object = objectHack(node.object);
|
3365
3622
|
return;
|
3366
3623
|
}
|
@@ -3394,36 +3651,59 @@ const generateFunc = (scope, decl) => {
|
|
3394
3651
|
if (decl.async) return todo(scope, 'async functions are not supported');
|
3395
3652
|
if (decl.generator) return todo(scope, 'generator functions are not supported');
|
3396
3653
|
|
3397
|
-
const name = decl.id ? decl.id.name : `
|
3654
|
+
const name = decl.id ? decl.id.name : `anonymous${randId()}`;
|
3398
3655
|
const params = decl.params ?? [];
|
3399
3656
|
|
3400
|
-
// const innerScope = { ...scope };
|
3401
3657
|
// TODO: share scope/locals between !!!
|
3402
|
-
const
|
3658
|
+
const func = {
|
3403
3659
|
locals: {},
|
3404
3660
|
localInd: 0,
|
3405
3661
|
// value, type
|
3406
3662
|
returns: [ valtypeBinary, Valtype.i32 ],
|
3407
3663
|
throws: false,
|
3408
|
-
name
|
3664
|
+
name,
|
3665
|
+
index: currentFuncIndex++
|
3409
3666
|
};
|
3410
3667
|
|
3668
|
+
funcIndex[name] = func.index;
|
3669
|
+
funcs.push(func);
|
3670
|
+
|
3411
3671
|
if (typedInput && decl.returnType) {
|
3412
3672
|
const { type } = extractTypeAnnotation(decl.returnType);
|
3413
|
-
if (type != null && !Prefs.indirectCalls) {
|
3414
|
-
|
3415
|
-
|
3673
|
+
// if (type != null && !Prefs.indirectCalls) {
|
3674
|
+
if (type != null) {
|
3675
|
+
func.returnType = type;
|
3676
|
+
func.returns = [ valtypeBinary ];
|
3416
3677
|
}
|
3417
3678
|
}
|
3418
3679
|
|
3680
|
+
const defaultValues = {};
|
3419
3681
|
for (let i = 0; i < params.length; i++) {
|
3420
|
-
|
3682
|
+
let name;
|
3683
|
+
const x = params[i];
|
3684
|
+
switch (x.type) {
|
3685
|
+
case 'Identifier': {
|
3686
|
+
name = x.name;
|
3687
|
+
break;
|
3688
|
+
}
|
3421
3689
|
|
3690
|
+
case 'AssignmentPattern': {
|
3691
|
+
name = x.left.name;
|
3692
|
+
defaultValues[name] = x.right;
|
3693
|
+
break;
|
3694
|
+
}
|
3695
|
+
}
|
3696
|
+
|
3697
|
+
// if (name == null) return todo('non-identifier args are not supported');
|
3698
|
+
|
3699
|
+
allocVar(func, name, false);
|
3422
3700
|
if (typedInput && params[i].typeAnnotation) {
|
3423
|
-
addVarMetadata(
|
3701
|
+
addVarMetadata(func, name, false, extractTypeAnnotation(params[i]));
|
3424
3702
|
}
|
3425
3703
|
}
|
3426
3704
|
|
3705
|
+
func.params = Object.values(func.locals).map(x => x.type);
|
3706
|
+
|
3427
3707
|
let body = objectHack(decl.body);
|
3428
3708
|
if (decl.type === 'ArrowFunctionExpression' && decl.expression) {
|
3429
3709
|
// hack: () => 0 -> () => return 0
|
@@ -3433,37 +3713,34 @@ const generateFunc = (scope, decl) => {
|
|
3433
3713
|
};
|
3434
3714
|
}
|
3435
3715
|
|
3436
|
-
const
|
3437
|
-
const
|
3438
|
-
|
3439
|
-
|
3440
|
-
|
3441
|
-
|
3442
|
-
|
3443
|
-
|
3444
|
-
|
3445
|
-
if (name === 'main') func.gotLastType = true;
|
3716
|
+
const prelude = [];
|
3717
|
+
for (const x in defaultValues) {
|
3718
|
+
prelude.push(
|
3719
|
+
...getType(func, x),
|
3720
|
+
...number(TYPES.undefined, Valtype.i32),
|
3721
|
+
[ Opcodes.i32_eq ],
|
3722
|
+
[ Opcodes.if, Blocktype.void ],
|
3723
|
+
...generate(func, defaultValues[x], false, x),
|
3724
|
+
[ Opcodes.local_set, func.locals[x].idx ],
|
3446
3725
|
|
3447
|
-
|
3448
|
-
|
3449
|
-
|
3450
|
-
inst[1] = func.index;
|
3451
|
-
}
|
3726
|
+
...setType(func, x, getNodeType(scope, defaultValues[x])),
|
3727
|
+
[ Opcodes.end ]
|
3728
|
+
);
|
3452
3729
|
}
|
3453
3730
|
|
3731
|
+
const wasm = func.wasm = prelude.concat(generate(func, body));
|
3732
|
+
|
3733
|
+
if (name === 'main') func.gotLastType = true;
|
3734
|
+
|
3454
3735
|
// add end return if not found
|
3455
3736
|
if (name !== 'main' && wasm[wasm.length - 1]?.[0] !== Opcodes.return && countLeftover(wasm) === 0) {
|
3456
3737
|
wasm.push(
|
3457
3738
|
...number(0),
|
3458
|
-
...(
|
3739
|
+
...(func.returnType != null ? [] : number(TYPES.undefined, Valtype.i32)),
|
3459
3740
|
[ Opcodes.return ]
|
3460
3741
|
);
|
3461
3742
|
}
|
3462
3743
|
|
3463
|
-
func.wasm = wasm;
|
3464
|
-
|
3465
|
-
funcs.push(func);
|
3466
|
-
|
3467
3744
|
return func;
|
3468
3745
|
};
|
3469
3746
|
|
@@ -3487,9 +3764,9 @@ const internalConstrs = {
|
|
3487
3764
|
|
3488
3765
|
// new Array(n)
|
3489
3766
|
|
3490
|
-
const [ , pointer ] = makeArray(scope, {
|
3767
|
+
const [ out, pointer ] = makeArray(scope, {
|
3491
3768
|
rawElements: new Array(0)
|
3492
|
-
}, global, name, true);
|
3769
|
+
}, global, name, true, undefined, true);
|
3493
3770
|
|
3494
3771
|
const arg = decl.arguments[0] ?? DEFAULT_VALUE;
|
3495
3772
|
|
@@ -3498,12 +3775,13 @@ const internalConstrs = {
|
|
3498
3775
|
if (literalValue < 0 || !Number.isFinite(literalValue) || literalValue > 4294967295) return internalThrow(scope, 'RangeThrow', 'Invalid array length', true);
|
3499
3776
|
|
3500
3777
|
return [
|
3501
|
-
...
|
3778
|
+
...out,
|
3502
3779
|
...generate(scope, arg, global, name),
|
3503
3780
|
Opcodes.i32_to_u,
|
3504
|
-
[ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1,
|
3781
|
+
[ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, 0 ],
|
3505
3782
|
|
3506
|
-
...
|
3783
|
+
...pointer,
|
3784
|
+
Opcodes.i32_from_u
|
3507
3785
|
];
|
3508
3786
|
},
|
3509
3787
|
type: TYPES.array,
|
@@ -3567,7 +3845,7 @@ const internalConstrs = {
|
|
3567
3845
|
generate: (scope, decl) => {
|
3568
3846
|
// todo: boolean object when used as constructor
|
3569
3847
|
const arg = decl.arguments[0] ?? DEFAULT_VALUE;
|
3570
|
-
return truthy(scope, generate(scope, arg), getNodeType(scope, arg));
|
3848
|
+
return truthy(scope, generate(scope, arg), getNodeType(scope, arg), false, false, 'full');
|
3571
3849
|
},
|
3572
3850
|
type: TYPES.boolean,
|
3573
3851
|
length: 1
|
@@ -3628,8 +3906,10 @@ const internalConstrs = {
|
|
3628
3906
|
}),
|
3629
3907
|
|
3630
3908
|
// print space
|
3631
|
-
...
|
3632
|
-
|
3909
|
+
...(i !== decl.arguments.length - 1 ? [
|
3910
|
+
...number(32),
|
3911
|
+
[ Opcodes.call, importedFuncs.printChar ]
|
3912
|
+
] : [])
|
3633
3913
|
);
|
3634
3914
|
}
|
3635
3915
|
|
@@ -3639,6 +3919,8 @@ const internalConstrs = {
|
|
3639
3919
|
[ Opcodes.call, importedFuncs.printChar ]
|
3640
3920
|
);
|
3641
3921
|
|
3922
|
+
out.push(...number(UNDEFINED));
|
3923
|
+
|
3642
3924
|
return out;
|
3643
3925
|
},
|
3644
3926
|
type: TYPES.undefined,
|
@@ -3648,8 +3930,9 @@ const internalConstrs = {
|
|
3648
3930
|
};
|
3649
3931
|
|
3650
3932
|
export default program => {
|
3651
|
-
globals = {
|
3652
|
-
|
3933
|
+
globals = {
|
3934
|
+
['#ind']: 0
|
3935
|
+
};
|
3653
3936
|
tags = [];
|
3654
3937
|
exceptions = [];
|
3655
3938
|
funcs = [];
|
@@ -3659,19 +3942,8 @@ export default program => {
|
|
3659
3942
|
data = [];
|
3660
3943
|
currentFuncIndex = importedFuncs.length;
|
3661
3944
|
|
3662
|
-
globalThis.valtype = 'f64';
|
3663
|
-
|
3664
|
-
const valtypeOpt = process.argv.find(x => x.startsWith('--valtype='));
|
3665
|
-
if (valtypeOpt) valtype = valtypeOpt.split('=')[1];
|
3666
|
-
|
3667
|
-
globalThis.valtypeBinary = Valtype[valtype];
|
3668
|
-
|
3669
3945
|
const valtypeInd = ['i32', 'i64', 'f64'].indexOf(valtype);
|
3670
3946
|
|
3671
|
-
globalThis.pageSize = PageSize;
|
3672
|
-
const pageSizeOpt = process.argv.find(x => x.startsWith('--page-size='));
|
3673
|
-
if (pageSizeOpt) pageSize = parseInt(pageSizeOpt.split('=')[1]) * 1024;
|
3674
|
-
|
3675
3947
|
// set generic opcodes for current valtype
|
3676
3948
|
Opcodes.const = [ Opcodes.i32_const, Opcodes.i64_const, Opcodes.f64_const ][valtypeInd];
|
3677
3949
|
Opcodes.eq = [ Opcodes.i32_eq, Opcodes.i64_eq, Opcodes.f64_eq ][valtypeInd];
|
@@ -3693,6 +3965,7 @@ export default program => {
|
|
3693
3965
|
builtinFuncs = new BuiltinFuncs();
|
3694
3966
|
builtinVars = new BuiltinVars();
|
3695
3967
|
prototypeFuncs = new PrototypeFuncs();
|
3968
|
+
allocator = makeAllocator(Prefs.allocator ?? 'static');
|
3696
3969
|
|
3697
3970
|
program.id = { name: 'main' };
|
3698
3971
|
|
@@ -3708,9 +3981,8 @@ export default program => {
|
|
3708
3981
|
|
3709
3982
|
if (Prefs.astLog) console.log(JSON.stringify(program.body.body, null, 2));
|
3710
3983
|
|
3711
|
-
generateFunc(scope, program);
|
3984
|
+
const main = generateFunc(scope, program);
|
3712
3985
|
|
3713
|
-
const main = funcs[funcs.length - 1];
|
3714
3986
|
main.export = true;
|
3715
3987
|
main.returns = [ valtypeBinary, Valtype.i32 ];
|
3716
3988
|
|
@@ -3736,8 +4008,10 @@ export default program => {
|
|
3736
4008
|
else main.returns = [];
|
3737
4009
|
}
|
3738
4010
|
|
4011
|
+
delete globals['#ind'];
|
4012
|
+
|
3739
4013
|
// if blank main func and other exports, remove it
|
3740
|
-
if (main.wasm.length === 0 && funcs.reduce((acc, x) => acc + (x.export ? 1 : 0), 0) > 1) funcs.splice(
|
4014
|
+
if (main.wasm.length === 0 && funcs.reduce((acc, x) => acc + (x.export ? 1 : 0), 0) > 1) funcs.splice(main.index - importedFuncs.length, 1);
|
3741
4015
|
|
3742
4016
|
return { funcs, globals, tags, exceptions, pages, data };
|
3743
4017
|
};
|