porffor 0.14.0-f67c123a1 → 0.16.0-594397507
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 +15 -9
- package/README.md +9 -13
- package/asur/index.js +1 -1
- package/compiler/2c.js +68 -3
- package/compiler/assemble.js +26 -1
- 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/console.ts +6 -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/porffor.d.ts +8 -0
- package/compiler/builtins/set.ts +20 -7
- package/compiler/builtins/string.ts +1 -0
- package/compiler/builtins/symbol.ts +62 -0
- package/compiler/builtins.js +27 -9
- package/compiler/codegen.js +576 -257
- package/compiler/decompile.js +5 -1
- package/compiler/generated_builtins.js +555 -60
- package/compiler/index.js +5 -9
- package/compiler/parse.js +2 -2
- 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 +5 -4
- package/runner/repl.js +18 -2
package/compiler/codegen.js
CHANGED
@@ -40,11 +40,11 @@ const todo = (scope, msg, expectsValue = undefined) => {
|
|
40
40
|
}
|
41
41
|
};
|
42
42
|
|
43
|
-
const isFuncType = type =>
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
43
|
+
const isFuncType = type =>
|
44
|
+
type === 'FunctionDeclaration' || type === 'FunctionExpression' || type === 'ArrowFunctionExpression';
|
45
|
+
const hasFuncWithName = name =>
|
46
|
+
funcIndex[name] != null || builtinFuncs[name] != null || importedFuncs[name] != null || internalConstrs[name] != null;
|
47
|
+
|
48
48
|
const generate = (scope, decl, global = false, name = undefined, valueUnused = false) => {
|
49
49
|
switch (decl.type) {
|
50
50
|
case 'BinaryExpression':
|
@@ -140,18 +140,23 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
140
140
|
case 'ArrayExpression':
|
141
141
|
return generateArray(scope, decl, global, name);
|
142
142
|
|
143
|
+
case 'ObjectExpression':
|
144
|
+
return generateObject(scope, decl, global, name);
|
145
|
+
|
143
146
|
case 'MemberExpression':
|
144
147
|
return generateMember(scope, decl, global, name);
|
145
148
|
|
146
149
|
case 'ExportNamedDeclaration':
|
147
|
-
|
148
|
-
const funcsBefore = funcs.length;
|
150
|
+
const funcsBefore = funcs.map(x => x.name);
|
149
151
|
generate(scope, decl.declaration);
|
150
152
|
|
151
|
-
|
152
|
-
|
153
|
-
const
|
154
|
-
|
153
|
+
// set new funcs as exported
|
154
|
+
if (funcsBefore.length !== funcs.length) {
|
155
|
+
const newFuncs = funcs.filter(x => !funcsBefore.includes(x.name)).filter(x => !x.internal);
|
156
|
+
|
157
|
+
for (const x of newFuncs) {
|
158
|
+
x.export = true;
|
159
|
+
}
|
155
160
|
}
|
156
161
|
|
157
162
|
return [];
|
@@ -200,19 +205,11 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
200
205
|
|
201
206
|
__Porffor_bs: str => [
|
202
207
|
...makeString(scope, str, global, name, true),
|
203
|
-
|
204
|
-
...(name ? setType(scope, name, TYPES.bytestring) : [
|
205
|
-
...number(TYPES.bytestring, Valtype.i32),
|
206
|
-
...setLastType(scope)
|
207
|
-
])
|
208
|
+
...(name ? setType(scope, name, TYPES.bytestring) : setLastType(scope, TYPES.bytestring))
|
208
209
|
],
|
209
210
|
__Porffor_s: str => [
|
210
211
|
...makeString(scope, str, global, name, false),
|
211
|
-
|
212
|
-
...(name ? setType(scope, name, TYPES.string) : [
|
213
|
-
...number(TYPES.string, Valtype.i32),
|
214
|
-
...setLastType(scope)
|
215
|
-
])
|
212
|
+
...(name ? setType(scope, name, TYPES.string) : setLastType(scope, TYPES.string))
|
216
213
|
],
|
217
214
|
};
|
218
215
|
|
@@ -351,9 +348,7 @@ const generateReturn = (scope, decl) => {
|
|
351
348
|
|
352
349
|
return [
|
353
350
|
...generate(scope, decl.argument),
|
354
|
-
...(scope.returnType != null ? [] :
|
355
|
-
...getNodeType(scope, decl.argument)
|
356
|
-
]),
|
351
|
+
...(scope.returnType != null ? [] : getNodeType(scope, decl.argument)),
|
357
352
|
[ Opcodes.return ]
|
358
353
|
];
|
359
354
|
};
|
@@ -368,7 +363,7 @@ const localTmp = (scope, name, type = valtypeBinary) => {
|
|
368
363
|
};
|
369
364
|
|
370
365
|
const isIntOp = op => op && ((op[0] >= 0x45 && op[0] <= 0x4f) || (op[0] >= 0x67 && op[0] <= 0x78) || op[0] === 0x41);
|
371
|
-
const
|
366
|
+
const isIntToFloatOp = op => op && (op[0] >= 0xb7 && op[0] <= 0xba);
|
372
367
|
|
373
368
|
const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
374
369
|
const checks = {
|
@@ -385,10 +380,10 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
385
380
|
|
386
381
|
// if we can, use int tmp and convert at the end to help prevent unneeded conversions
|
387
382
|
// (like if we are in an if condition - very common)
|
388
|
-
const
|
389
|
-
const
|
383
|
+
const leftWasInt = isIntToFloatOp(left[left.length - 1]);
|
384
|
+
const rightWasInt = isIntToFloatOp(right[right.length - 1]);
|
390
385
|
|
391
|
-
const canInt =
|
386
|
+
const canInt = leftWasInt && rightWasInt;
|
392
387
|
|
393
388
|
if (canInt) {
|
394
389
|
// remove int -> float conversions from left and right
|
@@ -402,13 +397,11 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
402
397
|
[ Opcodes.if, Valtype.i32 ],
|
403
398
|
...right,
|
404
399
|
// note type
|
405
|
-
...rightType,
|
406
|
-
...setLastType(scope),
|
400
|
+
...setLastType(scope, rightType),
|
407
401
|
[ Opcodes.else ],
|
408
402
|
[ Opcodes.local_get, localTmp(scope, 'logictmpi', Valtype.i32) ],
|
409
403
|
// note type
|
410
|
-
...leftType,
|
411
|
-
...setLastType(scope),
|
404
|
+
...setLastType(scope, leftType),
|
412
405
|
[ Opcodes.end ],
|
413
406
|
Opcodes.i32_from
|
414
407
|
];
|
@@ -421,13 +414,11 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
421
414
|
[ Opcodes.if, valtypeBinary ],
|
422
415
|
...right,
|
423
416
|
// note type
|
424
|
-
...rightType,
|
425
|
-
...setLastType(scope),
|
417
|
+
...setLastType(scope, rightType),
|
426
418
|
[ Opcodes.else ],
|
427
419
|
[ Opcodes.local_get, localTmp(scope, 'logictmp') ],
|
428
420
|
// note type
|
429
|
-
...leftType,
|
430
|
-
...setLastType(scope),
|
421
|
+
...setLastType(scope, leftType),
|
431
422
|
[ Opcodes.end ]
|
432
423
|
];
|
433
424
|
};
|
@@ -455,11 +446,11 @@ const concatStrings = (scope, left, right, global, name, assign = false, bytestr
|
|
455
446
|
...number(0, Valtype.i32), // base 0 for store later
|
456
447
|
|
457
448
|
...number(pointer, Valtype.i32),
|
458
|
-
[ Opcodes.i32_load,
|
449
|
+
[ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
|
459
450
|
[ Opcodes.local_tee, leftLength ],
|
460
451
|
|
461
452
|
[ Opcodes.local_get, rightPointer ],
|
462
|
-
[ Opcodes.i32_load,
|
453
|
+
[ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
|
463
454
|
[ Opcodes.local_tee, rightLength ],
|
464
455
|
|
465
456
|
[ Opcodes.i32_add ],
|
@@ -515,11 +506,11 @@ const concatStrings = (scope, left, right, global, name, assign = false, bytestr
|
|
515
506
|
...number(0, Valtype.i32), // base 0 for store later
|
516
507
|
|
517
508
|
[ Opcodes.local_get, leftPointer ],
|
518
|
-
[ Opcodes.i32_load,
|
509
|
+
[ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
|
519
510
|
[ Opcodes.local_tee, leftLength ],
|
520
511
|
|
521
512
|
[ Opcodes.local_get, rightPointer ],
|
522
|
-
[ Opcodes.i32_load,
|
513
|
+
[ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
|
523
514
|
[ Opcodes.local_tee, rightLength ],
|
524
515
|
|
525
516
|
[ Opcodes.i32_add ],
|
@@ -597,11 +588,11 @@ const compareStrings = (scope, left, right, bytestrings = false) => {
|
|
597
588
|
|
598
589
|
// get lengths
|
599
590
|
[ Opcodes.local_get, leftPointer ],
|
600
|
-
[ Opcodes.i32_load,
|
591
|
+
[ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
|
601
592
|
[ Opcodes.local_tee, leftLength ],
|
602
593
|
|
603
594
|
[ Opcodes.local_get, rightPointer ],
|
604
|
-
[ Opcodes.i32_load,
|
595
|
+
[ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
|
605
596
|
|
606
597
|
// fast path: check leftLength != rightLength
|
607
598
|
[ Opcodes.i32_ne ],
|
@@ -657,9 +648,9 @@ const compareStrings = (scope, left, right, bytestrings = false) => {
|
|
657
648
|
[ Opcodes.i32_add ],
|
658
649
|
[ Opcodes.local_tee, index ],
|
659
650
|
|
660
|
-
// if index
|
651
|
+
// if index < index end (length * sizeof valtype), loop
|
661
652
|
[ Opcodes.local_get, indexEnd ],
|
662
|
-
[ Opcodes.
|
653
|
+
[ Opcodes.i32_lt_s ],
|
663
654
|
[ Opcodes.br_if, 0 ],
|
664
655
|
[ Opcodes.end ],
|
665
656
|
|
@@ -677,38 +668,50 @@ const compareStrings = (scope, left, right, bytestrings = false) => {
|
|
677
668
|
];
|
678
669
|
};
|
679
670
|
|
680
|
-
const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
681
|
-
if (
|
671
|
+
const truthy = (scope, wasm, type, intIn = false, intOut = false, forceTruthyMode = undefined) => {
|
672
|
+
if (isIntToFloatOp(wasm[wasm.length - 1])) return [
|
682
673
|
...wasm,
|
683
674
|
...(!intIn && intOut ? [ Opcodes.i32_to_u ] : [])
|
684
675
|
];
|
685
676
|
// if (isIntOp(wasm[wasm.length - 1])) return [ ...wasm ];
|
686
677
|
|
678
|
+
// todo/perf: use knownType and custom bytecode here instead of typeSwitch
|
679
|
+
|
687
680
|
const useTmp = knownType(scope, type) == null;
|
688
681
|
const tmp = useTmp && localTmp(scope, `#logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
|
689
682
|
|
690
|
-
const def =
|
691
|
-
|
692
|
-
|
683
|
+
const def = (truthyMode => {
|
684
|
+
if (truthyMode === 'full') return [
|
685
|
+
// if value != 0 or NaN
|
686
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
687
|
+
...(intIn ? [ ] : [ Opcodes.i32_to ]),
|
693
688
|
|
694
|
-
|
695
|
-
|
689
|
+
[ Opcodes.i32_eqz ],
|
690
|
+
[ Opcodes.i32_eqz ],
|
696
691
|
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
692
|
+
...(intOut ? [] : [ Opcodes.i32_from ]),
|
693
|
+
];
|
694
|
+
|
695
|
+
if (truthyMode === 'no_negative') return [
|
696
|
+
// if value != 0 or NaN, non-binary output. negative numbers not truthy :/
|
697
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
698
|
+
...(intIn ? [] : [ Opcodes.i32_to ]),
|
699
|
+
...(intOut ? [] : [ Opcodes.i32_from ])
|
700
|
+
];
|
701
|
+
|
702
|
+
if (truthyMode === 'no_nan_negative') return [
|
703
|
+
// simpler and faster but makes NaN truthy and negative numbers not truthy,
|
704
|
+
// plus non-binary output
|
705
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
706
|
+
...(!intOut || (intIn && intOut) ? [] : [ Opcodes.i32_to_u ])
|
707
|
+
];
|
708
|
+
})(forceTruthyMode ?? Prefs.truthy ?? 'full');
|
701
709
|
|
702
710
|
return [
|
703
711
|
...wasm,
|
704
712
|
...(!useTmp ? [] : [ [ Opcodes.local_set, tmp ] ]),
|
705
713
|
|
706
714
|
...typeSwitch(scope, type, {
|
707
|
-
// [TYPES.number]: def,
|
708
|
-
[TYPES.array]: [
|
709
|
-
// arrays are always truthy
|
710
|
-
...number(1, intOut ? Valtype.i32 : valtypeBinary)
|
711
|
-
],
|
712
715
|
[TYPES.string]: [
|
713
716
|
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
714
717
|
...(intIn ? [] : [ Opcodes.i32_to_u ]),
|
@@ -744,10 +747,6 @@ const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
744
747
|
...(!useTmp ? [] : [ [ Opcodes.local_set, tmp ] ]),
|
745
748
|
|
746
749
|
...typeSwitch(scope, type, {
|
747
|
-
[TYPES.array]: [
|
748
|
-
// arrays are always truthy
|
749
|
-
...number(0, intOut ? Valtype.i32 : valtypeBinary)
|
750
|
-
],
|
751
750
|
[TYPES.string]: [
|
752
751
|
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
753
752
|
...(intIn ? [] : [ Opcodes.i32_to_u ]),
|
@@ -991,7 +990,7 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
991
990
|
// if both are true
|
992
991
|
[ Opcodes.i32_and ],
|
993
992
|
[ Opcodes.if, Blocktype.void ],
|
994
|
-
...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ]),
|
993
|
+
...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ], false),
|
995
994
|
...(op === '!==' || op === '!=' ? [ [ Opcodes.i32_eqz ] ] : []),
|
996
995
|
[ Opcodes.br, 1 ],
|
997
996
|
[ Opcodes.end ],
|
@@ -1040,14 +1039,14 @@ const generateBinaryExp = (scope, decl, _global, _name) => {
|
|
1040
1039
|
return out;
|
1041
1040
|
};
|
1042
1041
|
|
1043
|
-
const asmFuncToAsm = (func,
|
1044
|
-
return func(
|
1042
|
+
const asmFuncToAsm = (func, scope) => {
|
1043
|
+
return func(scope, {
|
1045
1044
|
TYPES, TYPE_NAMES, typeSwitch, makeArray, makeString, allocPage, internalThrow,
|
1046
|
-
builtin:
|
1047
|
-
let idx = funcIndex[
|
1048
|
-
if (idx
|
1049
|
-
includeBuiltin(null,
|
1050
|
-
idx = funcIndex[
|
1045
|
+
builtin: n => {
|
1046
|
+
let idx = funcIndex[n] ?? importedFuncs[n];
|
1047
|
+
if (idx == null && builtinFuncs[n]) {
|
1048
|
+
includeBuiltin(null, n);
|
1049
|
+
idx = funcIndex[n];
|
1051
1050
|
}
|
1052
1051
|
|
1053
1052
|
return idx;
|
@@ -1055,7 +1054,7 @@ const asmFuncToAsm = (func, { name = '#unknown_asm_func', params = [], locals =
|
|
1055
1054
|
});
|
1056
1055
|
};
|
1057
1056
|
|
1058
|
-
const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits, returns, returnType, localNames = [], globalNames = [], data: _data = [] }) => {
|
1057
|
+
const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits, returns, returnType, localNames = [], globalNames = [], data: _data = [], table = false }) => {
|
1059
1058
|
const existing = funcs.find(x => x.name === name);
|
1060
1059
|
if (existing) return existing;
|
1061
1060
|
|
@@ -1073,7 +1072,22 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
|
|
1073
1072
|
data.push(copy);
|
1074
1073
|
}
|
1075
1074
|
|
1076
|
-
|
1075
|
+
const func = {
|
1076
|
+
name,
|
1077
|
+
params,
|
1078
|
+
locals,
|
1079
|
+
localInd: allLocals.length,
|
1080
|
+
returns,
|
1081
|
+
returnType: returnType ?? TYPES.number,
|
1082
|
+
internal: true,
|
1083
|
+
index: currentFuncIndex++,
|
1084
|
+
table
|
1085
|
+
};
|
1086
|
+
|
1087
|
+
funcs.push(func);
|
1088
|
+
funcIndex[name] = func.index;
|
1089
|
+
|
1090
|
+
if (typeof wasm === 'function') wasm = asmFuncToAsm(wasm, func);
|
1077
1091
|
|
1078
1092
|
let baseGlobalIdx, i = 0;
|
1079
1093
|
for (const type of globalTypes) {
|
@@ -1092,19 +1106,14 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
|
|
1092
1106
|
}
|
1093
1107
|
}
|
1094
1108
|
|
1095
|
-
const
|
1096
|
-
|
1097
|
-
|
1098
|
-
|
1099
|
-
|
1100
|
-
|
1101
|
-
wasm,
|
1102
|
-
internal: true,
|
1103
|
-
index: currentFuncIndex++
|
1104
|
-
};
|
1109
|
+
if (table) for (const inst of wasm) {
|
1110
|
+
if (inst[0] === Opcodes.i32_load16_u && inst.at(-1) === 'read_argc') {
|
1111
|
+
inst.splice(2, 99);
|
1112
|
+
inst.push(...unsignedLEB128(allocPage({}, 'func argc lut') * pageSize));
|
1113
|
+
}
|
1114
|
+
}
|
1105
1115
|
|
1106
|
-
|
1107
|
-
funcIndex[name] = func.index;
|
1116
|
+
func.wasm = wasm;
|
1108
1117
|
|
1109
1118
|
return func;
|
1110
1119
|
};
|
@@ -1196,9 +1205,10 @@ const getLastType = scope => {
|
|
1196
1205
|
return [ [ Opcodes.local_get, localTmp(scope, '#last_type', Valtype.i32) ] ];
|
1197
1206
|
};
|
1198
1207
|
|
1199
|
-
const setLastType = scope =>
|
1200
|
-
|
1201
|
-
|
1208
|
+
const setLastType = (scope, type = []) => [
|
1209
|
+
...(typeof type === 'number' ? number(type, Valtype.i32) : type),
|
1210
|
+
[ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ]
|
1211
|
+
];
|
1202
1212
|
|
1203
1213
|
const getNodeType = (scope, node) => {
|
1204
1214
|
const ret = (() => {
|
@@ -1259,7 +1269,17 @@ const getNodeType = (scope, node) => {
|
|
1259
1269
|
|
1260
1270
|
const func = spl[spl.length - 1];
|
1261
1271
|
const protoFuncs = Object.keys(prototypeFuncs).filter(x => x != TYPES.bytestring && prototypeFuncs[x][func] != null);
|
1262
|
-
if (protoFuncs.length === 1)
|
1272
|
+
if (protoFuncs.length === 1) {
|
1273
|
+
if (protoFuncs[0].returnType) return protoFuncs[0].returnType;
|
1274
|
+
}
|
1275
|
+
|
1276
|
+
if (protoFuncs.length > 0) {
|
1277
|
+
if (scope.locals['#last_type']) return getLastType(scope);
|
1278
|
+
|
1279
|
+
// presume
|
1280
|
+
// todo: warn here?
|
1281
|
+
return TYPES.number;
|
1282
|
+
}
|
1263
1283
|
}
|
1264
1284
|
|
1265
1285
|
if (name.startsWith('__Porffor_wasm_')) {
|
@@ -1354,22 +1374,27 @@ const getNodeType = (scope, node) => {
|
|
1354
1374
|
}
|
1355
1375
|
|
1356
1376
|
if (node.type === 'MemberExpression') {
|
1357
|
-
|
1358
|
-
|
1359
|
-
|
1360
|
-
|
1361
|
-
|
1362
|
-
return TYPES.undefined;
|
1363
|
-
}
|
1377
|
+
const name = node.property.name;
|
1378
|
+
|
1379
|
+
if (name === 'length') {
|
1380
|
+
if (hasFuncWithName(node.object.name)) return TYPES.number;
|
1381
|
+
if (Prefs.fastLength) return TYPES.number;
|
1364
1382
|
}
|
1365
1383
|
|
1366
|
-
// hack: if something.length, number type
|
1367
|
-
if (node.property.name === 'length') return TYPES.number;
|
1368
1384
|
|
1369
|
-
|
1370
|
-
if (
|
1371
|
-
|
1372
|
-
|
1385
|
+
const objectKnownType = knownType(scope, getNodeType(scope, node.object));
|
1386
|
+
if (objectKnownType != null) {
|
1387
|
+
if (name === 'length') {
|
1388
|
+
if ([ TYPES.string, TYPES.bytestring, TYPES.array ].includes(objectKnownType)) return TYPES.number;
|
1389
|
+
else return TYPES.undefined;
|
1390
|
+
}
|
1391
|
+
|
1392
|
+
if (node.computed) {
|
1393
|
+
if (objectKnownType === TYPES.string) return TYPES.string;
|
1394
|
+
if (objectKnownType === TYPES.bytestring) return TYPES.bytestring;
|
1395
|
+
if (objectKnownType === TYPES.array) return TYPES.number;
|
1396
|
+
}
|
1397
|
+
}
|
1373
1398
|
|
1374
1399
|
if (scope.locals['#last_type']) return getLastType(scope);
|
1375
1400
|
|
@@ -1440,17 +1465,16 @@ const countLeftover = wasm => {
|
|
1440
1465
|
else if (inst[0] === Opcodes.return) count = 0;
|
1441
1466
|
else if (inst[0] === Opcodes.call) {
|
1442
1467
|
let func = funcs.find(x => x.index === inst[1]);
|
1443
|
-
if (inst[1]
|
1444
|
-
|
1445
|
-
|
1446
|
-
count -= importedFuncs[inst[1]].params;
|
1447
|
-
count += importedFuncs[inst[1]].returns;
|
1468
|
+
if (inst[1] < importedFuncs.length) {
|
1469
|
+
func = importedFuncs[inst[1]];
|
1470
|
+
count = count - func.params + func.returns;
|
1448
1471
|
} else {
|
1449
|
-
|
1450
|
-
count -= func.params.length;
|
1451
|
-
} else count--;
|
1452
|
-
if (func) count += func.returns.length;
|
1472
|
+
count = count - func.params.length + func.returns.length;
|
1453
1473
|
}
|
1474
|
+
} else if (inst[0] === Opcodes.call_indirect) {
|
1475
|
+
count--; // funcidx
|
1476
|
+
count -= inst[1] * 2; // params * 2 (typed)
|
1477
|
+
count += 2; // fixed return (value, type)
|
1454
1478
|
} else count--;
|
1455
1479
|
|
1456
1480
|
// console.log(count, decompile([ inst ]).slice(0, -1));
|
@@ -1468,7 +1492,7 @@ const disposeLeftover = wasm => {
|
|
1468
1492
|
const generateExp = (scope, decl) => {
|
1469
1493
|
const expression = decl.expression;
|
1470
1494
|
|
1471
|
-
const out = generate(scope, expression, undefined, undefined,
|
1495
|
+
const out = generate(scope, expression, undefined, undefined, Prefs.optUnused);
|
1472
1496
|
disposeLeftover(out);
|
1473
1497
|
|
1474
1498
|
return out;
|
@@ -1559,16 +1583,10 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1559
1583
|
out.splice(out.length - 1, 1);
|
1560
1584
|
|
1561
1585
|
const finalStatement = parsed.body[parsed.body.length - 1];
|
1562
|
-
out.push(
|
1563
|
-
...getNodeType(scope, finalStatement),
|
1564
|
-
...setLastType(scope)
|
1565
|
-
);
|
1586
|
+
out.push(...setLastType(scope, getNodeType(scope, finalStatement)));
|
1566
1587
|
} else if (countLeftover(out) === 0) {
|
1567
1588
|
out.push(...number(UNDEFINED));
|
1568
|
-
out.push(
|
1569
|
-
...number(TYPES.undefined, Valtype.i32),
|
1570
|
-
...setLastType(scope)
|
1571
|
-
);
|
1589
|
+
out.push(...setLastType(scope, TYPES.undefined));
|
1572
1590
|
}
|
1573
1591
|
|
1574
1592
|
// if (lastInst && lastInst[0] === Opcodes.drop) {
|
@@ -1604,6 +1622,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1604
1622
|
|
1605
1623
|
if (!funcIndex[rhemynName]) {
|
1606
1624
|
const func = Rhemyn[funcName](regex, currentFuncIndex++, rhemynName);
|
1625
|
+
func.internal = true;
|
1607
1626
|
|
1608
1627
|
funcIndex[func.name] = func.index;
|
1609
1628
|
funcs.push(func);
|
@@ -1620,8 +1639,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1620
1639
|
[ Opcodes.call, idx ],
|
1621
1640
|
Opcodes.i32_from_u,
|
1622
1641
|
|
1623
|
-
...
|
1624
|
-
...setLastType(scope)
|
1642
|
+
...setLastType(scope, TYPES.boolean)
|
1625
1643
|
];
|
1626
1644
|
}
|
1627
1645
|
|
@@ -1693,9 +1711,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1693
1711
|
if (protoFunc.noArgRetLength && decl.arguments.length === 0) {
|
1694
1712
|
protoBC[x] = [
|
1695
1713
|
...RTArrayUtil.getLength(getPointer),
|
1696
|
-
|
1697
|
-
...number(TYPES.number, Valtype.i32),
|
1698
|
-
...setLastType(scope)
|
1714
|
+
...setLastType(scope, TYPES.number)
|
1699
1715
|
];
|
1700
1716
|
continue;
|
1701
1717
|
}
|
@@ -1714,7 +1730,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1714
1730
|
getI32: () => RTArrayUtil.getLengthI32(getPointer),
|
1715
1731
|
set: value => RTArrayUtil.setLength(getPointer, value),
|
1716
1732
|
setI32: value => RTArrayUtil.setLengthI32(getPointer, value)
|
1717
|
-
}, generate(scope, decl.arguments[0] ?? DEFAULT_VALUE), protoLocal, protoLocal2, (length, itemType) => {
|
1733
|
+
}, generate(scope, decl.arguments[0] ?? DEFAULT_VALUE), getNodeType(scope, decl.arguments[0] ?? DEFAULT_VALUE), protoLocal, protoLocal2, (length, itemType) => {
|
1718
1734
|
return makeArray(scope, {
|
1719
1735
|
rawElements: new Array(length)
|
1720
1736
|
}, _global, _name, true, itemType);
|
@@ -1728,9 +1744,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1728
1744
|
protoBC[x] = [
|
1729
1745
|
[ Opcodes.block, unusedValue && optUnused ? Blocktype.void : valtypeBinary ],
|
1730
1746
|
...protoOut,
|
1731
|
-
|
1732
|
-
...number(protoFunc.returnType ?? TYPES.number, Valtype.i32),
|
1733
|
-
...setLastType(scope),
|
1747
|
+
...(unusedValue && optUnused ? [] : (protoFunc.returnType != null ? setLastType(scope, protoFunc.returnType) : setLastType(scope))),
|
1734
1748
|
[ Opcodes.end ]
|
1735
1749
|
];
|
1736
1750
|
}
|
@@ -1780,11 +1794,6 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1780
1794
|
|
1781
1795
|
if (idx === undefined && internalConstrs[name]) return internalConstrs[name].generate(scope, decl, _global, _name);
|
1782
1796
|
|
1783
|
-
if (idx === undefined && name === scope.name) {
|
1784
|
-
// hack: calling self, func generator will fix later
|
1785
|
-
idx = -1;
|
1786
|
-
}
|
1787
|
-
|
1788
1797
|
if (idx === undefined && name.startsWith('__Porffor_wasm_')) {
|
1789
1798
|
const wasmOps = {
|
1790
1799
|
// pointer, align, offset
|
@@ -1833,30 +1842,143 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1833
1842
|
|
1834
1843
|
if (idx === undefined) {
|
1835
1844
|
if (scope.locals[name] !== undefined || globals[name] !== undefined || builtinVars[name] !== undefined) {
|
1836
|
-
if (!Prefs.indirectCalls) return internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, true);
|
1837
|
-
|
1838
1845
|
const [ local, global ] = lookupName(scope, name);
|
1846
|
+
if (!Prefs.indirectCalls || local == null) return internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, true);
|
1847
|
+
|
1848
|
+
// todo: only works when function uses typedParams and typedReturns
|
1849
|
+
|
1850
|
+
const indirectMode = Prefs.indirectCallMode ?? 'vararg';
|
1851
|
+
// options: vararg, strict
|
1852
|
+
// - strict: simpler, smaller size usage, no func argc lut needed.
|
1853
|
+
// ONLY works when arg count of call == arg count of function being called
|
1854
|
+
// - vararg: large size usage, cursed.
|
1855
|
+
// works when arg count of call != arg count of function being called*
|
1856
|
+
// * most of the time, some edgecases
|
1857
|
+
|
1839
1858
|
funcs.table = true;
|
1859
|
+
scope.table = true;
|
1860
|
+
|
1861
|
+
let args = decl.arguments;
|
1862
|
+
let out = [];
|
1863
|
+
|
1864
|
+
let locals = [];
|
1865
|
+
|
1866
|
+
if (indirectMode === 'vararg') {
|
1867
|
+
const minArgc = Prefs.indirectCallMinArgc ?? 3;
|
1868
|
+
|
1869
|
+
if (args.length < minArgc) {
|
1870
|
+
args = args.concat(new Array(minArgc - args.length).fill(DEFAULT_VALUE));
|
1871
|
+
}
|
1872
|
+
}
|
1873
|
+
|
1874
|
+
for (let i = 0; i < args.length; i++) {
|
1875
|
+
const arg = args[i];
|
1876
|
+
out = out.concat(generate(scope, arg));
|
1877
|
+
|
1878
|
+
if (valtypeBinary !== Valtype.i32 && (
|
1879
|
+
(builtinFuncs[name] && builtinFuncs[name].params[i * (typedParams ? 2 : 1)] === Valtype.i32) ||
|
1880
|
+
(importedFuncs[name] && name.startsWith('profile'))
|
1881
|
+
)) {
|
1882
|
+
out.push(Opcodes.i32_to);
|
1883
|
+
}
|
1884
|
+
|
1885
|
+
out = out.concat(getNodeType(scope, arg));
|
1886
|
+
|
1887
|
+
if (indirectMode === 'vararg') {
|
1888
|
+
const typeLocal = localTmp(scope, `#indirect_arg${i}_type`, Valtype.i32);
|
1889
|
+
const valLocal = localTmp(scope, `#indirect_arg${i}_val`);
|
1890
|
+
|
1891
|
+
locals.push([valLocal, typeLocal]);
|
1892
|
+
|
1893
|
+
out.push(
|
1894
|
+
[ Opcodes.local_set, typeLocal ],
|
1895
|
+
[ Opcodes.local_set, valLocal ]
|
1896
|
+
);
|
1897
|
+
}
|
1898
|
+
}
|
1899
|
+
|
1900
|
+
if (indirectMode === 'strict') {
|
1901
|
+
return typeSwitch(scope, getNodeType(scope, decl.callee), {
|
1902
|
+
[TYPES.function]: [
|
1903
|
+
...argWasm,
|
1904
|
+
[ global ? Opcodes.global_get : Opcodes.local_get, local.idx ],
|
1905
|
+
Opcodes.i32_to_u,
|
1906
|
+
[ Opcodes.call_indirect, args.length, 0 ],
|
1907
|
+
...setLastType(scope)
|
1908
|
+
],
|
1909
|
+
default: internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, true)
|
1910
|
+
});
|
1911
|
+
}
|
1912
|
+
|
1913
|
+
// hi, I will now explain how vararg mode works:
|
1914
|
+
// wasm's indirect_call instruction requires you know the func type at compile-time
|
1915
|
+
// since we have varargs (variable argument count), we do not know it.
|
1916
|
+
// we could just store args in memory and not use wasm func args,
|
1917
|
+
// but that is slow (probably) and breaks js exports.
|
1918
|
+
// instead, we generate every* possibility of argc and use different indirect_call
|
1919
|
+
// ops for each one, with type depending on argc for that branch.
|
1920
|
+
// then we load the argc for the wanted function from a memory lut,
|
1921
|
+
// and call the branch with the matching argc we require.
|
1922
|
+
// sorry, yes it is very cursed (and size inefficient), but indirect calls
|
1923
|
+
// are kind of rare anyway (mostly callbacks) so I am not concerned atm.
|
1924
|
+
// *for argc 0-3, in future (todo:) the max number should be
|
1925
|
+
// dynamically changed to the max argc of any func in the js file.
|
1926
|
+
|
1927
|
+
const funcLocal = localTmp(scope, `#indirect_func`, Valtype.i32);
|
1928
|
+
|
1929
|
+
const gen = argc => {
|
1930
|
+
const out = [];
|
1931
|
+
for (let i = 0; i < argc; i++) {
|
1932
|
+
out.push(
|
1933
|
+
[ Opcodes.local_get, locals[i][0] ],
|
1934
|
+
[ Opcodes.local_get, locals[i][1] ]
|
1935
|
+
);
|
1936
|
+
}
|
1937
|
+
|
1938
|
+
out.push(
|
1939
|
+
[ Opcodes.local_get, funcLocal ],
|
1940
|
+
[ Opcodes.call_indirect, argc, 0 ],
|
1941
|
+
...setLastType(scope)
|
1942
|
+
)
|
1943
|
+
|
1944
|
+
return out;
|
1945
|
+
};
|
1946
|
+
|
1947
|
+
const tableBc = {};
|
1948
|
+
for (let i = 0; i <= args.length; i++) {
|
1949
|
+
tableBc[i] = gen(i);
|
1950
|
+
}
|
1840
1951
|
|
1841
|
-
// todo:
|
1842
|
-
|
1843
|
-
|
1844
|
-
return typeSwitch(scope, getNodeType(decl.callee), {
|
1952
|
+
// todo/perf: check if we should use br_table here or just generate our own big if..elses
|
1953
|
+
|
1954
|
+
return typeSwitch(scope, getNodeType(scope, decl.callee), {
|
1845
1955
|
[TYPES.function]: [
|
1956
|
+
...out,
|
1957
|
+
|
1846
1958
|
[ global ? Opcodes.global_get : Opcodes.local_get, local.idx ],
|
1847
|
-
|
1959
|
+
Opcodes.i32_to_u,
|
1960
|
+
[ Opcodes.local_set, funcLocal ],
|
1961
|
+
|
1962
|
+
...brTable([
|
1963
|
+
// get argc of func we are calling
|
1964
|
+
[ Opcodes.local_get, funcLocal ],
|
1965
|
+
...number(ValtypeSize.i16, Valtype.i32),
|
1966
|
+
[ Opcodes.i32_mul ],
|
1967
|
+
|
1968
|
+
[ Opcodes.i32_load16_u, 0, ...unsignedLEB128(allocPage(scope, 'func argc lut') * pageSize), 'read_argc' ]
|
1969
|
+
], tableBc, valtypeBinary)
|
1848
1970
|
],
|
1849
1971
|
default: internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, true)
|
1850
1972
|
});
|
1851
1973
|
}
|
1974
|
+
|
1852
1975
|
return internalThrow(scope, 'ReferenceError', `${unhackName(name)} is not defined`, true);
|
1853
1976
|
}
|
1854
1977
|
|
1855
|
-
const func = funcs.find(x => x.index === idx);
|
1856
|
-
|
1857
|
-
const userFunc = (funcIndex[name] && !importedFuncs[name] && !builtinFuncs[name] && !internalConstrs[name]) || idx === -1;
|
1978
|
+
const func = funcs[idx - importedFuncs.length]; // idx === scope.index ? scope : funcs.find(x => x.index === idx);
|
1979
|
+
const userFunc = func && !func.internal;
|
1858
1980
|
const typedParams = userFunc || builtinFuncs[name]?.typedParams;
|
1859
|
-
const typedReturns = (func
|
1981
|
+
const typedReturns = (func && func.returnType == null) || builtinFuncs[name]?.typedReturns;
|
1860
1982
|
const paramCount = func && (typedParams ? func.params.length / 2 : func.params.length);
|
1861
1983
|
|
1862
1984
|
let args = decl.arguments;
|
@@ -1877,6 +1999,13 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1877
1999
|
const arg = args[i];
|
1878
2000
|
out = out.concat(generate(scope, arg));
|
1879
2001
|
|
2002
|
+
// todo: this should be used instead of the too many args thing above (by removing that)
|
2003
|
+
if (i >= paramCount) {
|
2004
|
+
// over param count of func, drop arg
|
2005
|
+
out.push([ Opcodes.drop ]);
|
2006
|
+
continue;
|
2007
|
+
}
|
2008
|
+
|
1880
2009
|
if (valtypeBinary !== Valtype.i32 && (
|
1881
2010
|
(builtinFuncs[name] && builtinFuncs[name].params[i * (typedParams ? 2 : 1)] === Valtype.i32) ||
|
1882
2011
|
(importedFuncs[name] && name.startsWith('profile'))
|
@@ -1925,6 +2054,11 @@ const generateNew = (scope, decl, _global, _name) => {
|
|
1925
2054
|
}, _global, _name);
|
1926
2055
|
}
|
1927
2056
|
|
2057
|
+
if (
|
2058
|
+
(builtinFuncs[name] && !builtinFuncs[name].constr) ||
|
2059
|
+
(internalConstrs[name] && builtinFuncs[name].notConstr)
|
2060
|
+
) return internalThrow(scope, 'TypeError', `${name} is not a constructor`);
|
2061
|
+
|
1928
2062
|
if (!builtinFuncs[name]) return todo(scope, `new statement is not supported yet`); // return todo(scope, `new statement is not supported yet (new ${unhackName(name)})`);
|
1929
2063
|
|
1930
2064
|
return generateCall(scope, decl, _global, _name);
|
@@ -1950,8 +2084,11 @@ const knownType = (scope, type) => {
|
|
1950
2084
|
const idx = type[0][1];
|
1951
2085
|
|
1952
2086
|
// type idx = var idx + 1
|
1953
|
-
const
|
1954
|
-
if (
|
2087
|
+
const name = Object.values(scope.locals).find(x => x.idx === idx)?.name;
|
2088
|
+
if (name) {
|
2089
|
+
const local = scope.locals[name];
|
2090
|
+
if (local.metadata?.type != null) return v.metadata.type;
|
2091
|
+
}
|
1955
2092
|
}
|
1956
2093
|
|
1957
2094
|
return null;
|
@@ -1986,16 +2123,17 @@ const brTable = (input, bc, returns) => {
|
|
1986
2123
|
}
|
1987
2124
|
|
1988
2125
|
for (let i = 0; i < count; i++) {
|
1989
|
-
if (i === 0) out.push([ Opcodes.block, returns, 'br table start' ]);
|
2126
|
+
// if (i === 0) out.push([ Opcodes.block, returns, 'br table start' ]);
|
2127
|
+
if (i === 0) out.push([ Opcodes.block, returns ]);
|
1990
2128
|
else out.push([ Opcodes.block, Blocktype.void ]);
|
1991
2129
|
}
|
1992
2130
|
|
1993
|
-
const nums = keys.filter(x => +x);
|
2131
|
+
const nums = keys.filter(x => +x >= 0);
|
1994
2132
|
const offset = Math.min(...nums);
|
1995
2133
|
const max = Math.max(...nums);
|
1996
2134
|
|
1997
2135
|
const table = [];
|
1998
|
-
let br =
|
2136
|
+
let br = 0;
|
1999
2137
|
|
2000
2138
|
for (let i = offset; i <= max; i++) {
|
2001
2139
|
// if branch for this num, go to that block
|
@@ -2035,10 +2173,9 @@ const brTable = (input, bc, returns) => {
|
|
2035
2173
|
br--;
|
2036
2174
|
}
|
2037
2175
|
|
2038
|
-
|
2039
|
-
|
2040
|
-
|
2041
|
-
];
|
2176
|
+
out.push([ Opcodes.end ]);
|
2177
|
+
|
2178
|
+
return out;
|
2042
2179
|
};
|
2043
2180
|
|
2044
2181
|
const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
|
@@ -2082,6 +2219,17 @@ const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
|
|
2082
2219
|
return out;
|
2083
2220
|
};
|
2084
2221
|
|
2222
|
+
const typeIsOneOf = (type, types, valtype = Valtype.i32) => {
|
2223
|
+
const out = [];
|
2224
|
+
|
2225
|
+
for (let i = 0; i < types.length; i++) {
|
2226
|
+
out.push(...type, ...number(types[i], valtype), valtype === Valtype.f64 ? [ Opcodes.f64_eq ] : [ Opcodes.i32_eq ]);
|
2227
|
+
if (i !== 0) out.push([ Opcodes.i32_or ]);
|
2228
|
+
}
|
2229
|
+
|
2230
|
+
return out;
|
2231
|
+
};
|
2232
|
+
|
2085
2233
|
const allocVar = (scope, name, global = false, type = true) => {
|
2086
2234
|
const target = global ? globals : scope.locals;
|
2087
2235
|
|
@@ -2098,7 +2246,7 @@ const allocVar = (scope, name, global = false, type = true) => {
|
|
2098
2246
|
|
2099
2247
|
if (type) {
|
2100
2248
|
let typeIdx = global ? globalInd++ : scope.localInd++;
|
2101
|
-
target[name + '#type'] = { idx: typeIdx, type: Valtype.i32 };
|
2249
|
+
target[name + '#type'] = { idx: typeIdx, type: Valtype.i32, name };
|
2102
2250
|
}
|
2103
2251
|
|
2104
2252
|
return idx;
|
@@ -2311,18 +2459,21 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
|
|
2311
2459
|
Opcodes.i32_to_u,
|
2312
2460
|
|
2313
2461
|
// turn into byte offset by * valtypeSize (4 for i32, 8 for i64/f64)
|
2314
|
-
...number(ValtypeSize[valtype], Valtype.i32),
|
2462
|
+
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
2315
2463
|
[ Opcodes.i32_mul ],
|
2316
2464
|
...(aotPointer ? [] : [ [ Opcodes.i32_add ] ]),
|
2317
2465
|
...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
|
2318
2466
|
|
2319
2467
|
...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
|
2320
2468
|
[ Opcodes.local_get, pointerTmp ],
|
2321
|
-
[ Opcodes.load,
|
2322
|
-
], generate(scope, decl.right),
|
2469
|
+
[ Opcodes.load, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
|
2470
|
+
], generate(scope, decl.right), [
|
2471
|
+
[ Opcodes.local_get, pointerTmp ],
|
2472
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32 + ValtypeSize[valtype]) ]
|
2473
|
+
], getNodeType(scope, decl.right), false, name, true)),
|
2323
2474
|
[ Opcodes.local_tee, newValueTmp ],
|
2324
2475
|
|
2325
|
-
[ Opcodes.store,
|
2476
|
+
[ Opcodes.store, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
|
2326
2477
|
],
|
2327
2478
|
|
2328
2479
|
default: internalThrow(scope, 'TypeError', `Cannot assign member with non-array`)
|
@@ -2430,6 +2581,11 @@ const generateUnary = (scope, decl) => {
|
|
2430
2581
|
];
|
2431
2582
|
|
2432
2583
|
case '!':
|
2584
|
+
const arg = decl.argument;
|
2585
|
+
if (arg.type === 'UnaryExpression' && arg.operator === '!') {
|
2586
|
+
// !!x -> is x truthy
|
2587
|
+
return truthy(scope, generate(scope, arg.argument), getNodeType(scope, arg.argument), false, false);
|
2588
|
+
}
|
2433
2589
|
// !=
|
2434
2590
|
return falsy(scope, generate(scope, decl.argument), getNodeType(scope, decl.argument), false, false);
|
2435
2591
|
|
@@ -2499,6 +2655,7 @@ const generateUnary = (scope, decl) => {
|
|
2499
2655
|
[TYPES.string]: makeString(scope, 'string', false, '#typeof_result'),
|
2500
2656
|
[TYPES.undefined]: makeString(scope, 'undefined', false, '#typeof_result'),
|
2501
2657
|
[TYPES.function]: makeString(scope, 'function', false, '#typeof_result'),
|
2658
|
+
[TYPES.symbol]: makeString(scope, 'symbol', false, '#typeof_result'),
|
2502
2659
|
|
2503
2660
|
[TYPES.bytestring]: makeString(scope, 'string', false, '#typeof_result'),
|
2504
2661
|
|
@@ -2575,21 +2732,16 @@ const generateConditional = (scope, decl) => {
|
|
2575
2732
|
out.push([ Opcodes.if, valtypeBinary ]);
|
2576
2733
|
depth.push('if');
|
2577
2734
|
|
2578
|
-
out.push(...generate(scope, decl.consequent));
|
2579
|
-
|
2580
|
-
// note type
|
2581
2735
|
out.push(
|
2582
|
-
...
|
2583
|
-
...setLastType(scope)
|
2736
|
+
...generate(scope, decl.consequent),
|
2737
|
+
...setLastType(scope, getNodeType(scope, decl.consequent))
|
2584
2738
|
);
|
2585
2739
|
|
2586
2740
|
out.push([ Opcodes.else ]);
|
2587
|
-
out.push(...generate(scope, decl.alternate));
|
2588
2741
|
|
2589
|
-
// note type
|
2590
2742
|
out.push(
|
2591
|
-
...
|
2592
|
-
...setLastType(scope)
|
2743
|
+
...generate(scope, decl.alternate),
|
2744
|
+
...setLastType(scope, getNodeType(scope, decl.alternate))
|
2593
2745
|
);
|
2594
2746
|
|
2595
2747
|
out.push([ Opcodes.end ]);
|
@@ -2737,12 +2889,15 @@ const generateForOf = (scope, decl) => {
|
|
2737
2889
|
// todo: optimize away counter and use end pointer
|
2738
2890
|
out.push(...typeSwitch(scope, getNodeType(scope, decl.right), {
|
2739
2891
|
[TYPES.array]: [
|
2740
|
-
...setType(scope, leftName, TYPES.number),
|
2741
|
-
|
2742
2892
|
[ Opcodes.loop, Blocktype.void ],
|
2743
2893
|
|
2744
2894
|
[ Opcodes.local_get, pointer ],
|
2745
|
-
[ Opcodes.load,
|
2895
|
+
[ Opcodes.load, 0, ...unsignedLEB128(ValtypeSize.i32) ],
|
2896
|
+
|
2897
|
+
...setType(scope, leftName, [
|
2898
|
+
[ Opcodes.local_get, pointer ],
|
2899
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32 + ValtypeSize[valtype]) ],
|
2900
|
+
]),
|
2746
2901
|
|
2747
2902
|
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
|
2748
2903
|
|
@@ -2751,9 +2906,9 @@ const generateForOf = (scope, decl) => {
|
|
2751
2906
|
...generate(scope, decl.body),
|
2752
2907
|
[ Opcodes.end ],
|
2753
2908
|
|
2754
|
-
// increment iter pointer by valtype size
|
2909
|
+
// increment iter pointer by valtype size + 1
|
2755
2910
|
[ Opcodes.local_get, pointer ],
|
2756
|
-
...number(ValtypeSize[valtype], Valtype.i32),
|
2911
|
+
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
2757
2912
|
[ Opcodes.i32_add ],
|
2758
2913
|
[ Opcodes.local_set, pointer ],
|
2759
2914
|
|
@@ -2869,6 +3024,44 @@ const generateForOf = (scope, decl) => {
|
|
2869
3024
|
[ Opcodes.end ],
|
2870
3025
|
[ Opcodes.end ]
|
2871
3026
|
],
|
3027
|
+
[TYPES.set]: [
|
3028
|
+
[ Opcodes.loop, Blocktype.void ],
|
3029
|
+
|
3030
|
+
[ Opcodes.local_get, pointer ],
|
3031
|
+
[ Opcodes.load, 0, ...unsignedLEB128(ValtypeSize.i32) ],
|
3032
|
+
|
3033
|
+
...setType(scope, leftName, [
|
3034
|
+
[ Opcodes.local_get, pointer ],
|
3035
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32 + ValtypeSize[valtype]) ],
|
3036
|
+
]),
|
3037
|
+
|
3038
|
+
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
|
3039
|
+
|
3040
|
+
[ Opcodes.block, Blocktype.void ],
|
3041
|
+
[ Opcodes.block, Blocktype.void ],
|
3042
|
+
...generate(scope, decl.body),
|
3043
|
+
[ Opcodes.end ],
|
3044
|
+
|
3045
|
+
// increment iter pointer by valtype size + 1
|
3046
|
+
[ Opcodes.local_get, pointer ],
|
3047
|
+
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
3048
|
+
[ Opcodes.i32_add ],
|
3049
|
+
[ Opcodes.local_set, pointer ],
|
3050
|
+
|
3051
|
+
// increment counter by 1
|
3052
|
+
[ Opcodes.local_get, counter ],
|
3053
|
+
...number(1, Valtype.i32),
|
3054
|
+
[ Opcodes.i32_add ],
|
3055
|
+
[ Opcodes.local_tee, counter ],
|
3056
|
+
|
3057
|
+
// loop if counter != length
|
3058
|
+
[ Opcodes.local_get, length ],
|
3059
|
+
[ Opcodes.i32_ne ],
|
3060
|
+
[ Opcodes.br_if, 1 ],
|
3061
|
+
|
3062
|
+
[ Opcodes.end ],
|
3063
|
+
[ Opcodes.end ]
|
3064
|
+
],
|
2872
3065
|
default: internalThrow(scope, 'TypeError', `Tried for..of on non-iterable type`)
|
2873
3066
|
}, Blocktype.void));
|
2874
3067
|
|
@@ -2970,14 +3163,18 @@ const generateThrow = (scope, decl) => {
|
|
2970
3163
|
};
|
2971
3164
|
|
2972
3165
|
const generateTry = (scope, decl) => {
|
2973
|
-
|
3166
|
+
// todo: handle control-flow pre-exit for finally
|
3167
|
+
// "Immediately before a control-flow statement (return, throw, break, continue) is executed in the try block or catch block."
|
2974
3168
|
|
2975
3169
|
const out = [];
|
2976
3170
|
|
3171
|
+
const finalizer = decl.finalizer ? generate(scope, decl.finalizer) : [];
|
3172
|
+
|
2977
3173
|
out.push([ Opcodes.try, Blocktype.void ]);
|
2978
3174
|
depth.push('try');
|
2979
3175
|
|
2980
3176
|
out.push(...generate(scope, decl.block));
|
3177
|
+
out.push(...finalizer);
|
2981
3178
|
|
2982
3179
|
if (decl.handler) {
|
2983
3180
|
depth.pop();
|
@@ -2985,6 +3182,7 @@ const generateTry = (scope, decl) => {
|
|
2985
3182
|
|
2986
3183
|
out.push([ Opcodes.catch_all ]);
|
2987
3184
|
out.push(...generate(scope, decl.handler.body));
|
3185
|
+
out.push(...finalizer);
|
2988
3186
|
}
|
2989
3187
|
|
2990
3188
|
out.push([ Opcodes.end ]);
|
@@ -3070,7 +3268,7 @@ const getAllocType = itemType => {
|
|
3070
3268
|
}
|
3071
3269
|
};
|
3072
3270
|
|
3073
|
-
const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false, itemType = valtype) => {
|
3271
|
+
const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false, itemType = valtype, typed = false) => {
|
3074
3272
|
const out = [];
|
3075
3273
|
|
3076
3274
|
scope.arrays ??= new Map();
|
@@ -3082,8 +3280,13 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
3082
3280
|
// todo: can we just have 1 undeclared array? probably not? but this is not really memory efficient
|
3083
3281
|
const uniqueName = name === '$undeclared' ? name + Math.random().toString().slice(2) : name;
|
3084
3282
|
|
3085
|
-
|
3086
|
-
|
3283
|
+
let page;
|
3284
|
+
if (Prefs.scopedPageNames) page = allocPage(scope, `${getAllocType(itemType)}: ${scope.name}/${uniqueName}`, itemType);
|
3285
|
+
else page = allocPage(scope, `${getAllocType(itemType)}: ${uniqueName}`, itemType);
|
3286
|
+
|
3287
|
+
// hack: use 1 for page 0 pointer for fast truthiness
|
3288
|
+
const ptr = page === 0 ? 1 : (page * pageSize);
|
3289
|
+
scope.arrays.set(name, ptr);
|
3087
3290
|
}
|
3088
3291
|
|
3089
3292
|
const pointer = scope.arrays.get(name);
|
@@ -3133,7 +3336,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
3133
3336
|
|
3134
3337
|
const pointerWasm = pointerTmp != null ? [ [ Opcodes.local_get, pointerTmp ] ] : number(pointer, Valtype.i32);
|
3135
3338
|
|
3136
|
-
// store length
|
3339
|
+
// store length
|
3137
3340
|
out.push(
|
3138
3341
|
...pointerWasm,
|
3139
3342
|
...number(length, Valtype.i32),
|
@@ -3141,14 +3344,20 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
3141
3344
|
);
|
3142
3345
|
|
3143
3346
|
const storeOp = StoreOps[itemType];
|
3144
|
-
|
3347
|
+
const sizePerEl = ValtypeSize[itemType] + (typed ? 1 : 0);
|
3145
3348
|
if (!initEmpty) for (let i = 0; i < length; i++) {
|
3146
3349
|
if (elements[i] == null) continue;
|
3147
3350
|
|
3351
|
+
const offset = ValtypeSize.i32 + i * sizePerEl;
|
3148
3352
|
out.push(
|
3149
3353
|
...pointerWasm,
|
3150
3354
|
...(useRawElements ? number(elements[i], Valtype[valtype]) : generate(scope, elements[i])),
|
3151
|
-
[ storeOp,
|
3355
|
+
[ storeOp, 0, ...unsignedLEB128(offset) ],
|
3356
|
+
...(!typed ? [] : [ // typed presumes !useRawElements
|
3357
|
+
...pointerWasm,
|
3358
|
+
...getNodeType(scope, elements[i]),
|
3359
|
+
[ Opcodes.i32_store8, 0, ...unsignedLEB128(offset + ValtypeSize[itemType]) ]
|
3360
|
+
])
|
3152
3361
|
);
|
3153
3362
|
}
|
3154
3363
|
|
@@ -3158,6 +3367,65 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
3158
3367
|
return [ out, pointer ];
|
3159
3368
|
};
|
3160
3369
|
|
3370
|
+
const storeArray = (scope, array, index, element, aotPointer = null) => {
|
3371
|
+
if (!Array.isArray(element)) element = generate(scope, element);
|
3372
|
+
if (typeof index === 'number') index = number(index);
|
3373
|
+
|
3374
|
+
const offset = localTmp(scope, '#storeArray_offset', Valtype.i32);
|
3375
|
+
|
3376
|
+
return [
|
3377
|
+
// calculate offset
|
3378
|
+
...index,
|
3379
|
+
Opcodes.i32_to_u,
|
3380
|
+
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
3381
|
+
[ Opcodes.i32_mul ],
|
3382
|
+
...(aotPointer ? [] : [
|
3383
|
+
...array,
|
3384
|
+
Opcodes.i32_to_u,
|
3385
|
+
[ Opcodes.i32_add ],
|
3386
|
+
]),
|
3387
|
+
[ Opcodes.local_set, offset ],
|
3388
|
+
|
3389
|
+
// store value
|
3390
|
+
[ Opcodes.local_get, offset ],
|
3391
|
+
...generate(scope, element),
|
3392
|
+
[ Opcodes.store, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
3393
|
+
|
3394
|
+
// store type
|
3395
|
+
[ Opcodes.local_get, offset ],
|
3396
|
+
...getNodeType(scope, element),
|
3397
|
+
[ Opcodes.i32_store8, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32 + ValtypeSize[valtype]) ]
|
3398
|
+
];
|
3399
|
+
};
|
3400
|
+
|
3401
|
+
const loadArray = (scope, array, index, aotPointer = null) => {
|
3402
|
+
if (typeof index === 'number') index = number(index);
|
3403
|
+
|
3404
|
+
const offset = localTmp(scope, '#loadArray_offset', Valtype.i32);
|
3405
|
+
|
3406
|
+
return [
|
3407
|
+
// calculate offset
|
3408
|
+
...index,
|
3409
|
+
Opcodes.i32_to_u,
|
3410
|
+
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
3411
|
+
[ Opcodes.i32_mul ],
|
3412
|
+
...(aotPointer ? [] : [
|
3413
|
+
...array,
|
3414
|
+
Opcodes.i32_to_u,
|
3415
|
+
[ Opcodes.i32_add ],
|
3416
|
+
]),
|
3417
|
+
[ Opcodes.local_set, offset ],
|
3418
|
+
|
3419
|
+
// load value
|
3420
|
+
[ Opcodes.local_get, offset ],
|
3421
|
+
[ Opcodes.load, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
3422
|
+
|
3423
|
+
// load type
|
3424
|
+
[ Opcodes.local_get, offset ],
|
3425
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32 + ValtypeSize[valtype]) ]
|
3426
|
+
];
|
3427
|
+
};
|
3428
|
+
|
3161
3429
|
const byteStringable = str => {
|
3162
3430
|
if (!Prefs.bytestring) return false;
|
3163
3431
|
|
@@ -3186,14 +3454,28 @@ const makeString = (scope, str, global = false, name = '$undeclared', forceBytes
|
|
3186
3454
|
};
|
3187
3455
|
|
3188
3456
|
const generateArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false) => {
|
3189
|
-
return makeArray(scope, decl, global, name, initEmpty, valtype)[0];
|
3457
|
+
return makeArray(scope, decl, global, name, initEmpty, valtype, true)[0];
|
3458
|
+
};
|
3459
|
+
|
3460
|
+
const generateObject = (scope, decl, global = false, name = '$undeclared') => {
|
3461
|
+
if (decl.properties.length > 0) return todo(scope, 'objects are not supported yet', true);
|
3462
|
+
|
3463
|
+
return [
|
3464
|
+
...number(1),
|
3465
|
+
...setLastType(scope, TYPES.object)
|
3466
|
+
];
|
3190
3467
|
};
|
3191
3468
|
|
3192
|
-
|
3469
|
+
const withType = (scope, wasm, type) => [
|
3470
|
+
...wasm,
|
3471
|
+
...setLastType(scope, type)
|
3472
|
+
];
|
3473
|
+
|
3474
|
+
const generateMember = (scope, decl, _global, _name) => {
|
3193
3475
|
const name = decl.object.name;
|
3194
3476
|
const pointer = scope.arrays?.get(name);
|
3195
3477
|
|
3196
|
-
const aotPointer = Prefs.aotPointerOpt && pointer
|
3478
|
+
const aotPointer = Prefs.aotPointerOpt && pointer;
|
3197
3479
|
|
3198
3480
|
// hack: .name
|
3199
3481
|
if (decl.property.name === 'name') {
|
@@ -3203,9 +3485,9 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3203
3485
|
// eg: __String_prototype_toLowerCase -> toLowerCase
|
3204
3486
|
if (nameProp.startsWith('__')) nameProp = nameProp.split('_').pop();
|
3205
3487
|
|
3206
|
-
return makeString(scope, nameProp, _global, _name, true);
|
3488
|
+
return withType(scope, makeString(scope, nameProp, _global, _name, true), TYPES.bytestring);
|
3207
3489
|
} else {
|
3208
|
-
return
|
3490
|
+
return withType(scope, number(0), TYPES.undefined);
|
3209
3491
|
}
|
3210
3492
|
}
|
3211
3493
|
|
@@ -3213,9 +3495,8 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3213
3495
|
if (decl.property.name === 'length') {
|
3214
3496
|
const func = funcs.find(x => x.name === name);
|
3215
3497
|
if (func) {
|
3216
|
-
const
|
3217
|
-
|
3218
|
-
return number(typedParams ? func.params.length / 2 : func.params.length);
|
3498
|
+
const typedParams = !func.internal || builtinFuncs[name]?.typedParams;
|
3499
|
+
return withType(scope, number(typedParams ? func.params.length / 2 : func.params.length), TYPES.number);
|
3219
3500
|
}
|
3220
3501
|
|
3221
3502
|
if (builtinFuncs[name + '$constructor']) {
|
@@ -3225,24 +3506,88 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3225
3506
|
const constructorFunc = builtinFuncs[name + '$constructor'];
|
3226
3507
|
const constructorParams = constructorFunc.typedParams ? (constructorFunc.params.length / 2) : constructorFunc.params.length;
|
3227
3508
|
|
3228
|
-
return number(Math.max(regularParams, constructorParams));
|
3509
|
+
return withType(scope, number(Math.max(regularParams, constructorParams)), TYPES.number);
|
3229
3510
|
}
|
3230
3511
|
|
3231
|
-
if (builtinFuncs[name]) return number(builtinFuncs[name].typedParams ? (builtinFuncs[name].params.length / 2) : builtinFuncs[name].params.length);
|
3232
|
-
if (importedFuncs[name]) return number(importedFuncs[name].params);
|
3233
|
-
if (internalConstrs[name]) return number(internalConstrs[name].length ?? 0);
|
3512
|
+
if (builtinFuncs[name]) return withType(scope, number(builtinFuncs[name].typedParams ? (builtinFuncs[name].params.length / 2) : builtinFuncs[name].params.length), TYPES.number);
|
3513
|
+
if (importedFuncs[name]) return withType(scope, number(importedFuncs[name].params), TYPES.number);
|
3514
|
+
if (internalConstrs[name]) return withType(scope, number(internalConstrs[name].length ?? 0), TYPES.number);
|
3515
|
+
|
3516
|
+
if (Prefs.fastLength) {
|
3517
|
+
// presume valid length object
|
3518
|
+
return [
|
3519
|
+
...(aotPointer ? number(0, Valtype.i32) : [
|
3520
|
+
...generate(scope, decl.object),
|
3521
|
+
Opcodes.i32_to_u
|
3522
|
+
]),
|
3523
|
+
|
3524
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
|
3525
|
+
Opcodes.i32_from_u
|
3526
|
+
];
|
3527
|
+
}
|
3528
|
+
|
3529
|
+
const type = getNodeType(scope, decl.object);
|
3530
|
+
const known = knownType(scope, type);
|
3531
|
+
if (known != null) {
|
3532
|
+
if ([ TYPES.string, TYPES.bytestring, TYPES.array ].includes(known)) return [
|
3533
|
+
...(aotPointer ? number(0, Valtype.i32) : [
|
3534
|
+
...generate(scope, decl.object),
|
3535
|
+
Opcodes.i32_to_u
|
3536
|
+
]),
|
3537
|
+
|
3538
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
|
3539
|
+
Opcodes.i32_from_u
|
3540
|
+
];
|
3541
|
+
|
3542
|
+
return number(0);
|
3543
|
+
}
|
3234
3544
|
|
3235
3545
|
return [
|
3236
|
-
...(
|
3237
|
-
|
3238
|
-
|
3239
|
-
|
3546
|
+
...typeIsOneOf(getNodeType(scope, decl.object), [ TYPES.string, TYPES.bytestring, TYPES.array ]),
|
3547
|
+
[ Opcodes.if, valtypeBinary ],
|
3548
|
+
...(aotPointer ? number(0, Valtype.i32) : [
|
3549
|
+
...generate(scope, decl.object),
|
3550
|
+
Opcodes.i32_to_u
|
3551
|
+
]),
|
3240
3552
|
|
3241
|
-
|
3242
|
-
|
3553
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
|
3554
|
+
Opcodes.i32_from_u,
|
3555
|
+
|
3556
|
+
...setLastType(scope, TYPES.number),
|
3557
|
+
[ Opcodes.else ],
|
3558
|
+
...number(0),
|
3559
|
+
...setLastType(scope, TYPES.undefined),
|
3560
|
+
[ Opcodes.end ]
|
3243
3561
|
];
|
3244
3562
|
}
|
3245
3563
|
|
3564
|
+
// todo: generate this array procedurally during builtinFuncs creation
|
3565
|
+
if (['size', 'description'].includes(decl.property.name)) {
|
3566
|
+
const bc = {};
|
3567
|
+
const cands = Object.keys(builtinFuncs).filter(x => x.startsWith('__') && x.endsWith('_prototype_' + decl.property.name + '$get'));
|
3568
|
+
|
3569
|
+
if (cands.length > 0) {
|
3570
|
+
for (const x of cands) {
|
3571
|
+
const type = TYPES[x.split('_prototype_')[0].slice(2).toLowerCase()];
|
3572
|
+
if (type == null) continue;
|
3573
|
+
|
3574
|
+
bc[type] = generateCall(scope, {
|
3575
|
+
callee: {
|
3576
|
+
type: 'Identifier',
|
3577
|
+
name: x
|
3578
|
+
},
|
3579
|
+
arguments: [ decl.object ],
|
3580
|
+
_protoInternalCall: true
|
3581
|
+
});
|
3582
|
+
}
|
3583
|
+
}
|
3584
|
+
|
3585
|
+
return typeSwitch(scope, getNodeType(scope, decl.object), {
|
3586
|
+
...bc,
|
3587
|
+
default: withType(scope, number(0), TYPES.undefined)
|
3588
|
+
}, valtypeBinary);
|
3589
|
+
}
|
3590
|
+
|
3246
3591
|
const object = generate(scope, decl.object);
|
3247
3592
|
const property = generate(scope, decl.property);
|
3248
3593
|
|
@@ -3257,24 +3602,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3257
3602
|
|
3258
3603
|
return typeSwitch(scope, getNodeType(scope, decl.object), {
|
3259
3604
|
[TYPES.array]: [
|
3260
|
-
|
3261
|
-
...property,
|
3262
|
-
|
3263
|
-
// convert to i32 and turn into byte offset by * valtypeSize (4 for i32, 8 for i64/f64)
|
3264
|
-
Opcodes.i32_to_u,
|
3265
|
-
...number(ValtypeSize[valtype], Valtype.i32),
|
3266
|
-
[ Opcodes.i32_mul ],
|
3267
|
-
|
3268
|
-
...(aotPointer ? [] : [
|
3269
|
-
...object,
|
3270
|
-
Opcodes.i32_to_u,
|
3271
|
-
[ Opcodes.i32_add ]
|
3272
|
-
]),
|
3273
|
-
|
3274
|
-
// read from memory
|
3275
|
-
[ Opcodes.load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
3276
|
-
|
3277
|
-
...number(TYPES.number, Valtype.i32),
|
3605
|
+
...loadArray(scope, object, property, aotPointer),
|
3278
3606
|
...setLastType(scope)
|
3279
3607
|
],
|
3280
3608
|
|
@@ -3305,9 +3633,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3305
3633
|
|
3306
3634
|
// return new string (page)
|
3307
3635
|
...number(newPointer),
|
3308
|
-
|
3309
|
-
...number(TYPES.string, Valtype.i32),
|
3310
|
-
...setLastType(scope)
|
3636
|
+
...setLastType(scope, TYPES.string)
|
3311
3637
|
],
|
3312
3638
|
[TYPES.bytestring]: [
|
3313
3639
|
// setup new/out array
|
@@ -3333,9 +3659,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3333
3659
|
|
3334
3660
|
// return new string (page)
|
3335
3661
|
...number(newPointer),
|
3336
|
-
|
3337
|
-
...number(TYPES.bytestring, Valtype.i32),
|
3338
|
-
...setLastType(scope)
|
3662
|
+
...setLastType(scope, TYPES.bytestring)
|
3339
3663
|
],
|
3340
3664
|
|
3341
3665
|
default: internalThrow(scope, 'TypeError', 'Member expression is not supported for non-string non-array yet', true)
|
@@ -3360,7 +3684,7 @@ const objectHack = node => {
|
|
3360
3684
|
if (!objectName) objectName = objectHack(node.object)?.name?.slice?.(2);
|
3361
3685
|
|
3362
3686
|
// if .name or .length, give up (hack within a hack!)
|
3363
|
-
if (['name', 'length'].includes(node.property.name)) {
|
3687
|
+
if (['name', 'length', 'size', 'description'].includes(node.property.name)) {
|
3364
3688
|
node.object = objectHack(node.object);
|
3365
3689
|
return;
|
3366
3690
|
}
|
@@ -3397,33 +3721,39 @@ const generateFunc = (scope, decl) => {
|
|
3397
3721
|
const name = decl.id ? decl.id.name : `anonymous_${randId()}`;
|
3398
3722
|
const params = decl.params ?? [];
|
3399
3723
|
|
3400
|
-
// const innerScope = { ...scope };
|
3401
3724
|
// TODO: share scope/locals between !!!
|
3402
|
-
const
|
3725
|
+
const func = {
|
3403
3726
|
locals: {},
|
3404
3727
|
localInd: 0,
|
3405
3728
|
// value, type
|
3406
3729
|
returns: [ valtypeBinary, Valtype.i32 ],
|
3407
3730
|
throws: false,
|
3408
|
-
name
|
3731
|
+
name,
|
3732
|
+
index: currentFuncIndex++
|
3409
3733
|
};
|
3410
3734
|
|
3411
3735
|
if (typedInput && decl.returnType) {
|
3412
3736
|
const { type } = extractTypeAnnotation(decl.returnType);
|
3413
|
-
if (type != null && !Prefs.indirectCalls) {
|
3414
|
-
|
3415
|
-
|
3737
|
+
// if (type != null && !Prefs.indirectCalls) {
|
3738
|
+
if (type != null) {
|
3739
|
+
func.returnType = type;
|
3740
|
+
func.returns = [ valtypeBinary ];
|
3416
3741
|
}
|
3417
3742
|
}
|
3418
3743
|
|
3419
3744
|
for (let i = 0; i < params.length; i++) {
|
3420
|
-
|
3745
|
+
const name = params[i].name;
|
3746
|
+
// if (name == null) return todo('non-identifier args are not supported');
|
3747
|
+
|
3748
|
+
allocVar(func, name, false);
|
3421
3749
|
|
3422
3750
|
if (typedInput && params[i].typeAnnotation) {
|
3423
|
-
addVarMetadata(
|
3751
|
+
addVarMetadata(func, name, false, extractTypeAnnotation(params[i]));
|
3424
3752
|
}
|
3425
3753
|
}
|
3426
3754
|
|
3755
|
+
func.params = Object.values(func.locals).map(x => x.type);
|
3756
|
+
|
3427
3757
|
let body = objectHack(decl.body);
|
3428
3758
|
if (decl.type === 'ArrowFunctionExpression' && decl.expression) {
|
3429
3759
|
// hack: () => 0 -> () => return 0
|
@@ -3433,37 +3763,23 @@ const generateFunc = (scope, decl) => {
|
|
3433
3763
|
};
|
3434
3764
|
}
|
3435
3765
|
|
3436
|
-
const wasm = generate(innerScope, body);
|
3437
|
-
const func = {
|
3438
|
-
name,
|
3439
|
-
params: Object.values(innerScope.locals).slice(0, params.length * 2).map(x => x.type),
|
3440
|
-
index: currentFuncIndex++,
|
3441
|
-
...innerScope
|
3442
|
-
};
|
3443
3766
|
funcIndex[name] = func.index;
|
3767
|
+
funcs.push(func);
|
3444
3768
|
|
3445
|
-
|
3769
|
+
const wasm = generate(func, body);
|
3770
|
+
func.wasm = wasm;
|
3446
3771
|
|
3447
|
-
|
3448
|
-
for (const inst of wasm) {
|
3449
|
-
if (inst[0] === Opcodes.call && inst[1] === -1) {
|
3450
|
-
inst[1] = func.index;
|
3451
|
-
}
|
3452
|
-
}
|
3772
|
+
if (name === 'main') func.gotLastType = true;
|
3453
3773
|
|
3454
3774
|
// add end return if not found
|
3455
3775
|
if (name !== 'main' && wasm[wasm.length - 1]?.[0] !== Opcodes.return && countLeftover(wasm) === 0) {
|
3456
3776
|
wasm.push(
|
3457
3777
|
...number(0),
|
3458
|
-
...(
|
3778
|
+
...(func.returnType != null ? [] : number(TYPES.undefined, Valtype.i32)),
|
3459
3779
|
[ Opcodes.return ]
|
3460
3780
|
);
|
3461
3781
|
}
|
3462
3782
|
|
3463
|
-
func.wasm = wasm;
|
3464
|
-
|
3465
|
-
funcs.push(func);
|
3466
|
-
|
3467
3783
|
return func;
|
3468
3784
|
};
|
3469
3785
|
|
@@ -3567,7 +3883,7 @@ const internalConstrs = {
|
|
3567
3883
|
generate: (scope, decl) => {
|
3568
3884
|
// todo: boolean object when used as constructor
|
3569
3885
|
const arg = decl.arguments[0] ?? DEFAULT_VALUE;
|
3570
|
-
return truthy(scope, generate(scope, arg), getNodeType(scope, arg));
|
3886
|
+
return truthy(scope, generate(scope, arg), getNodeType(scope, arg), false, false, 'full');
|
3571
3887
|
},
|
3572
3888
|
type: TYPES.boolean,
|
3573
3889
|
length: 1
|
@@ -3628,8 +3944,10 @@ const internalConstrs = {
|
|
3628
3944
|
}),
|
3629
3945
|
|
3630
3946
|
// print space
|
3631
|
-
...
|
3632
|
-
|
3947
|
+
...(i !== decl.arguments.length - 1 ? [
|
3948
|
+
...number(32),
|
3949
|
+
[ Opcodes.call, importedFuncs.printChar ]
|
3950
|
+
] : [])
|
3633
3951
|
);
|
3634
3952
|
}
|
3635
3953
|
|
@@ -3639,6 +3957,8 @@ const internalConstrs = {
|
|
3639
3957
|
[ Opcodes.call, importedFuncs.printChar ]
|
3640
3958
|
);
|
3641
3959
|
|
3960
|
+
out.push(...number(UNDEFINED));
|
3961
|
+
|
3642
3962
|
return out;
|
3643
3963
|
},
|
3644
3964
|
type: TYPES.undefined,
|
@@ -3708,9 +4028,8 @@ export default program => {
|
|
3708
4028
|
|
3709
4029
|
if (Prefs.astLog) console.log(JSON.stringify(program.body.body, null, 2));
|
3710
4030
|
|
3711
|
-
generateFunc(scope, program);
|
4031
|
+
const main = generateFunc(scope, program);
|
3712
4032
|
|
3713
|
-
const main = funcs[funcs.length - 1];
|
3714
4033
|
main.export = true;
|
3715
4034
|
main.returns = [ valtypeBinary, Valtype.i32 ];
|
3716
4035
|
|
@@ -3737,7 +4056,7 @@ export default program => {
|
|
3737
4056
|
}
|
3738
4057
|
|
3739
4058
|
// if blank main func and other exports, remove it
|
3740
|
-
if (main.wasm.length === 0 && funcs.reduce((acc, x) => acc + (x.export ? 1 : 0), 0) > 1) funcs.splice(
|
4059
|
+
if (main.wasm.length === 0 && funcs.reduce((acc, x) => acc + (x.export ? 1 : 0), 0) > 1) funcs.splice(main.index - importedFuncs.length, 1);
|
3741
4060
|
|
3742
4061
|
return { funcs, globals, tags, exceptions, pages, data };
|
3743
4062
|
};
|