porffor 0.14.0-a24ac8cf2 → 0.14.0-b1e1c2265
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 +3 -1
- 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 +63 -0
- package/compiler/builtins.js +14 -8
- package/compiler/codegen.js +502 -255
- package/compiler/decompile.js +1 -1
- package/compiler/generated_builtins.js +535 -59
- package/compiler/precompile.js +5 -4
- package/compiler/prefs.js +6 -2
- package/compiler/prototype.js +180 -157
- package/compiler/wrap.js +82 -40
- 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':
|
@@ -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,8 +668,8 @@ 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
|
];
|
@@ -685,28 +678,38 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
685
678
|
const useTmp = knownType(scope, type) == null;
|
686
679
|
const tmp = useTmp && localTmp(scope, `#logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
|
687
680
|
|
688
|
-
const def =
|
689
|
-
|
690
|
-
|
681
|
+
const def = (truthyMode => {
|
682
|
+
if (truthyMode === 'full') return [
|
683
|
+
// if value != 0 or NaN
|
684
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
685
|
+
...(intIn ? [ ] : [ Opcodes.i32_to ]),
|
691
686
|
|
692
|
-
|
693
|
-
|
687
|
+
[ Opcodes.i32_eqz ],
|
688
|
+
[ Opcodes.i32_eqz ],
|
694
689
|
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
690
|
+
...(intOut ? [] : [ Opcodes.i32_from ]),
|
691
|
+
];
|
692
|
+
|
693
|
+
if (truthyMode === 'no_negative') return [
|
694
|
+
// if value != 0 or NaN, non-binary output. negative numbers not truthy :/
|
695
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
696
|
+
...(intIn ? [] : [ Opcodes.i32_to ]),
|
697
|
+
...(intOut ? [] : [ Opcodes.i32_from ])
|
698
|
+
];
|
699
|
+
|
700
|
+
if (truthyMode === 'no_nan_negative') return [
|
701
|
+
// simpler and faster but makes NaN truthy and negative numbers not truthy,
|
702
|
+
// plus non-binary output
|
703
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
704
|
+
...(!intOut || (intIn && intOut) ? [] : [ Opcodes.i32_to_u ])
|
705
|
+
];
|
706
|
+
})(forceTruthyMode ?? Prefs.truthy ?? 'full');
|
699
707
|
|
700
708
|
return [
|
701
709
|
...wasm,
|
702
710
|
...(!useTmp ? [] : [ [ Opcodes.local_set, tmp ] ]),
|
703
711
|
|
704
712
|
...typeSwitch(scope, type, {
|
705
|
-
// [TYPES.number]: def,
|
706
|
-
[TYPES.array]: [
|
707
|
-
// arrays are always truthy
|
708
|
-
...number(1, intOut ? Valtype.i32 : valtypeBinary)
|
709
|
-
],
|
710
713
|
[TYPES.string]: [
|
711
714
|
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
712
715
|
...(intIn ? [] : [ Opcodes.i32_to_u ]),
|
@@ -742,10 +745,6 @@ const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
742
745
|
...(!useTmp ? [] : [ [ Opcodes.local_set, tmp ] ]),
|
743
746
|
|
744
747
|
...typeSwitch(scope, type, {
|
745
|
-
[TYPES.array]: [
|
746
|
-
// arrays are always truthy
|
747
|
-
...number(0, intOut ? Valtype.i32 : valtypeBinary)
|
748
|
-
],
|
749
748
|
[TYPES.string]: [
|
750
749
|
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
751
750
|
...(intIn ? [] : [ Opcodes.i32_to_u ]),
|
@@ -989,7 +988,7 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
989
988
|
// if both are true
|
990
989
|
[ Opcodes.i32_and ],
|
991
990
|
[ Opcodes.if, Blocktype.void ],
|
992
|
-
...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ]),
|
991
|
+
...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ], false),
|
993
992
|
...(op === '!==' || op === '!=' ? [ [ Opcodes.i32_eqz ] ] : []),
|
994
993
|
[ Opcodes.br, 1 ],
|
995
994
|
[ Opcodes.end ],
|
@@ -1038,14 +1037,14 @@ const generateBinaryExp = (scope, decl, _global, _name) => {
|
|
1038
1037
|
return out;
|
1039
1038
|
};
|
1040
1039
|
|
1041
|
-
const asmFuncToAsm = (func,
|
1042
|
-
return func(
|
1040
|
+
const asmFuncToAsm = (func, scope) => {
|
1041
|
+
return func(scope, {
|
1043
1042
|
TYPES, TYPE_NAMES, typeSwitch, makeArray, makeString, allocPage, internalThrow,
|
1044
|
-
builtin:
|
1045
|
-
let idx = funcIndex[
|
1046
|
-
if (idx
|
1047
|
-
includeBuiltin(null,
|
1048
|
-
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];
|
1049
1048
|
}
|
1050
1049
|
|
1051
1050
|
return idx;
|
@@ -1053,7 +1052,7 @@ const asmFuncToAsm = (func, { name = '#unknown_asm_func', params = [], locals =
|
|
1053
1052
|
});
|
1054
1053
|
};
|
1055
1054
|
|
1056
|
-
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 }) => {
|
1057
1056
|
const existing = funcs.find(x => x.name === name);
|
1058
1057
|
if (existing) return existing;
|
1059
1058
|
|
@@ -1071,7 +1070,22 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
|
|
1071
1070
|
data.push(copy);
|
1072
1071
|
}
|
1073
1072
|
|
1074
|
-
|
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);
|
1075
1089
|
|
1076
1090
|
let baseGlobalIdx, i = 0;
|
1077
1091
|
for (const type of globalTypes) {
|
@@ -1090,19 +1104,14 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
|
|
1090
1104
|
}
|
1091
1105
|
}
|
1092
1106
|
|
1093
|
-
const
|
1094
|
-
|
1095
|
-
|
1096
|
-
|
1097
|
-
|
1098
|
-
|
1099
|
-
wasm,
|
1100
|
-
internal: true,
|
1101
|
-
index: currentFuncIndex++
|
1102
|
-
};
|
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
|
+
}
|
1103
1113
|
|
1104
|
-
|
1105
|
-
funcIndex[name] = func.index;
|
1114
|
+
func.wasm = wasm;
|
1106
1115
|
|
1107
1116
|
return func;
|
1108
1117
|
};
|
@@ -1194,9 +1203,10 @@ const getLastType = scope => {
|
|
1194
1203
|
return [ [ Opcodes.local_get, localTmp(scope, '#last_type', Valtype.i32) ] ];
|
1195
1204
|
};
|
1196
1205
|
|
1197
|
-
const setLastType = scope =>
|
1198
|
-
|
1199
|
-
|
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
|
+
];
|
1200
1210
|
|
1201
1211
|
const getNodeType = (scope, node) => {
|
1202
1212
|
const ret = (() => {
|
@@ -1257,7 +1267,17 @@ const getNodeType = (scope, node) => {
|
|
1257
1267
|
|
1258
1268
|
const func = spl[spl.length - 1];
|
1259
1269
|
const protoFuncs = Object.keys(prototypeFuncs).filter(x => x != TYPES.bytestring && prototypeFuncs[x][func] != null);
|
1260
|
-
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
|
+
}
|
1261
1281
|
}
|
1262
1282
|
|
1263
1283
|
if (name.startsWith('__Porffor_wasm_')) {
|
@@ -1352,22 +1372,27 @@ const getNodeType = (scope, node) => {
|
|
1352
1372
|
}
|
1353
1373
|
|
1354
1374
|
if (node.type === 'MemberExpression') {
|
1355
|
-
|
1356
|
-
|
1357
|
-
|
1358
|
-
|
1359
|
-
|
1360
|
-
return TYPES.undefined;
|
1361
|
-
}
|
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;
|
1362
1380
|
}
|
1363
1381
|
|
1364
|
-
// hack: if something.length, number type
|
1365
|
-
if (node.property.name === 'length') return TYPES.number;
|
1366
1382
|
|
1367
|
-
|
1368
|
-
if (
|
1369
|
-
|
1370
|
-
|
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
|
+
}
|
1371
1396
|
|
1372
1397
|
if (scope.locals['#last_type']) return getLastType(scope);
|
1373
1398
|
|
@@ -1438,16 +1463,11 @@ const countLeftover = wasm => {
|
|
1438
1463
|
else if (inst[0] === Opcodes.return) count = 0;
|
1439
1464
|
else if (inst[0] === Opcodes.call) {
|
1440
1465
|
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;
|
1466
|
+
if (inst[1] < importedFuncs.length) {
|
1467
|
+
func = importedFuncs[inst[1]];
|
1468
|
+
count = count - func.params + func.returns;
|
1446
1469
|
} else {
|
1447
|
-
|
1448
|
-
count -= func.params.length;
|
1449
|
-
} else count--;
|
1450
|
-
if (func) count += func.returns.length;
|
1470
|
+
count = count - func.params.length + func.returns.length;
|
1451
1471
|
}
|
1452
1472
|
} else if (inst[0] === Opcodes.call_indirect) {
|
1453
1473
|
count--; // funcidx
|
@@ -1470,7 +1490,7 @@ const disposeLeftover = wasm => {
|
|
1470
1490
|
const generateExp = (scope, decl) => {
|
1471
1491
|
const expression = decl.expression;
|
1472
1492
|
|
1473
|
-
const out = generate(scope, expression, undefined, undefined,
|
1493
|
+
const out = generate(scope, expression, undefined, undefined, Prefs.optUnused);
|
1474
1494
|
disposeLeftover(out);
|
1475
1495
|
|
1476
1496
|
return out;
|
@@ -1561,16 +1581,10 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1561
1581
|
out.splice(out.length - 1, 1);
|
1562
1582
|
|
1563
1583
|
const finalStatement = parsed.body[parsed.body.length - 1];
|
1564
|
-
out.push(
|
1565
|
-
...getNodeType(scope, finalStatement),
|
1566
|
-
...setLastType(scope)
|
1567
|
-
);
|
1584
|
+
out.push(...setLastType(scope, getNodeType(scope, finalStatement)));
|
1568
1585
|
} else if (countLeftover(out) === 0) {
|
1569
1586
|
out.push(...number(UNDEFINED));
|
1570
|
-
out.push(
|
1571
|
-
...number(TYPES.undefined, Valtype.i32),
|
1572
|
-
...setLastType(scope)
|
1573
|
-
);
|
1587
|
+
out.push(...setLastType(scope, TYPES.undefined));
|
1574
1588
|
}
|
1575
1589
|
|
1576
1590
|
// if (lastInst && lastInst[0] === Opcodes.drop) {
|
@@ -1606,6 +1620,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1606
1620
|
|
1607
1621
|
if (!funcIndex[rhemynName]) {
|
1608
1622
|
const func = Rhemyn[funcName](regex, currentFuncIndex++, rhemynName);
|
1623
|
+
func.internal = true;
|
1609
1624
|
|
1610
1625
|
funcIndex[func.name] = func.index;
|
1611
1626
|
funcs.push(func);
|
@@ -1622,8 +1637,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1622
1637
|
[ Opcodes.call, idx ],
|
1623
1638
|
Opcodes.i32_from_u,
|
1624
1639
|
|
1625
|
-
...
|
1626
|
-
...setLastType(scope)
|
1640
|
+
...setLastType(scope, TYPES.boolean)
|
1627
1641
|
];
|
1628
1642
|
}
|
1629
1643
|
|
@@ -1695,9 +1709,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1695
1709
|
if (protoFunc.noArgRetLength && decl.arguments.length === 0) {
|
1696
1710
|
protoBC[x] = [
|
1697
1711
|
...RTArrayUtil.getLength(getPointer),
|
1698
|
-
|
1699
|
-
...number(TYPES.number, Valtype.i32),
|
1700
|
-
...setLastType(scope)
|
1712
|
+
...setLastType(scope, TYPES.number)
|
1701
1713
|
];
|
1702
1714
|
continue;
|
1703
1715
|
}
|
@@ -1716,7 +1728,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1716
1728
|
getI32: () => RTArrayUtil.getLengthI32(getPointer),
|
1717
1729
|
set: value => RTArrayUtil.setLength(getPointer, value),
|
1718
1730
|
setI32: value => RTArrayUtil.setLengthI32(getPointer, value)
|
1719
|
-
}, 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) => {
|
1720
1732
|
return makeArray(scope, {
|
1721
1733
|
rawElements: new Array(length)
|
1722
1734
|
}, _global, _name, true, itemType);
|
@@ -1730,9 +1742,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1730
1742
|
protoBC[x] = [
|
1731
1743
|
[ Opcodes.block, unusedValue && optUnused ? Blocktype.void : valtypeBinary ],
|
1732
1744
|
...protoOut,
|
1733
|
-
|
1734
|
-
...number(protoFunc.returnType ?? TYPES.number, Valtype.i32),
|
1735
|
-
...setLastType(scope),
|
1745
|
+
...(unusedValue && optUnused ? [] : (protoFunc.returnType != null ? setLastType(scope, protoFunc.returnType) : setLastType(scope))),
|
1736
1746
|
[ Opcodes.end ]
|
1737
1747
|
];
|
1738
1748
|
}
|
@@ -1782,11 +1792,6 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1782
1792
|
|
1783
1793
|
if (idx === undefined && internalConstrs[name]) return internalConstrs[name].generate(scope, decl, _global, _name);
|
1784
1794
|
|
1785
|
-
if (idx === undefined && name === scope.name) {
|
1786
|
-
// hack: calling self, func generator will fix later
|
1787
|
-
idx = -1;
|
1788
|
-
}
|
1789
|
-
|
1790
1795
|
if (idx === undefined && name.startsWith('__Porffor_wasm_')) {
|
1791
1796
|
const wasmOps = {
|
1792
1797
|
// pointer, align, offset
|
@@ -1838,36 +1843,128 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1838
1843
|
const [ local, global ] = lookupName(scope, name);
|
1839
1844
|
if (!Prefs.indirectCalls || local == null) return internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, true);
|
1840
1845
|
|
1841
|
-
// todo: only works when
|
1842
|
-
|
1843
|
-
|
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
|
1844
1855
|
|
1845
1856
|
funcs.table = true;
|
1857
|
+
scope.table = true;
|
1846
1858
|
|
1847
1859
|
let args = decl.arguments;
|
1848
|
-
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
|
+
}
|
1849
1871
|
|
1850
1872
|
for (let i = 0; i < args.length; i++) {
|
1851
1873
|
const arg = args[i];
|
1852
|
-
|
1874
|
+
out = out.concat(generate(scope, arg));
|
1853
1875
|
|
1854
1876
|
if (valtypeBinary !== Valtype.i32 && (
|
1855
1877
|
(builtinFuncs[name] && builtinFuncs[name].params[i * (typedParams ? 2 : 1)] === Valtype.i32) ||
|
1856
1878
|
(importedFuncs[name] && name.startsWith('profile'))
|
1857
1879
|
)) {
|
1858
|
-
|
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
|
+
);
|
1895
|
+
}
|
1896
|
+
}
|
1897
|
+
|
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
|
+
});
|
1909
|
+
}
|
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
|
+
);
|
1859
1934
|
}
|
1860
1935
|
|
1861
|
-
|
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);
|
1862
1948
|
}
|
1863
1949
|
|
1950
|
+
// todo/perf: check if we should use br_table here or just generate our own big if..elses
|
1951
|
+
|
1864
1952
|
return typeSwitch(scope, getNodeType(scope, decl.callee), {
|
1865
1953
|
[TYPES.function]: [
|
1866
|
-
...
|
1954
|
+
...out,
|
1955
|
+
|
1867
1956
|
[ global ? Opcodes.global_get : Opcodes.local_get, local.idx ],
|
1868
1957
|
Opcodes.i32_to_u,
|
1869
|
-
[ Opcodes.
|
1870
|
-
|
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)
|
1871
1968
|
],
|
1872
1969
|
default: internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, true)
|
1873
1970
|
});
|
@@ -1876,11 +1973,10 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1876
1973
|
return internalThrow(scope, 'ReferenceError', `${unhackName(name)} is not defined`, true);
|
1877
1974
|
}
|
1878
1975
|
|
1879
|
-
const func = funcs.find(x => x.index === idx);
|
1880
|
-
|
1881
|
-
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;
|
1882
1978
|
const typedParams = userFunc || builtinFuncs[name]?.typedParams;
|
1883
|
-
const typedReturns = (func
|
1979
|
+
const typedReturns = (func && func.returnType == null) || builtinFuncs[name]?.typedReturns;
|
1884
1980
|
const paramCount = func && (typedParams ? func.params.length / 2 : func.params.length);
|
1885
1981
|
|
1886
1982
|
let args = decl.arguments;
|
@@ -1901,6 +1997,13 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1901
1997
|
const arg = args[i];
|
1902
1998
|
out = out.concat(generate(scope, arg));
|
1903
1999
|
|
2000
|
+
// todo: this should be used instead of the too many args thing above (by removing that)
|
2001
|
+
if (i >= paramCount) {
|
2002
|
+
// over param count of func, drop arg
|
2003
|
+
out.push([ Opcodes.drop ]);
|
2004
|
+
continue;
|
2005
|
+
}
|
2006
|
+
|
1904
2007
|
if (valtypeBinary !== Valtype.i32 && (
|
1905
2008
|
(builtinFuncs[name] && builtinFuncs[name].params[i * (typedParams ? 2 : 1)] === Valtype.i32) ||
|
1906
2009
|
(importedFuncs[name] && name.startsWith('profile'))
|
@@ -1949,6 +2052,11 @@ const generateNew = (scope, decl, _global, _name) => {
|
|
1949
2052
|
}, _global, _name);
|
1950
2053
|
}
|
1951
2054
|
|
2055
|
+
if (
|
2056
|
+
(builtinFuncs[name] && !builtinFuncs[name].constr) ||
|
2057
|
+
(internalConstrs[name] && builtinFuncs[name].notConstr)
|
2058
|
+
) return internalThrow(scope, 'TypeError', `${name} is not a constructor`);
|
2059
|
+
|
1952
2060
|
if (!builtinFuncs[name]) return todo(scope, `new statement is not supported yet`); // return todo(scope, `new statement is not supported yet (new ${unhackName(name)})`);
|
1953
2061
|
|
1954
2062
|
return generateCall(scope, decl, _global, _name);
|
@@ -1974,8 +2082,11 @@ const knownType = (scope, type) => {
|
|
1974
2082
|
const idx = type[0][1];
|
1975
2083
|
|
1976
2084
|
// type idx = var idx + 1
|
1977
|
-
const
|
1978
|
-
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
|
+
}
|
1979
2090
|
}
|
1980
2091
|
|
1981
2092
|
return null;
|
@@ -2010,16 +2121,17 @@ const brTable = (input, bc, returns) => {
|
|
2010
2121
|
}
|
2011
2122
|
|
2012
2123
|
for (let i = 0; i < count; i++) {
|
2013
|
-
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 ]);
|
2014
2126
|
else out.push([ Opcodes.block, Blocktype.void ]);
|
2015
2127
|
}
|
2016
2128
|
|
2017
|
-
const nums = keys.filter(x => +x);
|
2129
|
+
const nums = keys.filter(x => +x >= 0);
|
2018
2130
|
const offset = Math.min(...nums);
|
2019
2131
|
const max = Math.max(...nums);
|
2020
2132
|
|
2021
2133
|
const table = [];
|
2022
|
-
let br =
|
2134
|
+
let br = 0;
|
2023
2135
|
|
2024
2136
|
for (let i = offset; i <= max; i++) {
|
2025
2137
|
// if branch for this num, go to that block
|
@@ -2059,10 +2171,9 @@ const brTable = (input, bc, returns) => {
|
|
2059
2171
|
br--;
|
2060
2172
|
}
|
2061
2173
|
|
2062
|
-
|
2063
|
-
|
2064
|
-
|
2065
|
-
];
|
2174
|
+
out.push([ Opcodes.end ]);
|
2175
|
+
|
2176
|
+
return out;
|
2066
2177
|
};
|
2067
2178
|
|
2068
2179
|
const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
|
@@ -2106,6 +2217,17 @@ const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
|
|
2106
2217
|
return out;
|
2107
2218
|
};
|
2108
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
|
+
|
2109
2231
|
const allocVar = (scope, name, global = false, type = true) => {
|
2110
2232
|
const target = global ? globals : scope.locals;
|
2111
2233
|
|
@@ -2122,7 +2244,7 @@ const allocVar = (scope, name, global = false, type = true) => {
|
|
2122
2244
|
|
2123
2245
|
if (type) {
|
2124
2246
|
let typeIdx = global ? globalInd++ : scope.localInd++;
|
2125
|
-
target[name + '#type'] = { idx: typeIdx, type: Valtype.i32 };
|
2247
|
+
target[name + '#type'] = { idx: typeIdx, type: Valtype.i32, name };
|
2126
2248
|
}
|
2127
2249
|
|
2128
2250
|
return idx;
|
@@ -2335,18 +2457,21 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
|
|
2335
2457
|
Opcodes.i32_to_u,
|
2336
2458
|
|
2337
2459
|
// turn into byte offset by * valtypeSize (4 for i32, 8 for i64/f64)
|
2338
|
-
...number(ValtypeSize[valtype], Valtype.i32),
|
2460
|
+
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
2339
2461
|
[ Opcodes.i32_mul ],
|
2340
2462
|
...(aotPointer ? [] : [ [ Opcodes.i32_add ] ]),
|
2341
2463
|
...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
|
2342
2464
|
|
2343
2465
|
...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
|
2344
2466
|
[ Opcodes.local_get, pointerTmp ],
|
2345
|
-
[ Opcodes.load,
|
2346
|
-
], 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)),
|
2347
2472
|
[ Opcodes.local_tee, newValueTmp ],
|
2348
2473
|
|
2349
|
-
[ Opcodes.store,
|
2474
|
+
[ Opcodes.store, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
|
2350
2475
|
],
|
2351
2476
|
|
2352
2477
|
default: internalThrow(scope, 'TypeError', `Cannot assign member with non-array`)
|
@@ -2454,6 +2579,7 @@ const generateUnary = (scope, decl) => {
|
|
2454
2579
|
];
|
2455
2580
|
|
2456
2581
|
case '!':
|
2582
|
+
// todo/perf: optimize !!
|
2457
2583
|
// !=
|
2458
2584
|
return falsy(scope, generate(scope, decl.argument), getNodeType(scope, decl.argument), false, false);
|
2459
2585
|
|
@@ -2523,6 +2649,7 @@ const generateUnary = (scope, decl) => {
|
|
2523
2649
|
[TYPES.string]: makeString(scope, 'string', false, '#typeof_result'),
|
2524
2650
|
[TYPES.undefined]: makeString(scope, 'undefined', false, '#typeof_result'),
|
2525
2651
|
[TYPES.function]: makeString(scope, 'function', false, '#typeof_result'),
|
2652
|
+
[TYPES.symbol]: makeString(scope, 'symbol', false, '#typeof_result'),
|
2526
2653
|
|
2527
2654
|
[TYPES.bytestring]: makeString(scope, 'string', false, '#typeof_result'),
|
2528
2655
|
|
@@ -2599,21 +2726,16 @@ const generateConditional = (scope, decl) => {
|
|
2599
2726
|
out.push([ Opcodes.if, valtypeBinary ]);
|
2600
2727
|
depth.push('if');
|
2601
2728
|
|
2602
|
-
out.push(...generate(scope, decl.consequent));
|
2603
|
-
|
2604
|
-
// note type
|
2605
2729
|
out.push(
|
2606
|
-
...
|
2607
|
-
...setLastType(scope)
|
2730
|
+
...generate(scope, decl.consequent),
|
2731
|
+
...setLastType(scope, getNodeType(scope, decl.consequent))
|
2608
2732
|
);
|
2609
2733
|
|
2610
2734
|
out.push([ Opcodes.else ]);
|
2611
|
-
out.push(...generate(scope, decl.alternate));
|
2612
2735
|
|
2613
|
-
// note type
|
2614
2736
|
out.push(
|
2615
|
-
...
|
2616
|
-
...setLastType(scope)
|
2737
|
+
...generate(scope, decl.alternate),
|
2738
|
+
...setLastType(scope, getNodeType(scope, decl.alternate))
|
2617
2739
|
);
|
2618
2740
|
|
2619
2741
|
out.push([ Opcodes.end ]);
|
@@ -2761,12 +2883,15 @@ const generateForOf = (scope, decl) => {
|
|
2761
2883
|
// todo: optimize away counter and use end pointer
|
2762
2884
|
out.push(...typeSwitch(scope, getNodeType(scope, decl.right), {
|
2763
2885
|
[TYPES.array]: [
|
2764
|
-
...setType(scope, leftName, TYPES.number),
|
2765
|
-
|
2766
2886
|
[ Opcodes.loop, Blocktype.void ],
|
2767
2887
|
|
2768
2888
|
[ Opcodes.local_get, pointer ],
|
2769
|
-
[ 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
|
+
]),
|
2770
2895
|
|
2771
2896
|
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
|
2772
2897
|
|
@@ -2775,9 +2900,9 @@ const generateForOf = (scope, decl) => {
|
|
2775
2900
|
...generate(scope, decl.body),
|
2776
2901
|
[ Opcodes.end ],
|
2777
2902
|
|
2778
|
-
// increment iter pointer by valtype size
|
2903
|
+
// increment iter pointer by valtype size + 1
|
2779
2904
|
[ Opcodes.local_get, pointer ],
|
2780
|
-
...number(ValtypeSize[valtype], Valtype.i32),
|
2905
|
+
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
2781
2906
|
[ Opcodes.i32_add ],
|
2782
2907
|
[ Opcodes.local_set, pointer ],
|
2783
2908
|
|
@@ -2994,14 +3119,18 @@ const generateThrow = (scope, decl) => {
|
|
2994
3119
|
};
|
2995
3120
|
|
2996
3121
|
const generateTry = (scope, decl) => {
|
2997
|
-
|
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."
|
2998
3124
|
|
2999
3125
|
const out = [];
|
3000
3126
|
|
3127
|
+
const finalizer = decl.finalizer ? generate(scope, decl.finalizer) : [];
|
3128
|
+
|
3001
3129
|
out.push([ Opcodes.try, Blocktype.void ]);
|
3002
3130
|
depth.push('try');
|
3003
3131
|
|
3004
3132
|
out.push(...generate(scope, decl.block));
|
3133
|
+
out.push(...finalizer);
|
3005
3134
|
|
3006
3135
|
if (decl.handler) {
|
3007
3136
|
depth.pop();
|
@@ -3009,6 +3138,7 @@ const generateTry = (scope, decl) => {
|
|
3009
3138
|
|
3010
3139
|
out.push([ Opcodes.catch_all ]);
|
3011
3140
|
out.push(...generate(scope, decl.handler.body));
|
3141
|
+
out.push(...finalizer);
|
3012
3142
|
}
|
3013
3143
|
|
3014
3144
|
out.push([ Opcodes.end ]);
|
@@ -3094,7 +3224,7 @@ const getAllocType = itemType => {
|
|
3094
3224
|
}
|
3095
3225
|
};
|
3096
3226
|
|
3097
|
-
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) => {
|
3098
3228
|
const out = [];
|
3099
3229
|
|
3100
3230
|
scope.arrays ??= new Map();
|
@@ -3106,8 +3236,13 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
3106
3236
|
// todo: can we just have 1 undeclared array? probably not? but this is not really memory efficient
|
3107
3237
|
const uniqueName = name === '$undeclared' ? name + Math.random().toString().slice(2) : name;
|
3108
3238
|
|
3109
|
-
|
3110
|
-
|
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);
|
3111
3246
|
}
|
3112
3247
|
|
3113
3248
|
const pointer = scope.arrays.get(name);
|
@@ -3157,7 +3292,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
3157
3292
|
|
3158
3293
|
const pointerWasm = pointerTmp != null ? [ [ Opcodes.local_get, pointerTmp ] ] : number(pointer, Valtype.i32);
|
3159
3294
|
|
3160
|
-
// store length
|
3295
|
+
// store length
|
3161
3296
|
out.push(
|
3162
3297
|
...pointerWasm,
|
3163
3298
|
...number(length, Valtype.i32),
|
@@ -3165,14 +3300,20 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
3165
3300
|
);
|
3166
3301
|
|
3167
3302
|
const storeOp = StoreOps[itemType];
|
3168
|
-
|
3303
|
+
const sizePerEl = ValtypeSize[itemType] + (typed ? 1 : 0);
|
3169
3304
|
if (!initEmpty) for (let i = 0; i < length; i++) {
|
3170
3305
|
if (elements[i] == null) continue;
|
3171
3306
|
|
3307
|
+
const offset = ValtypeSize.i32 + i * sizePerEl;
|
3172
3308
|
out.push(
|
3173
3309
|
...pointerWasm,
|
3174
3310
|
...(useRawElements ? number(elements[i], Valtype[valtype]) : generate(scope, elements[i])),
|
3175
|
-
[ 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
|
+
])
|
3176
3317
|
);
|
3177
3318
|
}
|
3178
3319
|
|
@@ -3182,6 +3323,65 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
3182
3323
|
return [ out, pointer ];
|
3183
3324
|
};
|
3184
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
|
+
|
3185
3385
|
const byteStringable = str => {
|
3186
3386
|
if (!Prefs.bytestring) return false;
|
3187
3387
|
|
@@ -3210,14 +3410,28 @@ const makeString = (scope, str, global = false, name = '$undeclared', forceBytes
|
|
3210
3410
|
};
|
3211
3411
|
|
3212
3412
|
const generateArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false) => {
|
3213
|
-
return makeArray(scope, decl, global, name, initEmpty, valtype)[0];
|
3413
|
+
return makeArray(scope, decl, global, name, initEmpty, valtype, true)[0];
|
3214
3414
|
};
|
3215
3415
|
|
3216
|
-
|
3416
|
+
const generateObject = (scope, decl, global = false, name = '$undeclared') => {
|
3417
|
+
if (decl.properties.length > 0) return todo(scope, 'objects are not supported yet', true);
|
3418
|
+
|
3419
|
+
return [
|
3420
|
+
...number(1),
|
3421
|
+
...setLastType(scope, TYPES.object)
|
3422
|
+
];
|
3423
|
+
};
|
3424
|
+
|
3425
|
+
const withType = (scope, wasm, type) => [
|
3426
|
+
...wasm,
|
3427
|
+
...setLastType(scope, type)
|
3428
|
+
];
|
3429
|
+
|
3430
|
+
const generateMember = (scope, decl, _global, _name) => {
|
3217
3431
|
const name = decl.object.name;
|
3218
3432
|
const pointer = scope.arrays?.get(name);
|
3219
3433
|
|
3220
|
-
const aotPointer = Prefs.aotPointerOpt && pointer
|
3434
|
+
const aotPointer = Prefs.aotPointerOpt && pointer;
|
3221
3435
|
|
3222
3436
|
// hack: .name
|
3223
3437
|
if (decl.property.name === 'name') {
|
@@ -3227,9 +3441,9 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3227
3441
|
// eg: __String_prototype_toLowerCase -> toLowerCase
|
3228
3442
|
if (nameProp.startsWith('__')) nameProp = nameProp.split('_').pop();
|
3229
3443
|
|
3230
|
-
return makeString(scope, nameProp, _global, _name, true);
|
3444
|
+
return withType(scope, makeString(scope, nameProp, _global, _name, true), TYPES.bytestring);
|
3231
3445
|
} else {
|
3232
|
-
return
|
3446
|
+
return withType(scope, number(0), TYPES.undefined);
|
3233
3447
|
}
|
3234
3448
|
}
|
3235
3449
|
|
@@ -3237,9 +3451,8 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3237
3451
|
if (decl.property.name === 'length') {
|
3238
3452
|
const func = funcs.find(x => x.name === name);
|
3239
3453
|
if (func) {
|
3240
|
-
const
|
3241
|
-
|
3242
|
-
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);
|
3243
3456
|
}
|
3244
3457
|
|
3245
3458
|
if (builtinFuncs[name + '$constructor']) {
|
@@ -3249,24 +3462,88 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3249
3462
|
const constructorFunc = builtinFuncs[name + '$constructor'];
|
3250
3463
|
const constructorParams = constructorFunc.typedParams ? (constructorFunc.params.length / 2) : constructorFunc.params.length;
|
3251
3464
|
|
3252
|
-
return number(Math.max(regularParams, constructorParams));
|
3465
|
+
return withType(scope, number(Math.max(regularParams, constructorParams)), TYPES.number);
|
3253
3466
|
}
|
3254
3467
|
|
3255
|
-
if (builtinFuncs[name]) return number(builtinFuncs[name].typedParams ? (builtinFuncs[name].params.length / 2) : builtinFuncs[name].params.length);
|
3256
|
-
if (importedFuncs[name]) return number(importedFuncs[name].params);
|
3257
|
-
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
|
+
}
|
3258
3500
|
|
3259
3501
|
return [
|
3260
|
-
...(
|
3261
|
-
|
3262
|
-
|
3263
|
-
|
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
|
+
]),
|
3264
3508
|
|
3265
|
-
|
3266
|
-
|
3509
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
|
3510
|
+
Opcodes.i32_from_u,
|
3511
|
+
|
3512
|
+
...setLastType(scope, TYPES.number),
|
3513
|
+
[ Opcodes.else ],
|
3514
|
+
...number(0),
|
3515
|
+
...setLastType(scope, TYPES.undefined),
|
3516
|
+
[ Opcodes.end ]
|
3267
3517
|
];
|
3268
3518
|
}
|
3269
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
|
+
|
3270
3547
|
const object = generate(scope, decl.object);
|
3271
3548
|
const property = generate(scope, decl.property);
|
3272
3549
|
|
@@ -3281,24 +3558,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3281
3558
|
|
3282
3559
|
return typeSwitch(scope, getNodeType(scope, decl.object), {
|
3283
3560
|
[TYPES.array]: [
|
3284
|
-
|
3285
|
-
...property,
|
3286
|
-
|
3287
|
-
// convert to i32 and turn into byte offset by * valtypeSize (4 for i32, 8 for i64/f64)
|
3288
|
-
Opcodes.i32_to_u,
|
3289
|
-
...number(ValtypeSize[valtype], Valtype.i32),
|
3290
|
-
[ Opcodes.i32_mul ],
|
3291
|
-
|
3292
|
-
...(aotPointer ? [] : [
|
3293
|
-
...object,
|
3294
|
-
Opcodes.i32_to_u,
|
3295
|
-
[ Opcodes.i32_add ]
|
3296
|
-
]),
|
3297
|
-
|
3298
|
-
// read from memory
|
3299
|
-
[ Opcodes.load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
3300
|
-
|
3301
|
-
...number(TYPES.number, Valtype.i32),
|
3561
|
+
...loadArray(scope, object, property, aotPointer),
|
3302
3562
|
...setLastType(scope)
|
3303
3563
|
],
|
3304
3564
|
|
@@ -3329,9 +3589,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3329
3589
|
|
3330
3590
|
// return new string (page)
|
3331
3591
|
...number(newPointer),
|
3332
|
-
|
3333
|
-
...number(TYPES.string, Valtype.i32),
|
3334
|
-
...setLastType(scope)
|
3592
|
+
...setLastType(scope, TYPES.string)
|
3335
3593
|
],
|
3336
3594
|
[TYPES.bytestring]: [
|
3337
3595
|
// setup new/out array
|
@@ -3357,9 +3615,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3357
3615
|
|
3358
3616
|
// return new string (page)
|
3359
3617
|
...number(newPointer),
|
3360
|
-
|
3361
|
-
...number(TYPES.bytestring, Valtype.i32),
|
3362
|
-
...setLastType(scope)
|
3618
|
+
...setLastType(scope, TYPES.bytestring)
|
3363
3619
|
],
|
3364
3620
|
|
3365
3621
|
default: internalThrow(scope, 'TypeError', 'Member expression is not supported for non-string non-array yet', true)
|
@@ -3384,7 +3640,7 @@ const objectHack = node => {
|
|
3384
3640
|
if (!objectName) objectName = objectHack(node.object)?.name?.slice?.(2);
|
3385
3641
|
|
3386
3642
|
// if .name or .length, give up (hack within a hack!)
|
3387
|
-
if (['name', 'length'].includes(node.property.name)) {
|
3643
|
+
if (['name', 'length', 'size', 'description'].includes(node.property.name)) {
|
3388
3644
|
node.object = objectHack(node.object);
|
3389
3645
|
return;
|
3390
3646
|
}
|
@@ -3421,33 +3677,39 @@ const generateFunc = (scope, decl) => {
|
|
3421
3677
|
const name = decl.id ? decl.id.name : `anonymous_${randId()}`;
|
3422
3678
|
const params = decl.params ?? [];
|
3423
3679
|
|
3424
|
-
// const innerScope = { ...scope };
|
3425
3680
|
// TODO: share scope/locals between !!!
|
3426
|
-
const
|
3681
|
+
const func = {
|
3427
3682
|
locals: {},
|
3428
3683
|
localInd: 0,
|
3429
3684
|
// value, type
|
3430
3685
|
returns: [ valtypeBinary, Valtype.i32 ],
|
3431
3686
|
throws: false,
|
3432
|
-
name
|
3687
|
+
name,
|
3688
|
+
index: currentFuncIndex++
|
3433
3689
|
};
|
3434
3690
|
|
3435
3691
|
if (typedInput && decl.returnType) {
|
3436
3692
|
const { type } = extractTypeAnnotation(decl.returnType);
|
3437
|
-
if (type != null && !Prefs.indirectCalls) {
|
3438
|
-
|
3439
|
-
|
3693
|
+
// if (type != null && !Prefs.indirectCalls) {
|
3694
|
+
if (type != null) {
|
3695
|
+
func.returnType = type;
|
3696
|
+
func.returns = [ valtypeBinary ];
|
3440
3697
|
}
|
3441
3698
|
}
|
3442
3699
|
|
3443
3700
|
for (let i = 0; i < params.length; i++) {
|
3444
|
-
|
3701
|
+
const name = params[i].name;
|
3702
|
+
// if (name == null) return todo('non-identifier args are not supported');
|
3703
|
+
|
3704
|
+
allocVar(func, name, false);
|
3445
3705
|
|
3446
3706
|
if (typedInput && params[i].typeAnnotation) {
|
3447
|
-
addVarMetadata(
|
3707
|
+
addVarMetadata(func, name, false, extractTypeAnnotation(params[i]));
|
3448
3708
|
}
|
3449
3709
|
}
|
3450
3710
|
|
3711
|
+
func.params = Object.values(func.locals).map(x => x.type);
|
3712
|
+
|
3451
3713
|
let body = objectHack(decl.body);
|
3452
3714
|
if (decl.type === 'ArrowFunctionExpression' && decl.expression) {
|
3453
3715
|
// hack: () => 0 -> () => return 0
|
@@ -3457,37 +3719,23 @@ const generateFunc = (scope, decl) => {
|
|
3457
3719
|
};
|
3458
3720
|
}
|
3459
3721
|
|
3460
|
-
const wasm = generate(innerScope, body);
|
3461
|
-
const func = {
|
3462
|
-
name,
|
3463
|
-
params: Object.values(innerScope.locals).slice(0, params.length * 2).map(x => x.type),
|
3464
|
-
index: currentFuncIndex++,
|
3465
|
-
...innerScope
|
3466
|
-
};
|
3467
3722
|
funcIndex[name] = func.index;
|
3723
|
+
funcs.push(func);
|
3468
3724
|
|
3469
|
-
|
3725
|
+
const wasm = generate(func, body);
|
3726
|
+
func.wasm = wasm;
|
3470
3727
|
|
3471
|
-
|
3472
|
-
for (const inst of wasm) {
|
3473
|
-
if (inst[0] === Opcodes.call && inst[1] === -1) {
|
3474
|
-
inst[1] = func.index;
|
3475
|
-
}
|
3476
|
-
}
|
3728
|
+
if (name === 'main') func.gotLastType = true;
|
3477
3729
|
|
3478
3730
|
// add end return if not found
|
3479
3731
|
if (name !== 'main' && wasm[wasm.length - 1]?.[0] !== Opcodes.return && countLeftover(wasm) === 0) {
|
3480
3732
|
wasm.push(
|
3481
3733
|
...number(0),
|
3482
|
-
...(
|
3734
|
+
...(func.returnType != null ? [] : number(TYPES.undefined, Valtype.i32)),
|
3483
3735
|
[ Opcodes.return ]
|
3484
3736
|
);
|
3485
3737
|
}
|
3486
3738
|
|
3487
|
-
func.wasm = wasm;
|
3488
|
-
|
3489
|
-
funcs.push(func);
|
3490
|
-
|
3491
3739
|
return func;
|
3492
3740
|
};
|
3493
3741
|
|
@@ -3591,7 +3839,7 @@ const internalConstrs = {
|
|
3591
3839
|
generate: (scope, decl) => {
|
3592
3840
|
// todo: boolean object when used as constructor
|
3593
3841
|
const arg = decl.arguments[0] ?? DEFAULT_VALUE;
|
3594
|
-
return truthy(scope, generate(scope, arg), getNodeType(scope, arg));
|
3842
|
+
return truthy(scope, generate(scope, arg), getNodeType(scope, arg), false, false, 'full');
|
3595
3843
|
},
|
3596
3844
|
type: TYPES.boolean,
|
3597
3845
|
length: 1
|
@@ -3736,9 +3984,8 @@ export default program => {
|
|
3736
3984
|
|
3737
3985
|
if (Prefs.astLog) console.log(JSON.stringify(program.body.body, null, 2));
|
3738
3986
|
|
3739
|
-
generateFunc(scope, program);
|
3987
|
+
const main = generateFunc(scope, program);
|
3740
3988
|
|
3741
|
-
const main = funcs[funcs.length - 1];
|
3742
3989
|
main.export = true;
|
3743
3990
|
main.returns = [ valtypeBinary, Valtype.i32 ];
|
3744
3991
|
|
@@ -3765,7 +4012,7 @@ export default program => {
|
|
3765
4012
|
}
|
3766
4013
|
|
3767
4014
|
// if blank main func and other exports, remove it
|
3768
|
-
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);
|
3769
4016
|
|
3770
4017
|
return { funcs, globals, tags, exceptions, pages, data };
|
3771
4018
|
};
|