porffor 0.2.0-fdf0fc5 → 0.14.0-0ad2c8a7c
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 +12 -7
- package/README.md +18 -15
- package/asur/index.js +1 -1
- package/byg/index.js +3 -24
- package/compiler/2c.js +69 -55
- package/compiler/assemble.js +51 -11
- package/compiler/builtins/annexb_string.js +10 -10
- package/compiler/builtins/annexb_string.ts +4 -3
- package/compiler/builtins/array.ts +85 -9
- package/compiler/builtins/base64.ts +2 -1
- package/compiler/builtins/boolean.ts +2 -2
- package/compiler/builtins/console.ts +4 -0
- package/compiler/builtins/crypto.ts +2 -1
- package/compiler/builtins/date.ts +2 -3
- package/compiler/builtins/error.js +22 -0
- package/compiler/builtins/escape.ts +2 -3
- package/compiler/builtins/function.ts +1 -1
- package/compiler/builtins/int.ts +1 -1
- package/compiler/builtins/math.ts +410 -0
- package/compiler/builtins/number.ts +4 -7
- package/compiler/builtins/object.ts +1 -1
- package/compiler/builtins/porffor.d.ts +9 -8
- package/compiler/builtins/set.ts +197 -3
- package/compiler/builtins/string.ts +2 -1
- package/compiler/builtins/symbol.ts +62 -0
- package/compiler/builtins.js +30 -15
- package/compiler/codegen.js +641 -365
- package/compiler/decompile.js +7 -3
- package/compiler/embedding.js +2 -2
- package/compiler/encoding.js +0 -14
- package/compiler/expression.js +1 -1
- package/compiler/generated_builtins.js +781 -187
- package/compiler/index.js +5 -11
- package/compiler/opt.js +7 -7
- package/compiler/parse.js +2 -4
- package/compiler/precompile.js +18 -25
- package/compiler/prefs.js +6 -2
- package/compiler/prototype.js +185 -162
- package/compiler/wasmSpec.js +5 -0
- package/compiler/wrap.js +150 -90
- package/package.json +1 -1
- package/runner/compare.js +0 -1
- package/runner/debug.js +1 -6
- package/runner/index.js +5 -4
- package/runner/profiler.js +15 -42
- package/runner/repl.js +20 -10
- package/runner/sizes.js +2 -2
- package/runner/version.js +10 -8
package/compiler/codegen.js
CHANGED
@@ -1,14 +1,14 @@
|
|
1
|
-
import { Blocktype, Opcodes, Valtype, PageSize, ValtypeSize } from
|
2
|
-
import { ieee754_binary64, signedLEB128, unsignedLEB128, encodeVector } from
|
3
|
-
import { operatorOpcode } from
|
4
|
-
import { BuiltinFuncs, BuiltinVars, importedFuncs, NULL, UNDEFINED } from
|
5
|
-
import { PrototypeFuncs } from
|
6
|
-
import { number
|
7
|
-
import { log } from "./log.js";
|
8
|
-
import parse from "./parse.js";
|
9
|
-
import * as Rhemyn from "../rhemyn/compile.js";
|
10
|
-
import Prefs from './prefs.js';
|
1
|
+
import { Blocktype, Opcodes, Valtype, PageSize, ValtypeSize } from './wasmSpec.js';
|
2
|
+
import { ieee754_binary64, signedLEB128, unsignedLEB128, encodeVector } from './encoding.js';
|
3
|
+
import { operatorOpcode } from './expression.js';
|
4
|
+
import { BuiltinFuncs, BuiltinVars, importedFuncs, NULL, UNDEFINED } from './builtins.js';
|
5
|
+
import { PrototypeFuncs } from './prototype.js';
|
6
|
+
import { number } from './embedding.js';
|
11
7
|
import { TYPES, TYPE_NAMES } from './types.js';
|
8
|
+
import * as Rhemyn from '../rhemyn/compile.js';
|
9
|
+
import parse from './parse.js';
|
10
|
+
import { log } from './log.js';
|
11
|
+
import Prefs from './prefs.js';
|
12
12
|
|
13
13
|
let globals = {};
|
14
14
|
let globalInd = 0;
|
@@ -19,24 +19,6 @@ let funcIndex = {};
|
|
19
19
|
let currentFuncIndex = importedFuncs.length;
|
20
20
|
let builtinFuncs = {}, builtinVars = {}, prototypeFuncs = {};
|
21
21
|
|
22
|
-
const debug = str => {
|
23
|
-
const code = [];
|
24
|
-
|
25
|
-
const logChar = n => {
|
26
|
-
code.push(...number(n));
|
27
|
-
|
28
|
-
code.push([ Opcodes.call, 0 ]);
|
29
|
-
};
|
30
|
-
|
31
|
-
for (let i = 0; i < str.length; i++) {
|
32
|
-
logChar(str.charCodeAt(i));
|
33
|
-
}
|
34
|
-
|
35
|
-
logChar(10); // new line
|
36
|
-
|
37
|
-
return code;
|
38
|
-
};
|
39
|
-
|
40
22
|
class TodoError extends Error {
|
41
23
|
constructor(message) {
|
42
24
|
super(message);
|
@@ -58,11 +40,11 @@ const todo = (scope, msg, expectsValue = undefined) => {
|
|
58
40
|
}
|
59
41
|
};
|
60
42
|
|
61
|
-
const isFuncType = type =>
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
43
|
+
const isFuncType = type =>
|
44
|
+
type === 'FunctionDeclaration' || type === 'FunctionExpression' || type === 'ArrowFunctionExpression';
|
45
|
+
const hasFuncWithName = name =>
|
46
|
+
funcIndex[name] != null || builtinFuncs[name] != null || importedFuncs[name] != null || internalConstrs[name] != null;
|
47
|
+
|
66
48
|
const generate = (scope, decl, global = false, name = undefined, valueUnused = false) => {
|
67
49
|
switch (decl.type) {
|
68
50
|
case 'BinaryExpression':
|
@@ -76,10 +58,11 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
76
58
|
|
77
59
|
case 'ArrowFunctionExpression':
|
78
60
|
case 'FunctionDeclaration':
|
61
|
+
case 'FunctionExpression':
|
79
62
|
const func = generateFunc(scope, decl);
|
80
63
|
|
81
64
|
if (decl.type.endsWith('Expression')) {
|
82
|
-
return number(func.index);
|
65
|
+
return number(func.index - importedFuncs.length);
|
83
66
|
}
|
84
67
|
|
85
68
|
return [];
|
@@ -157,24 +140,24 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
157
140
|
case 'ArrayExpression':
|
158
141
|
return generateArray(scope, decl, global, name);
|
159
142
|
|
143
|
+
case 'ObjectExpression':
|
144
|
+
return generateObject(scope, decl, global, name);
|
145
|
+
|
160
146
|
case 'MemberExpression':
|
161
147
|
return generateMember(scope, decl, global, name);
|
162
148
|
|
163
149
|
case 'ExportNamedDeclaration':
|
164
|
-
|
165
|
-
const funcsBefore = funcs.length;
|
150
|
+
const funcsBefore = funcs.map(x => x.name);
|
166
151
|
generate(scope, decl.declaration);
|
167
152
|
|
168
|
-
|
169
|
-
|
170
|
-
const
|
171
|
-
newFunc.export = true;
|
172
|
-
}
|
173
|
-
|
174
|
-
// if (funcsBefore === funcs.length) throw new Error('no new func added in export');
|
153
|
+
// set new funcs as exported
|
154
|
+
if (funcsBefore.length !== funcs.length) {
|
155
|
+
const newFuncs = funcs.filter(x => !funcsBefore.includes(x.name)).filter(x => !x.internal);
|
175
156
|
|
176
|
-
|
177
|
-
|
157
|
+
for (const x of newFuncs) {
|
158
|
+
x.export = true;
|
159
|
+
}
|
160
|
+
}
|
178
161
|
|
179
162
|
return [];
|
180
163
|
|
@@ -210,7 +193,7 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
210
193
|
if (!Array.isArray(inst)) inst = [ inst ];
|
211
194
|
const immediates = asm.slice(1).map(x => {
|
212
195
|
const int = parseInt(x);
|
213
|
-
if (Number.isNaN(int)) return scope.locals[x]?.idx;
|
196
|
+
if (Number.isNaN(int)) return scope.locals[x]?.idx ?? globals[x].idx;
|
214
197
|
return int;
|
215
198
|
});
|
216
199
|
|
@@ -222,19 +205,11 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
222
205
|
|
223
206
|
__Porffor_bs: str => [
|
224
207
|
...makeString(scope, str, global, name, true),
|
225
|
-
|
226
|
-
...(name ? setType(scope, name, TYPES.bytestring) : [
|
227
|
-
...number(TYPES.bytestring, Valtype.i32),
|
228
|
-
...setLastType(scope)
|
229
|
-
])
|
208
|
+
...(name ? setType(scope, name, TYPES.bytestring) : setLastType(scope, TYPES.bytestring))
|
230
209
|
],
|
231
210
|
__Porffor_s: str => [
|
232
211
|
...makeString(scope, str, global, name, false),
|
233
|
-
|
234
|
-
...(name ? setType(scope, name, TYPES.string) : [
|
235
|
-
...number(TYPES.string, Valtype.i32),
|
236
|
-
...setLastType(scope)
|
237
|
-
])
|
212
|
+
...(name ? setType(scope, name, TYPES.string) : setLastType(scope, TYPES.string))
|
238
213
|
],
|
239
214
|
};
|
240
215
|
|
@@ -336,10 +311,10 @@ const generateIdent = (scope, decl) => {
|
|
336
311
|
|
337
312
|
if (local?.idx === undefined) {
|
338
313
|
// no local var with name
|
339
|
-
if (Object.hasOwn(importedFuncs, name)) return number(importedFuncs[name]);
|
340
|
-
if (Object.hasOwn(funcIndex, name)) return number(funcIndex[name]);
|
341
|
-
|
342
314
|
if (Object.hasOwn(globals, name)) return [ [ Opcodes.global_get, globals[name].idx ] ];
|
315
|
+
|
316
|
+
if (Object.hasOwn(importedFuncs, name)) return number(importedFuncs[name] - importedFuncs.length);
|
317
|
+
if (Object.hasOwn(funcIndex, name)) return number(funcIndex[name] - importedFuncs.length);
|
343
318
|
}
|
344
319
|
|
345
320
|
if (local?.idx === undefined && rawName.startsWith('__')) {
|
@@ -373,9 +348,7 @@ const generateReturn = (scope, decl) => {
|
|
373
348
|
|
374
349
|
return [
|
375
350
|
...generate(scope, decl.argument),
|
376
|
-
...(scope.returnType != null ? [] :
|
377
|
-
...getNodeType(scope, decl.argument)
|
378
|
-
]),
|
351
|
+
...(scope.returnType != null ? [] : getNodeType(scope, decl.argument)),
|
379
352
|
[ Opcodes.return ]
|
380
353
|
];
|
381
354
|
};
|
@@ -390,7 +363,7 @@ const localTmp = (scope, name, type = valtypeBinary) => {
|
|
390
363
|
};
|
391
364
|
|
392
365
|
const isIntOp = op => op && ((op[0] >= 0x45 && op[0] <= 0x4f) || (op[0] >= 0x67 && op[0] <= 0x78) || op[0] === 0x41);
|
393
|
-
const
|
366
|
+
const isIntToFloatOp = op => op && (op[0] >= 0xb7 && op[0] <= 0xba);
|
394
367
|
|
395
368
|
const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
396
369
|
const checks = {
|
@@ -407,10 +380,10 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
407
380
|
|
408
381
|
// if we can, use int tmp and convert at the end to help prevent unneeded conversions
|
409
382
|
// (like if we are in an if condition - very common)
|
410
|
-
const
|
411
|
-
const
|
383
|
+
const leftWasInt = isIntToFloatOp(left[left.length - 1]);
|
384
|
+
const rightWasInt = isIntToFloatOp(right[right.length - 1]);
|
412
385
|
|
413
|
-
const canInt =
|
386
|
+
const canInt = leftWasInt && rightWasInt;
|
414
387
|
|
415
388
|
if (canInt) {
|
416
389
|
// remove int -> float conversions from left and right
|
@@ -424,13 +397,11 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
424
397
|
[ Opcodes.if, Valtype.i32 ],
|
425
398
|
...right,
|
426
399
|
// note type
|
427
|
-
...rightType,
|
428
|
-
...setLastType(scope),
|
400
|
+
...setLastType(scope, rightType),
|
429
401
|
[ Opcodes.else ],
|
430
402
|
[ Opcodes.local_get, localTmp(scope, 'logictmpi', Valtype.i32) ],
|
431
403
|
// note type
|
432
|
-
...leftType,
|
433
|
-
...setLastType(scope),
|
404
|
+
...setLastType(scope, leftType),
|
434
405
|
[ Opcodes.end ],
|
435
406
|
Opcodes.i32_from
|
436
407
|
];
|
@@ -443,13 +414,11 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
443
414
|
[ Opcodes.if, valtypeBinary ],
|
444
415
|
...right,
|
445
416
|
// note type
|
446
|
-
...rightType,
|
447
|
-
...setLastType(scope),
|
417
|
+
...setLastType(scope, rightType),
|
448
418
|
[ Opcodes.else ],
|
449
419
|
[ Opcodes.local_get, localTmp(scope, 'logictmp') ],
|
450
420
|
// note type
|
451
|
-
...leftType,
|
452
|
-
...setLastType(scope),
|
421
|
+
...setLastType(scope, leftType),
|
453
422
|
[ Opcodes.end ]
|
454
423
|
];
|
455
424
|
};
|
@@ -477,11 +446,11 @@ const concatStrings = (scope, left, right, global, name, assign = false, bytestr
|
|
477
446
|
...number(0, Valtype.i32), // base 0 for store later
|
478
447
|
|
479
448
|
...number(pointer, Valtype.i32),
|
480
|
-
[ Opcodes.i32_load,
|
449
|
+
[ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
|
481
450
|
[ Opcodes.local_tee, leftLength ],
|
482
451
|
|
483
452
|
[ Opcodes.local_get, rightPointer ],
|
484
|
-
[ Opcodes.i32_load,
|
453
|
+
[ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
|
485
454
|
[ Opcodes.local_tee, rightLength ],
|
486
455
|
|
487
456
|
[ Opcodes.i32_add ],
|
@@ -537,11 +506,11 @@ const concatStrings = (scope, left, right, global, name, assign = false, bytestr
|
|
537
506
|
...number(0, Valtype.i32), // base 0 for store later
|
538
507
|
|
539
508
|
[ Opcodes.local_get, leftPointer ],
|
540
|
-
[ Opcodes.i32_load,
|
509
|
+
[ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
|
541
510
|
[ Opcodes.local_tee, leftLength ],
|
542
511
|
|
543
512
|
[ Opcodes.local_get, rightPointer ],
|
544
|
-
[ Opcodes.i32_load,
|
513
|
+
[ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
|
545
514
|
[ Opcodes.local_tee, rightLength ],
|
546
515
|
|
547
516
|
[ Opcodes.i32_add ],
|
@@ -619,11 +588,11 @@ const compareStrings = (scope, left, right, bytestrings = false) => {
|
|
619
588
|
|
620
589
|
// get lengths
|
621
590
|
[ Opcodes.local_get, leftPointer ],
|
622
|
-
[ Opcodes.i32_load,
|
591
|
+
[ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
|
623
592
|
[ Opcodes.local_tee, leftLength ],
|
624
593
|
|
625
594
|
[ Opcodes.local_get, rightPointer ],
|
626
|
-
[ Opcodes.i32_load,
|
595
|
+
[ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
|
627
596
|
|
628
597
|
// fast path: check leftLength != rightLength
|
629
598
|
[ Opcodes.i32_ne ],
|
@@ -679,9 +648,9 @@ const compareStrings = (scope, left, right, bytestrings = false) => {
|
|
679
648
|
[ Opcodes.i32_add ],
|
680
649
|
[ Opcodes.local_tee, index ],
|
681
650
|
|
682
|
-
// if index
|
651
|
+
// if index < index end (length * sizeof valtype), loop
|
683
652
|
[ Opcodes.local_get, indexEnd ],
|
684
|
-
[ Opcodes.
|
653
|
+
[ Opcodes.i32_lt_s ],
|
685
654
|
[ Opcodes.br_if, 0 ],
|
686
655
|
[ Opcodes.end ],
|
687
656
|
|
@@ -699,38 +668,50 @@ const compareStrings = (scope, left, right, bytestrings = false) => {
|
|
699
668
|
];
|
700
669
|
};
|
701
670
|
|
702
|
-
const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
703
|
-
if (
|
671
|
+
const truthy = (scope, wasm, type, intIn = false, intOut = false, forceTruthyMode = undefined) => {
|
672
|
+
if (isIntToFloatOp(wasm[wasm.length - 1])) return [
|
704
673
|
...wasm,
|
705
674
|
...(!intIn && intOut ? [ Opcodes.i32_to_u ] : [])
|
706
675
|
];
|
707
676
|
// if (isIntOp(wasm[wasm.length - 1])) return [ ...wasm ];
|
708
677
|
|
678
|
+
// todo/perf: use knownType and custom bytecode here instead of typeSwitch
|
679
|
+
|
709
680
|
const useTmp = knownType(scope, type) == null;
|
710
681
|
const tmp = useTmp && localTmp(scope, `#logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
|
711
682
|
|
712
|
-
const def =
|
713
|
-
|
714
|
-
|
683
|
+
const def = (truthyMode => {
|
684
|
+
if (truthyMode === 'full') return [
|
685
|
+
// if value != 0 or NaN
|
686
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
687
|
+
...(intIn ? [ ] : [ Opcodes.i32_to ]),
|
715
688
|
|
716
|
-
|
717
|
-
|
689
|
+
[ Opcodes.i32_eqz ],
|
690
|
+
[ Opcodes.i32_eqz ],
|
718
691
|
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
692
|
+
...(intOut ? [] : [ Opcodes.i32_from ]),
|
693
|
+
];
|
694
|
+
|
695
|
+
if (truthyMode === 'no_negative') return [
|
696
|
+
// if value != 0 or NaN, non-binary output. negative numbers not truthy :/
|
697
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
698
|
+
...(intIn ? [] : [ Opcodes.i32_to ]),
|
699
|
+
...(intOut ? [] : [ Opcodes.i32_from ])
|
700
|
+
];
|
701
|
+
|
702
|
+
if (truthyMode === 'no_nan_negative') return [
|
703
|
+
// simpler and faster but makes NaN truthy and negative numbers not truthy,
|
704
|
+
// plus non-binary output
|
705
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
706
|
+
...(!intOut || (intIn && intOut) ? [] : [ Opcodes.i32_to_u ])
|
707
|
+
];
|
708
|
+
})(forceTruthyMode ?? Prefs.truthy ?? 'full');
|
723
709
|
|
724
710
|
return [
|
725
711
|
...wasm,
|
726
712
|
...(!useTmp ? [] : [ [ Opcodes.local_set, tmp ] ]),
|
727
713
|
|
728
714
|
...typeSwitch(scope, type, {
|
729
|
-
// [TYPES.number]: def,
|
730
|
-
[TYPES.array]: [
|
731
|
-
// arrays are always truthy
|
732
|
-
...number(1, intOut ? Valtype.i32 : valtypeBinary)
|
733
|
-
],
|
734
715
|
[TYPES.string]: [
|
735
716
|
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
736
717
|
...(intIn ? [] : [ Opcodes.i32_to_u ]),
|
@@ -766,10 +747,6 @@ const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
766
747
|
...(!useTmp ? [] : [ [ Opcodes.local_set, tmp ] ]),
|
767
748
|
|
768
749
|
...typeSwitch(scope, type, {
|
769
|
-
[TYPES.array]: [
|
770
|
-
// arrays are always truthy
|
771
|
-
...number(0, intOut ? Valtype.i32 : valtypeBinary)
|
772
|
-
],
|
773
750
|
[TYPES.string]: [
|
774
751
|
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
775
752
|
...(intIn ? [] : [ Opcodes.i32_to_u ]),
|
@@ -858,31 +835,6 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
858
835
|
|
859
836
|
// if strict (in)equal check types match
|
860
837
|
if (strictOp) {
|
861
|
-
// startOut.push(
|
862
|
-
// ...leftType,
|
863
|
-
// ...rightType,
|
864
|
-
// [ Opcodes.i32_eq ]
|
865
|
-
// );
|
866
|
-
|
867
|
-
// endOut.push(
|
868
|
-
// [ Opcodes.i32_and ]
|
869
|
-
// );
|
870
|
-
|
871
|
-
// startOut.push(
|
872
|
-
// [ Opcodes.block, Valtype.i32 ],
|
873
|
-
// ...leftType,
|
874
|
-
// ...rightType,
|
875
|
-
// [ Opcodes.i32_ne ],
|
876
|
-
// [ Opcodes.if, Blocktype.void ],
|
877
|
-
// ...number(op === '===' ? 0 : 1, Valtype.i32),
|
878
|
-
// [ Opcodes.br, 1 ],
|
879
|
-
// [ Opcodes.end ]
|
880
|
-
// );
|
881
|
-
|
882
|
-
// endOut.push(
|
883
|
-
// [ Opcodes.end ]
|
884
|
-
// );
|
885
|
-
|
886
838
|
endOut.push(
|
887
839
|
...leftType,
|
888
840
|
...rightType,
|
@@ -1038,7 +990,7 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
1038
990
|
// if both are true
|
1039
991
|
[ Opcodes.i32_and ],
|
1040
992
|
[ Opcodes.if, Blocktype.void ],
|
1041
|
-
...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ]),
|
993
|
+
...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ], false),
|
1042
994
|
...(op === '!==' || op === '!=' ? [ [ Opcodes.i32_eqz ] ] : []),
|
1043
995
|
[ Opcodes.br, 1 ],
|
1044
996
|
[ Opcodes.end ],
|
@@ -1087,14 +1039,14 @@ const generateBinaryExp = (scope, decl, _global, _name) => {
|
|
1087
1039
|
return out;
|
1088
1040
|
};
|
1089
1041
|
|
1090
|
-
const asmFuncToAsm = (func,
|
1091
|
-
return func(
|
1092
|
-
TYPES, TYPE_NAMES, typeSwitch, makeArray, makeString, allocPage,
|
1093
|
-
builtin:
|
1094
|
-
let idx = funcIndex[
|
1095
|
-
if (idx
|
1096
|
-
includeBuiltin(null,
|
1097
|
-
idx = funcIndex[
|
1042
|
+
const asmFuncToAsm = (func, scope) => {
|
1043
|
+
return func(scope, {
|
1044
|
+
TYPES, TYPE_NAMES, typeSwitch, makeArray, makeString, allocPage, internalThrow,
|
1045
|
+
builtin: n => {
|
1046
|
+
let idx = funcIndex[n] ?? importedFuncs[n];
|
1047
|
+
if (idx == null && builtinFuncs[n]) {
|
1048
|
+
includeBuiltin(null, n);
|
1049
|
+
idx = funcIndex[n];
|
1098
1050
|
}
|
1099
1051
|
|
1100
1052
|
return idx;
|
@@ -1102,7 +1054,7 @@ const asmFuncToAsm = (func, { name = '#unknown_asm_func', params = [], locals =
|
|
1102
1054
|
});
|
1103
1055
|
};
|
1104
1056
|
|
1105
|
-
const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits, returns, returnType, localNames = [], globalNames = [], data: _data = [] }) => {
|
1057
|
+
const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits, returns, returnType, localNames = [], globalNames = [], data: _data = [], table = false }) => {
|
1106
1058
|
const existing = funcs.find(x => x.name === name);
|
1107
1059
|
if (existing) return existing;
|
1108
1060
|
|
@@ -1120,7 +1072,22 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
|
|
1120
1072
|
data.push(copy);
|
1121
1073
|
}
|
1122
1074
|
|
1123
|
-
|
1075
|
+
const func = {
|
1076
|
+
name,
|
1077
|
+
params,
|
1078
|
+
locals,
|
1079
|
+
localInd: allLocals.length,
|
1080
|
+
returns,
|
1081
|
+
returnType: returnType ?? TYPES.number,
|
1082
|
+
internal: true,
|
1083
|
+
index: currentFuncIndex++,
|
1084
|
+
table
|
1085
|
+
};
|
1086
|
+
|
1087
|
+
funcs.push(func);
|
1088
|
+
funcIndex[name] = func.index;
|
1089
|
+
|
1090
|
+
if (typeof wasm === 'function') wasm = asmFuncToAsm(wasm, func);
|
1124
1091
|
|
1125
1092
|
let baseGlobalIdx, i = 0;
|
1126
1093
|
for (const type of globalTypes) {
|
@@ -1139,19 +1106,14 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
|
|
1139
1106
|
}
|
1140
1107
|
}
|
1141
1108
|
|
1142
|
-
const
|
1143
|
-
|
1144
|
-
|
1145
|
-
|
1146
|
-
|
1147
|
-
|
1148
|
-
wasm,
|
1149
|
-
internal: true,
|
1150
|
-
index: currentFuncIndex++
|
1151
|
-
};
|
1109
|
+
if (table) for (const inst of wasm) {
|
1110
|
+
if (inst[0] === Opcodes.i32_load16_u && inst.at(-1) === 'read_argc') {
|
1111
|
+
inst.splice(2, 99);
|
1112
|
+
inst.push(...unsignedLEB128(allocPage({}, 'func argc lut') * pageSize));
|
1113
|
+
}
|
1114
|
+
}
|
1152
1115
|
|
1153
|
-
|
1154
|
-
funcIndex[name] = func.index;
|
1116
|
+
func.wasm = wasm;
|
1155
1117
|
|
1156
1118
|
return func;
|
1157
1119
|
};
|
@@ -1243,12 +1205,13 @@ const getLastType = scope => {
|
|
1243
1205
|
return [ [ Opcodes.local_get, localTmp(scope, '#last_type', Valtype.i32) ] ];
|
1244
1206
|
};
|
1245
1207
|
|
1246
|
-
const setLastType = scope =>
|
1247
|
-
|
1248
|
-
|
1208
|
+
const setLastType = (scope, type = []) => [
|
1209
|
+
...(typeof type === 'number' ? number(type, Valtype.i32) : type),
|
1210
|
+
[ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ]
|
1211
|
+
];
|
1249
1212
|
|
1250
1213
|
const getNodeType = (scope, node) => {
|
1251
|
-
const
|
1214
|
+
const ret = (() => {
|
1252
1215
|
if (node.type === 'Literal') {
|
1253
1216
|
if (node.regex) return TYPES.regexp;
|
1254
1217
|
|
@@ -1291,7 +1254,6 @@ const getNodeType = (scope, node) => {
|
|
1291
1254
|
const func = funcs.find(x => x.name === name);
|
1292
1255
|
|
1293
1256
|
if (func) {
|
1294
|
-
// console.log(scope, func, func.returnType);
|
1295
1257
|
if (func.returnType) return func.returnType;
|
1296
1258
|
}
|
1297
1259
|
|
@@ -1307,7 +1269,17 @@ const getNodeType = (scope, node) => {
|
|
1307
1269
|
|
1308
1270
|
const func = spl[spl.length - 1];
|
1309
1271
|
const protoFuncs = Object.keys(prototypeFuncs).filter(x => x != TYPES.bytestring && prototypeFuncs[x][func] != null);
|
1310
|
-
if (protoFuncs.length === 1)
|
1272
|
+
if (protoFuncs.length === 1) {
|
1273
|
+
if (protoFuncs[0].returnType) return protoFuncs[0].returnType;
|
1274
|
+
}
|
1275
|
+
|
1276
|
+
if (protoFuncs.length > 0) {
|
1277
|
+
if (scope.locals['#last_type']) return getLastType(scope);
|
1278
|
+
|
1279
|
+
// presume
|
1280
|
+
// todo: warn here?
|
1281
|
+
return TYPES.number;
|
1282
|
+
}
|
1311
1283
|
}
|
1312
1284
|
|
1313
1285
|
if (name.startsWith('__Porffor_wasm_')) {
|
@@ -1402,22 +1374,27 @@ const getNodeType = (scope, node) => {
|
|
1402
1374
|
}
|
1403
1375
|
|
1404
1376
|
if (node.type === 'MemberExpression') {
|
1405
|
-
|
1406
|
-
|
1407
|
-
|
1408
|
-
|
1409
|
-
|
1410
|
-
return TYPES.undefined;
|
1411
|
-
}
|
1377
|
+
const name = node.property.name;
|
1378
|
+
|
1379
|
+
if (name === 'length') {
|
1380
|
+
if (hasFuncWithName(node.object.name)) return TYPES.number;
|
1381
|
+
if (Prefs.fastLength) return TYPES.number;
|
1412
1382
|
}
|
1413
1383
|
|
1414
|
-
// hack: if something.length, number type
|
1415
|
-
if (node.property.name === 'length') return TYPES.number;
|
1416
1384
|
|
1417
|
-
|
1418
|
-
if (
|
1419
|
-
|
1420
|
-
|
1385
|
+
const objectKnownType = knownType(scope, getNodeType(scope, node.object));
|
1386
|
+
if (objectKnownType != null) {
|
1387
|
+
if (name === 'length') {
|
1388
|
+
if ([ TYPES.string, TYPES.bytestring, TYPES.array ].includes(objectKnownType)) return TYPES.number;
|
1389
|
+
else return TYPES.undefined;
|
1390
|
+
}
|
1391
|
+
|
1392
|
+
if (node.computed) {
|
1393
|
+
if (objectKnownType === TYPES.string) return TYPES.string;
|
1394
|
+
if (objectKnownType === TYPES.bytestring) return TYPES.bytestring;
|
1395
|
+
if (objectKnownType === TYPES.array) return TYPES.number;
|
1396
|
+
}
|
1397
|
+
}
|
1421
1398
|
|
1422
1399
|
if (scope.locals['#last_type']) return getLastType(scope);
|
1423
1400
|
|
@@ -1435,10 +1412,8 @@ const getNodeType = (scope, node) => {
|
|
1435
1412
|
// presume
|
1436
1413
|
// todo: warn here?
|
1437
1414
|
return TYPES.number;
|
1438
|
-
};
|
1415
|
+
})();
|
1439
1416
|
|
1440
|
-
const ret = inner();
|
1441
|
-
// console.trace(node, ret);
|
1442
1417
|
if (typeof ret === 'number') return number(ret, Valtype.i32);
|
1443
1418
|
return ret;
|
1444
1419
|
};
|
@@ -1490,17 +1465,16 @@ const countLeftover = wasm => {
|
|
1490
1465
|
else if (inst[0] === Opcodes.return) count = 0;
|
1491
1466
|
else if (inst[0] === Opcodes.call) {
|
1492
1467
|
let func = funcs.find(x => x.index === inst[1]);
|
1493
|
-
if (inst[1]
|
1494
|
-
|
1495
|
-
|
1496
|
-
count -= importedFuncs[inst[1]].params;
|
1497
|
-
count += importedFuncs[inst[1]].returns;
|
1468
|
+
if (inst[1] < importedFuncs.length) {
|
1469
|
+
func = importedFuncs[inst[1]];
|
1470
|
+
count = count - func.params + func.returns;
|
1498
1471
|
} else {
|
1499
|
-
|
1500
|
-
count -= func.params.length;
|
1501
|
-
} else count--;
|
1502
|
-
if (func) count += func.returns.length;
|
1472
|
+
count = count - func.params.length + func.returns.length;
|
1503
1473
|
}
|
1474
|
+
} else if (inst[0] === Opcodes.call_indirect) {
|
1475
|
+
count--; // funcidx
|
1476
|
+
count -= inst[1] * 2; // params * 2 (typed)
|
1477
|
+
count += 2; // fixed return (value, type)
|
1504
1478
|
} else count--;
|
1505
1479
|
|
1506
1480
|
// console.log(count, decompile([ inst ]).slice(0, -1));
|
@@ -1518,7 +1492,7 @@ const disposeLeftover = wasm => {
|
|
1518
1492
|
const generateExp = (scope, decl) => {
|
1519
1493
|
const expression = decl.expression;
|
1520
1494
|
|
1521
|
-
const out = generate(scope, expression, undefined, undefined,
|
1495
|
+
const out = generate(scope, expression, undefined, undefined, Prefs.optUnused);
|
1522
1496
|
disposeLeftover(out);
|
1523
1497
|
|
1524
1498
|
return out;
|
@@ -1577,15 +1551,6 @@ const RTArrayUtil = {
|
|
1577
1551
|
};
|
1578
1552
|
|
1579
1553
|
const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
1580
|
-
/* const callee = decl.callee;
|
1581
|
-
const args = decl.arguments;
|
1582
|
-
|
1583
|
-
return [
|
1584
|
-
...generate(args),
|
1585
|
-
...generate(callee),
|
1586
|
-
Opcodes.call_indirect,
|
1587
|
-
]; */
|
1588
|
-
|
1589
1554
|
let name = mapName(decl.callee.name);
|
1590
1555
|
if (isFuncType(decl.callee.type)) { // iife
|
1591
1556
|
const func = generateFunc(scope, decl.callee);
|
@@ -1618,16 +1583,10 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1618
1583
|
out.splice(out.length - 1, 1);
|
1619
1584
|
|
1620
1585
|
const finalStatement = parsed.body[parsed.body.length - 1];
|
1621
|
-
out.push(
|
1622
|
-
...getNodeType(scope, finalStatement),
|
1623
|
-
...setLastType(scope)
|
1624
|
-
);
|
1586
|
+
out.push(...setLastType(scope, getNodeType(scope, finalStatement)));
|
1625
1587
|
} else if (countLeftover(out) === 0) {
|
1626
1588
|
out.push(...number(UNDEFINED));
|
1627
|
-
out.push(
|
1628
|
-
...number(TYPES.undefined, Valtype.i32),
|
1629
|
-
...setLastType(scope)
|
1630
|
-
);
|
1589
|
+
out.push(...setLastType(scope, TYPES.undefined));
|
1631
1590
|
}
|
1632
1591
|
|
1633
1592
|
// if (lastInst && lastInst[0] === Opcodes.drop) {
|
@@ -1663,6 +1622,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1663
1622
|
|
1664
1623
|
if (!funcIndex[rhemynName]) {
|
1665
1624
|
const func = Rhemyn[funcName](regex, currentFuncIndex++, rhemynName);
|
1625
|
+
func.internal = true;
|
1666
1626
|
|
1667
1627
|
funcIndex[func.name] = func.index;
|
1668
1628
|
funcs.push(func);
|
@@ -1679,8 +1639,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1679
1639
|
[ Opcodes.call, idx ],
|
1680
1640
|
Opcodes.i32_from_u,
|
1681
1641
|
|
1682
|
-
...
|
1683
|
-
...setLastType(scope)
|
1642
|
+
...setLastType(scope, TYPES.boolean)
|
1684
1643
|
];
|
1685
1644
|
}
|
1686
1645
|
|
@@ -1752,15 +1711,11 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1752
1711
|
if (protoFunc.noArgRetLength && decl.arguments.length === 0) {
|
1753
1712
|
protoBC[x] = [
|
1754
1713
|
...RTArrayUtil.getLength(getPointer),
|
1755
|
-
|
1756
|
-
...number(TYPES.number, Valtype.i32),
|
1757
|
-
...setLastType(scope)
|
1714
|
+
...setLastType(scope, TYPES.number)
|
1758
1715
|
];
|
1759
1716
|
continue;
|
1760
1717
|
}
|
1761
1718
|
|
1762
|
-
// const protoLocal = protoFunc.local ? localTmp(scope, `__${TYPE_NAMES[x]}_${protoName}_tmp`, protoFunc.local) : -1;
|
1763
|
-
// const protoLocal2 = protoFunc.local2 ? localTmp(scope, `__${TYPE_NAMES[x]}_${protoName}_tmp2`, protoFunc.local2) : -1;
|
1764
1719
|
const protoLocal = protoFunc.local ? localTmp(scope, `__${protoName}_tmp`, protoFunc.local) : -1;
|
1765
1720
|
const protoLocal2 = protoFunc.local2 ? localTmp(scope, `__${protoName}_tmp2`, protoFunc.local2) : -1;
|
1766
1721
|
|
@@ -1775,7 +1730,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1775
1730
|
getI32: () => RTArrayUtil.getLengthI32(getPointer),
|
1776
1731
|
set: value => RTArrayUtil.setLength(getPointer, value),
|
1777
1732
|
setI32: value => RTArrayUtil.setLengthI32(getPointer, value)
|
1778
|
-
}, generate(scope, decl.arguments[0] ?? DEFAULT_VALUE), protoLocal, protoLocal2, (length, itemType) => {
|
1733
|
+
}, generate(scope, decl.arguments[0] ?? DEFAULT_VALUE), getNodeType(scope, decl.arguments[0] ?? DEFAULT_VALUE), protoLocal, protoLocal2, (length, itemType) => {
|
1779
1734
|
return makeArray(scope, {
|
1780
1735
|
rawElements: new Array(length)
|
1781
1736
|
}, _global, _name, true, itemType);
|
@@ -1789,9 +1744,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1789
1744
|
protoBC[x] = [
|
1790
1745
|
[ Opcodes.block, unusedValue && optUnused ? Blocktype.void : valtypeBinary ],
|
1791
1746
|
...protoOut,
|
1792
|
-
|
1793
|
-
...number(protoFunc.returnType ?? TYPES.number, Valtype.i32),
|
1794
|
-
...setLastType(scope),
|
1747
|
+
...(unusedValue && optUnused ? [] : (protoFunc.returnType != null ? setLastType(scope, protoFunc.returnType) : setLastType(scope))),
|
1795
1748
|
[ Opcodes.end ]
|
1796
1749
|
];
|
1797
1750
|
}
|
@@ -1837,31 +1790,10 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1837
1790
|
|
1838
1791
|
includeBuiltin(scope, name);
|
1839
1792
|
idx = funcIndex[name];
|
1840
|
-
|
1841
|
-
// infer arguments types from builtins params
|
1842
|
-
// const func = funcs.find(x => x.name === name);
|
1843
|
-
// for (let i = 0; i < decl.arguments.length; i++) {
|
1844
|
-
// const arg = decl.arguments[i];
|
1845
|
-
// if (!arg.name) continue;
|
1846
|
-
|
1847
|
-
// const local = scope.locals[arg.name];
|
1848
|
-
// if (!local) continue;
|
1849
|
-
|
1850
|
-
// local.type = func.params[i];
|
1851
|
-
// if (local.type === Valtype.v128) {
|
1852
|
-
// // specify vec subtype inferred from last vec type in function name
|
1853
|
-
// local.vecType = name.split('_').reverse().find(x => x.includes('x'));
|
1854
|
-
// }
|
1855
|
-
// }
|
1856
1793
|
}
|
1857
1794
|
|
1858
1795
|
if (idx === undefined && internalConstrs[name]) return internalConstrs[name].generate(scope, decl, _global, _name);
|
1859
1796
|
|
1860
|
-
if (idx === undefined && name === scope.name) {
|
1861
|
-
// hack: calling self, func generator will fix later
|
1862
|
-
idx = -1;
|
1863
|
-
}
|
1864
|
-
|
1865
1797
|
if (idx === undefined && name.startsWith('__Porffor_wasm_')) {
|
1866
1798
|
const wasmOps = {
|
1867
1799
|
// pointer, align, offset
|
@@ -1909,15 +1841,144 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1909
1841
|
}
|
1910
1842
|
|
1911
1843
|
if (idx === undefined) {
|
1912
|
-
if (scope.locals[name] !== undefined || globals[name] !== undefined || builtinVars[name] !== undefined)
|
1844
|
+
if (scope.locals[name] !== undefined || globals[name] !== undefined || builtinVars[name] !== undefined) {
|
1845
|
+
const [ local, global ] = lookupName(scope, name);
|
1846
|
+
if (!Prefs.indirectCalls || local == null) return internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, true);
|
1847
|
+
|
1848
|
+
// todo: only works when function uses typedParams and typedReturns
|
1849
|
+
|
1850
|
+
const indirectMode = Prefs.indirectCallMode ?? 'vararg';
|
1851
|
+
// options: vararg, strict
|
1852
|
+
// - strict: simpler, smaller size usage, no func argc lut needed.
|
1853
|
+
// ONLY works when arg count of call == arg count of function being called
|
1854
|
+
// - vararg: large size usage, cursed.
|
1855
|
+
// works when arg count of call != arg count of function being called*
|
1856
|
+
// * most of the time, some edgecases
|
1857
|
+
|
1858
|
+
funcs.table = true;
|
1859
|
+
scope.table = true;
|
1860
|
+
|
1861
|
+
let args = decl.arguments;
|
1862
|
+
let out = [];
|
1863
|
+
|
1864
|
+
let locals = [];
|
1865
|
+
|
1866
|
+
if (indirectMode === 'vararg') {
|
1867
|
+
const minArgc = Prefs.indirectCallMinArgc ?? 3;
|
1868
|
+
|
1869
|
+
if (args.length < minArgc) {
|
1870
|
+
args = args.concat(new Array(minArgc - args.length).fill(DEFAULT_VALUE));
|
1871
|
+
}
|
1872
|
+
}
|
1873
|
+
|
1874
|
+
for (let i = 0; i < args.length; i++) {
|
1875
|
+
const arg = args[i];
|
1876
|
+
out = out.concat(generate(scope, arg));
|
1877
|
+
|
1878
|
+
if (valtypeBinary !== Valtype.i32 && (
|
1879
|
+
(builtinFuncs[name] && builtinFuncs[name].params[i * (typedParams ? 2 : 1)] === Valtype.i32) ||
|
1880
|
+
(importedFuncs[name] && name.startsWith('profile'))
|
1881
|
+
)) {
|
1882
|
+
out.push(Opcodes.i32_to);
|
1883
|
+
}
|
1884
|
+
|
1885
|
+
out = out.concat(getNodeType(scope, arg));
|
1886
|
+
|
1887
|
+
if (indirectMode === 'vararg') {
|
1888
|
+
const typeLocal = localTmp(scope, `#indirect_arg${i}_type`, Valtype.i32);
|
1889
|
+
const valLocal = localTmp(scope, `#indirect_arg${i}_val`);
|
1890
|
+
|
1891
|
+
locals.push([valLocal, typeLocal]);
|
1892
|
+
|
1893
|
+
out.push(
|
1894
|
+
[ Opcodes.local_set, typeLocal ],
|
1895
|
+
[ Opcodes.local_set, valLocal ]
|
1896
|
+
);
|
1897
|
+
}
|
1898
|
+
}
|
1899
|
+
|
1900
|
+
if (indirectMode === 'strict') {
|
1901
|
+
return typeSwitch(scope, getNodeType(scope, decl.callee), {
|
1902
|
+
[TYPES.function]: [
|
1903
|
+
...argWasm,
|
1904
|
+
[ global ? Opcodes.global_get : Opcodes.local_get, local.idx ],
|
1905
|
+
Opcodes.i32_to_u,
|
1906
|
+
[ Opcodes.call_indirect, args.length, 0 ],
|
1907
|
+
...setLastType(scope)
|
1908
|
+
],
|
1909
|
+
default: internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, true)
|
1910
|
+
});
|
1911
|
+
}
|
1912
|
+
|
1913
|
+
// hi, I will now explain how vararg mode works:
|
1914
|
+
// wasm's indirect_call instruction requires you know the func type at compile-time
|
1915
|
+
// since we have varargs (variable argument count), we do not know it.
|
1916
|
+
// we could just store args in memory and not use wasm func args,
|
1917
|
+
// but that is slow (probably) and breaks js exports.
|
1918
|
+
// instead, we generate every* possibility of argc and use different indirect_call
|
1919
|
+
// ops for each one, with type depending on argc for that branch.
|
1920
|
+
// then we load the argc for the wanted function from a memory lut,
|
1921
|
+
// and call the branch with the matching argc we require.
|
1922
|
+
// sorry, yes it is very cursed (and size inefficient), but indirect calls
|
1923
|
+
// are kind of rare anyway (mostly callbacks) so I am not concerned atm.
|
1924
|
+
// *for argc 0-3, in future (todo:) the max number should be
|
1925
|
+
// dynamically changed to the max argc of any func in the js file.
|
1926
|
+
|
1927
|
+
const funcLocal = localTmp(scope, `#indirect_func`, Valtype.i32);
|
1928
|
+
|
1929
|
+
const gen = argc => {
|
1930
|
+
const out = [];
|
1931
|
+
for (let i = 0; i < argc; i++) {
|
1932
|
+
out.push(
|
1933
|
+
[ Opcodes.local_get, locals[i][0] ],
|
1934
|
+
[ Opcodes.local_get, locals[i][1] ]
|
1935
|
+
);
|
1936
|
+
}
|
1937
|
+
|
1938
|
+
out.push(
|
1939
|
+
[ Opcodes.local_get, funcLocal ],
|
1940
|
+
[ Opcodes.call_indirect, argc, 0 ],
|
1941
|
+
...setLastType(scope)
|
1942
|
+
)
|
1943
|
+
|
1944
|
+
return out;
|
1945
|
+
};
|
1946
|
+
|
1947
|
+
const tableBc = {};
|
1948
|
+
for (let i = 0; i <= args.length; i++) {
|
1949
|
+
tableBc[i] = gen(i);
|
1950
|
+
}
|
1951
|
+
|
1952
|
+
// todo/perf: check if we should use br_table here or just generate our own big if..elses
|
1953
|
+
|
1954
|
+
return typeSwitch(scope, getNodeType(scope, decl.callee), {
|
1955
|
+
[TYPES.function]: [
|
1956
|
+
...out,
|
1957
|
+
|
1958
|
+
[ global ? Opcodes.global_get : Opcodes.local_get, local.idx ],
|
1959
|
+
Opcodes.i32_to_u,
|
1960
|
+
[ Opcodes.local_set, funcLocal ],
|
1961
|
+
|
1962
|
+
...brTable([
|
1963
|
+
// get argc of func we are calling
|
1964
|
+
[ Opcodes.local_get, funcLocal ],
|
1965
|
+
...number(ValtypeSize.i16, Valtype.i32),
|
1966
|
+
[ Opcodes.i32_mul ],
|
1967
|
+
|
1968
|
+
[ Opcodes.i32_load16_u, 0, ...unsignedLEB128(allocPage(scope, 'func argc lut') * pageSize), 'read_argc' ]
|
1969
|
+
], tableBc, valtypeBinary)
|
1970
|
+
],
|
1971
|
+
default: internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, true)
|
1972
|
+
});
|
1973
|
+
}
|
1974
|
+
|
1913
1975
|
return internalThrow(scope, 'ReferenceError', `${unhackName(name)} is not defined`, true);
|
1914
1976
|
}
|
1915
1977
|
|
1916
|
-
const func = funcs.find(x => x.index === idx);
|
1917
|
-
|
1918
|
-
const userFunc = (funcIndex[name] && !importedFuncs[name] && !builtinFuncs[name] && !internalConstrs[name]) || idx === -1;
|
1978
|
+
const func = funcs[idx - importedFuncs.length]; // idx === scope.index ? scope : funcs.find(x => x.index === idx);
|
1979
|
+
const userFunc = func && !func.internal;
|
1919
1980
|
const typedParams = userFunc || builtinFuncs[name]?.typedParams;
|
1920
|
-
const typedReturns = (func
|
1981
|
+
const typedReturns = (func && func.returnType == null) || builtinFuncs[name]?.typedReturns;
|
1921
1982
|
const paramCount = func && (typedParams ? func.params.length / 2 : func.params.length);
|
1922
1983
|
|
1923
1984
|
let args = decl.arguments;
|
@@ -1938,11 +1999,17 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1938
1999
|
const arg = args[i];
|
1939
2000
|
out = out.concat(generate(scope, arg));
|
1940
2001
|
|
1941
|
-
|
1942
|
-
|
2002
|
+
// todo: this should be used instead of the too many args thing above (by removing that)
|
2003
|
+
if (i >= paramCount) {
|
2004
|
+
// over param count of func, drop arg
|
2005
|
+
out.push([ Opcodes.drop ]);
|
2006
|
+
continue;
|
1943
2007
|
}
|
1944
2008
|
|
1945
|
-
if (
|
2009
|
+
if (valtypeBinary !== Valtype.i32 && (
|
2010
|
+
(builtinFuncs[name] && builtinFuncs[name].params[i * (typedParams ? 2 : 1)] === Valtype.i32) ||
|
2011
|
+
(importedFuncs[name] && name.startsWith('profile'))
|
2012
|
+
)) {
|
1946
2013
|
out.push(Opcodes.i32_to);
|
1947
2014
|
}
|
1948
2015
|
|
@@ -1987,6 +2054,11 @@ const generateNew = (scope, decl, _global, _name) => {
|
|
1987
2054
|
}, _global, _name);
|
1988
2055
|
}
|
1989
2056
|
|
2057
|
+
if (
|
2058
|
+
(builtinFuncs[name] && !builtinFuncs[name].constr) ||
|
2059
|
+
(internalConstrs[name] && builtinFuncs[name].notConstr)
|
2060
|
+
) return internalThrow(scope, 'TypeError', `${name} is not a constructor`);
|
2061
|
+
|
1990
2062
|
if (!builtinFuncs[name]) return todo(scope, `new statement is not supported yet`); // return todo(scope, `new statement is not supported yet (new ${unhackName(name)})`);
|
1991
2063
|
|
1992
2064
|
return generateCall(scope, decl, _global, _name);
|
@@ -2012,8 +2084,11 @@ const knownType = (scope, type) => {
|
|
2012
2084
|
const idx = type[0][1];
|
2013
2085
|
|
2014
2086
|
// type idx = var idx + 1
|
2015
|
-
const
|
2016
|
-
if (
|
2087
|
+
const name = Object.values(scope.locals).find(x => x.idx === idx)?.name;
|
2088
|
+
if (name) {
|
2089
|
+
const local = scope.locals[name];
|
2090
|
+
if (local.metadata?.type != null) return v.metadata.type;
|
2091
|
+
}
|
2017
2092
|
}
|
2018
2093
|
|
2019
2094
|
return null;
|
@@ -2048,16 +2123,17 @@ const brTable = (input, bc, returns) => {
|
|
2048
2123
|
}
|
2049
2124
|
|
2050
2125
|
for (let i = 0; i < count; i++) {
|
2051
|
-
if (i === 0) out.push([ Opcodes.block, returns, 'br table start' ]);
|
2126
|
+
// if (i === 0) out.push([ Opcodes.block, returns, 'br table start' ]);
|
2127
|
+
if (i === 0) out.push([ Opcodes.block, returns ]);
|
2052
2128
|
else out.push([ Opcodes.block, Blocktype.void ]);
|
2053
2129
|
}
|
2054
2130
|
|
2055
|
-
const nums = keys.filter(x => +x);
|
2131
|
+
const nums = keys.filter(x => +x >= 0);
|
2056
2132
|
const offset = Math.min(...nums);
|
2057
2133
|
const max = Math.max(...nums);
|
2058
2134
|
|
2059
2135
|
const table = [];
|
2060
|
-
let br =
|
2136
|
+
let br = 0;
|
2061
2137
|
|
2062
2138
|
for (let i = offset; i <= max; i++) {
|
2063
2139
|
// if branch for this num, go to that block
|
@@ -2097,10 +2173,9 @@ const brTable = (input, bc, returns) => {
|
|
2097
2173
|
br--;
|
2098
2174
|
}
|
2099
2175
|
|
2100
|
-
|
2101
|
-
|
2102
|
-
|
2103
|
-
];
|
2176
|
+
out.push([ Opcodes.end ]);
|
2177
|
+
|
2178
|
+
return out;
|
2104
2179
|
};
|
2105
2180
|
|
2106
2181
|
const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
|
@@ -2144,6 +2219,17 @@ const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
|
|
2144
2219
|
return out;
|
2145
2220
|
};
|
2146
2221
|
|
2222
|
+
const typeIsOneOf = (type, types, valtype = Valtype.i32) => {
|
2223
|
+
const out = [];
|
2224
|
+
|
2225
|
+
for (let i = 0; i < types.length; i++) {
|
2226
|
+
out.push(...type, ...number(types[i], valtype), valtype === Valtype.f64 ? [ Opcodes.f64_eq ] : [ Opcodes.i32_eq ]);
|
2227
|
+
if (i !== 0) out.push([ Opcodes.i32_or ]);
|
2228
|
+
}
|
2229
|
+
|
2230
|
+
return out;
|
2231
|
+
};
|
2232
|
+
|
2147
2233
|
const allocVar = (scope, name, global = false, type = true) => {
|
2148
2234
|
const target = global ? globals : scope.locals;
|
2149
2235
|
|
@@ -2160,7 +2246,7 @@ const allocVar = (scope, name, global = false, type = true) => {
|
|
2160
2246
|
|
2161
2247
|
if (type) {
|
2162
2248
|
let typeIdx = global ? globalInd++ : scope.localInd++;
|
2163
|
-
target[name + '#type'] = { idx: typeIdx, type: Valtype.i32 };
|
2249
|
+
target[name + '#type'] = { idx: typeIdx, type: Valtype.i32, name };
|
2164
2250
|
}
|
2165
2251
|
|
2166
2252
|
return idx;
|
@@ -2178,7 +2264,6 @@ const addVarMetadata = (scope, name, global = false, metadata = {}) => {
|
|
2178
2264
|
const typeAnnoToPorfType = x => {
|
2179
2265
|
if (!x) return null;
|
2180
2266
|
if (TYPES[x.toLowerCase()] != null) return TYPES[x.toLowerCase()];
|
2181
|
-
if (TYPES['_' + x.toLowerCase()] != null) return TYPES['_' + x.toLowerCase()];
|
2182
2267
|
|
2183
2268
|
switch (x) {
|
2184
2269
|
case 'i32':
|
@@ -2219,9 +2304,8 @@ const generateVar = (scope, decl) => {
|
|
2219
2304
|
|
2220
2305
|
const topLevel = scope.name === 'main';
|
2221
2306
|
|
2222
|
-
// global variable if in top scope (main)
|
2223
|
-
const global = topLevel || decl._bare;
|
2224
|
-
const target = global ? globals : scope.locals;
|
2307
|
+
// global variable if in top scope (main) or if internally wanted
|
2308
|
+
const global = topLevel || decl._bare;
|
2225
2309
|
|
2226
2310
|
for (const x of decl.declarations) {
|
2227
2311
|
const name = mapName(x.id.name);
|
@@ -2235,7 +2319,6 @@ const generateVar = (scope, decl) => {
|
|
2235
2319
|
continue;
|
2236
2320
|
}
|
2237
2321
|
|
2238
|
-
// console.log(name);
|
2239
2322
|
if (topLevel && builtinVars[name]) {
|
2240
2323
|
// cannot redeclare
|
2241
2324
|
if (decl.kind !== 'var') return internalThrow(scope, 'SyntaxError', `Identifier '${unhackName(name)}' has already been declared`);
|
@@ -2255,6 +2338,22 @@ const generateVar = (scope, decl) => {
|
|
2255
2338
|
}
|
2256
2339
|
|
2257
2340
|
if (x.init) {
|
2341
|
+
// if (isFuncType(x.init.type)) {
|
2342
|
+
// // let a = function () { ... }
|
2343
|
+
// x.init.id = { name };
|
2344
|
+
|
2345
|
+
// const func = generateFunc(scope, x.init);
|
2346
|
+
|
2347
|
+
// out.push(
|
2348
|
+
// ...number(func.index - importedFuncs.length),
|
2349
|
+
// [ global ? Opcodes.global_set : Opcodes.local_set, idx ],
|
2350
|
+
|
2351
|
+
// ...setType(scope, name, TYPES.function)
|
2352
|
+
// );
|
2353
|
+
|
2354
|
+
// continue;
|
2355
|
+
// }
|
2356
|
+
|
2258
2357
|
const generated = generate(scope, x.init, global, name);
|
2259
2358
|
if (scope.arrays?.get(name) != null) {
|
2260
2359
|
// hack to set local as pointer before
|
@@ -2266,6 +2365,7 @@ const generateVar = (scope, decl) => {
|
|
2266
2365
|
out = out.concat(generated);
|
2267
2366
|
out.push([ global ? Opcodes.global_set : Opcodes.local_set, idx ]);
|
2268
2367
|
}
|
2368
|
+
|
2269
2369
|
out.push(...setType(scope, name, getNodeType(scope, x.init)));
|
2270
2370
|
}
|
2271
2371
|
|
@@ -2279,6 +2379,7 @@ const generateVar = (scope, decl) => {
|
|
2279
2379
|
// todo: optimize this func for valueUnused
|
2280
2380
|
const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
|
2281
2381
|
const { type, name } = decl.left;
|
2382
|
+
const [ local, isGlobal ] = lookupName(scope, name);
|
2282
2383
|
|
2283
2384
|
if (type === 'ObjectPattern') {
|
2284
2385
|
// hack: ignore object parts of `var a = {} = 2`
|
@@ -2288,8 +2389,18 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
|
|
2288
2389
|
if (isFuncType(decl.right.type)) {
|
2289
2390
|
// hack for a = function () { ... }
|
2290
2391
|
decl.right.id = { name };
|
2291
|
-
|
2292
|
-
|
2392
|
+
|
2393
|
+
const func = generateFunc(scope, decl.right);
|
2394
|
+
|
2395
|
+
return [
|
2396
|
+
...number(func.index - importedFuncs.length),
|
2397
|
+
...(local != null ? [
|
2398
|
+
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
|
2399
|
+
[ isGlobal ? Opcodes.global_get : Opcodes.local_get, local.idx ],
|
2400
|
+
|
2401
|
+
...setType(scope, name, TYPES.function)
|
2402
|
+
] : [])
|
2403
|
+
];
|
2293
2404
|
}
|
2294
2405
|
|
2295
2406
|
const op = decl.operator.slice(0, -1) || '=';
|
@@ -2348,18 +2459,21 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
|
|
2348
2459
|
Opcodes.i32_to_u,
|
2349
2460
|
|
2350
2461
|
// turn into byte offset by * valtypeSize (4 for i32, 8 for i64/f64)
|
2351
|
-
...number(ValtypeSize[valtype], Valtype.i32),
|
2462
|
+
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
2352
2463
|
[ Opcodes.i32_mul ],
|
2353
2464
|
...(aotPointer ? [] : [ [ Opcodes.i32_add ] ]),
|
2354
2465
|
...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
|
2355
2466
|
|
2356
2467
|
...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
|
2357
2468
|
[ Opcodes.local_get, pointerTmp ],
|
2358
|
-
[ Opcodes.load,
|
2359
|
-
], generate(scope, decl.right),
|
2469
|
+
[ Opcodes.load, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
|
2470
|
+
], generate(scope, decl.right), [
|
2471
|
+
[ Opcodes.local_get, pointerTmp ],
|
2472
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32 + ValtypeSize[valtype]) ]
|
2473
|
+
], getNodeType(scope, decl.right), false, name, true)),
|
2360
2474
|
[ Opcodes.local_tee, newValueTmp ],
|
2361
2475
|
|
2362
|
-
[ Opcodes.store,
|
2476
|
+
[ Opcodes.store, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
|
2363
2477
|
],
|
2364
2478
|
|
2365
2479
|
default: internalThrow(scope, 'TypeError', `Cannot assign member with non-array`)
|
@@ -2388,8 +2502,6 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
|
|
2388
2502
|
|
2389
2503
|
if (!name) return todo(scope, 'destructuring is not supported yet', true);
|
2390
2504
|
|
2391
|
-
const [ local, isGlobal ] = lookupName(scope, name);
|
2392
|
-
|
2393
2505
|
if (local === undefined) {
|
2394
2506
|
// todo: this should be a sloppy mode only thing
|
2395
2507
|
|
@@ -2469,6 +2581,11 @@ const generateUnary = (scope, decl) => {
|
|
2469
2581
|
];
|
2470
2582
|
|
2471
2583
|
case '!':
|
2584
|
+
const arg = decl.argument;
|
2585
|
+
if (arg.type === "UnaryExpression" && arg.operator === "!") {
|
2586
|
+
// !!x -> is x truthy
|
2587
|
+
return truthy(scope, generate(scope, arg.argument), getNodeType(scope, arg.argument), false, false);
|
2588
|
+
}
|
2472
2589
|
// !=
|
2473
2590
|
return falsy(scope, generate(scope, decl.argument), getNodeType(scope, decl.argument), false, false);
|
2474
2591
|
|
@@ -2538,6 +2655,7 @@ const generateUnary = (scope, decl) => {
|
|
2538
2655
|
[TYPES.string]: makeString(scope, 'string', false, '#typeof_result'),
|
2539
2656
|
[TYPES.undefined]: makeString(scope, 'undefined', false, '#typeof_result'),
|
2540
2657
|
[TYPES.function]: makeString(scope, 'function', false, '#typeof_result'),
|
2658
|
+
[TYPES.symbol]: makeString(scope, 'symbol', false, '#typeof_result'),
|
2541
2659
|
|
2542
2660
|
[TYPES.bytestring]: makeString(scope, 'string', false, '#typeof_result'),
|
2543
2661
|
|
@@ -2614,21 +2732,16 @@ const generateConditional = (scope, decl) => {
|
|
2614
2732
|
out.push([ Opcodes.if, valtypeBinary ]);
|
2615
2733
|
depth.push('if');
|
2616
2734
|
|
2617
|
-
out.push(...generate(scope, decl.consequent));
|
2618
|
-
|
2619
|
-
// note type
|
2620
2735
|
out.push(
|
2621
|
-
...
|
2622
|
-
...setLastType(scope)
|
2736
|
+
...generate(scope, decl.consequent),
|
2737
|
+
...setLastType(scope, getNodeType(scope, decl.consequent))
|
2623
2738
|
);
|
2624
2739
|
|
2625
2740
|
out.push([ Opcodes.else ]);
|
2626
|
-
out.push(...generate(scope, decl.alternate));
|
2627
2741
|
|
2628
|
-
// note type
|
2629
2742
|
out.push(
|
2630
|
-
...
|
2631
|
-
...setLastType(scope)
|
2743
|
+
...generate(scope, decl.alternate),
|
2744
|
+
...setLastType(scope, getNodeType(scope, decl.alternate))
|
2632
2745
|
);
|
2633
2746
|
|
2634
2747
|
out.push([ Opcodes.end ]);
|
@@ -2776,12 +2889,15 @@ const generateForOf = (scope, decl) => {
|
|
2776
2889
|
// todo: optimize away counter and use end pointer
|
2777
2890
|
out.push(...typeSwitch(scope, getNodeType(scope, decl.right), {
|
2778
2891
|
[TYPES.array]: [
|
2779
|
-
...setType(scope, leftName, TYPES.number),
|
2780
|
-
|
2781
2892
|
[ Opcodes.loop, Blocktype.void ],
|
2782
2893
|
|
2783
2894
|
[ Opcodes.local_get, pointer ],
|
2784
|
-
[ Opcodes.load,
|
2895
|
+
[ Opcodes.load, 0, ...unsignedLEB128(ValtypeSize.i32) ],
|
2896
|
+
|
2897
|
+
...setType(scope, leftName, [
|
2898
|
+
[ Opcodes.local_get, pointer ],
|
2899
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32 + ValtypeSize[valtype]) ],
|
2900
|
+
]),
|
2785
2901
|
|
2786
2902
|
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
|
2787
2903
|
|
@@ -2790,9 +2906,9 @@ const generateForOf = (scope, decl) => {
|
|
2790
2906
|
...generate(scope, decl.body),
|
2791
2907
|
[ Opcodes.end ],
|
2792
2908
|
|
2793
|
-
// increment iter pointer by valtype size
|
2909
|
+
// increment iter pointer by valtype size + 1
|
2794
2910
|
[ Opcodes.local_get, pointer ],
|
2795
|
-
...number(ValtypeSize[valtype], Valtype.i32),
|
2911
|
+
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
2796
2912
|
[ Opcodes.i32_add ],
|
2797
2913
|
[ Opcodes.local_set, pointer ],
|
2798
2914
|
|
@@ -2908,6 +3024,44 @@ const generateForOf = (scope, decl) => {
|
|
2908
3024
|
[ Opcodes.end ],
|
2909
3025
|
[ Opcodes.end ]
|
2910
3026
|
],
|
3027
|
+
[TYPES.set]: [
|
3028
|
+
[ Opcodes.loop, Blocktype.void ],
|
3029
|
+
|
3030
|
+
[ Opcodes.local_get, pointer ],
|
3031
|
+
[ Opcodes.load, 0, ...unsignedLEB128(ValtypeSize.i32) ],
|
3032
|
+
|
3033
|
+
...setType(scope, leftName, [
|
3034
|
+
[ Opcodes.local_get, pointer ],
|
3035
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32 + ValtypeSize[valtype]) ],
|
3036
|
+
]),
|
3037
|
+
|
3038
|
+
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
|
3039
|
+
|
3040
|
+
[ Opcodes.block, Blocktype.void ],
|
3041
|
+
[ Opcodes.block, Blocktype.void ],
|
3042
|
+
...generate(scope, decl.body),
|
3043
|
+
[ Opcodes.end ],
|
3044
|
+
|
3045
|
+
// increment iter pointer by valtype size + 1
|
3046
|
+
[ Opcodes.local_get, pointer ],
|
3047
|
+
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
3048
|
+
[ Opcodes.i32_add ],
|
3049
|
+
[ Opcodes.local_set, pointer ],
|
3050
|
+
|
3051
|
+
// increment counter by 1
|
3052
|
+
[ Opcodes.local_get, counter ],
|
3053
|
+
...number(1, Valtype.i32),
|
3054
|
+
[ Opcodes.i32_add ],
|
3055
|
+
[ Opcodes.local_tee, counter ],
|
3056
|
+
|
3057
|
+
// loop if counter != length
|
3058
|
+
[ Opcodes.local_get, length ],
|
3059
|
+
[ Opcodes.i32_ne ],
|
3060
|
+
[ Opcodes.br_if, 1 ],
|
3061
|
+
|
3062
|
+
[ Opcodes.end ],
|
3063
|
+
[ Opcodes.end ]
|
3064
|
+
],
|
2911
3065
|
default: internalThrow(scope, 'TypeError', `Tried for..of on non-iterable type`)
|
2912
3066
|
}, Blocktype.void));
|
2913
3067
|
|
@@ -3009,14 +3163,18 @@ const generateThrow = (scope, decl) => {
|
|
3009
3163
|
};
|
3010
3164
|
|
3011
3165
|
const generateTry = (scope, decl) => {
|
3012
|
-
|
3166
|
+
// todo: handle control-flow pre-exit for finally
|
3167
|
+
// "Immediately before a control-flow statement (return, throw, break, continue) is executed in the try block or catch block."
|
3013
3168
|
|
3014
3169
|
const out = [];
|
3015
3170
|
|
3171
|
+
const finalizer = decl.finalizer ? generate(scope, decl.finalizer) : [];
|
3172
|
+
|
3016
3173
|
out.push([ Opcodes.try, Blocktype.void ]);
|
3017
3174
|
depth.push('try');
|
3018
3175
|
|
3019
3176
|
out.push(...generate(scope, decl.block));
|
3177
|
+
out.push(...finalizer);
|
3020
3178
|
|
3021
3179
|
if (decl.handler) {
|
3022
3180
|
depth.pop();
|
@@ -3024,6 +3182,7 @@ const generateTry = (scope, decl) => {
|
|
3024
3182
|
|
3025
3183
|
out.push([ Opcodes.catch_all ]);
|
3026
3184
|
out.push(...generate(scope, decl.handler.body));
|
3185
|
+
out.push(...finalizer);
|
3027
3186
|
}
|
3028
3187
|
|
3029
3188
|
out.push([ Opcodes.end ]);
|
@@ -3036,13 +3195,6 @@ const generateEmpty = (scope, decl) => {
|
|
3036
3195
|
return [];
|
3037
3196
|
};
|
3038
3197
|
|
3039
|
-
const generateAssignPat = (scope, decl) => {
|
3040
|
-
// TODO
|
3041
|
-
// if identifier declared, use that
|
3042
|
-
// else, use default (right)
|
3043
|
-
return todo(scope, 'assignment pattern (optional arg)');
|
3044
|
-
};
|
3045
|
-
|
3046
3198
|
let pages = new Map();
|
3047
3199
|
const allocPage = (scope, reason, type) => {
|
3048
3200
|
if (pages.has(reason)) return pages.get(reason).ind;
|
@@ -3116,7 +3268,7 @@ const getAllocType = itemType => {
|
|
3116
3268
|
}
|
3117
3269
|
};
|
3118
3270
|
|
3119
|
-
const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false, itemType = valtype) => {
|
3271
|
+
const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false, itemType = valtype, typed = false) => {
|
3120
3272
|
const out = [];
|
3121
3273
|
|
3122
3274
|
scope.arrays ??= new Map();
|
@@ -3128,8 +3280,13 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
3128
3280
|
// todo: can we just have 1 undeclared array? probably not? but this is not really memory efficient
|
3129
3281
|
const uniqueName = name === '$undeclared' ? name + Math.random().toString().slice(2) : name;
|
3130
3282
|
|
3131
|
-
|
3132
|
-
|
3283
|
+
let page;
|
3284
|
+
if (Prefs.scopedPageNames) page = allocPage(scope, `${getAllocType(itemType)}: ${scope.name}/${uniqueName}`, itemType);
|
3285
|
+
else page = allocPage(scope, `${getAllocType(itemType)}: ${uniqueName}`, itemType);
|
3286
|
+
|
3287
|
+
// hack: use 1 for page 0 pointer for fast truthiness
|
3288
|
+
const ptr = page === 0 ? 1 : (page * pageSize);
|
3289
|
+
scope.arrays.set(name, ptr);
|
3133
3290
|
}
|
3134
3291
|
|
3135
3292
|
const pointer = scope.arrays.get(name);
|
@@ -3179,7 +3336,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
3179
3336
|
|
3180
3337
|
const pointerWasm = pointerTmp != null ? [ [ Opcodes.local_get, pointerTmp ] ] : number(pointer, Valtype.i32);
|
3181
3338
|
|
3182
|
-
// store length
|
3339
|
+
// store length
|
3183
3340
|
out.push(
|
3184
3341
|
...pointerWasm,
|
3185
3342
|
...number(length, Valtype.i32),
|
@@ -3187,14 +3344,20 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
3187
3344
|
);
|
3188
3345
|
|
3189
3346
|
const storeOp = StoreOps[itemType];
|
3190
|
-
|
3347
|
+
const sizePerEl = ValtypeSize[itemType] + (typed ? 1 : 0);
|
3191
3348
|
if (!initEmpty) for (let i = 0; i < length; i++) {
|
3192
3349
|
if (elements[i] == null) continue;
|
3193
3350
|
|
3351
|
+
const offset = ValtypeSize.i32 + i * sizePerEl;
|
3194
3352
|
out.push(
|
3195
3353
|
...pointerWasm,
|
3196
3354
|
...(useRawElements ? number(elements[i], Valtype[valtype]) : generate(scope, elements[i])),
|
3197
|
-
[ storeOp,
|
3355
|
+
[ storeOp, 0, ...unsignedLEB128(offset) ],
|
3356
|
+
...(!typed ? [] : [ // typed presumes !useRawElements
|
3357
|
+
...pointerWasm,
|
3358
|
+
...getNodeType(scope, elements[i]),
|
3359
|
+
[ Opcodes.i32_store8, 0, ...unsignedLEB128(offset + ValtypeSize[itemType]) ]
|
3360
|
+
])
|
3198
3361
|
);
|
3199
3362
|
}
|
3200
3363
|
|
@@ -3204,6 +3367,65 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
3204
3367
|
return [ out, pointer ];
|
3205
3368
|
};
|
3206
3369
|
|
3370
|
+
const storeArray = (scope, array, index, element, aotPointer = null) => {
|
3371
|
+
if (!Array.isArray(element)) element = generate(scope, element);
|
3372
|
+
if (typeof index === 'number') index = number(index);
|
3373
|
+
|
3374
|
+
const offset = localTmp(scope, '#storeArray_offset', Valtype.i32);
|
3375
|
+
|
3376
|
+
return [
|
3377
|
+
// calculate offset
|
3378
|
+
...index,
|
3379
|
+
Opcodes.i32_to_u,
|
3380
|
+
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
3381
|
+
[ Opcodes.i32_mul ],
|
3382
|
+
...(aotPointer ? [] : [
|
3383
|
+
...array,
|
3384
|
+
Opcodes.i32_to_u,
|
3385
|
+
[ Opcodes.i32_add ],
|
3386
|
+
]),
|
3387
|
+
[ Opcodes.local_set, offset ],
|
3388
|
+
|
3389
|
+
// store value
|
3390
|
+
[ Opcodes.local_get, offset ],
|
3391
|
+
...generate(scope, element),
|
3392
|
+
[ Opcodes.store, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
3393
|
+
|
3394
|
+
// store type
|
3395
|
+
[ Opcodes.local_get, offset ],
|
3396
|
+
...getNodeType(scope, element),
|
3397
|
+
[ Opcodes.i32_store8, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32 + ValtypeSize[valtype]) ]
|
3398
|
+
];
|
3399
|
+
};
|
3400
|
+
|
3401
|
+
const loadArray = (scope, array, index, aotPointer = null) => {
|
3402
|
+
if (typeof index === 'number') index = number(index);
|
3403
|
+
|
3404
|
+
const offset = localTmp(scope, '#loadArray_offset', Valtype.i32);
|
3405
|
+
|
3406
|
+
return [
|
3407
|
+
// calculate offset
|
3408
|
+
...index,
|
3409
|
+
Opcodes.i32_to_u,
|
3410
|
+
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
3411
|
+
[ Opcodes.i32_mul ],
|
3412
|
+
...(aotPointer ? [] : [
|
3413
|
+
...array,
|
3414
|
+
Opcodes.i32_to_u,
|
3415
|
+
[ Opcodes.i32_add ],
|
3416
|
+
]),
|
3417
|
+
[ Opcodes.local_set, offset ],
|
3418
|
+
|
3419
|
+
// load value
|
3420
|
+
[ Opcodes.local_get, offset ],
|
3421
|
+
[ Opcodes.load, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
3422
|
+
|
3423
|
+
// load type
|
3424
|
+
[ Opcodes.local_get, offset ],
|
3425
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32 + ValtypeSize[valtype]) ]
|
3426
|
+
];
|
3427
|
+
};
|
3428
|
+
|
3207
3429
|
const byteStringable = str => {
|
3208
3430
|
if (!Prefs.bytestring) return false;
|
3209
3431
|
|
@@ -3232,14 +3454,28 @@ const makeString = (scope, str, global = false, name = '$undeclared', forceBytes
|
|
3232
3454
|
};
|
3233
3455
|
|
3234
3456
|
const generateArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false) => {
|
3235
|
-
return makeArray(scope, decl, global, name, initEmpty, valtype)[0];
|
3457
|
+
return makeArray(scope, decl, global, name, initEmpty, valtype, true)[0];
|
3236
3458
|
};
|
3237
3459
|
|
3238
|
-
|
3460
|
+
const generateObject = (scope, decl, global = false, name = '$undeclared') => {
|
3461
|
+
if (decl.properties.length > 0) return todo(scope, 'objects are not supported yet', true);
|
3462
|
+
|
3463
|
+
return [
|
3464
|
+
...number(1),
|
3465
|
+
...setLastType(scope, TYPES.object)
|
3466
|
+
];
|
3467
|
+
};
|
3468
|
+
|
3469
|
+
const withType = (scope, wasm, type) => [
|
3470
|
+
...wasm,
|
3471
|
+
...setLastType(scope, type)
|
3472
|
+
];
|
3473
|
+
|
3474
|
+
const generateMember = (scope, decl, _global, _name) => {
|
3239
3475
|
const name = decl.object.name;
|
3240
3476
|
const pointer = scope.arrays?.get(name);
|
3241
3477
|
|
3242
|
-
const aotPointer = Prefs.aotPointerOpt && pointer
|
3478
|
+
const aotPointer = Prefs.aotPointerOpt && pointer;
|
3243
3479
|
|
3244
3480
|
// hack: .name
|
3245
3481
|
if (decl.property.name === 'name') {
|
@@ -3249,9 +3485,9 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3249
3485
|
// eg: __String_prototype_toLowerCase -> toLowerCase
|
3250
3486
|
if (nameProp.startsWith('__')) nameProp = nameProp.split('_').pop();
|
3251
3487
|
|
3252
|
-
return makeString(scope, nameProp, _global, _name, true);
|
3488
|
+
return withType(scope, makeString(scope, nameProp, _global, _name, true), TYPES.bytestring);
|
3253
3489
|
} else {
|
3254
|
-
return
|
3490
|
+
return withType(scope, number(0), TYPES.undefined);
|
3255
3491
|
}
|
3256
3492
|
}
|
3257
3493
|
|
@@ -3259,9 +3495,8 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3259
3495
|
if (decl.property.name === 'length') {
|
3260
3496
|
const func = funcs.find(x => x.name === name);
|
3261
3497
|
if (func) {
|
3262
|
-
const
|
3263
|
-
|
3264
|
-
return number(typedParams ? func.params.length / 2 : func.params.length);
|
3498
|
+
const typedParams = !func.internal || builtinFuncs[name]?.typedParams;
|
3499
|
+
return withType(scope, number(typedParams ? func.params.length / 2 : func.params.length), TYPES.number);
|
3265
3500
|
}
|
3266
3501
|
|
3267
3502
|
if (builtinFuncs[name + '$constructor']) {
|
@@ -3271,24 +3506,88 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3271
3506
|
const constructorFunc = builtinFuncs[name + '$constructor'];
|
3272
3507
|
const constructorParams = constructorFunc.typedParams ? (constructorFunc.params.length / 2) : constructorFunc.params.length;
|
3273
3508
|
|
3274
|
-
return number(Math.max(regularParams, constructorParams));
|
3509
|
+
return withType(scope, number(Math.max(regularParams, constructorParams)), TYPES.number);
|
3510
|
+
}
|
3511
|
+
|
3512
|
+
if (builtinFuncs[name]) return withType(scope, number(builtinFuncs[name].typedParams ? (builtinFuncs[name].params.length / 2) : builtinFuncs[name].params.length), TYPES.number);
|
3513
|
+
if (importedFuncs[name]) return withType(scope, number(importedFuncs[name].params), TYPES.number);
|
3514
|
+
if (internalConstrs[name]) return withType(scope, number(internalConstrs[name].length ?? 0), TYPES.number);
|
3515
|
+
|
3516
|
+
if (Prefs.fastLength) {
|
3517
|
+
// presume valid length object
|
3518
|
+
return [
|
3519
|
+
...(aotPointer ? number(0, Valtype.i32) : [
|
3520
|
+
...generate(scope, decl.object),
|
3521
|
+
Opcodes.i32_to_u
|
3522
|
+
]),
|
3523
|
+
|
3524
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
|
3525
|
+
Opcodes.i32_from_u
|
3526
|
+
];
|
3275
3527
|
}
|
3276
3528
|
|
3277
|
-
|
3278
|
-
|
3279
|
-
if (
|
3529
|
+
const type = getNodeType(scope, decl.object);
|
3530
|
+
const known = knownType(scope, type);
|
3531
|
+
if (known != null) {
|
3532
|
+
if ([ TYPES.string, TYPES.bytestring, TYPES.array ].includes(known)) return [
|
3533
|
+
...(aotPointer ? number(0, Valtype.i32) : [
|
3534
|
+
...generate(scope, decl.object),
|
3535
|
+
Opcodes.i32_to_u
|
3536
|
+
]),
|
3537
|
+
|
3538
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
|
3539
|
+
Opcodes.i32_from_u
|
3540
|
+
];
|
3541
|
+
|
3542
|
+
return number(0);
|
3543
|
+
}
|
3280
3544
|
|
3281
3545
|
return [
|
3282
|
-
...(
|
3283
|
-
|
3284
|
-
|
3285
|
-
|
3546
|
+
...typeIsOneOf(getNodeType(scope, decl.object), [ TYPES.string, TYPES.bytestring, TYPES.array ]),
|
3547
|
+
[ Opcodes.if, valtypeBinary ],
|
3548
|
+
...(aotPointer ? number(0, Valtype.i32) : [
|
3549
|
+
...generate(scope, decl.object),
|
3550
|
+
Opcodes.i32_to_u
|
3551
|
+
]),
|
3286
3552
|
|
3287
|
-
|
3288
|
-
|
3553
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
|
3554
|
+
Opcodes.i32_from_u,
|
3555
|
+
|
3556
|
+
...setLastType(scope, TYPES.number),
|
3557
|
+
[ Opcodes.else ],
|
3558
|
+
...number(0),
|
3559
|
+
...setLastType(scope, TYPES.undefined),
|
3560
|
+
[ Opcodes.end ]
|
3289
3561
|
];
|
3290
3562
|
}
|
3291
3563
|
|
3564
|
+
// todo: generate this array procedurally during builtinFuncs creation
|
3565
|
+
if (['size', 'description'].includes(decl.property.name)) {
|
3566
|
+
const bc = {};
|
3567
|
+
const cands = Object.keys(builtinFuncs).filter(x => x.startsWith('__') && x.endsWith('_prototype_' + decl.property.name + '$get'));
|
3568
|
+
|
3569
|
+
if (cands.length > 0) {
|
3570
|
+
for (const x of cands) {
|
3571
|
+
const type = TYPES[x.split('_prototype_')[0].slice(2).toLowerCase()];
|
3572
|
+
if (type == null) continue;
|
3573
|
+
|
3574
|
+
bc[type] = generateCall(scope, {
|
3575
|
+
callee: {
|
3576
|
+
type: 'Identifier',
|
3577
|
+
name: x
|
3578
|
+
},
|
3579
|
+
arguments: [ decl.object ],
|
3580
|
+
_protoInternalCall: true
|
3581
|
+
});
|
3582
|
+
}
|
3583
|
+
}
|
3584
|
+
|
3585
|
+
return typeSwitch(scope, getNodeType(scope, decl.object), {
|
3586
|
+
...bc,
|
3587
|
+
default: withType(scope, number(0), TYPES.undefined)
|
3588
|
+
}, valtypeBinary);
|
3589
|
+
}
|
3590
|
+
|
3292
3591
|
const object = generate(scope, decl.object);
|
3293
3592
|
const property = generate(scope, decl.property);
|
3294
3593
|
|
@@ -3303,24 +3602,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3303
3602
|
|
3304
3603
|
return typeSwitch(scope, getNodeType(scope, decl.object), {
|
3305
3604
|
[TYPES.array]: [
|
3306
|
-
|
3307
|
-
...property,
|
3308
|
-
|
3309
|
-
// convert to i32 and turn into byte offset by * valtypeSize (4 for i32, 8 for i64/f64)
|
3310
|
-
Opcodes.i32_to_u,
|
3311
|
-
...number(ValtypeSize[valtype], Valtype.i32),
|
3312
|
-
[ Opcodes.i32_mul ],
|
3313
|
-
|
3314
|
-
...(aotPointer ? [] : [
|
3315
|
-
...object,
|
3316
|
-
Opcodes.i32_to_u,
|
3317
|
-
[ Opcodes.i32_add ]
|
3318
|
-
]),
|
3319
|
-
|
3320
|
-
// read from memory
|
3321
|
-
[ Opcodes.load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
3322
|
-
|
3323
|
-
...number(TYPES.number, Valtype.i32),
|
3605
|
+
...loadArray(scope, object, property, aotPointer),
|
3324
3606
|
...setLastType(scope)
|
3325
3607
|
],
|
3326
3608
|
|
@@ -3351,9 +3633,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3351
3633
|
|
3352
3634
|
// return new string (page)
|
3353
3635
|
...number(newPointer),
|
3354
|
-
|
3355
|
-
...number(TYPES.string, Valtype.i32),
|
3356
|
-
...setLastType(scope)
|
3636
|
+
...setLastType(scope, TYPES.string)
|
3357
3637
|
],
|
3358
3638
|
[TYPES.bytestring]: [
|
3359
3639
|
// setup new/out array
|
@@ -3379,9 +3659,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3379
3659
|
|
3380
3660
|
// return new string (page)
|
3381
3661
|
...number(newPointer),
|
3382
|
-
|
3383
|
-
...number(TYPES.bytestring, Valtype.i32),
|
3384
|
-
...setLastType(scope)
|
3662
|
+
...setLastType(scope, TYPES.bytestring)
|
3385
3663
|
],
|
3386
3664
|
|
3387
3665
|
default: internalThrow(scope, 'TypeError', 'Member expression is not supported for non-string non-array yet', true)
|
@@ -3406,7 +3684,7 @@ const objectHack = node => {
|
|
3406
3684
|
if (!objectName) objectName = objectHack(node.object)?.name?.slice?.(2);
|
3407
3685
|
|
3408
3686
|
// if .name or .length, give up (hack within a hack!)
|
3409
|
-
if (['name', 'length'].includes(node.property.name)) {
|
3687
|
+
if (['name', 'length', 'size', 'description'].includes(node.property.name)) {
|
3410
3688
|
node.object = objectHack(node.object);
|
3411
3689
|
return;
|
3412
3690
|
}
|
@@ -3443,30 +3721,39 @@ const generateFunc = (scope, decl) => {
|
|
3443
3721
|
const name = decl.id ? decl.id.name : `anonymous_${randId()}`;
|
3444
3722
|
const params = decl.params ?? [];
|
3445
3723
|
|
3446
|
-
// const innerScope = { ...scope };
|
3447
3724
|
// TODO: share scope/locals between !!!
|
3448
|
-
const
|
3725
|
+
const func = {
|
3449
3726
|
locals: {},
|
3450
3727
|
localInd: 0,
|
3451
3728
|
// value, type
|
3452
3729
|
returns: [ valtypeBinary, Valtype.i32 ],
|
3453
3730
|
throws: false,
|
3454
|
-
name
|
3731
|
+
name,
|
3732
|
+
index: currentFuncIndex++
|
3455
3733
|
};
|
3456
3734
|
|
3457
3735
|
if (typedInput && decl.returnType) {
|
3458
|
-
|
3459
|
-
|
3736
|
+
const { type } = extractTypeAnnotation(decl.returnType);
|
3737
|
+
// if (type != null && !Prefs.indirectCalls) {
|
3738
|
+
if (type != null) {
|
3739
|
+
func.returnType = type;
|
3740
|
+
func.returns = [ valtypeBinary ];
|
3741
|
+
}
|
3460
3742
|
}
|
3461
3743
|
|
3462
3744
|
for (let i = 0; i < params.length; i++) {
|
3463
|
-
|
3745
|
+
const name = params[i].name;
|
3746
|
+
// if (name == null) return todo('non-identifier args are not supported');
|
3747
|
+
|
3748
|
+
allocVar(func, name, false);
|
3464
3749
|
|
3465
3750
|
if (typedInput && params[i].typeAnnotation) {
|
3466
|
-
addVarMetadata(
|
3751
|
+
addVarMetadata(func, name, false, extractTypeAnnotation(params[i]));
|
3467
3752
|
}
|
3468
3753
|
}
|
3469
3754
|
|
3755
|
+
func.params = Object.values(func.locals).map(x => x.type);
|
3756
|
+
|
3470
3757
|
let body = objectHack(decl.body);
|
3471
3758
|
if (decl.type === 'ArrowFunctionExpression' && decl.expression) {
|
3472
3759
|
// hack: () => 0 -> () => return 0
|
@@ -3476,37 +3763,23 @@ const generateFunc = (scope, decl) => {
|
|
3476
3763
|
};
|
3477
3764
|
}
|
3478
3765
|
|
3479
|
-
const wasm = generate(innerScope, body);
|
3480
|
-
const func = {
|
3481
|
-
name,
|
3482
|
-
params: Object.values(innerScope.locals).slice(0, params.length * 2).map(x => x.type),
|
3483
|
-
index: currentFuncIndex++,
|
3484
|
-
...innerScope
|
3485
|
-
};
|
3486
3766
|
funcIndex[name] = func.index;
|
3767
|
+
funcs.push(func);
|
3487
3768
|
|
3488
|
-
|
3769
|
+
const wasm = generate(func, body);
|
3770
|
+
func.wasm = wasm;
|
3489
3771
|
|
3490
|
-
|
3491
|
-
for (const inst of wasm) {
|
3492
|
-
if (inst[0] === Opcodes.call && inst[1] === -1) {
|
3493
|
-
inst[1] = func.index;
|
3494
|
-
}
|
3495
|
-
}
|
3772
|
+
if (name === 'main') func.gotLastType = true;
|
3496
3773
|
|
3497
3774
|
// add end return if not found
|
3498
3775
|
if (name !== 'main' && wasm[wasm.length - 1]?.[0] !== Opcodes.return && countLeftover(wasm) === 0) {
|
3499
3776
|
wasm.push(
|
3500
3777
|
...number(0),
|
3501
|
-
...number(TYPES.undefined, Valtype.i32),
|
3778
|
+
...(func.returnType != null ? [] : number(TYPES.undefined, Valtype.i32)),
|
3502
3779
|
[ Opcodes.return ]
|
3503
3780
|
);
|
3504
3781
|
}
|
3505
3782
|
|
3506
|
-
func.wasm = wasm;
|
3507
|
-
|
3508
|
-
funcs.push(func);
|
3509
|
-
|
3510
3783
|
return func;
|
3511
3784
|
};
|
3512
3785
|
|
@@ -3610,7 +3883,7 @@ const internalConstrs = {
|
|
3610
3883
|
generate: (scope, decl) => {
|
3611
3884
|
// todo: boolean object when used as constructor
|
3612
3885
|
const arg = decl.arguments[0] ?? DEFAULT_VALUE;
|
3613
|
-
return truthy(scope, generate(scope, arg), getNodeType(scope, arg));
|
3886
|
+
return truthy(scope, generate(scope, arg), getNodeType(scope, arg), false, false, 'full');
|
3614
3887
|
},
|
3615
3888
|
type: TYPES.boolean,
|
3616
3889
|
length: 1
|
@@ -3671,8 +3944,10 @@ const internalConstrs = {
|
|
3671
3944
|
}),
|
3672
3945
|
|
3673
3946
|
// print space
|
3674
|
-
...
|
3675
|
-
|
3947
|
+
...(i !== decl.arguments.length - 1 ? [
|
3948
|
+
...number(32),
|
3949
|
+
[ Opcodes.call, importedFuncs.printChar ]
|
3950
|
+
] : [])
|
3676
3951
|
);
|
3677
3952
|
}
|
3678
3953
|
|
@@ -3682,6 +3957,8 @@ const internalConstrs = {
|
|
3682
3957
|
[ Opcodes.call, importedFuncs.printChar ]
|
3683
3958
|
);
|
3684
3959
|
|
3960
|
+
out.push(...number(UNDEFINED));
|
3961
|
+
|
3685
3962
|
return out;
|
3686
3963
|
},
|
3687
3964
|
type: TYPES.undefined,
|
@@ -3751,9 +4028,8 @@ export default program => {
|
|
3751
4028
|
|
3752
4029
|
if (Prefs.astLog) console.log(JSON.stringify(program.body.body, null, 2));
|
3753
4030
|
|
3754
|
-
generateFunc(scope, program);
|
4031
|
+
const main = generateFunc(scope, program);
|
3755
4032
|
|
3756
|
-
const main = funcs[funcs.length - 1];
|
3757
4033
|
main.export = true;
|
3758
4034
|
main.returns = [ valtypeBinary, Valtype.i32 ];
|
3759
4035
|
|
@@ -3780,7 +4056,7 @@ export default program => {
|
|
3780
4056
|
}
|
3781
4057
|
|
3782
4058
|
// if blank main func and other exports, remove it
|
3783
|
-
if (main.wasm.length === 0 && funcs.reduce((acc, x) => acc + (x.export ? 1 : 0), 0) > 1) funcs.splice(
|
4059
|
+
if (main.wasm.length === 0 && funcs.reduce((acc, x) => acc + (x.export ? 1 : 0), 0) > 1) funcs.splice(main.index - importedFuncs.length, 1);
|
3784
4060
|
|
3785
4061
|
return { funcs, globals, tags, exceptions, pages, data };
|
3786
4062
|
};
|