porffor 0.14.0-33cb5e44b → 0.14.0-3905158d3
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 +5 -1
- package/asur/index.js +1 -1
- package/compiler/assemble.js +14 -0
- package/compiler/builtins/array.ts +82 -4
- package/compiler/builtins/boolean.ts +1 -1
- package/compiler/builtins/error.js +22 -0
- package/compiler/builtins/math.ts +408 -0
- package/compiler/builtins/set.ts +5 -6
- package/compiler/builtins/symbol.ts +1 -1
- package/compiler/builtins.js +10 -6
- package/compiler/codegen.js +495 -252
- package/compiler/generated_builtins.js +566 -135
- package/compiler/precompile.js +4 -3
- package/compiler/prefs.js +1 -1
- package/compiler/prototype.js +180 -157
- package/compiler/wrap.js +66 -37
- package/package.json +1 -1
- package/porffor_tmp.c +152 -0
- package/runner/index.js +1 -1
- package/runner/repl.js +18 -2
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':
|
@@ -140,18 +140,23 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
140
140
|
case 'ArrayExpression':
|
141
141
|
return generateArray(scope, decl, global, name);
|
142
142
|
|
143
|
+
case 'ObjectExpression':
|
144
|
+
return generateObject(scope, decl, global, name);
|
145
|
+
|
143
146
|
case 'MemberExpression':
|
144
147
|
return generateMember(scope, decl, global, name);
|
145
148
|
|
146
149
|
case 'ExportNamedDeclaration':
|
147
|
-
|
148
|
-
const funcsBefore = funcs.length;
|
150
|
+
const funcsBefore = funcs.map(x => x.name);
|
149
151
|
generate(scope, decl.declaration);
|
150
152
|
|
151
|
-
|
152
|
-
|
153
|
-
const
|
154
|
-
|
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
|
+
}
|
155
160
|
}
|
156
161
|
|
157
162
|
return [];
|
@@ -200,19 +205,11 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
200
205
|
|
201
206
|
__Porffor_bs: str => [
|
202
207
|
...makeString(scope, str, global, name, true),
|
203
|
-
|
204
|
-
...(name ? setType(scope, name, TYPES.bytestring) : [
|
205
|
-
...number(TYPES.bytestring, Valtype.i32),
|
206
|
-
...setLastType(scope)
|
207
|
-
])
|
208
|
+
...(name ? setType(scope, name, TYPES.bytestring) : setLastType(scope, TYPES.bytestring))
|
208
209
|
],
|
209
210
|
__Porffor_s: str => [
|
210
211
|
...makeString(scope, str, global, name, false),
|
211
|
-
|
212
|
-
...(name ? setType(scope, name, TYPES.string) : [
|
213
|
-
...number(TYPES.string, Valtype.i32),
|
214
|
-
...setLastType(scope)
|
215
|
-
])
|
212
|
+
...(name ? setType(scope, name, TYPES.string) : setLastType(scope, TYPES.string))
|
216
213
|
],
|
217
214
|
};
|
218
215
|
|
@@ -366,7 +363,7 @@ const localTmp = (scope, name, type = valtypeBinary) => {
|
|
366
363
|
};
|
367
364
|
|
368
365
|
const isIntOp = op => op && ((op[0] >= 0x45 && op[0] <= 0x4f) || (op[0] >= 0x67 && op[0] <= 0x78) || op[0] === 0x41);
|
369
|
-
const
|
366
|
+
const isIntToFloatOp = op => op && (op[0] >= 0xb7 && op[0] <= 0xba);
|
370
367
|
|
371
368
|
const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
372
369
|
const checks = {
|
@@ -383,10 +380,10 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
383
380
|
|
384
381
|
// if we can, use int tmp and convert at the end to help prevent unneeded conversions
|
385
382
|
// (like if we are in an if condition - very common)
|
386
|
-
const
|
387
|
-
const
|
383
|
+
const leftWasInt = isIntToFloatOp(left[left.length - 1]);
|
384
|
+
const rightWasInt = isIntToFloatOp(right[right.length - 1]);
|
388
385
|
|
389
|
-
const canInt =
|
386
|
+
const canInt = leftWasInt && rightWasInt;
|
390
387
|
|
391
388
|
if (canInt) {
|
392
389
|
// remove int -> float conversions from left and right
|
@@ -400,13 +397,11 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
400
397
|
[ Opcodes.if, Valtype.i32 ],
|
401
398
|
...right,
|
402
399
|
// note type
|
403
|
-
...rightType,
|
404
|
-
...setLastType(scope),
|
400
|
+
...setLastType(scope, rightType),
|
405
401
|
[ Opcodes.else ],
|
406
402
|
[ Opcodes.local_get, localTmp(scope, 'logictmpi', Valtype.i32) ],
|
407
403
|
// note type
|
408
|
-
...leftType,
|
409
|
-
...setLastType(scope),
|
404
|
+
...setLastType(scope, leftType),
|
410
405
|
[ Opcodes.end ],
|
411
406
|
Opcodes.i32_from
|
412
407
|
];
|
@@ -419,13 +414,11 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
419
414
|
[ Opcodes.if, valtypeBinary ],
|
420
415
|
...right,
|
421
416
|
// note type
|
422
|
-
...rightType,
|
423
|
-
...setLastType(scope),
|
417
|
+
...setLastType(scope, rightType),
|
424
418
|
[ Opcodes.else ],
|
425
419
|
[ Opcodes.local_get, localTmp(scope, 'logictmp') ],
|
426
420
|
// note type
|
427
|
-
...leftType,
|
428
|
-
...setLastType(scope),
|
421
|
+
...setLastType(scope, leftType),
|
429
422
|
[ Opcodes.end ]
|
430
423
|
];
|
431
424
|
};
|
@@ -453,11 +446,11 @@ const concatStrings = (scope, left, right, global, name, assign = false, bytestr
|
|
453
446
|
...number(0, Valtype.i32), // base 0 for store later
|
454
447
|
|
455
448
|
...number(pointer, Valtype.i32),
|
456
|
-
[ Opcodes.i32_load,
|
449
|
+
[ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
|
457
450
|
[ Opcodes.local_tee, leftLength ],
|
458
451
|
|
459
452
|
[ Opcodes.local_get, rightPointer ],
|
460
|
-
[ Opcodes.i32_load,
|
453
|
+
[ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
|
461
454
|
[ Opcodes.local_tee, rightLength ],
|
462
455
|
|
463
456
|
[ Opcodes.i32_add ],
|
@@ -513,11 +506,11 @@ const concatStrings = (scope, left, right, global, name, assign = false, bytestr
|
|
513
506
|
...number(0, Valtype.i32), // base 0 for store later
|
514
507
|
|
515
508
|
[ Opcodes.local_get, leftPointer ],
|
516
|
-
[ Opcodes.i32_load,
|
509
|
+
[ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
|
517
510
|
[ Opcodes.local_tee, leftLength ],
|
518
511
|
|
519
512
|
[ Opcodes.local_get, rightPointer ],
|
520
|
-
[ Opcodes.i32_load,
|
513
|
+
[ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
|
521
514
|
[ Opcodes.local_tee, rightLength ],
|
522
515
|
|
523
516
|
[ Opcodes.i32_add ],
|
@@ -595,11 +588,11 @@ const compareStrings = (scope, left, right, bytestrings = false) => {
|
|
595
588
|
|
596
589
|
// get lengths
|
597
590
|
[ Opcodes.local_get, leftPointer ],
|
598
|
-
[ Opcodes.i32_load,
|
591
|
+
[ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
|
599
592
|
[ Opcodes.local_tee, leftLength ],
|
600
593
|
|
601
594
|
[ Opcodes.local_get, rightPointer ],
|
602
|
-
[ Opcodes.i32_load,
|
595
|
+
[ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
|
603
596
|
|
604
597
|
// fast path: check leftLength != rightLength
|
605
598
|
[ Opcodes.i32_ne ],
|
@@ -655,9 +648,9 @@ const compareStrings = (scope, left, right, bytestrings = false) => {
|
|
655
648
|
[ Opcodes.i32_add ],
|
656
649
|
[ Opcodes.local_tee, index ],
|
657
650
|
|
658
|
-
// if index
|
651
|
+
// if index < index end (length * sizeof valtype), loop
|
659
652
|
[ Opcodes.local_get, indexEnd ],
|
660
|
-
[ Opcodes.
|
653
|
+
[ Opcodes.i32_lt_s ],
|
661
654
|
[ Opcodes.br_if, 0 ],
|
662
655
|
[ Opcodes.end ],
|
663
656
|
|
@@ -676,7 +669,7 @@ const compareStrings = (scope, left, right, bytestrings = false) => {
|
|
676
669
|
};
|
677
670
|
|
678
671
|
const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
679
|
-
if (
|
672
|
+
if (isIntToFloatOp(wasm[wasm.length - 1])) return [
|
680
673
|
...wasm,
|
681
674
|
...(!intIn && intOut ? [ Opcodes.i32_to_u ] : [])
|
682
675
|
];
|
@@ -685,17 +678,32 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
685
678
|
const useTmp = knownType(scope, type) == null;
|
686
679
|
const tmp = useTmp && localTmp(scope, `#logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
|
687
680
|
|
688
|
-
const def =
|
689
|
-
|
690
|
-
|
681
|
+
const def = (truthyMode => {
|
682
|
+
if (truthyMode === 'full') return [
|
683
|
+
// if value != 0 or NaN
|
684
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
685
|
+
...(intIn ? [ ] : [ Opcodes.i32_to ]),
|
691
686
|
|
692
|
-
|
693
|
-
|
687
|
+
[ Opcodes.i32_eqz ],
|
688
|
+
[ Opcodes.i32_eqz ],
|
694
689
|
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
690
|
+
...(intOut ? [] : [ Opcodes.i32_from ]),
|
691
|
+
];
|
692
|
+
|
693
|
+
if (truthyMode === 'no_negative') return [
|
694
|
+
// if value != 0 or NaN, non-binary output. negative numbers not truthy :/
|
695
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
696
|
+
...(intIn ? [] : [ Opcodes.i32_to ]),
|
697
|
+
...(intOut ? [] : [ Opcodes.i32_from ])
|
698
|
+
];
|
699
|
+
|
700
|
+
if (truthyMode === 'no_nan_negative') return [
|
701
|
+
// simpler and faster but makes NaN truthy and negative numbers not truthy,
|
702
|
+
// plus non-binary output
|
703
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
704
|
+
...(!intOut || (intIn && intOut) ? [] : [ Opcodes.i32_to_u ])
|
705
|
+
];
|
706
|
+
})(Prefs.truthy ?? 'full');
|
699
707
|
|
700
708
|
return [
|
701
709
|
...wasm,
|
@@ -703,10 +711,10 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
703
711
|
|
704
712
|
...typeSwitch(scope, type, {
|
705
713
|
// [TYPES.number]: def,
|
706
|
-
[TYPES.array]: [
|
707
|
-
|
708
|
-
|
709
|
-
],
|
714
|
+
// [TYPES.array]: [
|
715
|
+
// // arrays are always truthy
|
716
|
+
// ...number(1, intOut ? Valtype.i32 : valtypeBinary)
|
717
|
+
// ],
|
710
718
|
[TYPES.string]: [
|
711
719
|
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
712
720
|
...(intIn ? [] : [ Opcodes.i32_to_u ]),
|
@@ -742,10 +750,10 @@ const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
742
750
|
...(!useTmp ? [] : [ [ Opcodes.local_set, tmp ] ]),
|
743
751
|
|
744
752
|
...typeSwitch(scope, type, {
|
745
|
-
[TYPES.array]: [
|
746
|
-
|
747
|
-
|
748
|
-
],
|
753
|
+
// [TYPES.array]: [
|
754
|
+
// // arrays are always truthy
|
755
|
+
// ...number(0, intOut ? Valtype.i32 : valtypeBinary)
|
756
|
+
// ],
|
749
757
|
[TYPES.string]: [
|
750
758
|
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
751
759
|
...(intIn ? [] : [ Opcodes.i32_to_u ]),
|
@@ -989,7 +997,7 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
989
997
|
// if both are true
|
990
998
|
[ Opcodes.i32_and ],
|
991
999
|
[ Opcodes.if, Blocktype.void ],
|
992
|
-
...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ]),
|
1000
|
+
...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ], false),
|
993
1001
|
...(op === '!==' || op === '!=' ? [ [ Opcodes.i32_eqz ] ] : []),
|
994
1002
|
[ Opcodes.br, 1 ],
|
995
1003
|
[ Opcodes.end ],
|
@@ -1038,14 +1046,14 @@ const generateBinaryExp = (scope, decl, _global, _name) => {
|
|
1038
1046
|
return out;
|
1039
1047
|
};
|
1040
1048
|
|
1041
|
-
const asmFuncToAsm = (func,
|
1042
|
-
return func(
|
1049
|
+
const asmFuncToAsm = (func, scope) => {
|
1050
|
+
return func(scope, {
|
1043
1051
|
TYPES, TYPE_NAMES, typeSwitch, makeArray, makeString, allocPage, internalThrow,
|
1044
|
-
builtin:
|
1045
|
-
let idx = funcIndex[
|
1046
|
-
if (idx
|
1047
|
-
includeBuiltin(null,
|
1048
|
-
idx = funcIndex[
|
1052
|
+
builtin: n => {
|
1053
|
+
let idx = funcIndex[n] ?? importedFuncs[n];
|
1054
|
+
if (idx == null && builtinFuncs[n]) {
|
1055
|
+
includeBuiltin(null, n);
|
1056
|
+
idx = funcIndex[n];
|
1049
1057
|
}
|
1050
1058
|
|
1051
1059
|
return idx;
|
@@ -1053,7 +1061,7 @@ const asmFuncToAsm = (func, { name = '#unknown_asm_func', params = [], locals =
|
|
1053
1061
|
});
|
1054
1062
|
};
|
1055
1063
|
|
1056
|
-
const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits, returns, returnType, localNames = [], globalNames = [], data: _data = [] }) => {
|
1064
|
+
const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits, returns, returnType, localNames = [], globalNames = [], data: _data = [], table = false }) => {
|
1057
1065
|
const existing = funcs.find(x => x.name === name);
|
1058
1066
|
if (existing) return existing;
|
1059
1067
|
|
@@ -1071,7 +1079,22 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
|
|
1071
1079
|
data.push(copy);
|
1072
1080
|
}
|
1073
1081
|
|
1074
|
-
|
1082
|
+
const func = {
|
1083
|
+
name,
|
1084
|
+
params,
|
1085
|
+
locals,
|
1086
|
+
localInd: allLocals.length,
|
1087
|
+
returns,
|
1088
|
+
returnType: returnType ?? TYPES.number,
|
1089
|
+
internal: true,
|
1090
|
+
index: currentFuncIndex++,
|
1091
|
+
table
|
1092
|
+
};
|
1093
|
+
|
1094
|
+
funcs.push(func);
|
1095
|
+
funcIndex[name] = func.index;
|
1096
|
+
|
1097
|
+
if (typeof wasm === 'function') wasm = asmFuncToAsm(wasm, func);
|
1075
1098
|
|
1076
1099
|
let baseGlobalIdx, i = 0;
|
1077
1100
|
for (const type of globalTypes) {
|
@@ -1090,19 +1113,14 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
|
|
1090
1113
|
}
|
1091
1114
|
}
|
1092
1115
|
|
1093
|
-
const
|
1094
|
-
|
1095
|
-
|
1096
|
-
|
1097
|
-
|
1098
|
-
|
1099
|
-
wasm,
|
1100
|
-
internal: true,
|
1101
|
-
index: currentFuncIndex++
|
1102
|
-
};
|
1116
|
+
if (table) for (const inst of wasm) {
|
1117
|
+
if (inst[0] === Opcodes.i32_load16_u && inst.at(-1) === 'read_argc') {
|
1118
|
+
inst.splice(2, 99);
|
1119
|
+
inst.push(...unsignedLEB128(allocPage({}, 'func argc lut') * pageSize));
|
1120
|
+
}
|
1121
|
+
}
|
1103
1122
|
|
1104
|
-
|
1105
|
-
funcIndex[name] = func.index;
|
1123
|
+
func.wasm = wasm;
|
1106
1124
|
|
1107
1125
|
return func;
|
1108
1126
|
};
|
@@ -1194,9 +1212,10 @@ const getLastType = scope => {
|
|
1194
1212
|
return [ [ Opcodes.local_get, localTmp(scope, '#last_type', Valtype.i32) ] ];
|
1195
1213
|
};
|
1196
1214
|
|
1197
|
-
const setLastType = scope =>
|
1198
|
-
|
1199
|
-
|
1215
|
+
const setLastType = (scope, type = []) => [
|
1216
|
+
...(typeof type === 'number' ? number(type, Valtype.i32) : type),
|
1217
|
+
[ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ]
|
1218
|
+
];
|
1200
1219
|
|
1201
1220
|
const getNodeType = (scope, node) => {
|
1202
1221
|
const ret = (() => {
|
@@ -1257,7 +1276,17 @@ const getNodeType = (scope, node) => {
|
|
1257
1276
|
|
1258
1277
|
const func = spl[spl.length - 1];
|
1259
1278
|
const protoFuncs = Object.keys(prototypeFuncs).filter(x => x != TYPES.bytestring && prototypeFuncs[x][func] != null);
|
1260
|
-
if (protoFuncs.length === 1)
|
1279
|
+
if (protoFuncs.length === 1) {
|
1280
|
+
if (protoFuncs[0].returnType) return protoFuncs[0].returnType;
|
1281
|
+
}
|
1282
|
+
|
1283
|
+
if (protoFuncs.length > 0) {
|
1284
|
+
if (scope.locals['#last_type']) return getLastType(scope);
|
1285
|
+
|
1286
|
+
// presume
|
1287
|
+
// todo: warn here?
|
1288
|
+
return TYPES.number;
|
1289
|
+
}
|
1261
1290
|
}
|
1262
1291
|
|
1263
1292
|
if (name.startsWith('__Porffor_wasm_')) {
|
@@ -1352,22 +1381,27 @@ const getNodeType = (scope, node) => {
|
|
1352
1381
|
}
|
1353
1382
|
|
1354
1383
|
if (node.type === 'MemberExpression') {
|
1355
|
-
|
1356
|
-
|
1357
|
-
|
1358
|
-
|
1359
|
-
|
1360
|
-
return TYPES.undefined;
|
1361
|
-
}
|
1384
|
+
const name = node.property.name;
|
1385
|
+
|
1386
|
+
if (name === 'length') {
|
1387
|
+
if (hasFuncWithName(node.object.name)) return TYPES.number;
|
1388
|
+
if (Prefs.fastLength) return TYPES.number;
|
1362
1389
|
}
|
1363
1390
|
|
1364
|
-
// hack: if something.length, number type
|
1365
|
-
if (node.property.name === 'length') return TYPES.number;
|
1366
1391
|
|
1367
|
-
|
1368
|
-
if (
|
1369
|
-
|
1370
|
-
|
1392
|
+
const objectKnownType = knownType(scope, getNodeType(scope, node.object));
|
1393
|
+
if (objectKnownType != null) {
|
1394
|
+
if (name === 'length') {
|
1395
|
+
if ([ TYPES.string, TYPES.bytestring, TYPES.array ].includes(objectKnownType)) return TYPES.number;
|
1396
|
+
else return TYPES.undefined;
|
1397
|
+
}
|
1398
|
+
|
1399
|
+
if (node.computed) {
|
1400
|
+
if (objectKnownType === TYPES.string) return TYPES.string;
|
1401
|
+
if (objectKnownType === TYPES.bytestring) return TYPES.bytestring;
|
1402
|
+
if (objectKnownType === TYPES.array) return TYPES.number;
|
1403
|
+
}
|
1404
|
+
}
|
1371
1405
|
|
1372
1406
|
if (scope.locals['#last_type']) return getLastType(scope);
|
1373
1407
|
|
@@ -1438,16 +1472,11 @@ const countLeftover = wasm => {
|
|
1438
1472
|
else if (inst[0] === Opcodes.return) count = 0;
|
1439
1473
|
else if (inst[0] === Opcodes.call) {
|
1440
1474
|
let func = funcs.find(x => x.index === inst[1]);
|
1441
|
-
if (inst[1]
|
1442
|
-
|
1443
|
-
|
1444
|
-
count -= importedFuncs[inst[1]].params;
|
1445
|
-
count += importedFuncs[inst[1]].returns;
|
1475
|
+
if (inst[1] < importedFuncs.length) {
|
1476
|
+
func = importedFuncs[inst[1]];
|
1477
|
+
count = count - func.params + func.returns;
|
1446
1478
|
} else {
|
1447
|
-
|
1448
|
-
count -= func.params.length;
|
1449
|
-
} else count--;
|
1450
|
-
if (func) count += func.returns.length;
|
1479
|
+
count = count - func.params.length + func.returns.length;
|
1451
1480
|
}
|
1452
1481
|
} else if (inst[0] === Opcodes.call_indirect) {
|
1453
1482
|
count--; // funcidx
|
@@ -1470,7 +1499,7 @@ const disposeLeftover = wasm => {
|
|
1470
1499
|
const generateExp = (scope, decl) => {
|
1471
1500
|
const expression = decl.expression;
|
1472
1501
|
|
1473
|
-
const out = generate(scope, expression, undefined, undefined,
|
1502
|
+
const out = generate(scope, expression, undefined, undefined, Prefs.optUnused);
|
1474
1503
|
disposeLeftover(out);
|
1475
1504
|
|
1476
1505
|
return out;
|
@@ -1561,16 +1590,10 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1561
1590
|
out.splice(out.length - 1, 1);
|
1562
1591
|
|
1563
1592
|
const finalStatement = parsed.body[parsed.body.length - 1];
|
1564
|
-
out.push(
|
1565
|
-
...getNodeType(scope, finalStatement),
|
1566
|
-
...setLastType(scope)
|
1567
|
-
);
|
1593
|
+
out.push(...setLastType(scope, getNodeType(scope, finalStatement)));
|
1568
1594
|
} else if (countLeftover(out) === 0) {
|
1569
1595
|
out.push(...number(UNDEFINED));
|
1570
|
-
out.push(
|
1571
|
-
...number(TYPES.undefined, Valtype.i32),
|
1572
|
-
...setLastType(scope)
|
1573
|
-
);
|
1596
|
+
out.push(...setLastType(scope, TYPES.undefined));
|
1574
1597
|
}
|
1575
1598
|
|
1576
1599
|
// if (lastInst && lastInst[0] === Opcodes.drop) {
|
@@ -1606,6 +1629,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1606
1629
|
|
1607
1630
|
if (!funcIndex[rhemynName]) {
|
1608
1631
|
const func = Rhemyn[funcName](regex, currentFuncIndex++, rhemynName);
|
1632
|
+
func.internal = true;
|
1609
1633
|
|
1610
1634
|
funcIndex[func.name] = func.index;
|
1611
1635
|
funcs.push(func);
|
@@ -1622,8 +1646,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1622
1646
|
[ Opcodes.call, idx ],
|
1623
1647
|
Opcodes.i32_from_u,
|
1624
1648
|
|
1625
|
-
...
|
1626
|
-
...setLastType(scope)
|
1649
|
+
...setLastType(scope, TYPES.boolean)
|
1627
1650
|
];
|
1628
1651
|
}
|
1629
1652
|
|
@@ -1695,9 +1718,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1695
1718
|
if (protoFunc.noArgRetLength && decl.arguments.length === 0) {
|
1696
1719
|
protoBC[x] = [
|
1697
1720
|
...RTArrayUtil.getLength(getPointer),
|
1698
|
-
|
1699
|
-
...number(TYPES.number, Valtype.i32),
|
1700
|
-
...setLastType(scope)
|
1721
|
+
...setLastType(scope, TYPES.number)
|
1701
1722
|
];
|
1702
1723
|
continue;
|
1703
1724
|
}
|
@@ -1716,7 +1737,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1716
1737
|
getI32: () => RTArrayUtil.getLengthI32(getPointer),
|
1717
1738
|
set: value => RTArrayUtil.setLength(getPointer, value),
|
1718
1739
|
setI32: value => RTArrayUtil.setLengthI32(getPointer, value)
|
1719
|
-
}, generate(scope, decl.arguments[0] ?? DEFAULT_VALUE), protoLocal, protoLocal2, (length, itemType) => {
|
1740
|
+
}, generate(scope, decl.arguments[0] ?? DEFAULT_VALUE), getNodeType(scope, decl.arguments[0] ?? DEFAULT_VALUE), protoLocal, protoLocal2, (length, itemType) => {
|
1720
1741
|
return makeArray(scope, {
|
1721
1742
|
rawElements: new Array(length)
|
1722
1743
|
}, _global, _name, true, itemType);
|
@@ -1730,9 +1751,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1730
1751
|
protoBC[x] = [
|
1731
1752
|
[ Opcodes.block, unusedValue && optUnused ? Blocktype.void : valtypeBinary ],
|
1732
1753
|
...protoOut,
|
1733
|
-
|
1734
|
-
...number(protoFunc.returnType ?? TYPES.number, Valtype.i32),
|
1735
|
-
...setLastType(scope),
|
1754
|
+
...(unusedValue && optUnused ? [] : (protoFunc.returnType != null ? setLastType(scope, protoFunc.returnType) : setLastType(scope))),
|
1736
1755
|
[ Opcodes.end ]
|
1737
1756
|
];
|
1738
1757
|
}
|
@@ -1782,11 +1801,6 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1782
1801
|
|
1783
1802
|
if (idx === undefined && internalConstrs[name]) return internalConstrs[name].generate(scope, decl, _global, _name);
|
1784
1803
|
|
1785
|
-
if (idx === undefined && name === scope.name) {
|
1786
|
-
// hack: calling self, func generator will fix later
|
1787
|
-
idx = -1;
|
1788
|
-
}
|
1789
|
-
|
1790
1804
|
if (idx === undefined && name.startsWith('__Porffor_wasm_')) {
|
1791
1805
|
const wasmOps = {
|
1792
1806
|
// pointer, align, offset
|
@@ -1838,36 +1852,128 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1838
1852
|
const [ local, global ] = lookupName(scope, name);
|
1839
1853
|
if (!Prefs.indirectCalls || local == null) return internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, true);
|
1840
1854
|
|
1841
|
-
// todo: only works when
|
1842
|
-
|
1843
|
-
|
1855
|
+
// todo: only works when function uses typedParams and typedReturns
|
1856
|
+
|
1857
|
+
const indirectMode = Prefs.indirectCallMode ?? 'vararg';
|
1858
|
+
// options: vararg, strict
|
1859
|
+
// - strict: simpler, smaller size usage, no func argc lut needed.
|
1860
|
+
// ONLY works when arg count of call == arg count of function being called
|
1861
|
+
// - vararg: large size usage, cursed.
|
1862
|
+
// works when arg count of call != arg count of function being called*
|
1863
|
+
// * most of the time, some edgecases
|
1844
1864
|
|
1845
1865
|
funcs.table = true;
|
1866
|
+
scope.table = true;
|
1846
1867
|
|
1847
1868
|
let args = decl.arguments;
|
1848
|
-
let
|
1869
|
+
let out = [];
|
1870
|
+
|
1871
|
+
let locals = [];
|
1872
|
+
|
1873
|
+
if (indirectMode === 'vararg') {
|
1874
|
+
const minArgc = Prefs.indirectCallMinArgc ?? 3;
|
1875
|
+
|
1876
|
+
if (args.length < minArgc) {
|
1877
|
+
args = args.concat(new Array(minArgc - args.length).fill(DEFAULT_VALUE));
|
1878
|
+
}
|
1879
|
+
}
|
1849
1880
|
|
1850
1881
|
for (let i = 0; i < args.length; i++) {
|
1851
1882
|
const arg = args[i];
|
1852
|
-
|
1883
|
+
out = out.concat(generate(scope, arg));
|
1853
1884
|
|
1854
1885
|
if (valtypeBinary !== Valtype.i32 && (
|
1855
1886
|
(builtinFuncs[name] && builtinFuncs[name].params[i * (typedParams ? 2 : 1)] === Valtype.i32) ||
|
1856
1887
|
(importedFuncs[name] && name.startsWith('profile'))
|
1857
1888
|
)) {
|
1858
|
-
|
1889
|
+
out.push(Opcodes.i32_to);
|
1859
1890
|
}
|
1860
1891
|
|
1861
|
-
|
1892
|
+
out = out.concat(getNodeType(scope, arg));
|
1893
|
+
|
1894
|
+
if (indirectMode === 'vararg') {
|
1895
|
+
const typeLocal = localTmp(scope, `#indirect_arg${i}_type`, Valtype.i32);
|
1896
|
+
const valLocal = localTmp(scope, `#indirect_arg${i}_val`);
|
1897
|
+
|
1898
|
+
locals.push([valLocal, typeLocal]);
|
1899
|
+
|
1900
|
+
out.push(
|
1901
|
+
[ Opcodes.local_set, typeLocal ],
|
1902
|
+
[ Opcodes.local_set, valLocal ]
|
1903
|
+
);
|
1904
|
+
}
|
1862
1905
|
}
|
1863
1906
|
|
1907
|
+
if (indirectMode === 'strict') {
|
1908
|
+
return typeSwitch(scope, getNodeType(scope, decl.callee), {
|
1909
|
+
[TYPES.function]: [
|
1910
|
+
...argWasm,
|
1911
|
+
[ global ? Opcodes.global_get : Opcodes.local_get, local.idx ],
|
1912
|
+
Opcodes.i32_to_u,
|
1913
|
+
[ Opcodes.call_indirect, args.length, 0 ],
|
1914
|
+
...setLastType(scope)
|
1915
|
+
],
|
1916
|
+
default: internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, true)
|
1917
|
+
});
|
1918
|
+
}
|
1919
|
+
|
1920
|
+
// hi, I will now explain how vararg mode works:
|
1921
|
+
// wasm's indirect_call instruction requires you know the func type at compile-time
|
1922
|
+
// since we have varargs (variable argument count), we do not know it.
|
1923
|
+
// we could just store args in memory and not use wasm func args,
|
1924
|
+
// but that is slow (probably) and breaks js exports.
|
1925
|
+
// instead, we generate every* possibility of argc and use different indirect_call
|
1926
|
+
// ops for each one, with type depending on argc for that branch.
|
1927
|
+
// then we load the argc for the wanted function from a memory lut,
|
1928
|
+
// and call the branch with the matching argc we require.
|
1929
|
+
// sorry, yes it is very cursed (and size inefficient), but indirect calls
|
1930
|
+
// are kind of rare anyway (mostly callbacks) so I am not concerned atm.
|
1931
|
+
// *for argc 0-3, in future (todo:) the max number should be
|
1932
|
+
// dynamically changed to the max argc of any func in the js file.
|
1933
|
+
|
1934
|
+
const funcLocal = localTmp(scope, `#indirect_func`, Valtype.i32);
|
1935
|
+
|
1936
|
+
const gen = argc => {
|
1937
|
+
const out = [];
|
1938
|
+
for (let i = 0; i < argc; i++) {
|
1939
|
+
out.push(
|
1940
|
+
[ Opcodes.local_get, locals[i][0] ],
|
1941
|
+
[ Opcodes.local_get, locals[i][1] ]
|
1942
|
+
);
|
1943
|
+
}
|
1944
|
+
|
1945
|
+
out.push(
|
1946
|
+
[ Opcodes.local_get, funcLocal ],
|
1947
|
+
[ Opcodes.call_indirect, argc, 0 ],
|
1948
|
+
...setLastType(scope)
|
1949
|
+
)
|
1950
|
+
|
1951
|
+
return out;
|
1952
|
+
};
|
1953
|
+
|
1954
|
+
const tableBc = {};
|
1955
|
+
for (let i = 0; i <= args.length; i++) {
|
1956
|
+
tableBc[i] = gen(i);
|
1957
|
+
}
|
1958
|
+
|
1959
|
+
// todo/perf: check if we should use br_table here or just generate our own big if..elses
|
1960
|
+
|
1864
1961
|
return typeSwitch(scope, getNodeType(scope, decl.callee), {
|
1865
1962
|
[TYPES.function]: [
|
1866
|
-
...
|
1963
|
+
...out,
|
1964
|
+
|
1867
1965
|
[ global ? Opcodes.global_get : Opcodes.local_get, local.idx ],
|
1868
1966
|
Opcodes.i32_to_u,
|
1869
|
-
[ Opcodes.
|
1870
|
-
|
1967
|
+
[ Opcodes.local_set, funcLocal ],
|
1968
|
+
|
1969
|
+
...brTable([
|
1970
|
+
// get argc of func we are calling
|
1971
|
+
[ Opcodes.local_get, funcLocal ],
|
1972
|
+
...number(ValtypeSize.i16, Valtype.i32),
|
1973
|
+
[ Opcodes.i32_mul ],
|
1974
|
+
|
1975
|
+
[ Opcodes.i32_load16_u, 0, ...unsignedLEB128(allocPage(scope, 'func argc lut') * pageSize), 'read_argc' ]
|
1976
|
+
], tableBc, valtypeBinary)
|
1871
1977
|
],
|
1872
1978
|
default: internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, true)
|
1873
1979
|
});
|
@@ -1876,11 +1982,10 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1876
1982
|
return internalThrow(scope, 'ReferenceError', `${unhackName(name)} is not defined`, true);
|
1877
1983
|
}
|
1878
1984
|
|
1879
|
-
const func = funcs.find(x => x.index === idx);
|
1880
|
-
|
1881
|
-
const userFunc = (funcIndex[name] && !importedFuncs[name] && !builtinFuncs[name] && !internalConstrs[name]) || idx === -1;
|
1985
|
+
const func = funcs[idx - importedFuncs.length]; // idx === scope.index ? scope : funcs.find(x => x.index === idx);
|
1986
|
+
const userFunc = func && !func.internal;
|
1882
1987
|
const typedParams = userFunc || builtinFuncs[name]?.typedParams;
|
1883
|
-
const typedReturns = (func
|
1988
|
+
const typedReturns = (func && func.returnType == null) || builtinFuncs[name]?.typedReturns;
|
1884
1989
|
const paramCount = func && (typedParams ? func.params.length / 2 : func.params.length);
|
1885
1990
|
|
1886
1991
|
let args = decl.arguments;
|
@@ -1901,6 +2006,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1901
2006
|
const arg = args[i];
|
1902
2007
|
out = out.concat(generate(scope, arg));
|
1903
2008
|
|
2009
|
+
// todo: this should be used instead of the too many args thing above (by removing that)
|
1904
2010
|
if (i >= paramCount) {
|
1905
2011
|
// over param count of func, drop arg
|
1906
2012
|
out.push([ Opcodes.drop ]);
|
@@ -1985,8 +2091,11 @@ const knownType = (scope, type) => {
|
|
1985
2091
|
const idx = type[0][1];
|
1986
2092
|
|
1987
2093
|
// type idx = var idx + 1
|
1988
|
-
const
|
1989
|
-
if (
|
2094
|
+
const name = Object.values(scope.locals).find(x => x.idx === idx)?.name;
|
2095
|
+
if (name) {
|
2096
|
+
const local = scope.locals[name];
|
2097
|
+
if (local.metadata?.type != null) return v.metadata.type;
|
2098
|
+
}
|
1990
2099
|
}
|
1991
2100
|
|
1992
2101
|
return null;
|
@@ -2021,16 +2130,17 @@ const brTable = (input, bc, returns) => {
|
|
2021
2130
|
}
|
2022
2131
|
|
2023
2132
|
for (let i = 0; i < count; i++) {
|
2024
|
-
if (i === 0) out.push([ Opcodes.block, returns, 'br table start' ]);
|
2133
|
+
// if (i === 0) out.push([ Opcodes.block, returns, 'br table start' ]);
|
2134
|
+
if (i === 0) out.push([ Opcodes.block, returns ]);
|
2025
2135
|
else out.push([ Opcodes.block, Blocktype.void ]);
|
2026
2136
|
}
|
2027
2137
|
|
2028
|
-
const nums = keys.filter(x => +x);
|
2138
|
+
const nums = keys.filter(x => +x >= 0);
|
2029
2139
|
const offset = Math.min(...nums);
|
2030
2140
|
const max = Math.max(...nums);
|
2031
2141
|
|
2032
2142
|
const table = [];
|
2033
|
-
let br =
|
2143
|
+
let br = 0;
|
2034
2144
|
|
2035
2145
|
for (let i = offset; i <= max; i++) {
|
2036
2146
|
// if branch for this num, go to that block
|
@@ -2070,10 +2180,9 @@ const brTable = (input, bc, returns) => {
|
|
2070
2180
|
br--;
|
2071
2181
|
}
|
2072
2182
|
|
2073
|
-
|
2074
|
-
|
2075
|
-
|
2076
|
-
];
|
2183
|
+
out.push([ Opcodes.end ]);
|
2184
|
+
|
2185
|
+
return out;
|
2077
2186
|
};
|
2078
2187
|
|
2079
2188
|
const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
|
@@ -2117,6 +2226,17 @@ const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
|
|
2117
2226
|
return out;
|
2118
2227
|
};
|
2119
2228
|
|
2229
|
+
const typeIsOneOf = (type, types, valtype = Valtype.i32) => {
|
2230
|
+
const out = [];
|
2231
|
+
|
2232
|
+
for (let i = 0; i < types.length; i++) {
|
2233
|
+
out.push(...type, ...number(types[i], valtype), valtype === Valtype.f64 ? [ Opcodes.f64_eq ] : [ Opcodes.i32_eq ]);
|
2234
|
+
if (i !== 0) out.push([ Opcodes.i32_or ]);
|
2235
|
+
}
|
2236
|
+
|
2237
|
+
return out;
|
2238
|
+
};
|
2239
|
+
|
2120
2240
|
const allocVar = (scope, name, global = false, type = true) => {
|
2121
2241
|
const target = global ? globals : scope.locals;
|
2122
2242
|
|
@@ -2133,7 +2253,7 @@ const allocVar = (scope, name, global = false, type = true) => {
|
|
2133
2253
|
|
2134
2254
|
if (type) {
|
2135
2255
|
let typeIdx = global ? globalInd++ : scope.localInd++;
|
2136
|
-
target[name + '#type'] = { idx: typeIdx, type: Valtype.i32 };
|
2256
|
+
target[name + '#type'] = { idx: typeIdx, type: Valtype.i32, name };
|
2137
2257
|
}
|
2138
2258
|
|
2139
2259
|
return idx;
|
@@ -2346,18 +2466,21 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
|
|
2346
2466
|
Opcodes.i32_to_u,
|
2347
2467
|
|
2348
2468
|
// turn into byte offset by * valtypeSize (4 for i32, 8 for i64/f64)
|
2349
|
-
...number(ValtypeSize[valtype], Valtype.i32),
|
2469
|
+
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
2350
2470
|
[ Opcodes.i32_mul ],
|
2351
2471
|
...(aotPointer ? [] : [ [ Opcodes.i32_add ] ]),
|
2352
2472
|
...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
|
2353
2473
|
|
2354
2474
|
...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
|
2355
2475
|
[ Opcodes.local_get, pointerTmp ],
|
2356
|
-
[ Opcodes.load,
|
2357
|
-
], generate(scope, decl.right),
|
2476
|
+
[ Opcodes.load, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
|
2477
|
+
], generate(scope, decl.right), [
|
2478
|
+
[ Opcodes.local_get, pointerTmp ],
|
2479
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32 + ValtypeSize[valtype]) ]
|
2480
|
+
], getNodeType(scope, decl.right), false, name, true)),
|
2358
2481
|
[ Opcodes.local_tee, newValueTmp ],
|
2359
2482
|
|
2360
|
-
[ Opcodes.store,
|
2483
|
+
[ Opcodes.store, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
|
2361
2484
|
],
|
2362
2485
|
|
2363
2486
|
default: internalThrow(scope, 'TypeError', `Cannot assign member with non-array`)
|
@@ -2611,21 +2734,16 @@ const generateConditional = (scope, decl) => {
|
|
2611
2734
|
out.push([ Opcodes.if, valtypeBinary ]);
|
2612
2735
|
depth.push('if');
|
2613
2736
|
|
2614
|
-
out.push(...generate(scope, decl.consequent));
|
2615
|
-
|
2616
|
-
// note type
|
2617
2737
|
out.push(
|
2618
|
-
...
|
2619
|
-
...setLastType(scope)
|
2738
|
+
...generate(scope, decl.consequent),
|
2739
|
+
...setLastType(scope, getNodeType(scope, decl.consequent))
|
2620
2740
|
);
|
2621
2741
|
|
2622
2742
|
out.push([ Opcodes.else ]);
|
2623
|
-
out.push(...generate(scope, decl.alternate));
|
2624
2743
|
|
2625
|
-
// note type
|
2626
2744
|
out.push(
|
2627
|
-
...
|
2628
|
-
...setLastType(scope)
|
2745
|
+
...generate(scope, decl.alternate),
|
2746
|
+
...setLastType(scope, getNodeType(scope, decl.alternate))
|
2629
2747
|
);
|
2630
2748
|
|
2631
2749
|
out.push([ Opcodes.end ]);
|
@@ -2773,12 +2891,15 @@ const generateForOf = (scope, decl) => {
|
|
2773
2891
|
// todo: optimize away counter and use end pointer
|
2774
2892
|
out.push(...typeSwitch(scope, getNodeType(scope, decl.right), {
|
2775
2893
|
[TYPES.array]: [
|
2776
|
-
...setType(scope, leftName, TYPES.number),
|
2777
|
-
|
2778
2894
|
[ Opcodes.loop, Blocktype.void ],
|
2779
2895
|
|
2780
2896
|
[ Opcodes.local_get, pointer ],
|
2781
|
-
[ Opcodes.load,
|
2897
|
+
[ Opcodes.load, 0, ...unsignedLEB128(ValtypeSize.i32) ],
|
2898
|
+
|
2899
|
+
...setType(scope, leftName, [
|
2900
|
+
[ Opcodes.local_get, pointer ],
|
2901
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32 + ValtypeSize[valtype]) ],
|
2902
|
+
]),
|
2782
2903
|
|
2783
2904
|
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
|
2784
2905
|
|
@@ -2787,9 +2908,9 @@ const generateForOf = (scope, decl) => {
|
|
2787
2908
|
...generate(scope, decl.body),
|
2788
2909
|
[ Opcodes.end ],
|
2789
2910
|
|
2790
|
-
// increment iter pointer by valtype size
|
2911
|
+
// increment iter pointer by valtype size + 1
|
2791
2912
|
[ Opcodes.local_get, pointer ],
|
2792
|
-
...number(ValtypeSize[valtype], Valtype.i32),
|
2913
|
+
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
2793
2914
|
[ Opcodes.i32_add ],
|
2794
2915
|
[ Opcodes.local_set, pointer ],
|
2795
2916
|
|
@@ -3006,14 +3127,18 @@ const generateThrow = (scope, decl) => {
|
|
3006
3127
|
};
|
3007
3128
|
|
3008
3129
|
const generateTry = (scope, decl) => {
|
3009
|
-
|
3130
|
+
// todo: handle control-flow pre-exit for finally
|
3131
|
+
// "Immediately before a control-flow statement (return, throw, break, continue) is executed in the try block or catch block."
|
3010
3132
|
|
3011
3133
|
const out = [];
|
3012
3134
|
|
3135
|
+
const finalizer = decl.finalizer ? generate(scope, decl.finalizer) : [];
|
3136
|
+
|
3013
3137
|
out.push([ Opcodes.try, Blocktype.void ]);
|
3014
3138
|
depth.push('try');
|
3015
3139
|
|
3016
3140
|
out.push(...generate(scope, decl.block));
|
3141
|
+
out.push(...finalizer);
|
3017
3142
|
|
3018
3143
|
if (decl.handler) {
|
3019
3144
|
depth.pop();
|
@@ -3021,6 +3146,7 @@ const generateTry = (scope, decl) => {
|
|
3021
3146
|
|
3022
3147
|
out.push([ Opcodes.catch_all ]);
|
3023
3148
|
out.push(...generate(scope, decl.handler.body));
|
3149
|
+
out.push(...finalizer);
|
3024
3150
|
}
|
3025
3151
|
|
3026
3152
|
out.push([ Opcodes.end ]);
|
@@ -3106,7 +3232,7 @@ const getAllocType = itemType => {
|
|
3106
3232
|
}
|
3107
3233
|
};
|
3108
3234
|
|
3109
|
-
const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false, itemType = valtype) => {
|
3235
|
+
const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false, itemType = valtype, typed = false) => {
|
3110
3236
|
const out = [];
|
3111
3237
|
|
3112
3238
|
scope.arrays ??= new Map();
|
@@ -3118,8 +3244,13 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
3118
3244
|
// todo: can we just have 1 undeclared array? probably not? but this is not really memory efficient
|
3119
3245
|
const uniqueName = name === '$undeclared' ? name + Math.random().toString().slice(2) : name;
|
3120
3246
|
|
3121
|
-
|
3122
|
-
|
3247
|
+
let page;
|
3248
|
+
if (Prefs.scopedPageNames) page = allocPage(scope, `${getAllocType(itemType)}: ${scope.name}/${uniqueName}`, itemType);
|
3249
|
+
else page = allocPage(scope, `${getAllocType(itemType)}: ${uniqueName}`, itemType);
|
3250
|
+
|
3251
|
+
// hack: use 1 for page 0 pointer for fast truthiness
|
3252
|
+
const ptr = page === 0 ? 1 : (page * pageSize);
|
3253
|
+
scope.arrays.set(name, ptr);
|
3123
3254
|
}
|
3124
3255
|
|
3125
3256
|
const pointer = scope.arrays.get(name);
|
@@ -3169,7 +3300,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
3169
3300
|
|
3170
3301
|
const pointerWasm = pointerTmp != null ? [ [ Opcodes.local_get, pointerTmp ] ] : number(pointer, Valtype.i32);
|
3171
3302
|
|
3172
|
-
// store length
|
3303
|
+
// store length
|
3173
3304
|
out.push(
|
3174
3305
|
...pointerWasm,
|
3175
3306
|
...number(length, Valtype.i32),
|
@@ -3177,14 +3308,20 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
3177
3308
|
);
|
3178
3309
|
|
3179
3310
|
const storeOp = StoreOps[itemType];
|
3180
|
-
|
3311
|
+
const sizePerEl = ValtypeSize[itemType] + (typed ? 1 : 0);
|
3181
3312
|
if (!initEmpty) for (let i = 0; i < length; i++) {
|
3182
3313
|
if (elements[i] == null) continue;
|
3183
3314
|
|
3315
|
+
const offset = ValtypeSize.i32 + i * sizePerEl;
|
3184
3316
|
out.push(
|
3185
3317
|
...pointerWasm,
|
3186
3318
|
...(useRawElements ? number(elements[i], Valtype[valtype]) : generate(scope, elements[i])),
|
3187
|
-
[ storeOp,
|
3319
|
+
[ storeOp, 0, ...unsignedLEB128(offset) ],
|
3320
|
+
...(!typed ? [] : [ // typed presumes !useRawElements
|
3321
|
+
...pointerWasm,
|
3322
|
+
...getNodeType(scope, elements[i]),
|
3323
|
+
[ Opcodes.i32_store8, 0, ...unsignedLEB128(offset + ValtypeSize[itemType]) ]
|
3324
|
+
])
|
3188
3325
|
);
|
3189
3326
|
}
|
3190
3327
|
|
@@ -3194,6 +3331,65 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
3194
3331
|
return [ out, pointer ];
|
3195
3332
|
};
|
3196
3333
|
|
3334
|
+
const storeArray = (scope, array, index, element, aotPointer = null) => {
|
3335
|
+
if (!Array.isArray(element)) element = generate(scope, element);
|
3336
|
+
if (typeof index === 'number') index = number(index);
|
3337
|
+
|
3338
|
+
const offset = localTmp(scope, '#storeArray_offset', Valtype.i32);
|
3339
|
+
|
3340
|
+
return [
|
3341
|
+
// calculate offset
|
3342
|
+
...index,
|
3343
|
+
Opcodes.i32_to_u,
|
3344
|
+
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
3345
|
+
[ Opcodes.i32_mul ],
|
3346
|
+
...(aotPointer ? [] : [
|
3347
|
+
...array,
|
3348
|
+
Opcodes.i32_to_u,
|
3349
|
+
[ Opcodes.i32_add ],
|
3350
|
+
]),
|
3351
|
+
[ Opcodes.local_set, offset ],
|
3352
|
+
|
3353
|
+
// store value
|
3354
|
+
[ Opcodes.local_get, offset ],
|
3355
|
+
...generate(scope, element),
|
3356
|
+
[ Opcodes.store, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
3357
|
+
|
3358
|
+
// store type
|
3359
|
+
[ Opcodes.local_get, offset ],
|
3360
|
+
...getNodeType(scope, element),
|
3361
|
+
[ Opcodes.i32_store8, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32 + ValtypeSize[valtype]) ]
|
3362
|
+
];
|
3363
|
+
};
|
3364
|
+
|
3365
|
+
const loadArray = (scope, array, index, aotPointer = null) => {
|
3366
|
+
if (typeof index === 'number') index = number(index);
|
3367
|
+
|
3368
|
+
const offset = localTmp(scope, '#loadArray_offset', Valtype.i32);
|
3369
|
+
|
3370
|
+
return [
|
3371
|
+
// calculate offset
|
3372
|
+
...index,
|
3373
|
+
Opcodes.i32_to_u,
|
3374
|
+
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
3375
|
+
[ Opcodes.i32_mul ],
|
3376
|
+
...(aotPointer ? [] : [
|
3377
|
+
...array,
|
3378
|
+
Opcodes.i32_to_u,
|
3379
|
+
[ Opcodes.i32_add ],
|
3380
|
+
]),
|
3381
|
+
[ Opcodes.local_set, offset ],
|
3382
|
+
|
3383
|
+
// load value
|
3384
|
+
[ Opcodes.local_get, offset ],
|
3385
|
+
[ Opcodes.load, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
3386
|
+
|
3387
|
+
// load type
|
3388
|
+
[ Opcodes.local_get, offset ],
|
3389
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32 + ValtypeSize[valtype]) ]
|
3390
|
+
];
|
3391
|
+
};
|
3392
|
+
|
3197
3393
|
const byteStringable = str => {
|
3198
3394
|
if (!Prefs.bytestring) return false;
|
3199
3395
|
|
@@ -3222,14 +3418,28 @@ const makeString = (scope, str, global = false, name = '$undeclared', forceBytes
|
|
3222
3418
|
};
|
3223
3419
|
|
3224
3420
|
const generateArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false) => {
|
3225
|
-
return makeArray(scope, decl, global, name, initEmpty, valtype)[0];
|
3421
|
+
return makeArray(scope, decl, global, name, initEmpty, valtype, true)[0];
|
3226
3422
|
};
|
3227
3423
|
|
3228
|
-
|
3424
|
+
const generateObject = (scope, decl, global = false, name = '$undeclared') => {
|
3425
|
+
if (decl.properties.length > 0) return todo(scope, 'objects are not supported yet', true);
|
3426
|
+
|
3427
|
+
return [
|
3428
|
+
...number(1),
|
3429
|
+
...setLastType(scope, TYPES.object)
|
3430
|
+
];
|
3431
|
+
};
|
3432
|
+
|
3433
|
+
const withType = (scope, wasm, type) => [
|
3434
|
+
...wasm,
|
3435
|
+
...setLastType(scope, type)
|
3436
|
+
];
|
3437
|
+
|
3438
|
+
const generateMember = (scope, decl, _global, _name) => {
|
3229
3439
|
const name = decl.object.name;
|
3230
3440
|
const pointer = scope.arrays?.get(name);
|
3231
3441
|
|
3232
|
-
const aotPointer = Prefs.aotPointerOpt && pointer
|
3442
|
+
const aotPointer = Prefs.aotPointerOpt && pointer;
|
3233
3443
|
|
3234
3444
|
// hack: .name
|
3235
3445
|
if (decl.property.name === 'name') {
|
@@ -3239,9 +3449,9 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3239
3449
|
// eg: __String_prototype_toLowerCase -> toLowerCase
|
3240
3450
|
if (nameProp.startsWith('__')) nameProp = nameProp.split('_').pop();
|
3241
3451
|
|
3242
|
-
return makeString(scope, nameProp, _global, _name, true);
|
3452
|
+
return withType(scope, makeString(scope, nameProp, _global, _name, true), TYPES.bytestring);
|
3243
3453
|
} else {
|
3244
|
-
return
|
3454
|
+
return withType(scope, number(0), TYPES.undefined);
|
3245
3455
|
}
|
3246
3456
|
}
|
3247
3457
|
|
@@ -3249,9 +3459,8 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3249
3459
|
if (decl.property.name === 'length') {
|
3250
3460
|
const func = funcs.find(x => x.name === name);
|
3251
3461
|
if (func) {
|
3252
|
-
const
|
3253
|
-
|
3254
|
-
return number(typedParams ? func.params.length / 2 : func.params.length);
|
3462
|
+
const typedParams = !func.internal || builtinFuncs[name]?.typedParams;
|
3463
|
+
return withType(scope, number(typedParams ? func.params.length / 2 : func.params.length), TYPES.number);
|
3255
3464
|
}
|
3256
3465
|
|
3257
3466
|
if (builtinFuncs[name + '$constructor']) {
|
@@ -3261,24 +3470,88 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3261
3470
|
const constructorFunc = builtinFuncs[name + '$constructor'];
|
3262
3471
|
const constructorParams = constructorFunc.typedParams ? (constructorFunc.params.length / 2) : constructorFunc.params.length;
|
3263
3472
|
|
3264
|
-
return number(Math.max(regularParams, constructorParams));
|
3473
|
+
return withType(scope, number(Math.max(regularParams, constructorParams)), TYPES.number);
|
3265
3474
|
}
|
3266
3475
|
|
3267
|
-
if (builtinFuncs[name]) return number(builtinFuncs[name].typedParams ? (builtinFuncs[name].params.length / 2) : builtinFuncs[name].params.length);
|
3268
|
-
if (importedFuncs[name]) return number(importedFuncs[name].params);
|
3269
|
-
if (internalConstrs[name]) return number(internalConstrs[name].length ?? 0);
|
3476
|
+
if (builtinFuncs[name]) return withType(scope, number(builtinFuncs[name].typedParams ? (builtinFuncs[name].params.length / 2) : builtinFuncs[name].params.length), TYPES.number);
|
3477
|
+
if (importedFuncs[name]) return withType(scope, number(importedFuncs[name].params), TYPES.number);
|
3478
|
+
if (internalConstrs[name]) return withType(scope, number(internalConstrs[name].length ?? 0), TYPES.number);
|
3479
|
+
|
3480
|
+
if (Prefs.fastLength) {
|
3481
|
+
// presume valid length object
|
3482
|
+
return [
|
3483
|
+
...(aotPointer ? number(0, Valtype.i32) : [
|
3484
|
+
...generate(scope, decl.object),
|
3485
|
+
Opcodes.i32_to_u
|
3486
|
+
]),
|
3487
|
+
|
3488
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
|
3489
|
+
Opcodes.i32_from_u
|
3490
|
+
];
|
3491
|
+
}
|
3492
|
+
|
3493
|
+
const type = getNodeType(scope, decl.object);
|
3494
|
+
const known = knownType(scope, type);
|
3495
|
+
if (known != null) {
|
3496
|
+
if ([ TYPES.string, TYPES.bytestring, TYPES.array ].includes(known)) return [
|
3497
|
+
...(aotPointer ? number(0, Valtype.i32) : [
|
3498
|
+
...generate(scope, decl.object),
|
3499
|
+
Opcodes.i32_to_u
|
3500
|
+
]),
|
3501
|
+
|
3502
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
|
3503
|
+
Opcodes.i32_from_u
|
3504
|
+
];
|
3505
|
+
|
3506
|
+
return number(0);
|
3507
|
+
}
|
3270
3508
|
|
3271
3509
|
return [
|
3272
|
-
...(
|
3273
|
-
|
3274
|
-
|
3275
|
-
|
3510
|
+
...typeIsOneOf(getNodeType(scope, decl.object), [ TYPES.string, TYPES.bytestring, TYPES.array ]),
|
3511
|
+
[ Opcodes.if, valtypeBinary ],
|
3512
|
+
...(aotPointer ? number(0, Valtype.i32) : [
|
3513
|
+
...generate(scope, decl.object),
|
3514
|
+
Opcodes.i32_to_u
|
3515
|
+
]),
|
3516
|
+
|
3517
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
|
3518
|
+
Opcodes.i32_from_u,
|
3276
3519
|
|
3277
|
-
|
3278
|
-
Opcodes.
|
3520
|
+
...setLastType(scope, TYPES.number),
|
3521
|
+
[ Opcodes.else ],
|
3522
|
+
...number(0),
|
3523
|
+
...setLastType(scope, TYPES.undefined),
|
3524
|
+
[ Opcodes.end ]
|
3279
3525
|
];
|
3280
3526
|
}
|
3281
3527
|
|
3528
|
+
// todo: generate this array procedurally during builtinFuncs creation
|
3529
|
+
if (['size', 'description'].includes(decl.property.name)) {
|
3530
|
+
const bc = {};
|
3531
|
+
const cands = Object.keys(builtinFuncs).filter(x => x.startsWith('__') && x.endsWith('_prototype_' + decl.property.name + '$get'));
|
3532
|
+
|
3533
|
+
if (cands.length > 0) {
|
3534
|
+
for (const x of cands) {
|
3535
|
+
const type = TYPES[x.split('_prototype_')[0].slice(2).toLowerCase()];
|
3536
|
+
if (type == null) continue;
|
3537
|
+
|
3538
|
+
bc[type] = generateCall(scope, {
|
3539
|
+
callee: {
|
3540
|
+
type: 'Identifier',
|
3541
|
+
name: x
|
3542
|
+
},
|
3543
|
+
arguments: [ decl.object ],
|
3544
|
+
_protoInternalCall: true
|
3545
|
+
});
|
3546
|
+
}
|
3547
|
+
}
|
3548
|
+
|
3549
|
+
return typeSwitch(scope, getNodeType(scope, decl.object), {
|
3550
|
+
...bc,
|
3551
|
+
default: withType(scope, number(0), TYPES.undefined)
|
3552
|
+
}, valtypeBinary);
|
3553
|
+
}
|
3554
|
+
|
3282
3555
|
const object = generate(scope, decl.object);
|
3283
3556
|
const property = generate(scope, decl.property);
|
3284
3557
|
|
@@ -3293,24 +3566,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3293
3566
|
|
3294
3567
|
return typeSwitch(scope, getNodeType(scope, decl.object), {
|
3295
3568
|
[TYPES.array]: [
|
3296
|
-
|
3297
|
-
...property,
|
3298
|
-
|
3299
|
-
// convert to i32 and turn into byte offset by * valtypeSize (4 for i32, 8 for i64/f64)
|
3300
|
-
Opcodes.i32_to_u,
|
3301
|
-
...number(ValtypeSize[valtype], Valtype.i32),
|
3302
|
-
[ Opcodes.i32_mul ],
|
3303
|
-
|
3304
|
-
...(aotPointer ? [] : [
|
3305
|
-
...object,
|
3306
|
-
Opcodes.i32_to_u,
|
3307
|
-
[ Opcodes.i32_add ]
|
3308
|
-
]),
|
3309
|
-
|
3310
|
-
// read from memory
|
3311
|
-
[ Opcodes.load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
3312
|
-
|
3313
|
-
...number(TYPES.number, Valtype.i32),
|
3569
|
+
...loadArray(scope, object, property, aotPointer),
|
3314
3570
|
...setLastType(scope)
|
3315
3571
|
],
|
3316
3572
|
|
@@ -3341,9 +3597,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3341
3597
|
|
3342
3598
|
// return new string (page)
|
3343
3599
|
...number(newPointer),
|
3344
|
-
|
3345
|
-
...number(TYPES.string, Valtype.i32),
|
3346
|
-
...setLastType(scope)
|
3600
|
+
...setLastType(scope, TYPES.string)
|
3347
3601
|
],
|
3348
3602
|
[TYPES.bytestring]: [
|
3349
3603
|
// setup new/out array
|
@@ -3369,9 +3623,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3369
3623
|
|
3370
3624
|
// return new string (page)
|
3371
3625
|
...number(newPointer),
|
3372
|
-
|
3373
|
-
...number(TYPES.bytestring, Valtype.i32),
|
3374
|
-
...setLastType(scope)
|
3626
|
+
...setLastType(scope, TYPES.bytestring)
|
3375
3627
|
],
|
3376
3628
|
|
3377
3629
|
default: internalThrow(scope, 'TypeError', 'Member expression is not supported for non-string non-array yet', true)
|
@@ -3396,7 +3648,7 @@ const objectHack = node => {
|
|
3396
3648
|
if (!objectName) objectName = objectHack(node.object)?.name?.slice?.(2);
|
3397
3649
|
|
3398
3650
|
// if .name or .length, give up (hack within a hack!)
|
3399
|
-
if (['name', 'length'].includes(node.property.name)) {
|
3651
|
+
if (['name', 'length', 'size', 'description'].includes(node.property.name)) {
|
3400
3652
|
node.object = objectHack(node.object);
|
3401
3653
|
return;
|
3402
3654
|
}
|
@@ -3433,33 +3685,39 @@ const generateFunc = (scope, decl) => {
|
|
3433
3685
|
const name = decl.id ? decl.id.name : `anonymous_${randId()}`;
|
3434
3686
|
const params = decl.params ?? [];
|
3435
3687
|
|
3436
|
-
// const innerScope = { ...scope };
|
3437
3688
|
// TODO: share scope/locals between !!!
|
3438
|
-
const
|
3689
|
+
const func = {
|
3439
3690
|
locals: {},
|
3440
3691
|
localInd: 0,
|
3441
3692
|
// value, type
|
3442
3693
|
returns: [ valtypeBinary, Valtype.i32 ],
|
3443
3694
|
throws: false,
|
3444
|
-
name
|
3695
|
+
name,
|
3696
|
+
index: currentFuncIndex++
|
3445
3697
|
};
|
3446
3698
|
|
3447
3699
|
if (typedInput && decl.returnType) {
|
3448
3700
|
const { type } = extractTypeAnnotation(decl.returnType);
|
3449
|
-
if (type != null && !Prefs.indirectCalls) {
|
3450
|
-
|
3451
|
-
|
3701
|
+
// if (type != null && !Prefs.indirectCalls) {
|
3702
|
+
if (type != null) {
|
3703
|
+
func.returnType = type;
|
3704
|
+
func.returns = [ valtypeBinary ];
|
3452
3705
|
}
|
3453
3706
|
}
|
3454
3707
|
|
3455
3708
|
for (let i = 0; i < params.length; i++) {
|
3456
|
-
|
3709
|
+
const name = params[i].name;
|
3710
|
+
// if (name == null) return todo('non-identifier args are not supported');
|
3711
|
+
|
3712
|
+
allocVar(func, name, false);
|
3457
3713
|
|
3458
3714
|
if (typedInput && params[i].typeAnnotation) {
|
3459
|
-
addVarMetadata(
|
3715
|
+
addVarMetadata(func, name, false, extractTypeAnnotation(params[i]));
|
3460
3716
|
}
|
3461
3717
|
}
|
3462
3718
|
|
3719
|
+
func.params = Object.values(func.locals).map(x => x.type);
|
3720
|
+
|
3463
3721
|
let body = objectHack(decl.body);
|
3464
3722
|
if (decl.type === 'ArrowFunctionExpression' && decl.expression) {
|
3465
3723
|
// hack: () => 0 -> () => return 0
|
@@ -3469,37 +3727,23 @@ const generateFunc = (scope, decl) => {
|
|
3469
3727
|
};
|
3470
3728
|
}
|
3471
3729
|
|
3472
|
-
const wasm = generate(innerScope, body);
|
3473
|
-
const func = {
|
3474
|
-
name,
|
3475
|
-
params: Object.values(innerScope.locals).slice(0, params.length * 2).map(x => x.type),
|
3476
|
-
index: currentFuncIndex++,
|
3477
|
-
...innerScope
|
3478
|
-
};
|
3479
3730
|
funcIndex[name] = func.index;
|
3731
|
+
funcs.push(func);
|
3480
3732
|
|
3481
|
-
|
3733
|
+
const wasm = generate(func, body);
|
3734
|
+
func.wasm = wasm;
|
3482
3735
|
|
3483
|
-
|
3484
|
-
for (const inst of wasm) {
|
3485
|
-
if (inst[0] === Opcodes.call && inst[1] === -1) {
|
3486
|
-
inst[1] = func.index;
|
3487
|
-
}
|
3488
|
-
}
|
3736
|
+
if (name === 'main') func.gotLastType = true;
|
3489
3737
|
|
3490
3738
|
// add end return if not found
|
3491
3739
|
if (name !== 'main' && wasm[wasm.length - 1]?.[0] !== Opcodes.return && countLeftover(wasm) === 0) {
|
3492
3740
|
wasm.push(
|
3493
3741
|
...number(0),
|
3494
|
-
...(
|
3742
|
+
...(func.returnType != null ? [] : number(TYPES.undefined, Valtype.i32)),
|
3495
3743
|
[ Opcodes.return ]
|
3496
3744
|
);
|
3497
3745
|
}
|
3498
3746
|
|
3499
|
-
func.wasm = wasm;
|
3500
|
-
|
3501
|
-
funcs.push(func);
|
3502
|
-
|
3503
3747
|
return func;
|
3504
3748
|
};
|
3505
3749
|
|
@@ -3748,9 +3992,8 @@ export default program => {
|
|
3748
3992
|
|
3749
3993
|
if (Prefs.astLog) console.log(JSON.stringify(program.body.body, null, 2));
|
3750
3994
|
|
3751
|
-
generateFunc(scope, program);
|
3995
|
+
const main = generateFunc(scope, program);
|
3752
3996
|
|
3753
|
-
const main = funcs[funcs.length - 1];
|
3754
3997
|
main.export = true;
|
3755
3998
|
main.returns = [ valtypeBinary, Valtype.i32 ];
|
3756
3999
|
|
@@ -3777,7 +4020,7 @@ export default program => {
|
|
3777
4020
|
}
|
3778
4021
|
|
3779
4022
|
// if blank main func and other exports, remove it
|
3780
|
-
if (main.wasm.length === 0 && funcs.reduce((acc, x) => acc + (x.export ? 1 : 0), 0) > 1) funcs.splice(
|
4023
|
+
if (main.wasm.length === 0 && funcs.reduce((acc, x) => acc + (x.export ? 1 : 0), 0) > 1) funcs.splice(main.index - importedFuncs.length, 1);
|
3781
4024
|
|
3782
4025
|
return { funcs, globals, tags, exceptions, pages, data };
|
3783
4026
|
};
|