porffor 0.14.0-cdebd5442 → 0.14.0-d6c141d91
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/bf_bun +0 -0
- package/bf_deno +0 -0
- package/bf_porf +0 -0
- package/bf_porf_fast +0 -0
- package/bf_porf_lto +0 -0
- package/compiler/2c.js +65 -2
- package/compiler/assemble.js +14 -0
- package/compiler/builtins/annexb_string.ts +1 -0
- package/compiler/builtins/array.ts +84 -4
- package/compiler/builtins/base64.ts +1 -0
- package/compiler/builtins/boolean.ts +2 -0
- package/compiler/builtins/crypto.ts +1 -0
- package/compiler/builtins/date.ts +2 -0
- package/compiler/builtins/error.js +22 -0
- package/compiler/builtins/escape.ts +1 -2
- package/compiler/builtins/function.ts +2 -0
- package/compiler/builtins/int.ts +2 -0
- package/compiler/builtins/math.ts +410 -0
- package/compiler/builtins/number.ts +2 -0
- package/compiler/builtins/object.ts +2 -0
- package/compiler/builtins/set.ts +7 -6
- package/compiler/builtins/string.ts +1 -0
- package/compiler/builtins/symbol.ts +3 -1
- package/compiler/builtins.js +33 -6
- package/compiler/codegen.js +502 -256
- package/compiler/generated_builtins.js +491 -60
- package/compiler/index.js +1 -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/w.js +1 -0
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);
|
1859
1881
|
}
|
1860
1882
|
|
1861
|
-
|
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
|
+
}
|
1862
1896
|
}
|
1863
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
|
+
);
|
1934
|
+
}
|
1935
|
+
|
1936
|
+
out.push(
|
1937
|
+
[ Opcodes.local_get, funcLocal ],
|
1938
|
+
[ Opcodes.call_indirect, argc, 0 ],
|
1939
|
+
...setLastType(scope)
|
1940
|
+
)
|
1941
|
+
|
1942
|
+
return out;
|
1943
|
+
};
|
1944
|
+
|
1945
|
+
const tableBc = {};
|
1946
|
+
for (let i = 0; i <= args.length; i++) {
|
1947
|
+
tableBc[i] = gen(i);
|
1948
|
+
}
|
1949
|
+
|
1950
|
+
// todo/perf: check if we should use br_table here or just generate our own big if..elses
|
1951
|
+
|
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,7 @@ 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)
|
1904
2001
|
if (i >= paramCount) {
|
1905
2002
|
// over param count of func, drop arg
|
1906
2003
|
out.push([ Opcodes.drop ]);
|
@@ -1985,8 +2082,11 @@ const knownType = (scope, type) => {
|
|
1985
2082
|
const idx = type[0][1];
|
1986
2083
|
|
1987
2084
|
// type idx = var idx + 1
|
1988
|
-
const
|
1989
|
-
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
|
+
}
|
1990
2090
|
}
|
1991
2091
|
|
1992
2092
|
return null;
|
@@ -2021,16 +2121,17 @@ const brTable = (input, bc, returns) => {
|
|
2021
2121
|
}
|
2022
2122
|
|
2023
2123
|
for (let i = 0; i < count; i++) {
|
2024
|
-
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 ]);
|
2025
2126
|
else out.push([ Opcodes.block, Blocktype.void ]);
|
2026
2127
|
}
|
2027
2128
|
|
2028
|
-
const nums = keys.filter(x => +x);
|
2129
|
+
const nums = keys.filter(x => +x >= 0);
|
2029
2130
|
const offset = Math.min(...nums);
|
2030
2131
|
const max = Math.max(...nums);
|
2031
2132
|
|
2032
2133
|
const table = [];
|
2033
|
-
let br =
|
2134
|
+
let br = 0;
|
2034
2135
|
|
2035
2136
|
for (let i = offset; i <= max; i++) {
|
2036
2137
|
// if branch for this num, go to that block
|
@@ -2070,10 +2171,9 @@ const brTable = (input, bc, returns) => {
|
|
2070
2171
|
br--;
|
2071
2172
|
}
|
2072
2173
|
|
2073
|
-
|
2074
|
-
|
2075
|
-
|
2076
|
-
];
|
2174
|
+
out.push([ Opcodes.end ]);
|
2175
|
+
|
2176
|
+
return out;
|
2077
2177
|
};
|
2078
2178
|
|
2079
2179
|
const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
|
@@ -2117,6 +2217,17 @@ const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
|
|
2117
2217
|
return out;
|
2118
2218
|
};
|
2119
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
|
+
|
2120
2231
|
const allocVar = (scope, name, global = false, type = true) => {
|
2121
2232
|
const target = global ? globals : scope.locals;
|
2122
2233
|
|
@@ -2133,7 +2244,7 @@ const allocVar = (scope, name, global = false, type = true) => {
|
|
2133
2244
|
|
2134
2245
|
if (type) {
|
2135
2246
|
let typeIdx = global ? globalInd++ : scope.localInd++;
|
2136
|
-
target[name + '#type'] = { idx: typeIdx, type: Valtype.i32 };
|
2247
|
+
target[name + '#type'] = { idx: typeIdx, type: Valtype.i32, name };
|
2137
2248
|
}
|
2138
2249
|
|
2139
2250
|
return idx;
|
@@ -2346,18 +2457,21 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
|
|
2346
2457
|
Opcodes.i32_to_u,
|
2347
2458
|
|
2348
2459
|
// turn into byte offset by * valtypeSize (4 for i32, 8 for i64/f64)
|
2349
|
-
...number(ValtypeSize[valtype], Valtype.i32),
|
2460
|
+
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
2350
2461
|
[ Opcodes.i32_mul ],
|
2351
2462
|
...(aotPointer ? [] : [ [ Opcodes.i32_add ] ]),
|
2352
2463
|
...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
|
2353
2464
|
|
2354
2465
|
...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
|
2355
2466
|
[ Opcodes.local_get, pointerTmp ],
|
2356
|
-
[ Opcodes.load,
|
2357
|
-
], 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)),
|
2358
2472
|
[ Opcodes.local_tee, newValueTmp ],
|
2359
2473
|
|
2360
|
-
[ Opcodes.store,
|
2474
|
+
[ Opcodes.store, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
|
2361
2475
|
],
|
2362
2476
|
|
2363
2477
|
default: internalThrow(scope, 'TypeError', `Cannot assign member with non-array`)
|
@@ -2465,6 +2579,7 @@ const generateUnary = (scope, decl) => {
|
|
2465
2579
|
];
|
2466
2580
|
|
2467
2581
|
case '!':
|
2582
|
+
// todo/perf: optimize !!
|
2468
2583
|
// !=
|
2469
2584
|
return falsy(scope, generate(scope, decl.argument), getNodeType(scope, decl.argument), false, false);
|
2470
2585
|
|
@@ -2611,21 +2726,16 @@ const generateConditional = (scope, decl) => {
|
|
2611
2726
|
out.push([ Opcodes.if, valtypeBinary ]);
|
2612
2727
|
depth.push('if');
|
2613
2728
|
|
2614
|
-
out.push(...generate(scope, decl.consequent));
|
2615
|
-
|
2616
|
-
// note type
|
2617
2729
|
out.push(
|
2618
|
-
...
|
2619
|
-
...setLastType(scope)
|
2730
|
+
...generate(scope, decl.consequent),
|
2731
|
+
...setLastType(scope, getNodeType(scope, decl.consequent))
|
2620
2732
|
);
|
2621
2733
|
|
2622
2734
|
out.push([ Opcodes.else ]);
|
2623
|
-
out.push(...generate(scope, decl.alternate));
|
2624
2735
|
|
2625
|
-
// note type
|
2626
2736
|
out.push(
|
2627
|
-
...
|
2628
|
-
...setLastType(scope)
|
2737
|
+
...generate(scope, decl.alternate),
|
2738
|
+
...setLastType(scope, getNodeType(scope, decl.alternate))
|
2629
2739
|
);
|
2630
2740
|
|
2631
2741
|
out.push([ Opcodes.end ]);
|
@@ -2773,12 +2883,15 @@ const generateForOf = (scope, decl) => {
|
|
2773
2883
|
// todo: optimize away counter and use end pointer
|
2774
2884
|
out.push(...typeSwitch(scope, getNodeType(scope, decl.right), {
|
2775
2885
|
[TYPES.array]: [
|
2776
|
-
...setType(scope, leftName, TYPES.number),
|
2777
|
-
|
2778
2886
|
[ Opcodes.loop, Blocktype.void ],
|
2779
2887
|
|
2780
2888
|
[ Opcodes.local_get, pointer ],
|
2781
|
-
[ 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
|
+
]),
|
2782
2895
|
|
2783
2896
|
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
|
2784
2897
|
|
@@ -2787,9 +2900,9 @@ const generateForOf = (scope, decl) => {
|
|
2787
2900
|
...generate(scope, decl.body),
|
2788
2901
|
[ Opcodes.end ],
|
2789
2902
|
|
2790
|
-
// increment iter pointer by valtype size
|
2903
|
+
// increment iter pointer by valtype size + 1
|
2791
2904
|
[ Opcodes.local_get, pointer ],
|
2792
|
-
...number(ValtypeSize[valtype], Valtype.i32),
|
2905
|
+
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
2793
2906
|
[ Opcodes.i32_add ],
|
2794
2907
|
[ Opcodes.local_set, pointer ],
|
2795
2908
|
|
@@ -3006,14 +3119,18 @@ const generateThrow = (scope, decl) => {
|
|
3006
3119
|
};
|
3007
3120
|
|
3008
3121
|
const generateTry = (scope, decl) => {
|
3009
|
-
|
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."
|
3010
3124
|
|
3011
3125
|
const out = [];
|
3012
3126
|
|
3127
|
+
const finalizer = decl.finalizer ? generate(scope, decl.finalizer) : [];
|
3128
|
+
|
3013
3129
|
out.push([ Opcodes.try, Blocktype.void ]);
|
3014
3130
|
depth.push('try');
|
3015
3131
|
|
3016
3132
|
out.push(...generate(scope, decl.block));
|
3133
|
+
out.push(...finalizer);
|
3017
3134
|
|
3018
3135
|
if (decl.handler) {
|
3019
3136
|
depth.pop();
|
@@ -3021,6 +3138,7 @@ const generateTry = (scope, decl) => {
|
|
3021
3138
|
|
3022
3139
|
out.push([ Opcodes.catch_all ]);
|
3023
3140
|
out.push(...generate(scope, decl.handler.body));
|
3141
|
+
out.push(...finalizer);
|
3024
3142
|
}
|
3025
3143
|
|
3026
3144
|
out.push([ Opcodes.end ]);
|
@@ -3106,7 +3224,7 @@ const getAllocType = itemType => {
|
|
3106
3224
|
}
|
3107
3225
|
};
|
3108
3226
|
|
3109
|
-
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) => {
|
3110
3228
|
const out = [];
|
3111
3229
|
|
3112
3230
|
scope.arrays ??= new Map();
|
@@ -3118,8 +3236,13 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
3118
3236
|
// todo: can we just have 1 undeclared array? probably not? but this is not really memory efficient
|
3119
3237
|
const uniqueName = name === '$undeclared' ? name + Math.random().toString().slice(2) : name;
|
3120
3238
|
|
3121
|
-
|
3122
|
-
|
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);
|
3123
3246
|
}
|
3124
3247
|
|
3125
3248
|
const pointer = scope.arrays.get(name);
|
@@ -3169,7 +3292,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
3169
3292
|
|
3170
3293
|
const pointerWasm = pointerTmp != null ? [ [ Opcodes.local_get, pointerTmp ] ] : number(pointer, Valtype.i32);
|
3171
3294
|
|
3172
|
-
// store length
|
3295
|
+
// store length
|
3173
3296
|
out.push(
|
3174
3297
|
...pointerWasm,
|
3175
3298
|
...number(length, Valtype.i32),
|
@@ -3177,14 +3300,20 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
3177
3300
|
);
|
3178
3301
|
|
3179
3302
|
const storeOp = StoreOps[itemType];
|
3180
|
-
|
3303
|
+
const sizePerEl = ValtypeSize[itemType] + (typed ? 1 : 0);
|
3181
3304
|
if (!initEmpty) for (let i = 0; i < length; i++) {
|
3182
3305
|
if (elements[i] == null) continue;
|
3183
3306
|
|
3307
|
+
const offset = ValtypeSize.i32 + i * sizePerEl;
|
3184
3308
|
out.push(
|
3185
3309
|
...pointerWasm,
|
3186
3310
|
...(useRawElements ? number(elements[i], Valtype[valtype]) : generate(scope, elements[i])),
|
3187
|
-
[ 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
|
+
])
|
3188
3317
|
);
|
3189
3318
|
}
|
3190
3319
|
|
@@ -3194,6 +3323,65 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
3194
3323
|
return [ out, pointer ];
|
3195
3324
|
};
|
3196
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
|
+
|
3197
3385
|
const byteStringable = str => {
|
3198
3386
|
if (!Prefs.bytestring) return false;
|
3199
3387
|
|
@@ -3222,14 +3410,39 @@ const makeString = (scope, str, global = false, name = '$undeclared', forceBytes
|
|
3222
3410
|
};
|
3223
3411
|
|
3224
3412
|
const generateArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false) => {
|
3225
|
-
return makeArray(scope, decl, global, name, initEmpty, valtype)[0];
|
3413
|
+
return makeArray(scope, decl, global, name, initEmpty, valtype, true)[0];
|
3414
|
+
};
|
3415
|
+
|
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
|
+
];
|
3226
3423
|
};
|
3227
3424
|
|
3228
|
-
|
3425
|
+
const withType = (scope, wasm, type) => [
|
3426
|
+
...wasm,
|
3427
|
+
...setLastType(scope, type)
|
3428
|
+
];
|
3429
|
+
|
3430
|
+
const generateMember = (scope, decl, _global, _name) => {
|
3229
3431
|
const name = decl.object.name;
|
3230
|
-
const pointer = scope.arrays?.get(name);
|
3231
3432
|
|
3232
|
-
|
3433
|
+
// hack: process.argv[n]
|
3434
|
+
if (name === '__process_argv') {
|
3435
|
+
const setPointer = scope.arrays?.get(_name);
|
3436
|
+
|
3437
|
+
return [
|
3438
|
+
...number(decl.property.value - 1),
|
3439
|
+
...(setPointer ? number(setPointer) : allocPage(scope, `__process_argv out (${randId()})`)),
|
3440
|
+
[ Opcodes.call, importedFuncs.__Porffor_readArgv ]
|
3441
|
+
];
|
3442
|
+
}
|
3443
|
+
|
3444
|
+
const pointer = scope.arrays?.get(name);
|
3445
|
+
const aotPointer = Prefs.aotPointerOpt && pointer;
|
3233
3446
|
|
3234
3447
|
// hack: .name
|
3235
3448
|
if (decl.property.name === 'name') {
|
@@ -3239,9 +3452,9 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3239
3452
|
// eg: __String_prototype_toLowerCase -> toLowerCase
|
3240
3453
|
if (nameProp.startsWith('__')) nameProp = nameProp.split('_').pop();
|
3241
3454
|
|
3242
|
-
return makeString(scope, nameProp, _global, _name, true);
|
3455
|
+
return withType(scope, makeString(scope, nameProp, _global, _name, true), TYPES.bytestring);
|
3243
3456
|
} else {
|
3244
|
-
return
|
3457
|
+
return withType(scope, number(0), TYPES.undefined);
|
3245
3458
|
}
|
3246
3459
|
}
|
3247
3460
|
|
@@ -3249,9 +3462,8 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3249
3462
|
if (decl.property.name === 'length') {
|
3250
3463
|
const func = funcs.find(x => x.name === name);
|
3251
3464
|
if (func) {
|
3252
|
-
const
|
3253
|
-
|
3254
|
-
return number(typedParams ? func.params.length / 2 : func.params.length);
|
3465
|
+
const typedParams = !func.internal || builtinFuncs[name]?.typedParams;
|
3466
|
+
return withType(scope, number(typedParams ? func.params.length / 2 : func.params.length), TYPES.number);
|
3255
3467
|
}
|
3256
3468
|
|
3257
3469
|
if (builtinFuncs[name + '$constructor']) {
|
@@ -3261,24 +3473,88 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3261
3473
|
const constructorFunc = builtinFuncs[name + '$constructor'];
|
3262
3474
|
const constructorParams = constructorFunc.typedParams ? (constructorFunc.params.length / 2) : constructorFunc.params.length;
|
3263
3475
|
|
3264
|
-
return number(Math.max(regularParams, constructorParams));
|
3476
|
+
return withType(scope, number(Math.max(regularParams, constructorParams)), TYPES.number);
|
3265
3477
|
}
|
3266
3478
|
|
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);
|
3479
|
+
if (builtinFuncs[name]) return withType(scope, number(builtinFuncs[name].typedParams ? (builtinFuncs[name].params.length / 2) : builtinFuncs[name].params.length), TYPES.number);
|
3480
|
+
if (importedFuncs[name]) return withType(scope, number(importedFuncs[name].params), TYPES.number);
|
3481
|
+
if (internalConstrs[name]) return withType(scope, number(internalConstrs[name].length ?? 0), TYPES.number);
|
3482
|
+
|
3483
|
+
if (Prefs.fastLength) {
|
3484
|
+
// presume valid length object
|
3485
|
+
return [
|
3486
|
+
...(aotPointer ? number(0, Valtype.i32) : [
|
3487
|
+
...generate(scope, decl.object),
|
3488
|
+
Opcodes.i32_to_u
|
3489
|
+
]),
|
3490
|
+
|
3491
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
|
3492
|
+
Opcodes.i32_from_u
|
3493
|
+
];
|
3494
|
+
}
|
3495
|
+
|
3496
|
+
const type = getNodeType(scope, decl.object);
|
3497
|
+
const known = knownType(scope, type);
|
3498
|
+
if (known != null) {
|
3499
|
+
if ([ TYPES.string, TYPES.bytestring, TYPES.array ].includes(known)) return [
|
3500
|
+
...(aotPointer ? number(0, Valtype.i32) : [
|
3501
|
+
...generate(scope, decl.object),
|
3502
|
+
Opcodes.i32_to_u
|
3503
|
+
]),
|
3504
|
+
|
3505
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
|
3506
|
+
Opcodes.i32_from_u
|
3507
|
+
];
|
3508
|
+
|
3509
|
+
return number(0);
|
3510
|
+
}
|
3270
3511
|
|
3271
3512
|
return [
|
3272
|
-
...(
|
3273
|
-
|
3274
|
-
|
3275
|
-
|
3513
|
+
...typeIsOneOf(getNodeType(scope, decl.object), [ TYPES.string, TYPES.bytestring, TYPES.array ]),
|
3514
|
+
[ Opcodes.if, valtypeBinary ],
|
3515
|
+
...(aotPointer ? number(0, Valtype.i32) : [
|
3516
|
+
...generate(scope, decl.object),
|
3517
|
+
Opcodes.i32_to_u
|
3518
|
+
]),
|
3519
|
+
|
3520
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
|
3521
|
+
Opcodes.i32_from_u,
|
3276
3522
|
|
3277
|
-
|
3278
|
-
Opcodes.
|
3523
|
+
...setLastType(scope, TYPES.number),
|
3524
|
+
[ Opcodes.else ],
|
3525
|
+
...number(0),
|
3526
|
+
...setLastType(scope, TYPES.undefined),
|
3527
|
+
[ Opcodes.end ]
|
3279
3528
|
];
|
3280
3529
|
}
|
3281
3530
|
|
3531
|
+
// todo: generate this array procedurally during builtinFuncs creation
|
3532
|
+
if (['size', 'description'].includes(decl.property.name)) {
|
3533
|
+
const bc = {};
|
3534
|
+
const cands = Object.keys(builtinFuncs).filter(x => x.startsWith('__') && x.endsWith('_prototype_' + decl.property.name + '$get'));
|
3535
|
+
|
3536
|
+
if (cands.length > 0) {
|
3537
|
+
for (const x of cands) {
|
3538
|
+
const type = TYPES[x.split('_prototype_')[0].slice(2).toLowerCase()];
|
3539
|
+
if (type == null) continue;
|
3540
|
+
|
3541
|
+
bc[type] = generateCall(scope, {
|
3542
|
+
callee: {
|
3543
|
+
type: 'Identifier',
|
3544
|
+
name: x
|
3545
|
+
},
|
3546
|
+
arguments: [ decl.object ],
|
3547
|
+
_protoInternalCall: true
|
3548
|
+
});
|
3549
|
+
}
|
3550
|
+
}
|
3551
|
+
|
3552
|
+
return typeSwitch(scope, getNodeType(scope, decl.object), {
|
3553
|
+
...bc,
|
3554
|
+
default: withType(scope, number(0), TYPES.undefined)
|
3555
|
+
}, valtypeBinary);
|
3556
|
+
}
|
3557
|
+
|
3282
3558
|
const object = generate(scope, decl.object);
|
3283
3559
|
const property = generate(scope, decl.property);
|
3284
3560
|
|
@@ -3293,24 +3569,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3293
3569
|
|
3294
3570
|
return typeSwitch(scope, getNodeType(scope, decl.object), {
|
3295
3571
|
[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),
|
3572
|
+
...loadArray(scope, object, property, aotPointer),
|
3314
3573
|
...setLastType(scope)
|
3315
3574
|
],
|
3316
3575
|
|
@@ -3341,9 +3600,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3341
3600
|
|
3342
3601
|
// return new string (page)
|
3343
3602
|
...number(newPointer),
|
3344
|
-
|
3345
|
-
...number(TYPES.string, Valtype.i32),
|
3346
|
-
...setLastType(scope)
|
3603
|
+
...setLastType(scope, TYPES.string)
|
3347
3604
|
],
|
3348
3605
|
[TYPES.bytestring]: [
|
3349
3606
|
// setup new/out array
|
@@ -3369,9 +3626,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3369
3626
|
|
3370
3627
|
// return new string (page)
|
3371
3628
|
...number(newPointer),
|
3372
|
-
|
3373
|
-
...number(TYPES.bytestring, Valtype.i32),
|
3374
|
-
...setLastType(scope)
|
3629
|
+
...setLastType(scope, TYPES.bytestring)
|
3375
3630
|
],
|
3376
3631
|
|
3377
3632
|
default: internalThrow(scope, 'TypeError', 'Member expression is not supported for non-string non-array yet', true)
|
@@ -3396,7 +3651,7 @@ const objectHack = node => {
|
|
3396
3651
|
if (!objectName) objectName = objectHack(node.object)?.name?.slice?.(2);
|
3397
3652
|
|
3398
3653
|
// if .name or .length, give up (hack within a hack!)
|
3399
|
-
if (['name', 'length'].includes(node.property.name)) {
|
3654
|
+
if (['name', 'length', 'size', 'description'].includes(node.property.name)) {
|
3400
3655
|
node.object = objectHack(node.object);
|
3401
3656
|
return;
|
3402
3657
|
}
|
@@ -3433,33 +3688,39 @@ const generateFunc = (scope, decl) => {
|
|
3433
3688
|
const name = decl.id ? decl.id.name : `anonymous_${randId()}`;
|
3434
3689
|
const params = decl.params ?? [];
|
3435
3690
|
|
3436
|
-
// const innerScope = { ...scope };
|
3437
3691
|
// TODO: share scope/locals between !!!
|
3438
|
-
const
|
3692
|
+
const func = {
|
3439
3693
|
locals: {},
|
3440
3694
|
localInd: 0,
|
3441
3695
|
// value, type
|
3442
3696
|
returns: [ valtypeBinary, Valtype.i32 ],
|
3443
3697
|
throws: false,
|
3444
|
-
name
|
3698
|
+
name,
|
3699
|
+
index: currentFuncIndex++
|
3445
3700
|
};
|
3446
3701
|
|
3447
3702
|
if (typedInput && decl.returnType) {
|
3448
3703
|
const { type } = extractTypeAnnotation(decl.returnType);
|
3449
|
-
if (type != null && !Prefs.indirectCalls) {
|
3450
|
-
|
3451
|
-
|
3704
|
+
// if (type != null && !Prefs.indirectCalls) {
|
3705
|
+
if (type != null) {
|
3706
|
+
func.returnType = type;
|
3707
|
+
func.returns = [ valtypeBinary ];
|
3452
3708
|
}
|
3453
3709
|
}
|
3454
3710
|
|
3455
3711
|
for (let i = 0; i < params.length; i++) {
|
3456
|
-
|
3712
|
+
const name = params[i].name;
|
3713
|
+
// if (name == null) return todo('non-identifier args are not supported');
|
3714
|
+
|
3715
|
+
allocVar(func, name, false);
|
3457
3716
|
|
3458
3717
|
if (typedInput && params[i].typeAnnotation) {
|
3459
|
-
addVarMetadata(
|
3718
|
+
addVarMetadata(func, name, false, extractTypeAnnotation(params[i]));
|
3460
3719
|
}
|
3461
3720
|
}
|
3462
3721
|
|
3722
|
+
func.params = Object.values(func.locals).map(x => x.type);
|
3723
|
+
|
3463
3724
|
let body = objectHack(decl.body);
|
3464
3725
|
if (decl.type === 'ArrowFunctionExpression' && decl.expression) {
|
3465
3726
|
// hack: () => 0 -> () => return 0
|
@@ -3469,37 +3730,23 @@ const generateFunc = (scope, decl) => {
|
|
3469
3730
|
};
|
3470
3731
|
}
|
3471
3732
|
|
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
3733
|
funcIndex[name] = func.index;
|
3734
|
+
funcs.push(func);
|
3480
3735
|
|
3481
|
-
|
3736
|
+
const wasm = generate(func, body);
|
3737
|
+
func.wasm = wasm;
|
3482
3738
|
|
3483
|
-
|
3484
|
-
for (const inst of wasm) {
|
3485
|
-
if (inst[0] === Opcodes.call && inst[1] === -1) {
|
3486
|
-
inst[1] = func.index;
|
3487
|
-
}
|
3488
|
-
}
|
3739
|
+
if (name === 'main') func.gotLastType = true;
|
3489
3740
|
|
3490
3741
|
// add end return if not found
|
3491
3742
|
if (name !== 'main' && wasm[wasm.length - 1]?.[0] !== Opcodes.return && countLeftover(wasm) === 0) {
|
3492
3743
|
wasm.push(
|
3493
3744
|
...number(0),
|
3494
|
-
...(
|
3745
|
+
...(func.returnType != null ? [] : number(TYPES.undefined, Valtype.i32)),
|
3495
3746
|
[ Opcodes.return ]
|
3496
3747
|
);
|
3497
3748
|
}
|
3498
3749
|
|
3499
|
-
func.wasm = wasm;
|
3500
|
-
|
3501
|
-
funcs.push(func);
|
3502
|
-
|
3503
3750
|
return func;
|
3504
3751
|
};
|
3505
3752
|
|
@@ -3603,7 +3850,7 @@ const internalConstrs = {
|
|
3603
3850
|
generate: (scope, decl) => {
|
3604
3851
|
// todo: boolean object when used as constructor
|
3605
3852
|
const arg = decl.arguments[0] ?? DEFAULT_VALUE;
|
3606
|
-
return truthy(scope, generate(scope, arg), getNodeType(scope, arg));
|
3853
|
+
return truthy(scope, generate(scope, arg), getNodeType(scope, arg), false, false, 'full');
|
3607
3854
|
},
|
3608
3855
|
type: TYPES.boolean,
|
3609
3856
|
length: 1
|
@@ -3748,9 +3995,8 @@ export default program => {
|
|
3748
3995
|
|
3749
3996
|
if (Prefs.astLog) console.log(JSON.stringify(program.body.body, null, 2));
|
3750
3997
|
|
3751
|
-
generateFunc(scope, program);
|
3998
|
+
const main = generateFunc(scope, program);
|
3752
3999
|
|
3753
|
-
const main = funcs[funcs.length - 1];
|
3754
4000
|
main.export = true;
|
3755
4001
|
main.returns = [ valtypeBinary, Valtype.i32 ];
|
3756
4002
|
|
@@ -3777,7 +4023,7 @@ export default program => {
|
|
3777
4023
|
}
|
3778
4024
|
|
3779
4025
|
// 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(
|
4026
|
+
if (main.wasm.length === 0 && funcs.reduce((acc, x) => acc + (x.export ? 1 : 0), 0) > 1) funcs.splice(main.index - importedFuncs.length, 1);
|
3781
4027
|
|
3782
4028
|
return { funcs, globals, tags, exceptions, pages, data };
|
3783
4029
|
};
|