porffor 0.14.0-c6edfd328 → 0.14.0-cb572e8d4
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 +4 -2
- package/README.md +9 -13
- package/compiler/2c.js +65 -2
- package/compiler/builtins/annexb_string.ts +1 -0
- package/compiler/builtins/array.ts +71 -1
- package/compiler/builtins/base64.ts +1 -0
- package/compiler/builtins/boolean.ts +2 -0
- package/compiler/builtins/crypto.ts +1 -0
- package/compiler/builtins/date.ts +2 -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 +2 -0
- package/compiler/builtins/object.ts +2 -0
- package/compiler/builtins/set.ts +14 -1
- package/compiler/builtins/string.ts +1 -0
- package/compiler/builtins/symbol.ts +2 -1
- package/compiler/builtins.js +15 -4
- package/compiler/codegen.js +158 -140
- package/compiler/decompile.js +4 -0
- package/compiler/generated_builtins.js +295 -9
- package/compiler/index.js +2 -1
- package/compiler/parse.js +1 -1
- package/compiler/precompile.js +2 -2
- package/package.json +1 -1
- package/runner/index.js +4 -3
package/compiler/codegen.js
CHANGED
@@ -40,11 +40,11 @@ const todo = (scope, msg, expectsValue = undefined) => {
|
|
40
40
|
}
|
41
41
|
};
|
42
42
|
|
43
|
-
const isFuncType = type =>
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
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
|
+
|
48
48
|
const generate = (scope, decl, global = false, name = undefined, valueUnused = false) => {
|
49
49
|
switch (decl.type) {
|
50
50
|
case 'BinaryExpression':
|
@@ -147,14 +147,16 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
147
147
|
return generateMember(scope, decl, global, name);
|
148
148
|
|
149
149
|
case 'ExportNamedDeclaration':
|
150
|
-
|
151
|
-
const funcsBefore = funcs.length;
|
150
|
+
const funcsBefore = funcs.map(x => x.name);
|
152
151
|
generate(scope, decl.declaration);
|
153
152
|
|
154
|
-
|
155
|
-
|
156
|
-
const
|
157
|
-
|
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);
|
156
|
+
|
157
|
+
for (const x of newFuncs) {
|
158
|
+
x.export = true;
|
159
|
+
}
|
158
160
|
}
|
159
161
|
|
160
162
|
return [];
|
@@ -361,7 +363,7 @@ const localTmp = (scope, name, type = valtypeBinary) => {
|
|
361
363
|
};
|
362
364
|
|
363
365
|
const isIntOp = op => op && ((op[0] >= 0x45 && op[0] <= 0x4f) || (op[0] >= 0x67 && op[0] <= 0x78) || op[0] === 0x41);
|
364
|
-
const
|
366
|
+
const isIntToFloatOp = op => op && (op[0] >= 0xb7 && op[0] <= 0xba);
|
365
367
|
|
366
368
|
const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
367
369
|
const checks = {
|
@@ -378,10 +380,10 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
378
380
|
|
379
381
|
// if we can, use int tmp and convert at the end to help prevent unneeded conversions
|
380
382
|
// (like if we are in an if condition - very common)
|
381
|
-
const
|
382
|
-
const
|
383
|
+
const leftWasInt = isIntToFloatOp(left[left.length - 1]);
|
384
|
+
const rightWasInt = isIntToFloatOp(right[right.length - 1]);
|
383
385
|
|
384
|
-
const canInt =
|
386
|
+
const canInt = leftWasInt && rightWasInt;
|
385
387
|
|
386
388
|
if (canInt) {
|
387
389
|
// remove int -> float conversions from left and right
|
@@ -646,9 +648,9 @@ const compareStrings = (scope, left, right, bytestrings = false) => {
|
|
646
648
|
[ Opcodes.i32_add ],
|
647
649
|
[ Opcodes.local_tee, index ],
|
648
650
|
|
649
|
-
// if index
|
651
|
+
// if index < index end (length * sizeof valtype), loop
|
650
652
|
[ Opcodes.local_get, indexEnd ],
|
651
|
-
[ Opcodes.
|
653
|
+
[ Opcodes.i32_lt_s ],
|
652
654
|
[ Opcodes.br_if, 0 ],
|
653
655
|
[ Opcodes.end ],
|
654
656
|
|
@@ -666,57 +668,50 @@ const compareStrings = (scope, left, right, bytestrings = false) => {
|
|
666
668
|
];
|
667
669
|
};
|
668
670
|
|
669
|
-
const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
670
|
-
if (
|
671
|
+
const truthy = (scope, wasm, type, intIn = false, intOut = false, forceTruthyMode = undefined) => {
|
672
|
+
if (isIntToFloatOp(wasm[wasm.length - 1])) return [
|
671
673
|
...wasm,
|
672
674
|
...(!intIn && intOut ? [ Opcodes.i32_to_u ] : [])
|
673
675
|
];
|
674
676
|
// if (isIntOp(wasm[wasm.length - 1])) return [ ...wasm ];
|
675
677
|
|
678
|
+
// todo/perf: use knownType and custom bytecode here instead of typeSwitch
|
679
|
+
|
676
680
|
const useTmp = knownType(scope, type) == null;
|
677
681
|
const tmp = useTmp && localTmp(scope, `#logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
|
678
682
|
|
679
|
-
const def =
|
680
|
-
|
681
|
-
|
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 ]),
|
688
|
+
|
689
|
+
[ Opcodes.i32_eqz ],
|
690
|
+
[ Opcodes.i32_eqz ],
|
682
691
|
|
683
|
-
|
684
|
-
|
692
|
+
...(intOut ? [] : [ Opcodes.i32_from ]),
|
693
|
+
];
|
685
694
|
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
// ...number(0, intOut ? Valtype.i32 : valtypeBinary),
|
701
|
-
// [ Opcodes.end ],
|
702
|
-
|
703
|
-
...(!intOut || (intIn && intOut) ? [] : [ Opcodes.i32_to_u ]),
|
704
|
-
|
705
|
-
/* Opcodes.eqz,
|
706
|
-
[ Opcodes.i32_eqz ],
|
707
|
-
Opcodes.i32_from */
|
708
|
-
];
|
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');
|
709
709
|
|
710
710
|
return [
|
711
711
|
...wasm,
|
712
712
|
...(!useTmp ? [] : [ [ Opcodes.local_set, tmp ] ]),
|
713
713
|
|
714
714
|
...typeSwitch(scope, type, {
|
715
|
-
// [TYPES.number]: def,
|
716
|
-
[TYPES.array]: [
|
717
|
-
// arrays are always truthy
|
718
|
-
...number(1, intOut ? Valtype.i32 : valtypeBinary)
|
719
|
-
],
|
720
715
|
[TYPES.string]: [
|
721
716
|
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
722
717
|
...(intIn ? [] : [ Opcodes.i32_to_u ]),
|
@@ -752,10 +747,6 @@ const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
752
747
|
...(!useTmp ? [] : [ [ Opcodes.local_set, tmp ] ]),
|
753
748
|
|
754
749
|
...typeSwitch(scope, type, {
|
755
|
-
[TYPES.array]: [
|
756
|
-
// arrays are always truthy
|
757
|
-
...number(0, intOut ? Valtype.i32 : valtypeBinary)
|
758
|
-
],
|
759
750
|
[TYPES.string]: [
|
760
751
|
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
761
752
|
...(intIn ? [] : [ Opcodes.i32_to_u ]),
|
@@ -999,7 +990,7 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
999
990
|
// if both are true
|
1000
991
|
[ Opcodes.i32_and ],
|
1001
992
|
[ Opcodes.if, Blocktype.void ],
|
1002
|
-
...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ]),
|
993
|
+
...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ], false),
|
1003
994
|
...(op === '!==' || op === '!=' ? [ [ Opcodes.i32_eqz ] ] : []),
|
1004
995
|
[ Opcodes.br, 1 ],
|
1005
996
|
[ Opcodes.end ],
|
@@ -1048,14 +1039,14 @@ const generateBinaryExp = (scope, decl, _global, _name) => {
|
|
1048
1039
|
return out;
|
1049
1040
|
};
|
1050
1041
|
|
1051
|
-
const asmFuncToAsm = (func,
|
1052
|
-
return func(
|
1042
|
+
const asmFuncToAsm = (func, scope) => {
|
1043
|
+
return func(scope, {
|
1053
1044
|
TYPES, TYPE_NAMES, typeSwitch, makeArray, makeString, allocPage, internalThrow,
|
1054
|
-
builtin:
|
1055
|
-
let idx = funcIndex[
|
1056
|
-
if (idx
|
1057
|
-
includeBuiltin(null,
|
1058
|
-
idx = funcIndex[
|
1045
|
+
builtin: n => {
|
1046
|
+
let idx = funcIndex[n] ?? importedFuncs[n];
|
1047
|
+
if (idx == null && builtinFuncs[n]) {
|
1048
|
+
includeBuiltin(null, n);
|
1049
|
+
idx = funcIndex[n];
|
1059
1050
|
}
|
1060
1051
|
|
1061
1052
|
return idx;
|
@@ -1063,7 +1054,7 @@ const asmFuncToAsm = (func, { name = '#unknown_asm_func', params = [], locals =
|
|
1063
1054
|
});
|
1064
1055
|
};
|
1065
1056
|
|
1066
|
-
const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits, returns, returnType, localNames = [], globalNames = [], data: _data = [], table = false
|
1057
|
+
const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits, returns, returnType, localNames = [], globalNames = [], data: _data = [], table = false }) => {
|
1067
1058
|
const existing = funcs.find(x => x.name === name);
|
1068
1059
|
if (existing) return existing;
|
1069
1060
|
|
@@ -1081,7 +1072,22 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
|
|
1081
1072
|
data.push(copy);
|
1082
1073
|
}
|
1083
1074
|
|
1084
|
-
|
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);
|
1085
1091
|
|
1086
1092
|
let baseGlobalIdx, i = 0;
|
1087
1093
|
for (const type of globalTypes) {
|
@@ -1100,23 +1106,6 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
|
|
1100
1106
|
}
|
1101
1107
|
}
|
1102
1108
|
|
1103
|
-
const func = {
|
1104
|
-
name,
|
1105
|
-
params,
|
1106
|
-
locals,
|
1107
|
-
returns,
|
1108
|
-
returnType: returnType ?? TYPES.number,
|
1109
|
-
wasm,
|
1110
|
-
internal: true,
|
1111
|
-
index: currentFuncIndex++
|
1112
|
-
};
|
1113
|
-
|
1114
|
-
if (callsSelf) for (const inst of wasm) {
|
1115
|
-
if (inst[0] === Opcodes.call && inst[1] === -1) {
|
1116
|
-
inst[1] = func.index;
|
1117
|
-
}
|
1118
|
-
}
|
1119
|
-
|
1120
1109
|
if (table) for (const inst of wasm) {
|
1121
1110
|
if (inst[0] === Opcodes.i32_load16_u && inst.at(-1) === 'read_argc') {
|
1122
1111
|
inst.splice(2, 99);
|
@@ -1124,10 +1113,7 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
|
|
1124
1113
|
}
|
1125
1114
|
}
|
1126
1115
|
|
1127
|
-
|
1128
|
-
funcIndex[name] = func.index;
|
1129
|
-
|
1130
|
-
if (table) funcs.table = true;
|
1116
|
+
func.wasm = wasm;
|
1131
1117
|
|
1132
1118
|
return func;
|
1133
1119
|
};
|
@@ -1479,16 +1465,11 @@ const countLeftover = wasm => {
|
|
1479
1465
|
else if (inst[0] === Opcodes.return) count = 0;
|
1480
1466
|
else if (inst[0] === Opcodes.call) {
|
1481
1467
|
let func = funcs.find(x => x.index === inst[1]);
|
1482
|
-
if (inst[1]
|
1483
|
-
|
1484
|
-
|
1485
|
-
count -= importedFuncs[inst[1]].params;
|
1486
|
-
count += importedFuncs[inst[1]].returns;
|
1468
|
+
if (inst[1] < importedFuncs.length) {
|
1469
|
+
func = importedFuncs[inst[1]];
|
1470
|
+
count = count - func.params + func.returns;
|
1487
1471
|
} else {
|
1488
|
-
|
1489
|
-
count -= func.params.length;
|
1490
|
-
} else count--;
|
1491
|
-
if (func) count += func.returns.length;
|
1472
|
+
count = count - func.params.length + func.returns.length;
|
1492
1473
|
}
|
1493
1474
|
} else if (inst[0] === Opcodes.call_indirect) {
|
1494
1475
|
count--; // funcidx
|
@@ -1641,6 +1622,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1641
1622
|
|
1642
1623
|
if (!funcIndex[rhemynName]) {
|
1643
1624
|
const func = Rhemyn[funcName](regex, currentFuncIndex++, rhemynName);
|
1625
|
+
func.internal = true;
|
1644
1626
|
|
1645
1627
|
funcIndex[func.name] = func.index;
|
1646
1628
|
funcs.push(func);
|
@@ -1812,11 +1794,6 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1812
1794
|
|
1813
1795
|
if (idx === undefined && internalConstrs[name]) return internalConstrs[name].generate(scope, decl, _global, _name);
|
1814
1796
|
|
1815
|
-
if (idx === undefined && name === scope.name) {
|
1816
|
-
// hack: calling self, func generator will fix later
|
1817
|
-
idx = -1;
|
1818
|
-
}
|
1819
|
-
|
1820
1797
|
if (idx === undefined && name.startsWith('__Porffor_wasm_')) {
|
1821
1798
|
const wasmOps = {
|
1822
1799
|
// pointer, align, offset
|
@@ -1998,11 +1975,10 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1998
1975
|
return internalThrow(scope, 'ReferenceError', `${unhackName(name)} is not defined`, true);
|
1999
1976
|
}
|
2000
1977
|
|
2001
|
-
const func = funcs.find(x => x.index === idx);
|
2002
|
-
|
2003
|
-
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;
|
2004
1980
|
const typedParams = userFunc || builtinFuncs[name]?.typedParams;
|
2005
|
-
const typedReturns = (func
|
1981
|
+
const typedReturns = (func && func.returnType == null) || builtinFuncs[name]?.typedReturns;
|
2006
1982
|
const paramCount = func && (typedParams ? func.params.length / 2 : func.params.length);
|
2007
1983
|
|
2008
1984
|
let args = decl.arguments;
|
@@ -2605,6 +2581,11 @@ const generateUnary = (scope, decl) => {
|
|
2605
2581
|
];
|
2606
2582
|
|
2607
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
|
+
}
|
2608
2589
|
// !=
|
2609
2590
|
return falsy(scope, generate(scope, decl.argument), getNodeType(scope, decl.argument), false, false);
|
2610
2591
|
|
@@ -3043,6 +3024,44 @@ const generateForOf = (scope, decl) => {
|
|
3043
3024
|
[ Opcodes.end ],
|
3044
3025
|
[ Opcodes.end ]
|
3045
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
|
+
],
|
3046
3065
|
default: internalThrow(scope, 'TypeError', `Tried for..of on non-iterable type`)
|
3047
3066
|
}, Blocktype.void));
|
3048
3067
|
|
@@ -3144,14 +3163,18 @@ const generateThrow = (scope, decl) => {
|
|
3144
3163
|
};
|
3145
3164
|
|
3146
3165
|
const generateTry = (scope, decl) => {
|
3147
|
-
|
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."
|
3148
3168
|
|
3149
3169
|
const out = [];
|
3150
3170
|
|
3171
|
+
const finalizer = decl.finalizer ? generate(scope, decl.finalizer) : [];
|
3172
|
+
|
3151
3173
|
out.push([ Opcodes.try, Blocktype.void ]);
|
3152
3174
|
depth.push('try');
|
3153
3175
|
|
3154
3176
|
out.push(...generate(scope, decl.block));
|
3177
|
+
out.push(...finalizer);
|
3155
3178
|
|
3156
3179
|
if (decl.handler) {
|
3157
3180
|
depth.pop();
|
@@ -3159,6 +3182,7 @@ const generateTry = (scope, decl) => {
|
|
3159
3182
|
|
3160
3183
|
out.push([ Opcodes.catch_all ]);
|
3161
3184
|
out.push(...generate(scope, decl.handler.body));
|
3185
|
+
out.push(...finalizer);
|
3162
3186
|
}
|
3163
3187
|
|
3164
3188
|
out.push([ Opcodes.end ]);
|
@@ -3256,8 +3280,13 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
3256
3280
|
// todo: can we just have 1 undeclared array? probably not? but this is not really memory efficient
|
3257
3281
|
const uniqueName = name === '$undeclared' ? name + Math.random().toString().slice(2) : name;
|
3258
3282
|
|
3259
|
-
|
3260
|
-
|
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);
|
3261
3290
|
}
|
3262
3291
|
|
3263
3292
|
const pointer = scope.arrays.get(name);
|
@@ -3466,8 +3495,7 @@ const generateMember = (scope, decl, _global, _name) => {
|
|
3466
3495
|
if (decl.property.name === 'length') {
|
3467
3496
|
const func = funcs.find(x => x.name === name);
|
3468
3497
|
if (func) {
|
3469
|
-
const
|
3470
|
-
const typedParams = userFunc || builtinFuncs[name]?.typedParams;
|
3498
|
+
const typedParams = !func.internal || builtinFuncs[name]?.typedParams;
|
3471
3499
|
return withType(scope, number(typedParams ? func.params.length / 2 : func.params.length), TYPES.number);
|
3472
3500
|
}
|
3473
3501
|
|
@@ -3693,34 +3721,39 @@ const generateFunc = (scope, decl) => {
|
|
3693
3721
|
const name = decl.id ? decl.id.name : `anonymous_${randId()}`;
|
3694
3722
|
const params = decl.params ?? [];
|
3695
3723
|
|
3696
|
-
// const innerScope = { ...scope };
|
3697
3724
|
// TODO: share scope/locals between !!!
|
3698
|
-
const
|
3725
|
+
const func = {
|
3699
3726
|
locals: {},
|
3700
3727
|
localInd: 0,
|
3701
3728
|
// value, type
|
3702
3729
|
returns: [ valtypeBinary, Valtype.i32 ],
|
3703
3730
|
throws: false,
|
3704
|
-
name
|
3731
|
+
name,
|
3732
|
+
index: currentFuncIndex++
|
3705
3733
|
};
|
3706
3734
|
|
3707
3735
|
if (typedInput && decl.returnType) {
|
3708
3736
|
const { type } = extractTypeAnnotation(decl.returnType);
|
3709
3737
|
// if (type != null && !Prefs.indirectCalls) {
|
3710
3738
|
if (type != null) {
|
3711
|
-
|
3712
|
-
|
3739
|
+
func.returnType = type;
|
3740
|
+
func.returns = [ valtypeBinary ];
|
3713
3741
|
}
|
3714
3742
|
}
|
3715
3743
|
|
3716
3744
|
for (let i = 0; i < params.length; i++) {
|
3717
|
-
|
3745
|
+
const name = params[i].name;
|
3746
|
+
// if (name == null) return todo('non-identifier args are not supported');
|
3747
|
+
|
3748
|
+
allocVar(func, name, false);
|
3718
3749
|
|
3719
3750
|
if (typedInput && params[i].typeAnnotation) {
|
3720
|
-
addVarMetadata(
|
3751
|
+
addVarMetadata(func, name, false, extractTypeAnnotation(params[i]));
|
3721
3752
|
}
|
3722
3753
|
}
|
3723
3754
|
|
3755
|
+
func.params = Object.values(func.locals).map(x => x.type);
|
3756
|
+
|
3724
3757
|
let body = objectHack(decl.body);
|
3725
3758
|
if (decl.type === 'ArrowFunctionExpression' && decl.expression) {
|
3726
3759
|
// hack: () => 0 -> () => return 0
|
@@ -3730,37 +3763,23 @@ const generateFunc = (scope, decl) => {
|
|
3730
3763
|
};
|
3731
3764
|
}
|
3732
3765
|
|
3733
|
-
const wasm = generate(innerScope, body);
|
3734
|
-
const func = {
|
3735
|
-
name,
|
3736
|
-
params: Object.values(innerScope.locals).slice(0, params.length * 2).map(x => x.type),
|
3737
|
-
index: currentFuncIndex++,
|
3738
|
-
...innerScope
|
3739
|
-
};
|
3740
3766
|
funcIndex[name] = func.index;
|
3767
|
+
funcs.push(func);
|
3741
3768
|
|
3742
|
-
|
3769
|
+
const wasm = generate(func, body);
|
3770
|
+
func.wasm = wasm;
|
3743
3771
|
|
3744
|
-
|
3745
|
-
for (const inst of wasm) {
|
3746
|
-
if (inst[0] === Opcodes.call && inst[1] === -1) {
|
3747
|
-
inst[1] = func.index;
|
3748
|
-
}
|
3749
|
-
}
|
3772
|
+
if (name === 'main') func.gotLastType = true;
|
3750
3773
|
|
3751
3774
|
// add end return if not found
|
3752
3775
|
if (name !== 'main' && wasm[wasm.length - 1]?.[0] !== Opcodes.return && countLeftover(wasm) === 0) {
|
3753
3776
|
wasm.push(
|
3754
3777
|
...number(0),
|
3755
|
-
...(
|
3778
|
+
...(func.returnType != null ? [] : number(TYPES.undefined, Valtype.i32)),
|
3756
3779
|
[ Opcodes.return ]
|
3757
3780
|
);
|
3758
3781
|
}
|
3759
3782
|
|
3760
|
-
func.wasm = wasm;
|
3761
|
-
|
3762
|
-
funcs.push(func);
|
3763
|
-
|
3764
3783
|
return func;
|
3765
3784
|
};
|
3766
3785
|
|
@@ -3864,7 +3883,7 @@ const internalConstrs = {
|
|
3864
3883
|
generate: (scope, decl) => {
|
3865
3884
|
// todo: boolean object when used as constructor
|
3866
3885
|
const arg = decl.arguments[0] ?? DEFAULT_VALUE;
|
3867
|
-
return truthy(scope, generate(scope, arg), getNodeType(scope, arg));
|
3886
|
+
return truthy(scope, generate(scope, arg), getNodeType(scope, arg), false, false, 'full');
|
3868
3887
|
},
|
3869
3888
|
type: TYPES.boolean,
|
3870
3889
|
length: 1
|
@@ -4009,9 +4028,8 @@ export default program => {
|
|
4009
4028
|
|
4010
4029
|
if (Prefs.astLog) console.log(JSON.stringify(program.body.body, null, 2));
|
4011
4030
|
|
4012
|
-
generateFunc(scope, program);
|
4031
|
+
const main = generateFunc(scope, program);
|
4013
4032
|
|
4014
|
-
const main = funcs[funcs.length - 1];
|
4015
4033
|
main.export = true;
|
4016
4034
|
main.returns = [ valtypeBinary, Valtype.i32 ];
|
4017
4035
|
|
@@ -4038,7 +4056,7 @@ export default program => {
|
|
4038
4056
|
}
|
4039
4057
|
|
4040
4058
|
// if blank main func and other exports, remove it
|
4041
|
-
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);
|
4042
4060
|
|
4043
4061
|
return { funcs, globals, tags, exceptions, pages, data };
|
4044
4062
|
};
|
package/compiler/decompile.js
CHANGED
@@ -89,6 +89,10 @@ export default (wasm, name = '', ind = 0, locals = {}, params = [], returns = []
|
|
89
89
|
if (inst[0] === Opcodes.call || inst[0] === Opcodes.return_call) {
|
90
90
|
const callFunc = funcs.find(x => x.index === inst[1]);
|
91
91
|
if (callFunc) out += ` ;; $${callFunc.name} ${makeSignature(callFunc.params, callFunc.returns)}`;
|
92
|
+
if (globalThis.importFuncs && inst[1] < importFuncs.length) {
|
93
|
+
const importFunc = importFuncs[inst[1]];
|
94
|
+
out += ` ;; import ${importFunc.name} ${makeSignature(new Array(importFunc.params).fill(valtypeBinary), new Array(importFunc.returns).fill(valtypeBinary),)}`;
|
95
|
+
}
|
92
96
|
}
|
93
97
|
|
94
98
|
if (inst[0] === Opcodes.local_get || inst[0] === Opcodes.local_set || inst[0] === Opcodes.local_tee) {
|