porffor 0.14.0-0d97d1e6a → 0.14.0-1169185ff
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/README.md +9 -13
- package/asur/index.js +1 -1
- package/compiler/2c.js +68 -3
- 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 +19 -7
- package/compiler/builtins/string.ts +1 -0
- package/compiler/builtins/symbol.ts +3 -2
- package/compiler/builtins.js +22 -6
- package/compiler/codegen.js +534 -255
- package/compiler/decompile.js +4 -0
- package/compiler/generated_builtins.js +501 -61
- package/compiler/index.js +2 -1
- package/compiler/parse.js +1 -1
- 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 +5 -4
- 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
|
|
@@ -675,38 +668,50 @@ const compareStrings = (scope, left, right, bytestrings = false) => {
|
|
675
668
|
];
|
676
669
|
};
|
677
670
|
|
678
|
-
const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
679
|
-
if (
|
671
|
+
const truthy = (scope, wasm, type, intIn = false, intOut = false, forceTruthyMode = undefined) => {
|
672
|
+
if (isIntToFloatOp(wasm[wasm.length - 1])) return [
|
680
673
|
...wasm,
|
681
674
|
...(!intIn && intOut ? [ Opcodes.i32_to_u ] : [])
|
682
675
|
];
|
683
676
|
// if (isIntOp(wasm[wasm.length - 1])) return [ ...wasm ];
|
684
677
|
|
678
|
+
// todo/perf: use knownType and custom bytecode here instead of typeSwitch
|
679
|
+
|
685
680
|
const useTmp = knownType(scope, type) == null;
|
686
681
|
const tmp = useTmp && localTmp(scope, `#logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
|
687
682
|
|
688
|
-
const def =
|
689
|
-
|
690
|
-
|
683
|
+
const def = (truthyMode => {
|
684
|
+
if (truthyMode === 'full') return [
|
685
|
+
// if value != 0 or NaN
|
686
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
687
|
+
...(intIn ? [ ] : [ Opcodes.i32_to ]),
|
691
688
|
|
692
|
-
|
693
|
-
|
689
|
+
[ Opcodes.i32_eqz ],
|
690
|
+
[ Opcodes.i32_eqz ],
|
694
691
|
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
692
|
+
...(intOut ? [] : [ Opcodes.i32_from ]),
|
693
|
+
];
|
694
|
+
|
695
|
+
if (truthyMode === 'no_negative') return [
|
696
|
+
// if value != 0 or NaN, non-binary output. negative numbers not truthy :/
|
697
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
698
|
+
...(intIn ? [] : [ Opcodes.i32_to ]),
|
699
|
+
...(intOut ? [] : [ Opcodes.i32_from ])
|
700
|
+
];
|
701
|
+
|
702
|
+
if (truthyMode === 'no_nan_negative') return [
|
703
|
+
// simpler and faster but makes NaN truthy and negative numbers not truthy,
|
704
|
+
// plus non-binary output
|
705
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
706
|
+
...(!intOut || (intIn && intOut) ? [] : [ Opcodes.i32_to_u ])
|
707
|
+
];
|
708
|
+
})(forceTruthyMode ?? Prefs.truthy ?? 'full');
|
699
709
|
|
700
710
|
return [
|
701
711
|
...wasm,
|
702
712
|
...(!useTmp ? [] : [ [ Opcodes.local_set, tmp ] ]),
|
703
713
|
|
704
714
|
...typeSwitch(scope, type, {
|
705
|
-
// [TYPES.number]: def,
|
706
|
-
[TYPES.array]: [
|
707
|
-
// arrays are always truthy
|
708
|
-
...number(1, intOut ? Valtype.i32 : valtypeBinary)
|
709
|
-
],
|
710
715
|
[TYPES.string]: [
|
711
716
|
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
712
717
|
...(intIn ? [] : [ Opcodes.i32_to_u ]),
|
@@ -742,10 +747,6 @@ const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
742
747
|
...(!useTmp ? [] : [ [ Opcodes.local_set, tmp ] ]),
|
743
748
|
|
744
749
|
...typeSwitch(scope, type, {
|
745
|
-
[TYPES.array]: [
|
746
|
-
// arrays are always truthy
|
747
|
-
...number(0, intOut ? Valtype.i32 : valtypeBinary)
|
748
|
-
],
|
749
750
|
[TYPES.string]: [
|
750
751
|
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
751
752
|
...(intIn ? [] : [ Opcodes.i32_to_u ]),
|
@@ -989,7 +990,7 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
989
990
|
// if both are true
|
990
991
|
[ Opcodes.i32_and ],
|
991
992
|
[ Opcodes.if, Blocktype.void ],
|
992
|
-
...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ]),
|
993
|
+
...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ], false),
|
993
994
|
...(op === '!==' || op === '!=' ? [ [ Opcodes.i32_eqz ] ] : []),
|
994
995
|
[ Opcodes.br, 1 ],
|
995
996
|
[ Opcodes.end ],
|
@@ -1038,14 +1039,14 @@ const generateBinaryExp = (scope, decl, _global, _name) => {
|
|
1038
1039
|
return out;
|
1039
1040
|
};
|
1040
1041
|
|
1041
|
-
const asmFuncToAsm = (func,
|
1042
|
-
return func(
|
1042
|
+
const asmFuncToAsm = (func, scope) => {
|
1043
|
+
return func(scope, {
|
1043
1044
|
TYPES, TYPE_NAMES, typeSwitch, makeArray, makeString, allocPage, internalThrow,
|
1044
|
-
builtin:
|
1045
|
-
let idx = funcIndex[
|
1046
|
-
if (idx
|
1047
|
-
includeBuiltin(null,
|
1048
|
-
idx = funcIndex[
|
1045
|
+
builtin: n => {
|
1046
|
+
let idx = funcIndex[n] ?? importedFuncs[n];
|
1047
|
+
if (idx == null && builtinFuncs[n]) {
|
1048
|
+
includeBuiltin(null, n);
|
1049
|
+
idx = funcIndex[n];
|
1049
1050
|
}
|
1050
1051
|
|
1051
1052
|
return idx;
|
@@ -1053,7 +1054,7 @@ const asmFuncToAsm = (func, { name = '#unknown_asm_func', params = [], locals =
|
|
1053
1054
|
});
|
1054
1055
|
};
|
1055
1056
|
|
1056
|
-
const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits, returns, returnType, localNames = [], globalNames = [], data: _data = [] }) => {
|
1057
|
+
const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits, returns, returnType, localNames = [], globalNames = [], data: _data = [], table = false }) => {
|
1057
1058
|
const existing = funcs.find(x => x.name === name);
|
1058
1059
|
if (existing) return existing;
|
1059
1060
|
|
@@ -1071,7 +1072,22 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
|
|
1071
1072
|
data.push(copy);
|
1072
1073
|
}
|
1073
1074
|
|
1074
|
-
|
1075
|
+
const func = {
|
1076
|
+
name,
|
1077
|
+
params,
|
1078
|
+
locals,
|
1079
|
+
localInd: allLocals.length,
|
1080
|
+
returns,
|
1081
|
+
returnType: returnType ?? TYPES.number,
|
1082
|
+
internal: true,
|
1083
|
+
index: currentFuncIndex++,
|
1084
|
+
table
|
1085
|
+
};
|
1086
|
+
|
1087
|
+
funcs.push(func);
|
1088
|
+
funcIndex[name] = func.index;
|
1089
|
+
|
1090
|
+
if (typeof wasm === 'function') wasm = asmFuncToAsm(wasm, func);
|
1075
1091
|
|
1076
1092
|
let baseGlobalIdx, i = 0;
|
1077
1093
|
for (const type of globalTypes) {
|
@@ -1090,19 +1106,14 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
|
|
1090
1106
|
}
|
1091
1107
|
}
|
1092
1108
|
|
1093
|
-
const
|
1094
|
-
|
1095
|
-
|
1096
|
-
|
1097
|
-
|
1098
|
-
|
1099
|
-
wasm,
|
1100
|
-
internal: true,
|
1101
|
-
index: currentFuncIndex++
|
1102
|
-
};
|
1109
|
+
if (table) for (const inst of wasm) {
|
1110
|
+
if (inst[0] === Opcodes.i32_load16_u && inst.at(-1) === 'read_argc') {
|
1111
|
+
inst.splice(2, 99);
|
1112
|
+
inst.push(...unsignedLEB128(allocPage({}, 'func argc lut') * pageSize));
|
1113
|
+
}
|
1114
|
+
}
|
1103
1115
|
|
1104
|
-
|
1105
|
-
funcIndex[name] = func.index;
|
1116
|
+
func.wasm = wasm;
|
1106
1117
|
|
1107
1118
|
return func;
|
1108
1119
|
};
|
@@ -1194,9 +1205,10 @@ const getLastType = scope => {
|
|
1194
1205
|
return [ [ Opcodes.local_get, localTmp(scope, '#last_type', Valtype.i32) ] ];
|
1195
1206
|
};
|
1196
1207
|
|
1197
|
-
const setLastType = scope =>
|
1198
|
-
|
1199
|
-
|
1208
|
+
const setLastType = (scope, type = []) => [
|
1209
|
+
...(typeof type === 'number' ? number(type, Valtype.i32) : type),
|
1210
|
+
[ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ]
|
1211
|
+
];
|
1200
1212
|
|
1201
1213
|
const getNodeType = (scope, node) => {
|
1202
1214
|
const ret = (() => {
|
@@ -1257,7 +1269,17 @@ const getNodeType = (scope, node) => {
|
|
1257
1269
|
|
1258
1270
|
const func = spl[spl.length - 1];
|
1259
1271
|
const protoFuncs = Object.keys(prototypeFuncs).filter(x => x != TYPES.bytestring && prototypeFuncs[x][func] != null);
|
1260
|
-
if (protoFuncs.length === 1)
|
1272
|
+
if (protoFuncs.length === 1) {
|
1273
|
+
if (protoFuncs[0].returnType) return protoFuncs[0].returnType;
|
1274
|
+
}
|
1275
|
+
|
1276
|
+
if (protoFuncs.length > 0) {
|
1277
|
+
if (scope.locals['#last_type']) return getLastType(scope);
|
1278
|
+
|
1279
|
+
// presume
|
1280
|
+
// todo: warn here?
|
1281
|
+
return TYPES.number;
|
1282
|
+
}
|
1261
1283
|
}
|
1262
1284
|
|
1263
1285
|
if (name.startsWith('__Porffor_wasm_')) {
|
@@ -1352,22 +1374,27 @@ const getNodeType = (scope, node) => {
|
|
1352
1374
|
}
|
1353
1375
|
|
1354
1376
|
if (node.type === 'MemberExpression') {
|
1355
|
-
|
1356
|
-
|
1357
|
-
|
1358
|
-
|
1359
|
-
|
1360
|
-
return TYPES.undefined;
|
1361
|
-
}
|
1377
|
+
const name = node.property.name;
|
1378
|
+
|
1379
|
+
if (name === 'length') {
|
1380
|
+
if (hasFuncWithName(node.object.name)) return TYPES.number;
|
1381
|
+
if (Prefs.fastLength) return TYPES.number;
|
1362
1382
|
}
|
1363
1383
|
|
1364
|
-
// hack: if something.length, number type
|
1365
|
-
if (node.property.name === 'length') return TYPES.number;
|
1366
1384
|
|
1367
|
-
|
1368
|
-
if (
|
1369
|
-
|
1370
|
-
|
1385
|
+
const objectKnownType = knownType(scope, getNodeType(scope, node.object));
|
1386
|
+
if (objectKnownType != null) {
|
1387
|
+
if (name === 'length') {
|
1388
|
+
if ([ TYPES.string, TYPES.bytestring, TYPES.array ].includes(objectKnownType)) return TYPES.number;
|
1389
|
+
else return TYPES.undefined;
|
1390
|
+
}
|
1391
|
+
|
1392
|
+
if (node.computed) {
|
1393
|
+
if (objectKnownType === TYPES.string) return TYPES.string;
|
1394
|
+
if (objectKnownType === TYPES.bytestring) return TYPES.bytestring;
|
1395
|
+
if (objectKnownType === TYPES.array) return TYPES.number;
|
1396
|
+
}
|
1397
|
+
}
|
1371
1398
|
|
1372
1399
|
if (scope.locals['#last_type']) return getLastType(scope);
|
1373
1400
|
|
@@ -1438,16 +1465,11 @@ const countLeftover = wasm => {
|
|
1438
1465
|
else if (inst[0] === Opcodes.return) count = 0;
|
1439
1466
|
else if (inst[0] === Opcodes.call) {
|
1440
1467
|
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;
|
1468
|
+
if (inst[1] < importedFuncs.length) {
|
1469
|
+
func = importedFuncs[inst[1]];
|
1470
|
+
count = count - func.params + func.returns;
|
1446
1471
|
} else {
|
1447
|
-
|
1448
|
-
count -= func.params.length;
|
1449
|
-
} else count--;
|
1450
|
-
if (func) count += func.returns.length;
|
1472
|
+
count = count - func.params.length + func.returns.length;
|
1451
1473
|
}
|
1452
1474
|
} else if (inst[0] === Opcodes.call_indirect) {
|
1453
1475
|
count--; // funcidx
|
@@ -1470,7 +1492,7 @@ const disposeLeftover = wasm => {
|
|
1470
1492
|
const generateExp = (scope, decl) => {
|
1471
1493
|
const expression = decl.expression;
|
1472
1494
|
|
1473
|
-
const out = generate(scope, expression, undefined, undefined,
|
1495
|
+
const out = generate(scope, expression, undefined, undefined, Prefs.optUnused);
|
1474
1496
|
disposeLeftover(out);
|
1475
1497
|
|
1476
1498
|
return out;
|
@@ -1561,16 +1583,10 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1561
1583
|
out.splice(out.length - 1, 1);
|
1562
1584
|
|
1563
1585
|
const finalStatement = parsed.body[parsed.body.length - 1];
|
1564
|
-
out.push(
|
1565
|
-
...getNodeType(scope, finalStatement),
|
1566
|
-
...setLastType(scope)
|
1567
|
-
);
|
1586
|
+
out.push(...setLastType(scope, getNodeType(scope, finalStatement)));
|
1568
1587
|
} else if (countLeftover(out) === 0) {
|
1569
1588
|
out.push(...number(UNDEFINED));
|
1570
|
-
out.push(
|
1571
|
-
...number(TYPES.undefined, Valtype.i32),
|
1572
|
-
...setLastType(scope)
|
1573
|
-
);
|
1589
|
+
out.push(...setLastType(scope, TYPES.undefined));
|
1574
1590
|
}
|
1575
1591
|
|
1576
1592
|
// if (lastInst && lastInst[0] === Opcodes.drop) {
|
@@ -1606,6 +1622,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1606
1622
|
|
1607
1623
|
if (!funcIndex[rhemynName]) {
|
1608
1624
|
const func = Rhemyn[funcName](regex, currentFuncIndex++, rhemynName);
|
1625
|
+
func.internal = true;
|
1609
1626
|
|
1610
1627
|
funcIndex[func.name] = func.index;
|
1611
1628
|
funcs.push(func);
|
@@ -1622,8 +1639,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1622
1639
|
[ Opcodes.call, idx ],
|
1623
1640
|
Opcodes.i32_from_u,
|
1624
1641
|
|
1625
|
-
...
|
1626
|
-
...setLastType(scope)
|
1642
|
+
...setLastType(scope, TYPES.boolean)
|
1627
1643
|
];
|
1628
1644
|
}
|
1629
1645
|
|
@@ -1695,9 +1711,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1695
1711
|
if (protoFunc.noArgRetLength && decl.arguments.length === 0) {
|
1696
1712
|
protoBC[x] = [
|
1697
1713
|
...RTArrayUtil.getLength(getPointer),
|
1698
|
-
|
1699
|
-
...number(TYPES.number, Valtype.i32),
|
1700
|
-
...setLastType(scope)
|
1714
|
+
...setLastType(scope, TYPES.number)
|
1701
1715
|
];
|
1702
1716
|
continue;
|
1703
1717
|
}
|
@@ -1716,7 +1730,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1716
1730
|
getI32: () => RTArrayUtil.getLengthI32(getPointer),
|
1717
1731
|
set: value => RTArrayUtil.setLength(getPointer, value),
|
1718
1732
|
setI32: value => RTArrayUtil.setLengthI32(getPointer, value)
|
1719
|
-
}, generate(scope, decl.arguments[0] ?? DEFAULT_VALUE), protoLocal, protoLocal2, (length, itemType) => {
|
1733
|
+
}, generate(scope, decl.arguments[0] ?? DEFAULT_VALUE), getNodeType(scope, decl.arguments[0] ?? DEFAULT_VALUE), protoLocal, protoLocal2, (length, itemType) => {
|
1720
1734
|
return makeArray(scope, {
|
1721
1735
|
rawElements: new Array(length)
|
1722
1736
|
}, _global, _name, true, itemType);
|
@@ -1730,9 +1744,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1730
1744
|
protoBC[x] = [
|
1731
1745
|
[ Opcodes.block, unusedValue && optUnused ? Blocktype.void : valtypeBinary ],
|
1732
1746
|
...protoOut,
|
1733
|
-
|
1734
|
-
...number(protoFunc.returnType ?? TYPES.number, Valtype.i32),
|
1735
|
-
...setLastType(scope),
|
1747
|
+
...(unusedValue && optUnused ? [] : (protoFunc.returnType != null ? setLastType(scope, protoFunc.returnType) : setLastType(scope))),
|
1736
1748
|
[ Opcodes.end ]
|
1737
1749
|
];
|
1738
1750
|
}
|
@@ -1782,11 +1794,6 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1782
1794
|
|
1783
1795
|
if (idx === undefined && internalConstrs[name]) return internalConstrs[name].generate(scope, decl, _global, _name);
|
1784
1796
|
|
1785
|
-
if (idx === undefined && name === scope.name) {
|
1786
|
-
// hack: calling self, func generator will fix later
|
1787
|
-
idx = -1;
|
1788
|
-
}
|
1789
|
-
|
1790
1797
|
if (idx === undefined && name.startsWith('__Porffor_wasm_')) {
|
1791
1798
|
const wasmOps = {
|
1792
1799
|
// pointer, align, offset
|
@@ -1838,36 +1845,128 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1838
1845
|
const [ local, global ] = lookupName(scope, name);
|
1839
1846
|
if (!Prefs.indirectCalls || local == null) return internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, true);
|
1840
1847
|
|
1841
|
-
// todo: only works when
|
1842
|
-
|
1843
|
-
|
1848
|
+
// todo: only works when function uses typedParams and typedReturns
|
1849
|
+
|
1850
|
+
const indirectMode = Prefs.indirectCallMode ?? 'vararg';
|
1851
|
+
// options: vararg, strict
|
1852
|
+
// - strict: simpler, smaller size usage, no func argc lut needed.
|
1853
|
+
// ONLY works when arg count of call == arg count of function being called
|
1854
|
+
// - vararg: large size usage, cursed.
|
1855
|
+
// works when arg count of call != arg count of function being called*
|
1856
|
+
// * most of the time, some edgecases
|
1844
1857
|
|
1845
1858
|
funcs.table = true;
|
1859
|
+
scope.table = true;
|
1846
1860
|
|
1847
1861
|
let args = decl.arguments;
|
1848
|
-
let
|
1862
|
+
let out = [];
|
1863
|
+
|
1864
|
+
let locals = [];
|
1865
|
+
|
1866
|
+
if (indirectMode === 'vararg') {
|
1867
|
+
const minArgc = Prefs.indirectCallMinArgc ?? 3;
|
1868
|
+
|
1869
|
+
if (args.length < minArgc) {
|
1870
|
+
args = args.concat(new Array(minArgc - args.length).fill(DEFAULT_VALUE));
|
1871
|
+
}
|
1872
|
+
}
|
1849
1873
|
|
1850
1874
|
for (let i = 0; i < args.length; i++) {
|
1851
1875
|
const arg = args[i];
|
1852
|
-
|
1876
|
+
out = out.concat(generate(scope, arg));
|
1853
1877
|
|
1854
1878
|
if (valtypeBinary !== Valtype.i32 && (
|
1855
1879
|
(builtinFuncs[name] && builtinFuncs[name].params[i * (typedParams ? 2 : 1)] === Valtype.i32) ||
|
1856
1880
|
(importedFuncs[name] && name.startsWith('profile'))
|
1857
1881
|
)) {
|
1858
|
-
|
1882
|
+
out.push(Opcodes.i32_to);
|
1859
1883
|
}
|
1860
1884
|
|
1861
|
-
|
1885
|
+
out = out.concat(getNodeType(scope, arg));
|
1886
|
+
|
1887
|
+
if (indirectMode === 'vararg') {
|
1888
|
+
const typeLocal = localTmp(scope, `#indirect_arg${i}_type`, Valtype.i32);
|
1889
|
+
const valLocal = localTmp(scope, `#indirect_arg${i}_val`);
|
1890
|
+
|
1891
|
+
locals.push([valLocal, typeLocal]);
|
1892
|
+
|
1893
|
+
out.push(
|
1894
|
+
[ Opcodes.local_set, typeLocal ],
|
1895
|
+
[ Opcodes.local_set, valLocal ]
|
1896
|
+
);
|
1897
|
+
}
|
1898
|
+
}
|
1899
|
+
|
1900
|
+
if (indirectMode === 'strict') {
|
1901
|
+
return typeSwitch(scope, getNodeType(scope, decl.callee), {
|
1902
|
+
[TYPES.function]: [
|
1903
|
+
...argWasm,
|
1904
|
+
[ global ? Opcodes.global_get : Opcodes.local_get, local.idx ],
|
1905
|
+
Opcodes.i32_to_u,
|
1906
|
+
[ Opcodes.call_indirect, args.length, 0 ],
|
1907
|
+
...setLastType(scope)
|
1908
|
+
],
|
1909
|
+
default: internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, true)
|
1910
|
+
});
|
1911
|
+
}
|
1912
|
+
|
1913
|
+
// hi, I will now explain how vararg mode works:
|
1914
|
+
// wasm's indirect_call instruction requires you know the func type at compile-time
|
1915
|
+
// since we have varargs (variable argument count), we do not know it.
|
1916
|
+
// we could just store args in memory and not use wasm func args,
|
1917
|
+
// but that is slow (probably) and breaks js exports.
|
1918
|
+
// instead, we generate every* possibility of argc and use different indirect_call
|
1919
|
+
// ops for each one, with type depending on argc for that branch.
|
1920
|
+
// then we load the argc for the wanted function from a memory lut,
|
1921
|
+
// and call the branch with the matching argc we require.
|
1922
|
+
// sorry, yes it is very cursed (and size inefficient), but indirect calls
|
1923
|
+
// are kind of rare anyway (mostly callbacks) so I am not concerned atm.
|
1924
|
+
// *for argc 0-3, in future (todo:) the max number should be
|
1925
|
+
// dynamically changed to the max argc of any func in the js file.
|
1926
|
+
|
1927
|
+
const funcLocal = localTmp(scope, `#indirect_func`, Valtype.i32);
|
1928
|
+
|
1929
|
+
const gen = argc => {
|
1930
|
+
const out = [];
|
1931
|
+
for (let i = 0; i < argc; i++) {
|
1932
|
+
out.push(
|
1933
|
+
[ Opcodes.local_get, locals[i][0] ],
|
1934
|
+
[ Opcodes.local_get, locals[i][1] ]
|
1935
|
+
);
|
1936
|
+
}
|
1937
|
+
|
1938
|
+
out.push(
|
1939
|
+
[ Opcodes.local_get, funcLocal ],
|
1940
|
+
[ Opcodes.call_indirect, argc, 0 ],
|
1941
|
+
...setLastType(scope)
|
1942
|
+
)
|
1943
|
+
|
1944
|
+
return out;
|
1945
|
+
};
|
1946
|
+
|
1947
|
+
const tableBc = {};
|
1948
|
+
for (let i = 0; i <= args.length; i++) {
|
1949
|
+
tableBc[i] = gen(i);
|
1862
1950
|
}
|
1863
1951
|
|
1952
|
+
// todo/perf: check if we should use br_table here or just generate our own big if..elses
|
1953
|
+
|
1864
1954
|
return typeSwitch(scope, getNodeType(scope, decl.callee), {
|
1865
1955
|
[TYPES.function]: [
|
1866
|
-
...
|
1956
|
+
...out,
|
1957
|
+
|
1867
1958
|
[ global ? Opcodes.global_get : Opcodes.local_get, local.idx ],
|
1868
1959
|
Opcodes.i32_to_u,
|
1869
|
-
[ Opcodes.
|
1870
|
-
|
1960
|
+
[ Opcodes.local_set, funcLocal ],
|
1961
|
+
|
1962
|
+
...brTable([
|
1963
|
+
// get argc of func we are calling
|
1964
|
+
[ Opcodes.local_get, funcLocal ],
|
1965
|
+
...number(ValtypeSize.i16, Valtype.i32),
|
1966
|
+
[ Opcodes.i32_mul ],
|
1967
|
+
|
1968
|
+
[ Opcodes.i32_load16_u, 0, ...unsignedLEB128(allocPage(scope, 'func argc lut') * pageSize), 'read_argc' ]
|
1969
|
+
], tableBc, valtypeBinary)
|
1871
1970
|
],
|
1872
1971
|
default: internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, true)
|
1873
1972
|
});
|
@@ -1876,11 +1975,10 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1876
1975
|
return internalThrow(scope, 'ReferenceError', `${unhackName(name)} is not defined`, true);
|
1877
1976
|
}
|
1878
1977
|
|
1879
|
-
const func = funcs.find(x => x.index === idx);
|
1880
|
-
|
1881
|
-
const userFunc = (funcIndex[name] && !importedFuncs[name] && !builtinFuncs[name] && !internalConstrs[name]) || idx === -1;
|
1978
|
+
const func = funcs[idx - importedFuncs.length]; // idx === scope.index ? scope : funcs.find(x => x.index === idx);
|
1979
|
+
const userFunc = func && !func.internal;
|
1882
1980
|
const typedParams = userFunc || builtinFuncs[name]?.typedParams;
|
1883
|
-
const typedReturns = (func
|
1981
|
+
const typedReturns = (func && func.returnType == null) || builtinFuncs[name]?.typedReturns;
|
1884
1982
|
const paramCount = func && (typedParams ? func.params.length / 2 : func.params.length);
|
1885
1983
|
|
1886
1984
|
let args = decl.arguments;
|
@@ -1901,6 +1999,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1901
1999
|
const arg = args[i];
|
1902
2000
|
out = out.concat(generate(scope, arg));
|
1903
2001
|
|
2002
|
+
// todo: this should be used instead of the too many args thing above (by removing that)
|
1904
2003
|
if (i >= paramCount) {
|
1905
2004
|
// over param count of func, drop arg
|
1906
2005
|
out.push([ Opcodes.drop ]);
|
@@ -1985,8 +2084,11 @@ const knownType = (scope, type) => {
|
|
1985
2084
|
const idx = type[0][1];
|
1986
2085
|
|
1987
2086
|
// type idx = var idx + 1
|
1988
|
-
const
|
1989
|
-
if (
|
2087
|
+
const name = Object.values(scope.locals).find(x => x.idx === idx)?.name;
|
2088
|
+
if (name) {
|
2089
|
+
const local = scope.locals[name];
|
2090
|
+
if (local.metadata?.type != null) return v.metadata.type;
|
2091
|
+
}
|
1990
2092
|
}
|
1991
2093
|
|
1992
2094
|
return null;
|
@@ -2021,16 +2123,17 @@ const brTable = (input, bc, returns) => {
|
|
2021
2123
|
}
|
2022
2124
|
|
2023
2125
|
for (let i = 0; i < count; i++) {
|
2024
|
-
if (i === 0) out.push([ Opcodes.block, returns, 'br table start' ]);
|
2126
|
+
// if (i === 0) out.push([ Opcodes.block, returns, 'br table start' ]);
|
2127
|
+
if (i === 0) out.push([ Opcodes.block, returns ]);
|
2025
2128
|
else out.push([ Opcodes.block, Blocktype.void ]);
|
2026
2129
|
}
|
2027
2130
|
|
2028
|
-
const nums = keys.filter(x => +x);
|
2131
|
+
const nums = keys.filter(x => +x >= 0);
|
2029
2132
|
const offset = Math.min(...nums);
|
2030
2133
|
const max = Math.max(...nums);
|
2031
2134
|
|
2032
2135
|
const table = [];
|
2033
|
-
let br =
|
2136
|
+
let br = 0;
|
2034
2137
|
|
2035
2138
|
for (let i = offset; i <= max; i++) {
|
2036
2139
|
// if branch for this num, go to that block
|
@@ -2070,10 +2173,9 @@ const brTable = (input, bc, returns) => {
|
|
2070
2173
|
br--;
|
2071
2174
|
}
|
2072
2175
|
|
2073
|
-
|
2074
|
-
|
2075
|
-
|
2076
|
-
];
|
2176
|
+
out.push([ Opcodes.end ]);
|
2177
|
+
|
2178
|
+
return out;
|
2077
2179
|
};
|
2078
2180
|
|
2079
2181
|
const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
|
@@ -2117,6 +2219,17 @@ const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
|
|
2117
2219
|
return out;
|
2118
2220
|
};
|
2119
2221
|
|
2222
|
+
const typeIsOneOf = (type, types, valtype = Valtype.i32) => {
|
2223
|
+
const out = [];
|
2224
|
+
|
2225
|
+
for (let i = 0; i < types.length; i++) {
|
2226
|
+
out.push(...type, ...number(types[i], valtype), valtype === Valtype.f64 ? [ Opcodes.f64_eq ] : [ Opcodes.i32_eq ]);
|
2227
|
+
if (i !== 0) out.push([ Opcodes.i32_or ]);
|
2228
|
+
}
|
2229
|
+
|
2230
|
+
return out;
|
2231
|
+
};
|
2232
|
+
|
2120
2233
|
const allocVar = (scope, name, global = false, type = true) => {
|
2121
2234
|
const target = global ? globals : scope.locals;
|
2122
2235
|
|
@@ -2133,7 +2246,7 @@ const allocVar = (scope, name, global = false, type = true) => {
|
|
2133
2246
|
|
2134
2247
|
if (type) {
|
2135
2248
|
let typeIdx = global ? globalInd++ : scope.localInd++;
|
2136
|
-
target[name + '#type'] = { idx: typeIdx, type: Valtype.i32 };
|
2249
|
+
target[name + '#type'] = { idx: typeIdx, type: Valtype.i32, name };
|
2137
2250
|
}
|
2138
2251
|
|
2139
2252
|
return idx;
|
@@ -2346,18 +2459,21 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
|
|
2346
2459
|
Opcodes.i32_to_u,
|
2347
2460
|
|
2348
2461
|
// turn into byte offset by * valtypeSize (4 for i32, 8 for i64/f64)
|
2349
|
-
...number(ValtypeSize[valtype], Valtype.i32),
|
2462
|
+
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
2350
2463
|
[ Opcodes.i32_mul ],
|
2351
2464
|
...(aotPointer ? [] : [ [ Opcodes.i32_add ] ]),
|
2352
2465
|
...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
|
2353
2466
|
|
2354
2467
|
...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
|
2355
2468
|
[ Opcodes.local_get, pointerTmp ],
|
2356
|
-
[ Opcodes.load,
|
2357
|
-
], generate(scope, decl.right),
|
2469
|
+
[ Opcodes.load, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
|
2470
|
+
], generate(scope, decl.right), [
|
2471
|
+
[ Opcodes.local_get, pointerTmp ],
|
2472
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32 + ValtypeSize[valtype]) ]
|
2473
|
+
], getNodeType(scope, decl.right), false, name, true)),
|
2358
2474
|
[ Opcodes.local_tee, newValueTmp ],
|
2359
2475
|
|
2360
|
-
[ Opcodes.store,
|
2476
|
+
[ Opcodes.store, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
|
2361
2477
|
],
|
2362
2478
|
|
2363
2479
|
default: internalThrow(scope, 'TypeError', `Cannot assign member with non-array`)
|
@@ -2465,6 +2581,11 @@ const generateUnary = (scope, decl) => {
|
|
2465
2581
|
];
|
2466
2582
|
|
2467
2583
|
case '!':
|
2584
|
+
const arg = decl.argument;
|
2585
|
+
if (arg.type === "UnaryExpression" && arg.operator === "!") {
|
2586
|
+
// !!x -> is x truthy
|
2587
|
+
return truthy(scope, generate(scope, arg.argument), getNodeType(scope, arg.argument), false, false);
|
2588
|
+
}
|
2468
2589
|
// !=
|
2469
2590
|
return falsy(scope, generate(scope, decl.argument), getNodeType(scope, decl.argument), false, false);
|
2470
2591
|
|
@@ -2611,21 +2732,16 @@ const generateConditional = (scope, decl) => {
|
|
2611
2732
|
out.push([ Opcodes.if, valtypeBinary ]);
|
2612
2733
|
depth.push('if');
|
2613
2734
|
|
2614
|
-
out.push(...generate(scope, decl.consequent));
|
2615
|
-
|
2616
|
-
// note type
|
2617
2735
|
out.push(
|
2618
|
-
...
|
2619
|
-
...setLastType(scope)
|
2736
|
+
...generate(scope, decl.consequent),
|
2737
|
+
...setLastType(scope, getNodeType(scope, decl.consequent))
|
2620
2738
|
);
|
2621
2739
|
|
2622
2740
|
out.push([ Opcodes.else ]);
|
2623
|
-
out.push(...generate(scope, decl.alternate));
|
2624
2741
|
|
2625
|
-
// note type
|
2626
2742
|
out.push(
|
2627
|
-
...
|
2628
|
-
...setLastType(scope)
|
2743
|
+
...generate(scope, decl.alternate),
|
2744
|
+
...setLastType(scope, getNodeType(scope, decl.alternate))
|
2629
2745
|
);
|
2630
2746
|
|
2631
2747
|
out.push([ Opcodes.end ]);
|
@@ -2773,12 +2889,15 @@ const generateForOf = (scope, decl) => {
|
|
2773
2889
|
// todo: optimize away counter and use end pointer
|
2774
2890
|
out.push(...typeSwitch(scope, getNodeType(scope, decl.right), {
|
2775
2891
|
[TYPES.array]: [
|
2776
|
-
...setType(scope, leftName, TYPES.number),
|
2777
|
-
|
2778
2892
|
[ Opcodes.loop, Blocktype.void ],
|
2779
2893
|
|
2780
2894
|
[ Opcodes.local_get, pointer ],
|
2781
|
-
[ Opcodes.load,
|
2895
|
+
[ Opcodes.load, 0, ...unsignedLEB128(ValtypeSize.i32) ],
|
2896
|
+
|
2897
|
+
...setType(scope, leftName, [
|
2898
|
+
[ Opcodes.local_get, pointer ],
|
2899
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32 + ValtypeSize[valtype]) ],
|
2900
|
+
]),
|
2782
2901
|
|
2783
2902
|
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
|
2784
2903
|
|
@@ -2787,9 +2906,9 @@ const generateForOf = (scope, decl) => {
|
|
2787
2906
|
...generate(scope, decl.body),
|
2788
2907
|
[ Opcodes.end ],
|
2789
2908
|
|
2790
|
-
// increment iter pointer by valtype size
|
2909
|
+
// increment iter pointer by valtype size + 1
|
2791
2910
|
[ Opcodes.local_get, pointer ],
|
2792
|
-
...number(ValtypeSize[valtype], Valtype.i32),
|
2911
|
+
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
2793
2912
|
[ Opcodes.i32_add ],
|
2794
2913
|
[ Opcodes.local_set, pointer ],
|
2795
2914
|
|
@@ -2905,6 +3024,44 @@ const generateForOf = (scope, decl) => {
|
|
2905
3024
|
[ Opcodes.end ],
|
2906
3025
|
[ Opcodes.end ]
|
2907
3026
|
],
|
3027
|
+
[TYPES.set]: [
|
3028
|
+
[ Opcodes.loop, Blocktype.void ],
|
3029
|
+
|
3030
|
+
[ Opcodes.local_get, pointer ],
|
3031
|
+
[ Opcodes.load, 0, ...unsignedLEB128(ValtypeSize.i32) ],
|
3032
|
+
|
3033
|
+
...setType(scope, leftName, [
|
3034
|
+
[ Opcodes.local_get, pointer ],
|
3035
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32 + ValtypeSize[valtype]) ],
|
3036
|
+
]),
|
3037
|
+
|
3038
|
+
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
|
3039
|
+
|
3040
|
+
[ Opcodes.block, Blocktype.void ],
|
3041
|
+
[ Opcodes.block, Blocktype.void ],
|
3042
|
+
...generate(scope, decl.body),
|
3043
|
+
[ Opcodes.end ],
|
3044
|
+
|
3045
|
+
// increment iter pointer by valtype size + 1
|
3046
|
+
[ Opcodes.local_get, pointer ],
|
3047
|
+
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
3048
|
+
[ Opcodes.i32_add ],
|
3049
|
+
[ Opcodes.local_set, pointer ],
|
3050
|
+
|
3051
|
+
// increment counter by 1
|
3052
|
+
[ Opcodes.local_get, counter ],
|
3053
|
+
...number(1, Valtype.i32),
|
3054
|
+
[ Opcodes.i32_add ],
|
3055
|
+
[ Opcodes.local_tee, counter ],
|
3056
|
+
|
3057
|
+
// loop if counter != length
|
3058
|
+
[ Opcodes.local_get, length ],
|
3059
|
+
[ Opcodes.i32_ne ],
|
3060
|
+
[ Opcodes.br_if, 1 ],
|
3061
|
+
|
3062
|
+
[ Opcodes.end ],
|
3063
|
+
[ Opcodes.end ]
|
3064
|
+
],
|
2908
3065
|
default: internalThrow(scope, 'TypeError', `Tried for..of on non-iterable type`)
|
2909
3066
|
}, Blocktype.void));
|
2910
3067
|
|
@@ -3006,14 +3163,18 @@ const generateThrow = (scope, decl) => {
|
|
3006
3163
|
};
|
3007
3164
|
|
3008
3165
|
const generateTry = (scope, decl) => {
|
3009
|
-
|
3166
|
+
// todo: handle control-flow pre-exit for finally
|
3167
|
+
// "Immediately before a control-flow statement (return, throw, break, continue) is executed in the try block or catch block."
|
3010
3168
|
|
3011
3169
|
const out = [];
|
3012
3170
|
|
3171
|
+
const finalizer = decl.finalizer ? generate(scope, decl.finalizer) : [];
|
3172
|
+
|
3013
3173
|
out.push([ Opcodes.try, Blocktype.void ]);
|
3014
3174
|
depth.push('try');
|
3015
3175
|
|
3016
3176
|
out.push(...generate(scope, decl.block));
|
3177
|
+
out.push(...finalizer);
|
3017
3178
|
|
3018
3179
|
if (decl.handler) {
|
3019
3180
|
depth.pop();
|
@@ -3021,6 +3182,7 @@ const generateTry = (scope, decl) => {
|
|
3021
3182
|
|
3022
3183
|
out.push([ Opcodes.catch_all ]);
|
3023
3184
|
out.push(...generate(scope, decl.handler.body));
|
3185
|
+
out.push(...finalizer);
|
3024
3186
|
}
|
3025
3187
|
|
3026
3188
|
out.push([ Opcodes.end ]);
|
@@ -3106,7 +3268,7 @@ const getAllocType = itemType => {
|
|
3106
3268
|
}
|
3107
3269
|
};
|
3108
3270
|
|
3109
|
-
const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false, itemType = valtype) => {
|
3271
|
+
const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false, itemType = valtype, typed = false) => {
|
3110
3272
|
const out = [];
|
3111
3273
|
|
3112
3274
|
scope.arrays ??= new Map();
|
@@ -3118,8 +3280,13 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
3118
3280
|
// todo: can we just have 1 undeclared array? probably not? but this is not really memory efficient
|
3119
3281
|
const uniqueName = name === '$undeclared' ? name + Math.random().toString().slice(2) : name;
|
3120
3282
|
|
3121
|
-
|
3122
|
-
|
3283
|
+
let page;
|
3284
|
+
if (Prefs.scopedPageNames) page = allocPage(scope, `${getAllocType(itemType)}: ${scope.name}/${uniqueName}`, itemType);
|
3285
|
+
else page = allocPage(scope, `${getAllocType(itemType)}: ${uniqueName}`, itemType);
|
3286
|
+
|
3287
|
+
// hack: use 1 for page 0 pointer for fast truthiness
|
3288
|
+
const ptr = page === 0 ? 1 : (page * pageSize);
|
3289
|
+
scope.arrays.set(name, ptr);
|
3123
3290
|
}
|
3124
3291
|
|
3125
3292
|
const pointer = scope.arrays.get(name);
|
@@ -3169,7 +3336,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
3169
3336
|
|
3170
3337
|
const pointerWasm = pointerTmp != null ? [ [ Opcodes.local_get, pointerTmp ] ] : number(pointer, Valtype.i32);
|
3171
3338
|
|
3172
|
-
// store length
|
3339
|
+
// store length
|
3173
3340
|
out.push(
|
3174
3341
|
...pointerWasm,
|
3175
3342
|
...number(length, Valtype.i32),
|
@@ -3177,14 +3344,20 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
3177
3344
|
);
|
3178
3345
|
|
3179
3346
|
const storeOp = StoreOps[itemType];
|
3180
|
-
|
3347
|
+
const sizePerEl = ValtypeSize[itemType] + (typed ? 1 : 0);
|
3181
3348
|
if (!initEmpty) for (let i = 0; i < length; i++) {
|
3182
3349
|
if (elements[i] == null) continue;
|
3183
3350
|
|
3351
|
+
const offset = ValtypeSize.i32 + i * sizePerEl;
|
3184
3352
|
out.push(
|
3185
3353
|
...pointerWasm,
|
3186
3354
|
...(useRawElements ? number(elements[i], Valtype[valtype]) : generate(scope, elements[i])),
|
3187
|
-
[ storeOp,
|
3355
|
+
[ storeOp, 0, ...unsignedLEB128(offset) ],
|
3356
|
+
...(!typed ? [] : [ // typed presumes !useRawElements
|
3357
|
+
...pointerWasm,
|
3358
|
+
...getNodeType(scope, elements[i]),
|
3359
|
+
[ Opcodes.i32_store8, 0, ...unsignedLEB128(offset + ValtypeSize[itemType]) ]
|
3360
|
+
])
|
3188
3361
|
);
|
3189
3362
|
}
|
3190
3363
|
|
@@ -3194,6 +3367,65 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
3194
3367
|
return [ out, pointer ];
|
3195
3368
|
};
|
3196
3369
|
|
3370
|
+
const storeArray = (scope, array, index, element, aotPointer = null) => {
|
3371
|
+
if (!Array.isArray(element)) element = generate(scope, element);
|
3372
|
+
if (typeof index === 'number') index = number(index);
|
3373
|
+
|
3374
|
+
const offset = localTmp(scope, '#storeArray_offset', Valtype.i32);
|
3375
|
+
|
3376
|
+
return [
|
3377
|
+
// calculate offset
|
3378
|
+
...index,
|
3379
|
+
Opcodes.i32_to_u,
|
3380
|
+
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
3381
|
+
[ Opcodes.i32_mul ],
|
3382
|
+
...(aotPointer ? [] : [
|
3383
|
+
...array,
|
3384
|
+
Opcodes.i32_to_u,
|
3385
|
+
[ Opcodes.i32_add ],
|
3386
|
+
]),
|
3387
|
+
[ Opcodes.local_set, offset ],
|
3388
|
+
|
3389
|
+
// store value
|
3390
|
+
[ Opcodes.local_get, offset ],
|
3391
|
+
...generate(scope, element),
|
3392
|
+
[ Opcodes.store, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
3393
|
+
|
3394
|
+
// store type
|
3395
|
+
[ Opcodes.local_get, offset ],
|
3396
|
+
...getNodeType(scope, element),
|
3397
|
+
[ Opcodes.i32_store8, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32 + ValtypeSize[valtype]) ]
|
3398
|
+
];
|
3399
|
+
};
|
3400
|
+
|
3401
|
+
const loadArray = (scope, array, index, aotPointer = null) => {
|
3402
|
+
if (typeof index === 'number') index = number(index);
|
3403
|
+
|
3404
|
+
const offset = localTmp(scope, '#loadArray_offset', Valtype.i32);
|
3405
|
+
|
3406
|
+
return [
|
3407
|
+
// calculate offset
|
3408
|
+
...index,
|
3409
|
+
Opcodes.i32_to_u,
|
3410
|
+
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
3411
|
+
[ Opcodes.i32_mul ],
|
3412
|
+
...(aotPointer ? [] : [
|
3413
|
+
...array,
|
3414
|
+
Opcodes.i32_to_u,
|
3415
|
+
[ Opcodes.i32_add ],
|
3416
|
+
]),
|
3417
|
+
[ Opcodes.local_set, offset ],
|
3418
|
+
|
3419
|
+
// load value
|
3420
|
+
[ Opcodes.local_get, offset ],
|
3421
|
+
[ Opcodes.load, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
3422
|
+
|
3423
|
+
// load type
|
3424
|
+
[ Opcodes.local_get, offset ],
|
3425
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32 + ValtypeSize[valtype]) ]
|
3426
|
+
];
|
3427
|
+
};
|
3428
|
+
|
3197
3429
|
const byteStringable = str => {
|
3198
3430
|
if (!Prefs.bytestring) return false;
|
3199
3431
|
|
@@ -3222,14 +3454,28 @@ const makeString = (scope, str, global = false, name = '$undeclared', forceBytes
|
|
3222
3454
|
};
|
3223
3455
|
|
3224
3456
|
const generateArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false) => {
|
3225
|
-
return makeArray(scope, decl, global, name, initEmpty, valtype)[0];
|
3457
|
+
return makeArray(scope, decl, global, name, initEmpty, valtype, true)[0];
|
3226
3458
|
};
|
3227
3459
|
|
3228
|
-
|
3460
|
+
const generateObject = (scope, decl, global = false, name = '$undeclared') => {
|
3461
|
+
if (decl.properties.length > 0) return todo(scope, 'objects are not supported yet', true);
|
3462
|
+
|
3463
|
+
return [
|
3464
|
+
...number(1),
|
3465
|
+
...setLastType(scope, TYPES.object)
|
3466
|
+
];
|
3467
|
+
};
|
3468
|
+
|
3469
|
+
const withType = (scope, wasm, type) => [
|
3470
|
+
...wasm,
|
3471
|
+
...setLastType(scope, type)
|
3472
|
+
];
|
3473
|
+
|
3474
|
+
const generateMember = (scope, decl, _global, _name) => {
|
3229
3475
|
const name = decl.object.name;
|
3230
3476
|
const pointer = scope.arrays?.get(name);
|
3231
3477
|
|
3232
|
-
const aotPointer = Prefs.aotPointerOpt && pointer
|
3478
|
+
const aotPointer = Prefs.aotPointerOpt && pointer;
|
3233
3479
|
|
3234
3480
|
// hack: .name
|
3235
3481
|
if (decl.property.name === 'name') {
|
@@ -3239,9 +3485,9 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3239
3485
|
// eg: __String_prototype_toLowerCase -> toLowerCase
|
3240
3486
|
if (nameProp.startsWith('__')) nameProp = nameProp.split('_').pop();
|
3241
3487
|
|
3242
|
-
return makeString(scope, nameProp, _global, _name, true);
|
3488
|
+
return withType(scope, makeString(scope, nameProp, _global, _name, true), TYPES.bytestring);
|
3243
3489
|
} else {
|
3244
|
-
return
|
3490
|
+
return withType(scope, number(0), TYPES.undefined);
|
3245
3491
|
}
|
3246
3492
|
}
|
3247
3493
|
|
@@ -3249,9 +3495,8 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3249
3495
|
if (decl.property.name === 'length') {
|
3250
3496
|
const func = funcs.find(x => x.name === name);
|
3251
3497
|
if (func) {
|
3252
|
-
const
|
3253
|
-
|
3254
|
-
return number(typedParams ? func.params.length / 2 : func.params.length);
|
3498
|
+
const typedParams = !func.internal || builtinFuncs[name]?.typedParams;
|
3499
|
+
return withType(scope, number(typedParams ? func.params.length / 2 : func.params.length), TYPES.number);
|
3255
3500
|
}
|
3256
3501
|
|
3257
3502
|
if (builtinFuncs[name + '$constructor']) {
|
@@ -3261,24 +3506,88 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3261
3506
|
const constructorFunc = builtinFuncs[name + '$constructor'];
|
3262
3507
|
const constructorParams = constructorFunc.typedParams ? (constructorFunc.params.length / 2) : constructorFunc.params.length;
|
3263
3508
|
|
3264
|
-
return number(Math.max(regularParams, constructorParams));
|
3509
|
+
return withType(scope, number(Math.max(regularParams, constructorParams)), TYPES.number);
|
3265
3510
|
}
|
3266
3511
|
|
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);
|
3512
|
+
if (builtinFuncs[name]) return withType(scope, number(builtinFuncs[name].typedParams ? (builtinFuncs[name].params.length / 2) : builtinFuncs[name].params.length), TYPES.number);
|
3513
|
+
if (importedFuncs[name]) return withType(scope, number(importedFuncs[name].params), TYPES.number);
|
3514
|
+
if (internalConstrs[name]) return withType(scope, number(internalConstrs[name].length ?? 0), TYPES.number);
|
3515
|
+
|
3516
|
+
if (Prefs.fastLength) {
|
3517
|
+
// presume valid length object
|
3518
|
+
return [
|
3519
|
+
...(aotPointer ? number(0, Valtype.i32) : [
|
3520
|
+
...generate(scope, decl.object),
|
3521
|
+
Opcodes.i32_to_u
|
3522
|
+
]),
|
3523
|
+
|
3524
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
|
3525
|
+
Opcodes.i32_from_u
|
3526
|
+
];
|
3527
|
+
}
|
3528
|
+
|
3529
|
+
const type = getNodeType(scope, decl.object);
|
3530
|
+
const known = knownType(scope, type);
|
3531
|
+
if (known != null) {
|
3532
|
+
if ([ TYPES.string, TYPES.bytestring, TYPES.array ].includes(known)) return [
|
3533
|
+
...(aotPointer ? number(0, Valtype.i32) : [
|
3534
|
+
...generate(scope, decl.object),
|
3535
|
+
Opcodes.i32_to_u
|
3536
|
+
]),
|
3537
|
+
|
3538
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
|
3539
|
+
Opcodes.i32_from_u
|
3540
|
+
];
|
3541
|
+
|
3542
|
+
return number(0);
|
3543
|
+
}
|
3270
3544
|
|
3271
3545
|
return [
|
3272
|
-
...(
|
3273
|
-
|
3274
|
-
|
3275
|
-
|
3546
|
+
...typeIsOneOf(getNodeType(scope, decl.object), [ TYPES.string, TYPES.bytestring, TYPES.array ]),
|
3547
|
+
[ Opcodes.if, valtypeBinary ],
|
3548
|
+
...(aotPointer ? number(0, Valtype.i32) : [
|
3549
|
+
...generate(scope, decl.object),
|
3550
|
+
Opcodes.i32_to_u
|
3551
|
+
]),
|
3552
|
+
|
3553
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
|
3554
|
+
Opcodes.i32_from_u,
|
3276
3555
|
|
3277
|
-
|
3278
|
-
Opcodes.
|
3556
|
+
...setLastType(scope, TYPES.number),
|
3557
|
+
[ Opcodes.else ],
|
3558
|
+
...number(0),
|
3559
|
+
...setLastType(scope, TYPES.undefined),
|
3560
|
+
[ Opcodes.end ]
|
3279
3561
|
];
|
3280
3562
|
}
|
3281
3563
|
|
3564
|
+
// todo: generate this array procedurally during builtinFuncs creation
|
3565
|
+
if (['size', 'description'].includes(decl.property.name)) {
|
3566
|
+
const bc = {};
|
3567
|
+
const cands = Object.keys(builtinFuncs).filter(x => x.startsWith('__') && x.endsWith('_prototype_' + decl.property.name + '$get'));
|
3568
|
+
|
3569
|
+
if (cands.length > 0) {
|
3570
|
+
for (const x of cands) {
|
3571
|
+
const type = TYPES[x.split('_prototype_')[0].slice(2).toLowerCase()];
|
3572
|
+
if (type == null) continue;
|
3573
|
+
|
3574
|
+
bc[type] = generateCall(scope, {
|
3575
|
+
callee: {
|
3576
|
+
type: 'Identifier',
|
3577
|
+
name: x
|
3578
|
+
},
|
3579
|
+
arguments: [ decl.object ],
|
3580
|
+
_protoInternalCall: true
|
3581
|
+
});
|
3582
|
+
}
|
3583
|
+
}
|
3584
|
+
|
3585
|
+
return typeSwitch(scope, getNodeType(scope, decl.object), {
|
3586
|
+
...bc,
|
3587
|
+
default: withType(scope, number(0), TYPES.undefined)
|
3588
|
+
}, valtypeBinary);
|
3589
|
+
}
|
3590
|
+
|
3282
3591
|
const object = generate(scope, decl.object);
|
3283
3592
|
const property = generate(scope, decl.property);
|
3284
3593
|
|
@@ -3293,24 +3602,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3293
3602
|
|
3294
3603
|
return typeSwitch(scope, getNodeType(scope, decl.object), {
|
3295
3604
|
[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),
|
3605
|
+
...loadArray(scope, object, property, aotPointer),
|
3314
3606
|
...setLastType(scope)
|
3315
3607
|
],
|
3316
3608
|
|
@@ -3341,9 +3633,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3341
3633
|
|
3342
3634
|
// return new string (page)
|
3343
3635
|
...number(newPointer),
|
3344
|
-
|
3345
|
-
...number(TYPES.string, Valtype.i32),
|
3346
|
-
...setLastType(scope)
|
3636
|
+
...setLastType(scope, TYPES.string)
|
3347
3637
|
],
|
3348
3638
|
[TYPES.bytestring]: [
|
3349
3639
|
// setup new/out array
|
@@ -3369,9 +3659,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3369
3659
|
|
3370
3660
|
// return new string (page)
|
3371
3661
|
...number(newPointer),
|
3372
|
-
|
3373
|
-
...number(TYPES.bytestring, Valtype.i32),
|
3374
|
-
...setLastType(scope)
|
3662
|
+
...setLastType(scope, TYPES.bytestring)
|
3375
3663
|
],
|
3376
3664
|
|
3377
3665
|
default: internalThrow(scope, 'TypeError', 'Member expression is not supported for non-string non-array yet', true)
|
@@ -3396,7 +3684,7 @@ const objectHack = node => {
|
|
3396
3684
|
if (!objectName) objectName = objectHack(node.object)?.name?.slice?.(2);
|
3397
3685
|
|
3398
3686
|
// if .name or .length, give up (hack within a hack!)
|
3399
|
-
if (['name', 'length'].includes(node.property.name)) {
|
3687
|
+
if (['name', 'length', 'size', 'description'].includes(node.property.name)) {
|
3400
3688
|
node.object = objectHack(node.object);
|
3401
3689
|
return;
|
3402
3690
|
}
|
@@ -3433,33 +3721,39 @@ const generateFunc = (scope, decl) => {
|
|
3433
3721
|
const name = decl.id ? decl.id.name : `anonymous_${randId()}`;
|
3434
3722
|
const params = decl.params ?? [];
|
3435
3723
|
|
3436
|
-
// const innerScope = { ...scope };
|
3437
3724
|
// TODO: share scope/locals between !!!
|
3438
|
-
const
|
3725
|
+
const func = {
|
3439
3726
|
locals: {},
|
3440
3727
|
localInd: 0,
|
3441
3728
|
// value, type
|
3442
3729
|
returns: [ valtypeBinary, Valtype.i32 ],
|
3443
3730
|
throws: false,
|
3444
|
-
name
|
3731
|
+
name,
|
3732
|
+
index: currentFuncIndex++
|
3445
3733
|
};
|
3446
3734
|
|
3447
3735
|
if (typedInput && decl.returnType) {
|
3448
3736
|
const { type } = extractTypeAnnotation(decl.returnType);
|
3449
|
-
if (type != null && !Prefs.indirectCalls) {
|
3450
|
-
|
3451
|
-
|
3737
|
+
// if (type != null && !Prefs.indirectCalls) {
|
3738
|
+
if (type != null) {
|
3739
|
+
func.returnType = type;
|
3740
|
+
func.returns = [ valtypeBinary ];
|
3452
3741
|
}
|
3453
3742
|
}
|
3454
3743
|
|
3455
3744
|
for (let i = 0; i < params.length; i++) {
|
3456
|
-
|
3745
|
+
const name = params[i].name;
|
3746
|
+
// if (name == null) return todo('non-identifier args are not supported');
|
3747
|
+
|
3748
|
+
allocVar(func, name, false);
|
3457
3749
|
|
3458
3750
|
if (typedInput && params[i].typeAnnotation) {
|
3459
|
-
addVarMetadata(
|
3751
|
+
addVarMetadata(func, name, false, extractTypeAnnotation(params[i]));
|
3460
3752
|
}
|
3461
3753
|
}
|
3462
3754
|
|
3755
|
+
func.params = Object.values(func.locals).map(x => x.type);
|
3756
|
+
|
3463
3757
|
let body = objectHack(decl.body);
|
3464
3758
|
if (decl.type === 'ArrowFunctionExpression' && decl.expression) {
|
3465
3759
|
// hack: () => 0 -> () => return 0
|
@@ -3469,37 +3763,23 @@ const generateFunc = (scope, decl) => {
|
|
3469
3763
|
};
|
3470
3764
|
}
|
3471
3765
|
|
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
3766
|
funcIndex[name] = func.index;
|
3767
|
+
funcs.push(func);
|
3480
3768
|
|
3481
|
-
|
3769
|
+
const wasm = generate(func, body);
|
3770
|
+
func.wasm = wasm;
|
3482
3771
|
|
3483
|
-
|
3484
|
-
for (const inst of wasm) {
|
3485
|
-
if (inst[0] === Opcodes.call && inst[1] === -1) {
|
3486
|
-
inst[1] = func.index;
|
3487
|
-
}
|
3488
|
-
}
|
3772
|
+
if (name === 'main') func.gotLastType = true;
|
3489
3773
|
|
3490
3774
|
// add end return if not found
|
3491
3775
|
if (name !== 'main' && wasm[wasm.length - 1]?.[0] !== Opcodes.return && countLeftover(wasm) === 0) {
|
3492
3776
|
wasm.push(
|
3493
3777
|
...number(0),
|
3494
|
-
...(
|
3778
|
+
...(func.returnType != null ? [] : number(TYPES.undefined, Valtype.i32)),
|
3495
3779
|
[ Opcodes.return ]
|
3496
3780
|
);
|
3497
3781
|
}
|
3498
3782
|
|
3499
|
-
func.wasm = wasm;
|
3500
|
-
|
3501
|
-
funcs.push(func);
|
3502
|
-
|
3503
3783
|
return func;
|
3504
3784
|
};
|
3505
3785
|
|
@@ -3603,7 +3883,7 @@ const internalConstrs = {
|
|
3603
3883
|
generate: (scope, decl) => {
|
3604
3884
|
// todo: boolean object when used as constructor
|
3605
3885
|
const arg = decl.arguments[0] ?? DEFAULT_VALUE;
|
3606
|
-
return truthy(scope, generate(scope, arg), getNodeType(scope, arg));
|
3886
|
+
return truthy(scope, generate(scope, arg), getNodeType(scope, arg), false, false, 'full');
|
3607
3887
|
},
|
3608
3888
|
type: TYPES.boolean,
|
3609
3889
|
length: 1
|
@@ -3748,9 +4028,8 @@ export default program => {
|
|
3748
4028
|
|
3749
4029
|
if (Prefs.astLog) console.log(JSON.stringify(program.body.body, null, 2));
|
3750
4030
|
|
3751
|
-
generateFunc(scope, program);
|
4031
|
+
const main = generateFunc(scope, program);
|
3752
4032
|
|
3753
|
-
const main = funcs[funcs.length - 1];
|
3754
4033
|
main.export = true;
|
3755
4034
|
main.returns = [ valtypeBinary, Valtype.i32 ];
|
3756
4035
|
|
@@ -3777,7 +4056,7 @@ export default program => {
|
|
3777
4056
|
}
|
3778
4057
|
|
3779
4058
|
// 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(
|
4059
|
+
if (main.wasm.length === 0 && funcs.reduce((acc, x) => acc + (x.export ? 1 : 0), 0) > 1) funcs.splice(main.index - importedFuncs.length, 1);
|
3781
4060
|
|
3782
4061
|
return { funcs, globals, tags, exceptions, pages, data };
|
3783
4062
|
};
|