porffor 0.14.0-2ce6edbb3 → 0.14.0-320727338
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 +9 -3
- package/asur/index.js +1 -1
- package/compiler/2c.js +3 -0
- package/compiler/assemble.js +14 -0
- package/compiler/builtins/annexb_string.ts +1 -0
- package/compiler/builtins/array.ts +84 -4
- 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/error.js +22 -0
- package/compiler/builtins/escape.ts +1 -2
- package/compiler/builtins/function.ts +2 -0
- package/compiler/builtins/int.ts +2 -0
- package/compiler/builtins/math.ts +410 -0
- package/compiler/builtins/number.ts +2 -0
- package/compiler/builtins/object.ts +2 -0
- package/compiler/builtins/set.ts +7 -6
- package/compiler/builtins/string.ts +1 -0
- package/compiler/builtins/symbol.ts +3 -1
- package/compiler/builtins.js +10 -6
- package/compiler/codegen.js +478 -257
- package/compiler/generated_builtins.js +491 -60
- package/compiler/precompile.js +5 -4
- package/compiler/prefs.js +1 -1
- package/compiler/prototype.js +180 -157
- package/compiler/wrap.js +66 -37
- package/package.json +1 -1
- 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':
|
@@ -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 [];
|
@@ -203,19 +205,11 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
203
205
|
|
204
206
|
__Porffor_bs: str => [
|
205
207
|
...makeString(scope, str, global, name, true),
|
206
|
-
|
207
|
-
...(name ? setType(scope, name, TYPES.bytestring) : [
|
208
|
-
...number(TYPES.bytestring, Valtype.i32),
|
209
|
-
...setLastType(scope)
|
210
|
-
])
|
208
|
+
...(name ? setType(scope, name, TYPES.bytestring) : setLastType(scope, TYPES.bytestring))
|
211
209
|
],
|
212
210
|
__Porffor_s: str => [
|
213
211
|
...makeString(scope, str, global, name, false),
|
214
|
-
|
215
|
-
...(name ? setType(scope, name, TYPES.string) : [
|
216
|
-
...number(TYPES.string, Valtype.i32),
|
217
|
-
...setLastType(scope)
|
218
|
-
])
|
212
|
+
...(name ? setType(scope, name, TYPES.string) : setLastType(scope, TYPES.string))
|
219
213
|
],
|
220
214
|
};
|
221
215
|
|
@@ -369,7 +363,7 @@ const localTmp = (scope, name, type = valtypeBinary) => {
|
|
369
363
|
};
|
370
364
|
|
371
365
|
const isIntOp = op => op && ((op[0] >= 0x45 && op[0] <= 0x4f) || (op[0] >= 0x67 && op[0] <= 0x78) || op[0] === 0x41);
|
372
|
-
const
|
366
|
+
const isIntToFloatOp = op => op && (op[0] >= 0xb7 && op[0] <= 0xba);
|
373
367
|
|
374
368
|
const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
375
369
|
const checks = {
|
@@ -386,10 +380,10 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
386
380
|
|
387
381
|
// if we can, use int tmp and convert at the end to help prevent unneeded conversions
|
388
382
|
// (like if we are in an if condition - very common)
|
389
|
-
const
|
390
|
-
const
|
383
|
+
const leftWasInt = isIntToFloatOp(left[left.length - 1]);
|
384
|
+
const rightWasInt = isIntToFloatOp(right[right.length - 1]);
|
391
385
|
|
392
|
-
const canInt =
|
386
|
+
const canInt = leftWasInt && rightWasInt;
|
393
387
|
|
394
388
|
if (canInt) {
|
395
389
|
// remove int -> float conversions from left and right
|
@@ -403,13 +397,11 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
403
397
|
[ Opcodes.if, Valtype.i32 ],
|
404
398
|
...right,
|
405
399
|
// note type
|
406
|
-
...rightType,
|
407
|
-
...setLastType(scope),
|
400
|
+
...setLastType(scope, rightType),
|
408
401
|
[ Opcodes.else ],
|
409
402
|
[ Opcodes.local_get, localTmp(scope, 'logictmpi', Valtype.i32) ],
|
410
403
|
// note type
|
411
|
-
...leftType,
|
412
|
-
...setLastType(scope),
|
404
|
+
...setLastType(scope, leftType),
|
413
405
|
[ Opcodes.end ],
|
414
406
|
Opcodes.i32_from
|
415
407
|
];
|
@@ -422,13 +414,11 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
422
414
|
[ Opcodes.if, valtypeBinary ],
|
423
415
|
...right,
|
424
416
|
// note type
|
425
|
-
...rightType,
|
426
|
-
...setLastType(scope),
|
417
|
+
...setLastType(scope, rightType),
|
427
418
|
[ Opcodes.else ],
|
428
419
|
[ Opcodes.local_get, localTmp(scope, 'logictmp') ],
|
429
420
|
// note type
|
430
|
-
...leftType,
|
431
|
-
...setLastType(scope),
|
421
|
+
...setLastType(scope, leftType),
|
432
422
|
[ Opcodes.end ]
|
433
423
|
];
|
434
424
|
};
|
@@ -456,11 +446,11 @@ const concatStrings = (scope, left, right, global, name, assign = false, bytestr
|
|
456
446
|
...number(0, Valtype.i32), // base 0 for store later
|
457
447
|
|
458
448
|
...number(pointer, Valtype.i32),
|
459
|
-
[ Opcodes.i32_load,
|
449
|
+
[ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
|
460
450
|
[ Opcodes.local_tee, leftLength ],
|
461
451
|
|
462
452
|
[ Opcodes.local_get, rightPointer ],
|
463
|
-
[ Opcodes.i32_load,
|
453
|
+
[ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
|
464
454
|
[ Opcodes.local_tee, rightLength ],
|
465
455
|
|
466
456
|
[ Opcodes.i32_add ],
|
@@ -516,11 +506,11 @@ const concatStrings = (scope, left, right, global, name, assign = false, bytestr
|
|
516
506
|
...number(0, Valtype.i32), // base 0 for store later
|
517
507
|
|
518
508
|
[ Opcodes.local_get, leftPointer ],
|
519
|
-
[ Opcodes.i32_load,
|
509
|
+
[ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
|
520
510
|
[ Opcodes.local_tee, leftLength ],
|
521
511
|
|
522
512
|
[ Opcodes.local_get, rightPointer ],
|
523
|
-
[ Opcodes.i32_load,
|
513
|
+
[ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
|
524
514
|
[ Opcodes.local_tee, rightLength ],
|
525
515
|
|
526
516
|
[ Opcodes.i32_add ],
|
@@ -598,11 +588,11 @@ const compareStrings = (scope, left, right, bytestrings = false) => {
|
|
598
588
|
|
599
589
|
// get lengths
|
600
590
|
[ Opcodes.local_get, leftPointer ],
|
601
|
-
[ Opcodes.i32_load,
|
591
|
+
[ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
|
602
592
|
[ Opcodes.local_tee, leftLength ],
|
603
593
|
|
604
594
|
[ Opcodes.local_get, rightPointer ],
|
605
|
-
[ Opcodes.i32_load,
|
595
|
+
[ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
|
606
596
|
|
607
597
|
// fast path: check leftLength != rightLength
|
608
598
|
[ Opcodes.i32_ne ],
|
@@ -658,9 +648,9 @@ const compareStrings = (scope, left, right, bytestrings = false) => {
|
|
658
648
|
[ Opcodes.i32_add ],
|
659
649
|
[ Opcodes.local_tee, index ],
|
660
650
|
|
661
|
-
// if index
|
651
|
+
// if index < index end (length * sizeof valtype), loop
|
662
652
|
[ Opcodes.local_get, indexEnd ],
|
663
|
-
[ Opcodes.
|
653
|
+
[ Opcodes.i32_lt_s ],
|
664
654
|
[ Opcodes.br_if, 0 ],
|
665
655
|
[ Opcodes.end ],
|
666
656
|
|
@@ -678,8 +668,8 @@ const compareStrings = (scope, left, right, bytestrings = false) => {
|
|
678
668
|
];
|
679
669
|
};
|
680
670
|
|
681
|
-
const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
682
|
-
if (
|
671
|
+
const truthy = (scope, wasm, type, intIn = false, intOut = false, forceTruthyMode = undefined) => {
|
672
|
+
if (isIntToFloatOp(wasm[wasm.length - 1])) return [
|
683
673
|
...wasm,
|
684
674
|
...(!intIn && intOut ? [ Opcodes.i32_to_u ] : [])
|
685
675
|
];
|
@@ -688,28 +678,38 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
688
678
|
const useTmp = knownType(scope, type) == null;
|
689
679
|
const tmp = useTmp && localTmp(scope, `#logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
|
690
680
|
|
691
|
-
const def =
|
692
|
-
|
693
|
-
|
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 ]),
|
694
686
|
|
695
|
-
|
696
|
-
|
687
|
+
[ Opcodes.i32_eqz ],
|
688
|
+
[ Opcodes.i32_eqz ],
|
697
689
|
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
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
|
+
})(forceTruthyMode ?? Prefs.truthy ?? 'full');
|
702
707
|
|
703
708
|
return [
|
704
709
|
...wasm,
|
705
710
|
...(!useTmp ? [] : [ [ Opcodes.local_set, tmp ] ]),
|
706
711
|
|
707
712
|
...typeSwitch(scope, type, {
|
708
|
-
// [TYPES.number]: def,
|
709
|
-
[TYPES.array]: [
|
710
|
-
// arrays are always truthy
|
711
|
-
...number(1, intOut ? Valtype.i32 : valtypeBinary)
|
712
|
-
],
|
713
713
|
[TYPES.string]: [
|
714
714
|
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
715
715
|
...(intIn ? [] : [ Opcodes.i32_to_u ]),
|
@@ -745,10 +745,6 @@ const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
745
745
|
...(!useTmp ? [] : [ [ Opcodes.local_set, tmp ] ]),
|
746
746
|
|
747
747
|
...typeSwitch(scope, type, {
|
748
|
-
[TYPES.array]: [
|
749
|
-
// arrays are always truthy
|
750
|
-
...number(0, intOut ? Valtype.i32 : valtypeBinary)
|
751
|
-
],
|
752
748
|
[TYPES.string]: [
|
753
749
|
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
754
750
|
...(intIn ? [] : [ Opcodes.i32_to_u ]),
|
@@ -992,7 +988,7 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
992
988
|
// if both are true
|
993
989
|
[ Opcodes.i32_and ],
|
994
990
|
[ Opcodes.if, Blocktype.void ],
|
995
|
-
...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ]),
|
991
|
+
...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ], false),
|
996
992
|
...(op === '!==' || op === '!=' ? [ [ Opcodes.i32_eqz ] ] : []),
|
997
993
|
[ Opcodes.br, 1 ],
|
998
994
|
[ Opcodes.end ],
|
@@ -1041,14 +1037,14 @@ const generateBinaryExp = (scope, decl, _global, _name) => {
|
|
1041
1037
|
return out;
|
1042
1038
|
};
|
1043
1039
|
|
1044
|
-
const asmFuncToAsm = (func,
|
1045
|
-
return func(
|
1040
|
+
const asmFuncToAsm = (func, scope) => {
|
1041
|
+
return func(scope, {
|
1046
1042
|
TYPES, TYPE_NAMES, typeSwitch, makeArray, makeString, allocPage, internalThrow,
|
1047
|
-
builtin:
|
1048
|
-
let idx = funcIndex[
|
1049
|
-
if (idx
|
1050
|
-
includeBuiltin(null,
|
1051
|
-
idx = funcIndex[
|
1043
|
+
builtin: n => {
|
1044
|
+
let idx = funcIndex[n] ?? importedFuncs[n];
|
1045
|
+
if (idx == null && builtinFuncs[n]) {
|
1046
|
+
includeBuiltin(null, n);
|
1047
|
+
idx = funcIndex[n];
|
1052
1048
|
}
|
1053
1049
|
|
1054
1050
|
return idx;
|
@@ -1056,7 +1052,7 @@ const asmFuncToAsm = (func, { name = '#unknown_asm_func', params = [], locals =
|
|
1056
1052
|
});
|
1057
1053
|
};
|
1058
1054
|
|
1059
|
-
const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits, returns, returnType, localNames = [], globalNames = [], data: _data = [] }) => {
|
1055
|
+
const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits, returns, returnType, localNames = [], globalNames = [], data: _data = [], table = false }) => {
|
1060
1056
|
const existing = funcs.find(x => x.name === name);
|
1061
1057
|
if (existing) return existing;
|
1062
1058
|
|
@@ -1074,7 +1070,22 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
|
|
1074
1070
|
data.push(copy);
|
1075
1071
|
}
|
1076
1072
|
|
1077
|
-
|
1073
|
+
const func = {
|
1074
|
+
name,
|
1075
|
+
params,
|
1076
|
+
locals,
|
1077
|
+
localInd: allLocals.length,
|
1078
|
+
returns,
|
1079
|
+
returnType: returnType ?? TYPES.number,
|
1080
|
+
internal: true,
|
1081
|
+
index: currentFuncIndex++,
|
1082
|
+
table
|
1083
|
+
};
|
1084
|
+
|
1085
|
+
funcs.push(func);
|
1086
|
+
funcIndex[name] = func.index;
|
1087
|
+
|
1088
|
+
if (typeof wasm === 'function') wasm = asmFuncToAsm(wasm, func);
|
1078
1089
|
|
1079
1090
|
let baseGlobalIdx, i = 0;
|
1080
1091
|
for (const type of globalTypes) {
|
@@ -1093,19 +1104,14 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
|
|
1093
1104
|
}
|
1094
1105
|
}
|
1095
1106
|
|
1096
|
-
const
|
1097
|
-
|
1098
|
-
|
1099
|
-
|
1100
|
-
|
1101
|
-
|
1102
|
-
wasm,
|
1103
|
-
internal: true,
|
1104
|
-
index: currentFuncIndex++
|
1105
|
-
};
|
1107
|
+
if (table) for (const inst of wasm) {
|
1108
|
+
if (inst[0] === Opcodes.i32_load16_u && inst.at(-1) === 'read_argc') {
|
1109
|
+
inst.splice(2, 99);
|
1110
|
+
inst.push(...unsignedLEB128(allocPage({}, 'func argc lut') * pageSize));
|
1111
|
+
}
|
1112
|
+
}
|
1106
1113
|
|
1107
|
-
|
1108
|
-
funcIndex[name] = func.index;
|
1114
|
+
func.wasm = wasm;
|
1109
1115
|
|
1110
1116
|
return func;
|
1111
1117
|
};
|
@@ -1197,9 +1203,10 @@ const getLastType = scope => {
|
|
1197
1203
|
return [ [ Opcodes.local_get, localTmp(scope, '#last_type', Valtype.i32) ] ];
|
1198
1204
|
};
|
1199
1205
|
|
1200
|
-
const setLastType = scope =>
|
1201
|
-
|
1202
|
-
|
1206
|
+
const setLastType = (scope, type = []) => [
|
1207
|
+
...(typeof type === 'number' ? number(type, Valtype.i32) : type),
|
1208
|
+
[ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ]
|
1209
|
+
];
|
1203
1210
|
|
1204
1211
|
const getNodeType = (scope, node) => {
|
1205
1212
|
const ret = (() => {
|
@@ -1260,7 +1267,17 @@ const getNodeType = (scope, node) => {
|
|
1260
1267
|
|
1261
1268
|
const func = spl[spl.length - 1];
|
1262
1269
|
const protoFuncs = Object.keys(prototypeFuncs).filter(x => x != TYPES.bytestring && prototypeFuncs[x][func] != null);
|
1263
|
-
if (protoFuncs.length === 1)
|
1270
|
+
if (protoFuncs.length === 1) {
|
1271
|
+
if (protoFuncs[0].returnType) return protoFuncs[0].returnType;
|
1272
|
+
}
|
1273
|
+
|
1274
|
+
if (protoFuncs.length > 0) {
|
1275
|
+
if (scope.locals['#last_type']) return getLastType(scope);
|
1276
|
+
|
1277
|
+
// presume
|
1278
|
+
// todo: warn here?
|
1279
|
+
return TYPES.number;
|
1280
|
+
}
|
1264
1281
|
}
|
1265
1282
|
|
1266
1283
|
if (name.startsWith('__Porffor_wasm_')) {
|
@@ -1355,22 +1372,27 @@ const getNodeType = (scope, node) => {
|
|
1355
1372
|
}
|
1356
1373
|
|
1357
1374
|
if (node.type === 'MemberExpression') {
|
1358
|
-
|
1359
|
-
|
1360
|
-
|
1361
|
-
|
1362
|
-
|
1363
|
-
return TYPES.undefined;
|
1364
|
-
}
|
1375
|
+
const name = node.property.name;
|
1376
|
+
|
1377
|
+
if (name === 'length') {
|
1378
|
+
if (hasFuncWithName(node.object.name)) return TYPES.number;
|
1379
|
+
if (Prefs.fastLength) return TYPES.number;
|
1365
1380
|
}
|
1366
1381
|
|
1367
|
-
// hack: if something.length, number type
|
1368
|
-
if (node.property.name === 'length') return TYPES.number;
|
1369
1382
|
|
1370
|
-
|
1371
|
-
if (
|
1372
|
-
|
1373
|
-
|
1383
|
+
const objectKnownType = knownType(scope, getNodeType(scope, node.object));
|
1384
|
+
if (objectKnownType != null) {
|
1385
|
+
if (name === 'length') {
|
1386
|
+
if ([ TYPES.string, TYPES.bytestring, TYPES.array ].includes(objectKnownType)) return TYPES.number;
|
1387
|
+
else return TYPES.undefined;
|
1388
|
+
}
|
1389
|
+
|
1390
|
+
if (node.computed) {
|
1391
|
+
if (objectKnownType === TYPES.string) return TYPES.string;
|
1392
|
+
if (objectKnownType === TYPES.bytestring) return TYPES.bytestring;
|
1393
|
+
if (objectKnownType === TYPES.array) return TYPES.number;
|
1394
|
+
}
|
1395
|
+
}
|
1374
1396
|
|
1375
1397
|
if (scope.locals['#last_type']) return getLastType(scope);
|
1376
1398
|
|
@@ -1441,16 +1463,11 @@ const countLeftover = wasm => {
|
|
1441
1463
|
else if (inst[0] === Opcodes.return) count = 0;
|
1442
1464
|
else if (inst[0] === Opcodes.call) {
|
1443
1465
|
let func = funcs.find(x => x.index === inst[1]);
|
1444
|
-
if (inst[1]
|
1445
|
-
|
1446
|
-
|
1447
|
-
count -= importedFuncs[inst[1]].params;
|
1448
|
-
count += importedFuncs[inst[1]].returns;
|
1466
|
+
if (inst[1] < importedFuncs.length) {
|
1467
|
+
func = importedFuncs[inst[1]];
|
1468
|
+
count = count - func.params + func.returns;
|
1449
1469
|
} else {
|
1450
|
-
|
1451
|
-
count -= func.params.length;
|
1452
|
-
} else count--;
|
1453
|
-
if (func) count += func.returns.length;
|
1470
|
+
count = count - func.params.length + func.returns.length;
|
1454
1471
|
}
|
1455
1472
|
} else if (inst[0] === Opcodes.call_indirect) {
|
1456
1473
|
count--; // funcidx
|
@@ -1473,7 +1490,7 @@ const disposeLeftover = wasm => {
|
|
1473
1490
|
const generateExp = (scope, decl) => {
|
1474
1491
|
const expression = decl.expression;
|
1475
1492
|
|
1476
|
-
const out = generate(scope, expression, undefined, undefined,
|
1493
|
+
const out = generate(scope, expression, undefined, undefined, Prefs.optUnused);
|
1477
1494
|
disposeLeftover(out);
|
1478
1495
|
|
1479
1496
|
return out;
|
@@ -1564,16 +1581,10 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1564
1581
|
out.splice(out.length - 1, 1);
|
1565
1582
|
|
1566
1583
|
const finalStatement = parsed.body[parsed.body.length - 1];
|
1567
|
-
out.push(
|
1568
|
-
...getNodeType(scope, finalStatement),
|
1569
|
-
...setLastType(scope)
|
1570
|
-
);
|
1584
|
+
out.push(...setLastType(scope, getNodeType(scope, finalStatement)));
|
1571
1585
|
} else if (countLeftover(out) === 0) {
|
1572
1586
|
out.push(...number(UNDEFINED));
|
1573
|
-
out.push(
|
1574
|
-
...number(TYPES.undefined, Valtype.i32),
|
1575
|
-
...setLastType(scope)
|
1576
|
-
);
|
1587
|
+
out.push(...setLastType(scope, TYPES.undefined));
|
1577
1588
|
}
|
1578
1589
|
|
1579
1590
|
// if (lastInst && lastInst[0] === Opcodes.drop) {
|
@@ -1609,6 +1620,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1609
1620
|
|
1610
1621
|
if (!funcIndex[rhemynName]) {
|
1611
1622
|
const func = Rhemyn[funcName](regex, currentFuncIndex++, rhemynName);
|
1623
|
+
func.internal = true;
|
1612
1624
|
|
1613
1625
|
funcIndex[func.name] = func.index;
|
1614
1626
|
funcs.push(func);
|
@@ -1625,8 +1637,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1625
1637
|
[ Opcodes.call, idx ],
|
1626
1638
|
Opcodes.i32_from_u,
|
1627
1639
|
|
1628
|
-
...
|
1629
|
-
...setLastType(scope)
|
1640
|
+
...setLastType(scope, TYPES.boolean)
|
1630
1641
|
];
|
1631
1642
|
}
|
1632
1643
|
|
@@ -1698,9 +1709,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1698
1709
|
if (protoFunc.noArgRetLength && decl.arguments.length === 0) {
|
1699
1710
|
protoBC[x] = [
|
1700
1711
|
...RTArrayUtil.getLength(getPointer),
|
1701
|
-
|
1702
|
-
...number(TYPES.number, Valtype.i32),
|
1703
|
-
...setLastType(scope)
|
1712
|
+
...setLastType(scope, TYPES.number)
|
1704
1713
|
];
|
1705
1714
|
continue;
|
1706
1715
|
}
|
@@ -1719,7 +1728,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1719
1728
|
getI32: () => RTArrayUtil.getLengthI32(getPointer),
|
1720
1729
|
set: value => RTArrayUtil.setLength(getPointer, value),
|
1721
1730
|
setI32: value => RTArrayUtil.setLengthI32(getPointer, value)
|
1722
|
-
}, generate(scope, decl.arguments[0] ?? DEFAULT_VALUE), protoLocal, protoLocal2, (length, itemType) => {
|
1731
|
+
}, generate(scope, decl.arguments[0] ?? DEFAULT_VALUE), getNodeType(scope, decl.arguments[0] ?? DEFAULT_VALUE), protoLocal, protoLocal2, (length, itemType) => {
|
1723
1732
|
return makeArray(scope, {
|
1724
1733
|
rawElements: new Array(length)
|
1725
1734
|
}, _global, _name, true, itemType);
|
@@ -1733,9 +1742,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1733
1742
|
protoBC[x] = [
|
1734
1743
|
[ Opcodes.block, unusedValue && optUnused ? Blocktype.void : valtypeBinary ],
|
1735
1744
|
...protoOut,
|
1736
|
-
|
1737
|
-
...number(protoFunc.returnType ?? TYPES.number, Valtype.i32),
|
1738
|
-
...setLastType(scope),
|
1745
|
+
...(unusedValue && optUnused ? [] : (protoFunc.returnType != null ? setLastType(scope, protoFunc.returnType) : setLastType(scope))),
|
1739
1746
|
[ Opcodes.end ]
|
1740
1747
|
];
|
1741
1748
|
}
|
@@ -1785,11 +1792,6 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1785
1792
|
|
1786
1793
|
if (idx === undefined && internalConstrs[name]) return internalConstrs[name].generate(scope, decl, _global, _name);
|
1787
1794
|
|
1788
|
-
if (idx === undefined && name === scope.name) {
|
1789
|
-
// hack: calling self, func generator will fix later
|
1790
|
-
idx = -1;
|
1791
|
-
}
|
1792
|
-
|
1793
1795
|
if (idx === undefined && name.startsWith('__Porffor_wasm_')) {
|
1794
1796
|
const wasmOps = {
|
1795
1797
|
// pointer, align, offset
|
@@ -1841,36 +1843,128 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1841
1843
|
const [ local, global ] = lookupName(scope, name);
|
1842
1844
|
if (!Prefs.indirectCalls || local == null) return internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, true);
|
1843
1845
|
|
1844
|
-
// todo: only works when
|
1845
|
-
|
1846
|
-
|
1846
|
+
// todo: only works when function uses typedParams and typedReturns
|
1847
|
+
|
1848
|
+
const indirectMode = Prefs.indirectCallMode ?? 'vararg';
|
1849
|
+
// options: vararg, strict
|
1850
|
+
// - strict: simpler, smaller size usage, no func argc lut needed.
|
1851
|
+
// ONLY works when arg count of call == arg count of function being called
|
1852
|
+
// - vararg: large size usage, cursed.
|
1853
|
+
// works when arg count of call != arg count of function being called*
|
1854
|
+
// * most of the time, some edgecases
|
1847
1855
|
|
1848
1856
|
funcs.table = true;
|
1857
|
+
scope.table = true;
|
1849
1858
|
|
1850
1859
|
let args = decl.arguments;
|
1851
|
-
let
|
1860
|
+
let out = [];
|
1861
|
+
|
1862
|
+
let locals = [];
|
1863
|
+
|
1864
|
+
if (indirectMode === 'vararg') {
|
1865
|
+
const minArgc = Prefs.indirectCallMinArgc ?? 3;
|
1866
|
+
|
1867
|
+
if (args.length < minArgc) {
|
1868
|
+
args = args.concat(new Array(minArgc - args.length).fill(DEFAULT_VALUE));
|
1869
|
+
}
|
1870
|
+
}
|
1852
1871
|
|
1853
1872
|
for (let i = 0; i < args.length; i++) {
|
1854
1873
|
const arg = args[i];
|
1855
|
-
|
1874
|
+
out = out.concat(generate(scope, arg));
|
1856
1875
|
|
1857
1876
|
if (valtypeBinary !== Valtype.i32 && (
|
1858
1877
|
(builtinFuncs[name] && builtinFuncs[name].params[i * (typedParams ? 2 : 1)] === Valtype.i32) ||
|
1859
1878
|
(importedFuncs[name] && name.startsWith('profile'))
|
1860
1879
|
)) {
|
1861
|
-
|
1880
|
+
out.push(Opcodes.i32_to);
|
1881
|
+
}
|
1882
|
+
|
1883
|
+
out = out.concat(getNodeType(scope, arg));
|
1884
|
+
|
1885
|
+
if (indirectMode === 'vararg') {
|
1886
|
+
const typeLocal = localTmp(scope, `#indirect_arg${i}_type`, Valtype.i32);
|
1887
|
+
const valLocal = localTmp(scope, `#indirect_arg${i}_val`);
|
1888
|
+
|
1889
|
+
locals.push([valLocal, typeLocal]);
|
1890
|
+
|
1891
|
+
out.push(
|
1892
|
+
[ Opcodes.local_set, typeLocal ],
|
1893
|
+
[ Opcodes.local_set, valLocal ]
|
1894
|
+
);
|
1862
1895
|
}
|
1896
|
+
}
|
1863
1897
|
|
1864
|
-
|
1898
|
+
if (indirectMode === 'strict') {
|
1899
|
+
return typeSwitch(scope, getNodeType(scope, decl.callee), {
|
1900
|
+
[TYPES.function]: [
|
1901
|
+
...argWasm,
|
1902
|
+
[ global ? Opcodes.global_get : Opcodes.local_get, local.idx ],
|
1903
|
+
Opcodes.i32_to_u,
|
1904
|
+
[ Opcodes.call_indirect, args.length, 0 ],
|
1905
|
+
...setLastType(scope)
|
1906
|
+
],
|
1907
|
+
default: internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, true)
|
1908
|
+
});
|
1865
1909
|
}
|
1866
1910
|
|
1911
|
+
// hi, I will now explain how vararg mode works:
|
1912
|
+
// wasm's indirect_call instruction requires you know the func type at compile-time
|
1913
|
+
// since we have varargs (variable argument count), we do not know it.
|
1914
|
+
// we could just store args in memory and not use wasm func args,
|
1915
|
+
// but that is slow (probably) and breaks js exports.
|
1916
|
+
// instead, we generate every* possibility of argc and use different indirect_call
|
1917
|
+
// ops for each one, with type depending on argc for that branch.
|
1918
|
+
// then we load the argc for the wanted function from a memory lut,
|
1919
|
+
// and call the branch with the matching argc we require.
|
1920
|
+
// sorry, yes it is very cursed (and size inefficient), but indirect calls
|
1921
|
+
// are kind of rare anyway (mostly callbacks) so I am not concerned atm.
|
1922
|
+
// *for argc 0-3, in future (todo:) the max number should be
|
1923
|
+
// dynamically changed to the max argc of any func in the js file.
|
1924
|
+
|
1925
|
+
const funcLocal = localTmp(scope, `#indirect_func`, Valtype.i32);
|
1926
|
+
|
1927
|
+
const gen = argc => {
|
1928
|
+
const out = [];
|
1929
|
+
for (let i = 0; i < argc; i++) {
|
1930
|
+
out.push(
|
1931
|
+
[ Opcodes.local_get, locals[i][0] ],
|
1932
|
+
[ Opcodes.local_get, locals[i][1] ]
|
1933
|
+
);
|
1934
|
+
}
|
1935
|
+
|
1936
|
+
out.push(
|
1937
|
+
[ Opcodes.local_get, funcLocal ],
|
1938
|
+
[ Opcodes.call_indirect, argc, 0 ],
|
1939
|
+
...setLastType(scope)
|
1940
|
+
)
|
1941
|
+
|
1942
|
+
return out;
|
1943
|
+
};
|
1944
|
+
|
1945
|
+
const tableBc = {};
|
1946
|
+
for (let i = 0; i <= args.length; i++) {
|
1947
|
+
tableBc[i] = gen(i);
|
1948
|
+
}
|
1949
|
+
|
1950
|
+
// todo/perf: check if we should use br_table here or just generate our own big if..elses
|
1951
|
+
|
1867
1952
|
return typeSwitch(scope, getNodeType(scope, decl.callee), {
|
1868
1953
|
[TYPES.function]: [
|
1869
|
-
...
|
1954
|
+
...out,
|
1955
|
+
|
1870
1956
|
[ global ? Opcodes.global_get : Opcodes.local_get, local.idx ],
|
1871
1957
|
Opcodes.i32_to_u,
|
1872
|
-
[ Opcodes.
|
1873
|
-
|
1958
|
+
[ Opcodes.local_set, funcLocal ],
|
1959
|
+
|
1960
|
+
...brTable([
|
1961
|
+
// get argc of func we are calling
|
1962
|
+
[ Opcodes.local_get, funcLocal ],
|
1963
|
+
...number(ValtypeSize.i16, Valtype.i32),
|
1964
|
+
[ Opcodes.i32_mul ],
|
1965
|
+
|
1966
|
+
[ Opcodes.i32_load16_u, 0, ...unsignedLEB128(allocPage(scope, 'func argc lut') * pageSize), 'read_argc' ]
|
1967
|
+
], tableBc, valtypeBinary)
|
1874
1968
|
],
|
1875
1969
|
default: internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, true)
|
1876
1970
|
});
|
@@ -1879,11 +1973,10 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1879
1973
|
return internalThrow(scope, 'ReferenceError', `${unhackName(name)} is not defined`, true);
|
1880
1974
|
}
|
1881
1975
|
|
1882
|
-
const func = funcs.find(x => x.index === idx);
|
1883
|
-
|
1884
|
-
const userFunc = (funcIndex[name] && !importedFuncs[name] && !builtinFuncs[name] && !internalConstrs[name]) || idx === -1;
|
1976
|
+
const func = funcs[idx - importedFuncs.length]; // idx === scope.index ? scope : funcs.find(x => x.index === idx);
|
1977
|
+
const userFunc = func && !func.internal;
|
1885
1978
|
const typedParams = userFunc || builtinFuncs[name]?.typedParams;
|
1886
|
-
const typedReturns = (func
|
1979
|
+
const typedReturns = (func && func.returnType == null) || builtinFuncs[name]?.typedReturns;
|
1887
1980
|
const paramCount = func && (typedParams ? func.params.length / 2 : func.params.length);
|
1888
1981
|
|
1889
1982
|
let args = decl.arguments;
|
@@ -1904,6 +1997,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1904
1997
|
const arg = args[i];
|
1905
1998
|
out = out.concat(generate(scope, arg));
|
1906
1999
|
|
2000
|
+
// todo: this should be used instead of the too many args thing above (by removing that)
|
1907
2001
|
if (i >= paramCount) {
|
1908
2002
|
// over param count of func, drop arg
|
1909
2003
|
out.push([ Opcodes.drop ]);
|
@@ -1988,8 +2082,11 @@ const knownType = (scope, type) => {
|
|
1988
2082
|
const idx = type[0][1];
|
1989
2083
|
|
1990
2084
|
// type idx = var idx + 1
|
1991
|
-
const
|
1992
|
-
if (
|
2085
|
+
const name = Object.values(scope.locals).find(x => x.idx === idx)?.name;
|
2086
|
+
if (name) {
|
2087
|
+
const local = scope.locals[name];
|
2088
|
+
if (local.metadata?.type != null) return v.metadata.type;
|
2089
|
+
}
|
1993
2090
|
}
|
1994
2091
|
|
1995
2092
|
return null;
|
@@ -2024,16 +2121,17 @@ const brTable = (input, bc, returns) => {
|
|
2024
2121
|
}
|
2025
2122
|
|
2026
2123
|
for (let i = 0; i < count; i++) {
|
2027
|
-
if (i === 0) out.push([ Opcodes.block, returns, 'br table start' ]);
|
2124
|
+
// if (i === 0) out.push([ Opcodes.block, returns, 'br table start' ]);
|
2125
|
+
if (i === 0) out.push([ Opcodes.block, returns ]);
|
2028
2126
|
else out.push([ Opcodes.block, Blocktype.void ]);
|
2029
2127
|
}
|
2030
2128
|
|
2031
|
-
const nums = keys.filter(x => +x);
|
2129
|
+
const nums = keys.filter(x => +x >= 0);
|
2032
2130
|
const offset = Math.min(...nums);
|
2033
2131
|
const max = Math.max(...nums);
|
2034
2132
|
|
2035
2133
|
const table = [];
|
2036
|
-
let br =
|
2134
|
+
let br = 0;
|
2037
2135
|
|
2038
2136
|
for (let i = offset; i <= max; i++) {
|
2039
2137
|
// if branch for this num, go to that block
|
@@ -2073,10 +2171,9 @@ const brTable = (input, bc, returns) => {
|
|
2073
2171
|
br--;
|
2074
2172
|
}
|
2075
2173
|
|
2076
|
-
|
2077
|
-
|
2078
|
-
|
2079
|
-
];
|
2174
|
+
out.push([ Opcodes.end ]);
|
2175
|
+
|
2176
|
+
return out;
|
2080
2177
|
};
|
2081
2178
|
|
2082
2179
|
const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
|
@@ -2120,6 +2217,17 @@ const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
|
|
2120
2217
|
return out;
|
2121
2218
|
};
|
2122
2219
|
|
2220
|
+
const typeIsOneOf = (type, types, valtype = Valtype.i32) => {
|
2221
|
+
const out = [];
|
2222
|
+
|
2223
|
+
for (let i = 0; i < types.length; i++) {
|
2224
|
+
out.push(...type, ...number(types[i], valtype), valtype === Valtype.f64 ? [ Opcodes.f64_eq ] : [ Opcodes.i32_eq ]);
|
2225
|
+
if (i !== 0) out.push([ Opcodes.i32_or ]);
|
2226
|
+
}
|
2227
|
+
|
2228
|
+
return out;
|
2229
|
+
};
|
2230
|
+
|
2123
2231
|
const allocVar = (scope, name, global = false, type = true) => {
|
2124
2232
|
const target = global ? globals : scope.locals;
|
2125
2233
|
|
@@ -2136,7 +2244,7 @@ const allocVar = (scope, name, global = false, type = true) => {
|
|
2136
2244
|
|
2137
2245
|
if (type) {
|
2138
2246
|
let typeIdx = global ? globalInd++ : scope.localInd++;
|
2139
|
-
target[name + '#type'] = { idx: typeIdx, type: Valtype.i32 };
|
2247
|
+
target[name + '#type'] = { idx: typeIdx, type: Valtype.i32, name };
|
2140
2248
|
}
|
2141
2249
|
|
2142
2250
|
return idx;
|
@@ -2349,18 +2457,21 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
|
|
2349
2457
|
Opcodes.i32_to_u,
|
2350
2458
|
|
2351
2459
|
// turn into byte offset by * valtypeSize (4 for i32, 8 for i64/f64)
|
2352
|
-
...number(ValtypeSize[valtype], Valtype.i32),
|
2460
|
+
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
2353
2461
|
[ Opcodes.i32_mul ],
|
2354
2462
|
...(aotPointer ? [] : [ [ Opcodes.i32_add ] ]),
|
2355
2463
|
...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
|
2356
2464
|
|
2357
2465
|
...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
|
2358
2466
|
[ Opcodes.local_get, pointerTmp ],
|
2359
|
-
[ Opcodes.load,
|
2360
|
-
], generate(scope, decl.right),
|
2467
|
+
[ Opcodes.load, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
|
2468
|
+
], generate(scope, decl.right), [
|
2469
|
+
[ Opcodes.local_get, pointerTmp ],
|
2470
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32 + ValtypeSize[valtype]) ]
|
2471
|
+
], getNodeType(scope, decl.right), false, name, true)),
|
2361
2472
|
[ Opcodes.local_tee, newValueTmp ],
|
2362
2473
|
|
2363
|
-
[ Opcodes.store,
|
2474
|
+
[ Opcodes.store, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
|
2364
2475
|
],
|
2365
2476
|
|
2366
2477
|
default: internalThrow(scope, 'TypeError', `Cannot assign member with non-array`)
|
@@ -2468,6 +2579,7 @@ const generateUnary = (scope, decl) => {
|
|
2468
2579
|
];
|
2469
2580
|
|
2470
2581
|
case '!':
|
2582
|
+
// todo/perf: optimize !!
|
2471
2583
|
// !=
|
2472
2584
|
return falsy(scope, generate(scope, decl.argument), getNodeType(scope, decl.argument), false, false);
|
2473
2585
|
|
@@ -2614,21 +2726,16 @@ const generateConditional = (scope, decl) => {
|
|
2614
2726
|
out.push([ Opcodes.if, valtypeBinary ]);
|
2615
2727
|
depth.push('if');
|
2616
2728
|
|
2617
|
-
out.push(...generate(scope, decl.consequent));
|
2618
|
-
|
2619
|
-
// note type
|
2620
2729
|
out.push(
|
2621
|
-
...
|
2622
|
-
...setLastType(scope)
|
2730
|
+
...generate(scope, decl.consequent),
|
2731
|
+
...setLastType(scope, getNodeType(scope, decl.consequent))
|
2623
2732
|
);
|
2624
2733
|
|
2625
2734
|
out.push([ Opcodes.else ]);
|
2626
|
-
out.push(...generate(scope, decl.alternate));
|
2627
2735
|
|
2628
|
-
// note type
|
2629
2736
|
out.push(
|
2630
|
-
...
|
2631
|
-
...setLastType(scope)
|
2737
|
+
...generate(scope, decl.alternate),
|
2738
|
+
...setLastType(scope, getNodeType(scope, decl.alternate))
|
2632
2739
|
);
|
2633
2740
|
|
2634
2741
|
out.push([ Opcodes.end ]);
|
@@ -2776,12 +2883,15 @@ const generateForOf = (scope, decl) => {
|
|
2776
2883
|
// todo: optimize away counter and use end pointer
|
2777
2884
|
out.push(...typeSwitch(scope, getNodeType(scope, decl.right), {
|
2778
2885
|
[TYPES.array]: [
|
2779
|
-
...setType(scope, leftName, TYPES.number),
|
2780
|
-
|
2781
2886
|
[ Opcodes.loop, Blocktype.void ],
|
2782
2887
|
|
2783
2888
|
[ Opcodes.local_get, pointer ],
|
2784
|
-
[ Opcodes.load,
|
2889
|
+
[ Opcodes.load, 0, ...unsignedLEB128(ValtypeSize.i32) ],
|
2890
|
+
|
2891
|
+
...setType(scope, leftName, [
|
2892
|
+
[ Opcodes.local_get, pointer ],
|
2893
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32 + ValtypeSize[valtype]) ],
|
2894
|
+
]),
|
2785
2895
|
|
2786
2896
|
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
|
2787
2897
|
|
@@ -2790,9 +2900,9 @@ const generateForOf = (scope, decl) => {
|
|
2790
2900
|
...generate(scope, decl.body),
|
2791
2901
|
[ Opcodes.end ],
|
2792
2902
|
|
2793
|
-
// increment iter pointer by valtype size
|
2903
|
+
// increment iter pointer by valtype size + 1
|
2794
2904
|
[ Opcodes.local_get, pointer ],
|
2795
|
-
...number(ValtypeSize[valtype], Valtype.i32),
|
2905
|
+
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
2796
2906
|
[ Opcodes.i32_add ],
|
2797
2907
|
[ Opcodes.local_set, pointer ],
|
2798
2908
|
|
@@ -3009,14 +3119,18 @@ const generateThrow = (scope, decl) => {
|
|
3009
3119
|
};
|
3010
3120
|
|
3011
3121
|
const generateTry = (scope, decl) => {
|
3012
|
-
|
3122
|
+
// todo: handle control-flow pre-exit for finally
|
3123
|
+
// "Immediately before a control-flow statement (return, throw, break, continue) is executed in the try block or catch block."
|
3013
3124
|
|
3014
3125
|
const out = [];
|
3015
3126
|
|
3127
|
+
const finalizer = decl.finalizer ? generate(scope, decl.finalizer) : [];
|
3128
|
+
|
3016
3129
|
out.push([ Opcodes.try, Blocktype.void ]);
|
3017
3130
|
depth.push('try');
|
3018
3131
|
|
3019
3132
|
out.push(...generate(scope, decl.block));
|
3133
|
+
out.push(...finalizer);
|
3020
3134
|
|
3021
3135
|
if (decl.handler) {
|
3022
3136
|
depth.pop();
|
@@ -3024,6 +3138,7 @@ const generateTry = (scope, decl) => {
|
|
3024
3138
|
|
3025
3139
|
out.push([ Opcodes.catch_all ]);
|
3026
3140
|
out.push(...generate(scope, decl.handler.body));
|
3141
|
+
out.push(...finalizer);
|
3027
3142
|
}
|
3028
3143
|
|
3029
3144
|
out.push([ Opcodes.end ]);
|
@@ -3109,7 +3224,7 @@ const getAllocType = itemType => {
|
|
3109
3224
|
}
|
3110
3225
|
};
|
3111
3226
|
|
3112
|
-
const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false, itemType = valtype) => {
|
3227
|
+
const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false, itemType = valtype, typed = false) => {
|
3113
3228
|
const out = [];
|
3114
3229
|
|
3115
3230
|
scope.arrays ??= new Map();
|
@@ -3121,8 +3236,13 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
3121
3236
|
// todo: can we just have 1 undeclared array? probably not? but this is not really memory efficient
|
3122
3237
|
const uniqueName = name === '$undeclared' ? name + Math.random().toString().slice(2) : name;
|
3123
3238
|
|
3124
|
-
|
3125
|
-
|
3239
|
+
let page;
|
3240
|
+
if (Prefs.scopedPageNames) page = allocPage(scope, `${getAllocType(itemType)}: ${scope.name}/${uniqueName}`, itemType);
|
3241
|
+
else page = allocPage(scope, `${getAllocType(itemType)}: ${uniqueName}`, itemType);
|
3242
|
+
|
3243
|
+
// hack: use 1 for page 0 pointer for fast truthiness
|
3244
|
+
const ptr = page === 0 ? 1 : (page * pageSize);
|
3245
|
+
scope.arrays.set(name, ptr);
|
3126
3246
|
}
|
3127
3247
|
|
3128
3248
|
const pointer = scope.arrays.get(name);
|
@@ -3172,7 +3292,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
3172
3292
|
|
3173
3293
|
const pointerWasm = pointerTmp != null ? [ [ Opcodes.local_get, pointerTmp ] ] : number(pointer, Valtype.i32);
|
3174
3294
|
|
3175
|
-
// store length
|
3295
|
+
// store length
|
3176
3296
|
out.push(
|
3177
3297
|
...pointerWasm,
|
3178
3298
|
...number(length, Valtype.i32),
|
@@ -3180,14 +3300,20 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
3180
3300
|
);
|
3181
3301
|
|
3182
3302
|
const storeOp = StoreOps[itemType];
|
3183
|
-
|
3303
|
+
const sizePerEl = ValtypeSize[itemType] + (typed ? 1 : 0);
|
3184
3304
|
if (!initEmpty) for (let i = 0; i < length; i++) {
|
3185
3305
|
if (elements[i] == null) continue;
|
3186
3306
|
|
3307
|
+
const offset = ValtypeSize.i32 + i * sizePerEl;
|
3187
3308
|
out.push(
|
3188
3309
|
...pointerWasm,
|
3189
3310
|
...(useRawElements ? number(elements[i], Valtype[valtype]) : generate(scope, elements[i])),
|
3190
|
-
[ storeOp,
|
3311
|
+
[ storeOp, 0, ...unsignedLEB128(offset) ],
|
3312
|
+
...(!typed ? [] : [ // typed presumes !useRawElements
|
3313
|
+
...pointerWasm,
|
3314
|
+
...getNodeType(scope, elements[i]),
|
3315
|
+
[ Opcodes.i32_store8, 0, ...unsignedLEB128(offset + ValtypeSize[itemType]) ]
|
3316
|
+
])
|
3191
3317
|
);
|
3192
3318
|
}
|
3193
3319
|
|
@@ -3197,6 +3323,65 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
3197
3323
|
return [ out, pointer ];
|
3198
3324
|
};
|
3199
3325
|
|
3326
|
+
const storeArray = (scope, array, index, element, aotPointer = null) => {
|
3327
|
+
if (!Array.isArray(element)) element = generate(scope, element);
|
3328
|
+
if (typeof index === 'number') index = number(index);
|
3329
|
+
|
3330
|
+
const offset = localTmp(scope, '#storeArray_offset', Valtype.i32);
|
3331
|
+
|
3332
|
+
return [
|
3333
|
+
// calculate offset
|
3334
|
+
...index,
|
3335
|
+
Opcodes.i32_to_u,
|
3336
|
+
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
3337
|
+
[ Opcodes.i32_mul ],
|
3338
|
+
...(aotPointer ? [] : [
|
3339
|
+
...array,
|
3340
|
+
Opcodes.i32_to_u,
|
3341
|
+
[ Opcodes.i32_add ],
|
3342
|
+
]),
|
3343
|
+
[ Opcodes.local_set, offset ],
|
3344
|
+
|
3345
|
+
// store value
|
3346
|
+
[ Opcodes.local_get, offset ],
|
3347
|
+
...generate(scope, element),
|
3348
|
+
[ Opcodes.store, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
3349
|
+
|
3350
|
+
// store type
|
3351
|
+
[ Opcodes.local_get, offset ],
|
3352
|
+
...getNodeType(scope, element),
|
3353
|
+
[ Opcodes.i32_store8, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32 + ValtypeSize[valtype]) ]
|
3354
|
+
];
|
3355
|
+
};
|
3356
|
+
|
3357
|
+
const loadArray = (scope, array, index, aotPointer = null) => {
|
3358
|
+
if (typeof index === 'number') index = number(index);
|
3359
|
+
|
3360
|
+
const offset = localTmp(scope, '#loadArray_offset', Valtype.i32);
|
3361
|
+
|
3362
|
+
return [
|
3363
|
+
// calculate offset
|
3364
|
+
...index,
|
3365
|
+
Opcodes.i32_to_u,
|
3366
|
+
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
3367
|
+
[ Opcodes.i32_mul ],
|
3368
|
+
...(aotPointer ? [] : [
|
3369
|
+
...array,
|
3370
|
+
Opcodes.i32_to_u,
|
3371
|
+
[ Opcodes.i32_add ],
|
3372
|
+
]),
|
3373
|
+
[ Opcodes.local_set, offset ],
|
3374
|
+
|
3375
|
+
// load value
|
3376
|
+
[ Opcodes.local_get, offset ],
|
3377
|
+
[ Opcodes.load, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
3378
|
+
|
3379
|
+
// load type
|
3380
|
+
[ Opcodes.local_get, offset ],
|
3381
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32 + ValtypeSize[valtype]) ]
|
3382
|
+
];
|
3383
|
+
};
|
3384
|
+
|
3200
3385
|
const byteStringable = str => {
|
3201
3386
|
if (!Prefs.bytestring) return false;
|
3202
3387
|
|
@@ -3225,7 +3410,7 @@ const makeString = (scope, str, global = false, name = '$undeclared', forceBytes
|
|
3225
3410
|
};
|
3226
3411
|
|
3227
3412
|
const generateArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false) => {
|
3228
|
-
return makeArray(scope, decl, global, name, initEmpty, valtype)[0];
|
3413
|
+
return makeArray(scope, decl, global, name, initEmpty, valtype, true)[0];
|
3229
3414
|
};
|
3230
3415
|
|
3231
3416
|
const generateObject = (scope, decl, global = false, name = '$undeclared') => {
|
@@ -3233,17 +3418,20 @@ const generateObject = (scope, decl, global = false, name = '$undeclared') => {
|
|
3233
3418
|
|
3234
3419
|
return [
|
3235
3420
|
...number(1),
|
3236
|
-
|
3237
|
-
...number(TYPES.object, Valtype.i32),
|
3238
|
-
...setLastType(scope)
|
3421
|
+
...setLastType(scope, TYPES.object)
|
3239
3422
|
];
|
3240
3423
|
};
|
3241
3424
|
|
3425
|
+
const withType = (scope, wasm, type) => [
|
3426
|
+
...wasm,
|
3427
|
+
...setLastType(scope, type)
|
3428
|
+
];
|
3429
|
+
|
3242
3430
|
const generateMember = (scope, decl, _global, _name) => {
|
3243
3431
|
const name = decl.object.name;
|
3244
3432
|
const pointer = scope.arrays?.get(name);
|
3245
3433
|
|
3246
|
-
const aotPointer = Prefs.aotPointerOpt && pointer
|
3434
|
+
const aotPointer = Prefs.aotPointerOpt && pointer;
|
3247
3435
|
|
3248
3436
|
// hack: .name
|
3249
3437
|
if (decl.property.name === 'name') {
|
@@ -3253,9 +3441,9 @@ const generateMember = (scope, decl, _global, _name) => {
|
|
3253
3441
|
// eg: __String_prototype_toLowerCase -> toLowerCase
|
3254
3442
|
if (nameProp.startsWith('__')) nameProp = nameProp.split('_').pop();
|
3255
3443
|
|
3256
|
-
return makeString(scope, nameProp, _global, _name, true);
|
3444
|
+
return withType(scope, makeString(scope, nameProp, _global, _name, true), TYPES.bytestring);
|
3257
3445
|
} else {
|
3258
|
-
return
|
3446
|
+
return withType(scope, number(0), TYPES.undefined);
|
3259
3447
|
}
|
3260
3448
|
}
|
3261
3449
|
|
@@ -3263,9 +3451,8 @@ const generateMember = (scope, decl, _global, _name) => {
|
|
3263
3451
|
if (decl.property.name === 'length') {
|
3264
3452
|
const func = funcs.find(x => x.name === name);
|
3265
3453
|
if (func) {
|
3266
|
-
const
|
3267
|
-
|
3268
|
-
return number(typedParams ? func.params.length / 2 : func.params.length);
|
3454
|
+
const typedParams = !func.internal || builtinFuncs[name]?.typedParams;
|
3455
|
+
return withType(scope, number(typedParams ? func.params.length / 2 : func.params.length), TYPES.number);
|
3269
3456
|
}
|
3270
3457
|
|
3271
3458
|
if (builtinFuncs[name + '$constructor']) {
|
@@ -3275,24 +3462,88 @@ const generateMember = (scope, decl, _global, _name) => {
|
|
3275
3462
|
const constructorFunc = builtinFuncs[name + '$constructor'];
|
3276
3463
|
const constructorParams = constructorFunc.typedParams ? (constructorFunc.params.length / 2) : constructorFunc.params.length;
|
3277
3464
|
|
3278
|
-
return number(Math.max(regularParams, constructorParams));
|
3465
|
+
return withType(scope, number(Math.max(regularParams, constructorParams)), TYPES.number);
|
3279
3466
|
}
|
3280
3467
|
|
3281
|
-
if (builtinFuncs[name]) return number(builtinFuncs[name].typedParams ? (builtinFuncs[name].params.length / 2) : builtinFuncs[name].params.length);
|
3282
|
-
if (importedFuncs[name]) return number(importedFuncs[name].params);
|
3283
|
-
if (internalConstrs[name]) return number(internalConstrs[name].length ?? 0);
|
3468
|
+
if (builtinFuncs[name]) return withType(scope, number(builtinFuncs[name].typedParams ? (builtinFuncs[name].params.length / 2) : builtinFuncs[name].params.length), TYPES.number);
|
3469
|
+
if (importedFuncs[name]) return withType(scope, number(importedFuncs[name].params), TYPES.number);
|
3470
|
+
if (internalConstrs[name]) return withType(scope, number(internalConstrs[name].length ?? 0), TYPES.number);
|
3471
|
+
|
3472
|
+
if (Prefs.fastLength) {
|
3473
|
+
// presume valid length object
|
3474
|
+
return [
|
3475
|
+
...(aotPointer ? number(0, Valtype.i32) : [
|
3476
|
+
...generate(scope, decl.object),
|
3477
|
+
Opcodes.i32_to_u
|
3478
|
+
]),
|
3479
|
+
|
3480
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
|
3481
|
+
Opcodes.i32_from_u
|
3482
|
+
];
|
3483
|
+
}
|
3484
|
+
|
3485
|
+
const type = getNodeType(scope, decl.object);
|
3486
|
+
const known = knownType(scope, type);
|
3487
|
+
if (known != null) {
|
3488
|
+
if ([ TYPES.string, TYPES.bytestring, TYPES.array ].includes(known)) return [
|
3489
|
+
...(aotPointer ? number(0, Valtype.i32) : [
|
3490
|
+
...generate(scope, decl.object),
|
3491
|
+
Opcodes.i32_to_u
|
3492
|
+
]),
|
3493
|
+
|
3494
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
|
3495
|
+
Opcodes.i32_from_u
|
3496
|
+
];
|
3497
|
+
|
3498
|
+
return number(0);
|
3499
|
+
}
|
3284
3500
|
|
3285
3501
|
return [
|
3286
|
-
...(
|
3287
|
-
|
3288
|
-
|
3289
|
-
|
3502
|
+
...typeIsOneOf(getNodeType(scope, decl.object), [ TYPES.string, TYPES.bytestring, TYPES.array ]),
|
3503
|
+
[ Opcodes.if, valtypeBinary ],
|
3504
|
+
...(aotPointer ? number(0, Valtype.i32) : [
|
3505
|
+
...generate(scope, decl.object),
|
3506
|
+
Opcodes.i32_to_u
|
3507
|
+
]),
|
3508
|
+
|
3509
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
|
3510
|
+
Opcodes.i32_from_u,
|
3290
3511
|
|
3291
|
-
|
3292
|
-
Opcodes.
|
3512
|
+
...setLastType(scope, TYPES.number),
|
3513
|
+
[ Opcodes.else ],
|
3514
|
+
...number(0),
|
3515
|
+
...setLastType(scope, TYPES.undefined),
|
3516
|
+
[ Opcodes.end ]
|
3293
3517
|
];
|
3294
3518
|
}
|
3295
3519
|
|
3520
|
+
// todo: generate this array procedurally during builtinFuncs creation
|
3521
|
+
if (['size', 'description'].includes(decl.property.name)) {
|
3522
|
+
const bc = {};
|
3523
|
+
const cands = Object.keys(builtinFuncs).filter(x => x.startsWith('__') && x.endsWith('_prototype_' + decl.property.name + '$get'));
|
3524
|
+
|
3525
|
+
if (cands.length > 0) {
|
3526
|
+
for (const x of cands) {
|
3527
|
+
const type = TYPES[x.split('_prototype_')[0].slice(2).toLowerCase()];
|
3528
|
+
if (type == null) continue;
|
3529
|
+
|
3530
|
+
bc[type] = generateCall(scope, {
|
3531
|
+
callee: {
|
3532
|
+
type: 'Identifier',
|
3533
|
+
name: x
|
3534
|
+
},
|
3535
|
+
arguments: [ decl.object ],
|
3536
|
+
_protoInternalCall: true
|
3537
|
+
});
|
3538
|
+
}
|
3539
|
+
}
|
3540
|
+
|
3541
|
+
return typeSwitch(scope, getNodeType(scope, decl.object), {
|
3542
|
+
...bc,
|
3543
|
+
default: withType(scope, number(0), TYPES.undefined)
|
3544
|
+
}, valtypeBinary);
|
3545
|
+
}
|
3546
|
+
|
3296
3547
|
const object = generate(scope, decl.object);
|
3297
3548
|
const property = generate(scope, decl.property);
|
3298
3549
|
|
@@ -3307,24 +3558,7 @@ const generateMember = (scope, decl, _global, _name) => {
|
|
3307
3558
|
|
3308
3559
|
return typeSwitch(scope, getNodeType(scope, decl.object), {
|
3309
3560
|
[TYPES.array]: [
|
3310
|
-
|
3311
|
-
...property,
|
3312
|
-
|
3313
|
-
// convert to i32 and turn into byte offset by * valtypeSize (4 for i32, 8 for i64/f64)
|
3314
|
-
Opcodes.i32_to_u,
|
3315
|
-
...number(ValtypeSize[valtype], Valtype.i32),
|
3316
|
-
[ Opcodes.i32_mul ],
|
3317
|
-
|
3318
|
-
...(aotPointer ? [] : [
|
3319
|
-
...object,
|
3320
|
-
Opcodes.i32_to_u,
|
3321
|
-
[ Opcodes.i32_add ]
|
3322
|
-
]),
|
3323
|
-
|
3324
|
-
// read from memory
|
3325
|
-
[ Opcodes.load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
3326
|
-
|
3327
|
-
...number(TYPES.number, Valtype.i32),
|
3561
|
+
...loadArray(scope, object, property, aotPointer),
|
3328
3562
|
...setLastType(scope)
|
3329
3563
|
],
|
3330
3564
|
|
@@ -3355,9 +3589,7 @@ const generateMember = (scope, decl, _global, _name) => {
|
|
3355
3589
|
|
3356
3590
|
// return new string (page)
|
3357
3591
|
...number(newPointer),
|
3358
|
-
|
3359
|
-
...number(TYPES.string, Valtype.i32),
|
3360
|
-
...setLastType(scope)
|
3592
|
+
...setLastType(scope, TYPES.string)
|
3361
3593
|
],
|
3362
3594
|
[TYPES.bytestring]: [
|
3363
3595
|
// setup new/out array
|
@@ -3383,9 +3615,7 @@ const generateMember = (scope, decl, _global, _name) => {
|
|
3383
3615
|
|
3384
3616
|
// return new string (page)
|
3385
3617
|
...number(newPointer),
|
3386
|
-
|
3387
|
-
...number(TYPES.bytestring, Valtype.i32),
|
3388
|
-
...setLastType(scope)
|
3618
|
+
...setLastType(scope, TYPES.bytestring)
|
3389
3619
|
],
|
3390
3620
|
|
3391
3621
|
default: internalThrow(scope, 'TypeError', 'Member expression is not supported for non-string non-array yet', true)
|
@@ -3410,7 +3640,7 @@ const objectHack = node => {
|
|
3410
3640
|
if (!objectName) objectName = objectHack(node.object)?.name?.slice?.(2);
|
3411
3641
|
|
3412
3642
|
// if .name or .length, give up (hack within a hack!)
|
3413
|
-
if (['name', 'length'].includes(node.property.name)) {
|
3643
|
+
if (['name', 'length', 'size', 'description'].includes(node.property.name)) {
|
3414
3644
|
node.object = objectHack(node.object);
|
3415
3645
|
return;
|
3416
3646
|
}
|
@@ -3447,33 +3677,39 @@ const generateFunc = (scope, decl) => {
|
|
3447
3677
|
const name = decl.id ? decl.id.name : `anonymous_${randId()}`;
|
3448
3678
|
const params = decl.params ?? [];
|
3449
3679
|
|
3450
|
-
// const innerScope = { ...scope };
|
3451
3680
|
// TODO: share scope/locals between !!!
|
3452
|
-
const
|
3681
|
+
const func = {
|
3453
3682
|
locals: {},
|
3454
3683
|
localInd: 0,
|
3455
3684
|
// value, type
|
3456
3685
|
returns: [ valtypeBinary, Valtype.i32 ],
|
3457
3686
|
throws: false,
|
3458
|
-
name
|
3687
|
+
name,
|
3688
|
+
index: currentFuncIndex++
|
3459
3689
|
};
|
3460
3690
|
|
3461
3691
|
if (typedInput && decl.returnType) {
|
3462
3692
|
const { type } = extractTypeAnnotation(decl.returnType);
|
3463
|
-
if (type != null && !Prefs.indirectCalls) {
|
3464
|
-
|
3465
|
-
|
3693
|
+
// if (type != null && !Prefs.indirectCalls) {
|
3694
|
+
if (type != null) {
|
3695
|
+
func.returnType = type;
|
3696
|
+
func.returns = [ valtypeBinary ];
|
3466
3697
|
}
|
3467
3698
|
}
|
3468
3699
|
|
3469
3700
|
for (let i = 0; i < params.length; i++) {
|
3470
|
-
|
3701
|
+
const name = params[i].name;
|
3702
|
+
// if (name == null) return todo('non-identifier args are not supported');
|
3703
|
+
|
3704
|
+
allocVar(func, name, false);
|
3471
3705
|
|
3472
3706
|
if (typedInput && params[i].typeAnnotation) {
|
3473
|
-
addVarMetadata(
|
3707
|
+
addVarMetadata(func, name, false, extractTypeAnnotation(params[i]));
|
3474
3708
|
}
|
3475
3709
|
}
|
3476
3710
|
|
3711
|
+
func.params = Object.values(func.locals).map(x => x.type);
|
3712
|
+
|
3477
3713
|
let body = objectHack(decl.body);
|
3478
3714
|
if (decl.type === 'ArrowFunctionExpression' && decl.expression) {
|
3479
3715
|
// hack: () => 0 -> () => return 0
|
@@ -3483,37 +3719,23 @@ const generateFunc = (scope, decl) => {
|
|
3483
3719
|
};
|
3484
3720
|
}
|
3485
3721
|
|
3486
|
-
const wasm = generate(innerScope, body);
|
3487
|
-
const func = {
|
3488
|
-
name,
|
3489
|
-
params: Object.values(innerScope.locals).slice(0, params.length * 2).map(x => x.type),
|
3490
|
-
index: currentFuncIndex++,
|
3491
|
-
...innerScope
|
3492
|
-
};
|
3493
3722
|
funcIndex[name] = func.index;
|
3723
|
+
funcs.push(func);
|
3494
3724
|
|
3495
|
-
|
3725
|
+
const wasm = generate(func, body);
|
3726
|
+
func.wasm = wasm;
|
3496
3727
|
|
3497
|
-
|
3498
|
-
for (const inst of wasm) {
|
3499
|
-
if (inst[0] === Opcodes.call && inst[1] === -1) {
|
3500
|
-
inst[1] = func.index;
|
3501
|
-
}
|
3502
|
-
}
|
3728
|
+
if (name === 'main') func.gotLastType = true;
|
3503
3729
|
|
3504
3730
|
// add end return if not found
|
3505
3731
|
if (name !== 'main' && wasm[wasm.length - 1]?.[0] !== Opcodes.return && countLeftover(wasm) === 0) {
|
3506
3732
|
wasm.push(
|
3507
3733
|
...number(0),
|
3508
|
-
...(
|
3734
|
+
...(func.returnType != null ? [] : number(TYPES.undefined, Valtype.i32)),
|
3509
3735
|
[ Opcodes.return ]
|
3510
3736
|
);
|
3511
3737
|
}
|
3512
3738
|
|
3513
|
-
func.wasm = wasm;
|
3514
|
-
|
3515
|
-
funcs.push(func);
|
3516
|
-
|
3517
3739
|
return func;
|
3518
3740
|
};
|
3519
3741
|
|
@@ -3617,7 +3839,7 @@ const internalConstrs = {
|
|
3617
3839
|
generate: (scope, decl) => {
|
3618
3840
|
// todo: boolean object when used as constructor
|
3619
3841
|
const arg = decl.arguments[0] ?? DEFAULT_VALUE;
|
3620
|
-
return truthy(scope, generate(scope, arg), getNodeType(scope, arg));
|
3842
|
+
return truthy(scope, generate(scope, arg), getNodeType(scope, arg), false, false, 'full');
|
3621
3843
|
},
|
3622
3844
|
type: TYPES.boolean,
|
3623
3845
|
length: 1
|
@@ -3762,9 +3984,8 @@ export default program => {
|
|
3762
3984
|
|
3763
3985
|
if (Prefs.astLog) console.log(JSON.stringify(program.body.body, null, 2));
|
3764
3986
|
|
3765
|
-
generateFunc(scope, program);
|
3987
|
+
const main = generateFunc(scope, program);
|
3766
3988
|
|
3767
|
-
const main = funcs[funcs.length - 1];
|
3768
3989
|
main.export = true;
|
3769
3990
|
main.returns = [ valtypeBinary, Valtype.i32 ];
|
3770
3991
|
|
@@ -3791,7 +4012,7 @@ export default program => {
|
|
3791
4012
|
}
|
3792
4013
|
|
3793
4014
|
// if blank main func and other exports, remove it
|
3794
|
-
if (main.wasm.length === 0 && funcs.reduce((acc, x) => acc + (x.export ? 1 : 0), 0) > 1) funcs.splice(
|
4015
|
+
if (main.wasm.length === 0 && funcs.reduce((acc, x) => acc + (x.export ? 1 : 0), 0) > 1) funcs.splice(main.index - importedFuncs.length, 1);
|
3795
4016
|
|
3796
4017
|
return { funcs, globals, tags, exceptions, pages, data };
|
3797
4018
|
};
|