porffor 0.14.0-f67c123a1 → 0.16.0-a2e115b05
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 +104 -51
- 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 +11 -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 +48 -10
- package/compiler/codegen.js +569 -258
- package/compiler/decompile.js +5 -1
- package/compiler/generated_builtins.js +555 -60
- package/compiler/index.js +5 -9
- package/compiler/opt.js +2 -2
- 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,
|
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 = (() => {
|
@@ -1244,7 +1254,7 @@ const getNodeType = (scope, node) => {
|
|
1244
1254
|
const func = funcs.find(x => x.name === name);
|
1245
1255
|
|
1246
1256
|
if (func) {
|
1247
|
-
if (func.returnType) return func.returnType;
|
1257
|
+
if (func.returnType != null) return func.returnType;
|
1248
1258
|
}
|
1249
1259
|
|
1250
1260
|
if (builtinFuncs[name] && !builtinFuncs[name].typedReturns) return builtinFuncs[name].returnType ?? TYPES.number;
|
@@ -1259,7 +1269,9 @@ 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 != null) return protoFuncs[0].returnType;
|
1274
|
+
}
|
1263
1275
|
}
|
1264
1276
|
|
1265
1277
|
if (name.startsWith('__Porffor_wasm_')) {
|
@@ -1354,22 +1366,27 @@ const getNodeType = (scope, node) => {
|
|
1354
1366
|
}
|
1355
1367
|
|
1356
1368
|
if (node.type === 'MemberExpression') {
|
1357
|
-
|
1358
|
-
|
1359
|
-
|
1360
|
-
|
1361
|
-
|
1362
|
-
return TYPES.undefined;
|
1363
|
-
}
|
1369
|
+
const name = node.property.name;
|
1370
|
+
|
1371
|
+
if (name === 'length') {
|
1372
|
+
if (hasFuncWithName(node.object.name)) return TYPES.number;
|
1373
|
+
if (Prefs.fastLength) return TYPES.number;
|
1364
1374
|
}
|
1365
1375
|
|
1366
|
-
// hack: if something.length, number type
|
1367
|
-
if (node.property.name === 'length') return TYPES.number;
|
1368
1376
|
|
1369
|
-
|
1370
|
-
if (
|
1371
|
-
|
1372
|
-
|
1377
|
+
const objectKnownType = knownType(scope, getNodeType(scope, node.object));
|
1378
|
+
if (objectKnownType != null) {
|
1379
|
+
if (name === 'length') {
|
1380
|
+
if ([ TYPES.string, TYPES.bytestring, TYPES.array ].includes(objectKnownType)) return TYPES.number;
|
1381
|
+
else return TYPES.undefined;
|
1382
|
+
}
|
1383
|
+
|
1384
|
+
if (node.computed) {
|
1385
|
+
if (objectKnownType === TYPES.string) return TYPES.string;
|
1386
|
+
if (objectKnownType === TYPES.bytestring) return TYPES.bytestring;
|
1387
|
+
if (objectKnownType === TYPES.array) return TYPES.number;
|
1388
|
+
}
|
1389
|
+
}
|
1373
1390
|
|
1374
1391
|
if (scope.locals['#last_type']) return getLastType(scope);
|
1375
1392
|
|
@@ -1440,17 +1457,16 @@ const countLeftover = wasm => {
|
|
1440
1457
|
else if (inst[0] === Opcodes.return) count = 0;
|
1441
1458
|
else if (inst[0] === Opcodes.call) {
|
1442
1459
|
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;
|
1460
|
+
if (inst[1] < importedFuncs.length) {
|
1461
|
+
func = importedFuncs[inst[1]];
|
1462
|
+
count = count - func.params + func.returns;
|
1448
1463
|
} else {
|
1449
|
-
|
1450
|
-
count -= func.params.length;
|
1451
|
-
} else count--;
|
1452
|
-
if (func) count += func.returns.length;
|
1464
|
+
count = count - func.params.length + func.returns.length;
|
1453
1465
|
}
|
1466
|
+
} else if (inst[0] === Opcodes.call_indirect) {
|
1467
|
+
count--; // funcidx
|
1468
|
+
count -= inst[1] * 2; // params * 2 (typed)
|
1469
|
+
count += 2; // fixed return (value, type)
|
1454
1470
|
} else count--;
|
1455
1471
|
|
1456
1472
|
// console.log(count, decompile([ inst ]).slice(0, -1));
|
@@ -1468,7 +1484,7 @@ const disposeLeftover = wasm => {
|
|
1468
1484
|
const generateExp = (scope, decl) => {
|
1469
1485
|
const expression = decl.expression;
|
1470
1486
|
|
1471
|
-
const out = generate(scope, expression, undefined, undefined,
|
1487
|
+
const out = generate(scope, expression, undefined, undefined, Prefs.optUnused);
|
1472
1488
|
disposeLeftover(out);
|
1473
1489
|
|
1474
1490
|
return out;
|
@@ -1559,16 +1575,10 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1559
1575
|
out.splice(out.length - 1, 1);
|
1560
1576
|
|
1561
1577
|
const finalStatement = parsed.body[parsed.body.length - 1];
|
1562
|
-
out.push(
|
1563
|
-
...getNodeType(scope, finalStatement),
|
1564
|
-
...setLastType(scope)
|
1565
|
-
);
|
1578
|
+
out.push(...setLastType(scope, getNodeType(scope, finalStatement)));
|
1566
1579
|
} else if (countLeftover(out) === 0) {
|
1567
1580
|
out.push(...number(UNDEFINED));
|
1568
|
-
out.push(
|
1569
|
-
...number(TYPES.undefined, Valtype.i32),
|
1570
|
-
...setLastType(scope)
|
1571
|
-
);
|
1581
|
+
out.push(...setLastType(scope, TYPES.undefined));
|
1572
1582
|
}
|
1573
1583
|
|
1574
1584
|
// if (lastInst && lastInst[0] === Opcodes.drop) {
|
@@ -1604,6 +1614,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1604
1614
|
|
1605
1615
|
if (!funcIndex[rhemynName]) {
|
1606
1616
|
const func = Rhemyn[funcName](regex, currentFuncIndex++, rhemynName);
|
1617
|
+
func.internal = true;
|
1607
1618
|
|
1608
1619
|
funcIndex[func.name] = func.index;
|
1609
1620
|
funcs.push(func);
|
@@ -1620,8 +1631,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1620
1631
|
[ Opcodes.call, idx ],
|
1621
1632
|
Opcodes.i32_from_u,
|
1622
1633
|
|
1623
|
-
...
|
1624
|
-
...setLastType(scope)
|
1634
|
+
...setLastType(scope, TYPES.boolean)
|
1625
1635
|
];
|
1626
1636
|
}
|
1627
1637
|
|
@@ -1693,9 +1703,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1693
1703
|
if (protoFunc.noArgRetLength && decl.arguments.length === 0) {
|
1694
1704
|
protoBC[x] = [
|
1695
1705
|
...RTArrayUtil.getLength(getPointer),
|
1696
|
-
|
1697
|
-
...number(TYPES.number, Valtype.i32),
|
1698
|
-
...setLastType(scope)
|
1706
|
+
...setLastType(scope, TYPES.number)
|
1699
1707
|
];
|
1700
1708
|
continue;
|
1701
1709
|
}
|
@@ -1714,7 +1722,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1714
1722
|
getI32: () => RTArrayUtil.getLengthI32(getPointer),
|
1715
1723
|
set: value => RTArrayUtil.setLength(getPointer, value),
|
1716
1724
|
setI32: value => RTArrayUtil.setLengthI32(getPointer, value)
|
1717
|
-
}, generate(scope, decl.arguments[0] ?? DEFAULT_VALUE), protoLocal, protoLocal2, (length, itemType) => {
|
1725
|
+
}, generate(scope, decl.arguments[0] ?? DEFAULT_VALUE), getNodeType(scope, decl.arguments[0] ?? DEFAULT_VALUE), protoLocal, protoLocal2, (length, itemType) => {
|
1718
1726
|
return makeArray(scope, {
|
1719
1727
|
rawElements: new Array(length)
|
1720
1728
|
}, _global, _name, true, itemType);
|
@@ -1728,9 +1736,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1728
1736
|
protoBC[x] = [
|
1729
1737
|
[ Opcodes.block, unusedValue && optUnused ? Blocktype.void : valtypeBinary ],
|
1730
1738
|
...protoOut,
|
1731
|
-
|
1732
|
-
...number(protoFunc.returnType ?? TYPES.number, Valtype.i32),
|
1733
|
-
...setLastType(scope),
|
1739
|
+
...(unusedValue && optUnused ? [] : (protoFunc.returnType != null ? setLastType(scope, protoFunc.returnType) : setLastType(scope))),
|
1734
1740
|
[ Opcodes.end ]
|
1735
1741
|
];
|
1736
1742
|
}
|
@@ -1780,11 +1786,6 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1780
1786
|
|
1781
1787
|
if (idx === undefined && internalConstrs[name]) return internalConstrs[name].generate(scope, decl, _global, _name);
|
1782
1788
|
|
1783
|
-
if (idx === undefined && name === scope.name) {
|
1784
|
-
// hack: calling self, func generator will fix later
|
1785
|
-
idx = -1;
|
1786
|
-
}
|
1787
|
-
|
1788
1789
|
if (idx === undefined && name.startsWith('__Porffor_wasm_')) {
|
1789
1790
|
const wasmOps = {
|
1790
1791
|
// pointer, align, offset
|
@@ -1833,30 +1834,143 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1833
1834
|
|
1834
1835
|
if (idx === undefined) {
|
1835
1836
|
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
1837
|
const [ local, global ] = lookupName(scope, name);
|
1838
|
+
if (!Prefs.indirectCalls || local == null) return internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, true);
|
1839
|
+
|
1840
|
+
// todo: only works when function uses typedParams and typedReturns
|
1841
|
+
|
1842
|
+
const indirectMode = Prefs.indirectCallMode ?? 'vararg';
|
1843
|
+
// options: vararg, strict
|
1844
|
+
// - strict: simpler, smaller size usage, no func argc lut needed.
|
1845
|
+
// ONLY works when arg count of call == arg count of function being called
|
1846
|
+
// - vararg: large size usage, cursed.
|
1847
|
+
// works when arg count of call != arg count of function being called*
|
1848
|
+
// * most of the time, some edgecases
|
1849
|
+
|
1839
1850
|
funcs.table = true;
|
1851
|
+
scope.table = true;
|
1852
|
+
|
1853
|
+
let args = decl.arguments;
|
1854
|
+
let out = [];
|
1855
|
+
|
1856
|
+
let locals = [];
|
1857
|
+
|
1858
|
+
if (indirectMode === 'vararg') {
|
1859
|
+
const minArgc = Prefs.indirectCallMinArgc ?? 3;
|
1860
|
+
|
1861
|
+
if (args.length < minArgc) {
|
1862
|
+
args = args.concat(new Array(minArgc - args.length).fill(DEFAULT_VALUE));
|
1863
|
+
}
|
1864
|
+
}
|
1865
|
+
|
1866
|
+
for (let i = 0; i < args.length; i++) {
|
1867
|
+
const arg = args[i];
|
1868
|
+
out = out.concat(generate(scope, arg));
|
1869
|
+
|
1870
|
+
if (valtypeBinary !== Valtype.i32 && (
|
1871
|
+
(builtinFuncs[name] && builtinFuncs[name].params[i * (typedParams ? 2 : 1)] === Valtype.i32) ||
|
1872
|
+
(importedFuncs[name] && name.startsWith('profile'))
|
1873
|
+
)) {
|
1874
|
+
out.push(Opcodes.i32_to);
|
1875
|
+
}
|
1840
1876
|
|
1841
|
-
|
1842
|
-
|
1843
|
-
|
1844
|
-
|
1877
|
+
out = out.concat(getNodeType(scope, arg));
|
1878
|
+
|
1879
|
+
if (indirectMode === 'vararg') {
|
1880
|
+
const typeLocal = localTmp(scope, `#indirect_arg${i}_type`, Valtype.i32);
|
1881
|
+
const valLocal = localTmp(scope, `#indirect_arg${i}_val`);
|
1882
|
+
|
1883
|
+
locals.push([valLocal, typeLocal]);
|
1884
|
+
|
1885
|
+
out.push(
|
1886
|
+
[ Opcodes.local_set, typeLocal ],
|
1887
|
+
[ Opcodes.local_set, valLocal ]
|
1888
|
+
);
|
1889
|
+
}
|
1890
|
+
}
|
1891
|
+
|
1892
|
+
if (indirectMode === 'strict') {
|
1893
|
+
return typeSwitch(scope, getNodeType(scope, decl.callee), {
|
1894
|
+
[TYPES.function]: [
|
1895
|
+
...argWasm,
|
1896
|
+
[ global ? Opcodes.global_get : Opcodes.local_get, local.idx ],
|
1897
|
+
Opcodes.i32_to_u,
|
1898
|
+
[ Opcodes.call_indirect, args.length, 0 ],
|
1899
|
+
...setLastType(scope)
|
1900
|
+
],
|
1901
|
+
default: internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, true)
|
1902
|
+
});
|
1903
|
+
}
|
1904
|
+
|
1905
|
+
// hi, I will now explain how vararg mode works:
|
1906
|
+
// wasm's indirect_call instruction requires you know the func type at compile-time
|
1907
|
+
// since we have varargs (variable argument count), we do not know it.
|
1908
|
+
// we could just store args in memory and not use wasm func args,
|
1909
|
+
// but that is slow (probably) and breaks js exports.
|
1910
|
+
// instead, we generate every* possibility of argc and use different indirect_call
|
1911
|
+
// ops for each one, with type depending on argc for that branch.
|
1912
|
+
// then we load the argc for the wanted function from a memory lut,
|
1913
|
+
// and call the branch with the matching argc we require.
|
1914
|
+
// sorry, yes it is very cursed (and size inefficient), but indirect calls
|
1915
|
+
// are kind of rare anyway (mostly callbacks) so I am not concerned atm.
|
1916
|
+
// *for argc 0-3, in future (todo:) the max number should be
|
1917
|
+
// dynamically changed to the max argc of any func in the js file.
|
1918
|
+
|
1919
|
+
const funcLocal = localTmp(scope, `#indirect_func`, Valtype.i32);
|
1920
|
+
|
1921
|
+
const gen = argc => {
|
1922
|
+
const out = [];
|
1923
|
+
for (let i = 0; i < argc; i++) {
|
1924
|
+
out.push(
|
1925
|
+
[ Opcodes.local_get, locals[i][0] ],
|
1926
|
+
[ Opcodes.local_get, locals[i][1] ]
|
1927
|
+
);
|
1928
|
+
}
|
1929
|
+
|
1930
|
+
out.push(
|
1931
|
+
[ Opcodes.local_get, funcLocal ],
|
1932
|
+
[ Opcodes.call_indirect, argc, 0 ],
|
1933
|
+
...setLastType(scope)
|
1934
|
+
)
|
1935
|
+
|
1936
|
+
return out;
|
1937
|
+
};
|
1938
|
+
|
1939
|
+
const tableBc = {};
|
1940
|
+
for (let i = 0; i <= args.length; i++) {
|
1941
|
+
tableBc[i] = gen(i);
|
1942
|
+
}
|
1943
|
+
|
1944
|
+
// todo/perf: check if we should use br_table here or just generate our own big if..elses
|
1945
|
+
|
1946
|
+
return typeSwitch(scope, getNodeType(scope, decl.callee), {
|
1845
1947
|
[TYPES.function]: [
|
1948
|
+
...out,
|
1949
|
+
|
1846
1950
|
[ global ? Opcodes.global_get : Opcodes.local_get, local.idx ],
|
1847
|
-
|
1951
|
+
Opcodes.i32_to_u,
|
1952
|
+
[ Opcodes.local_set, funcLocal ],
|
1953
|
+
|
1954
|
+
...brTable([
|
1955
|
+
// get argc of func we are calling
|
1956
|
+
[ Opcodes.local_get, funcLocal ],
|
1957
|
+
...number(ValtypeSize.i16, Valtype.i32),
|
1958
|
+
[ Opcodes.i32_mul ],
|
1959
|
+
|
1960
|
+
[ Opcodes.i32_load16_u, 0, ...unsignedLEB128(allocPage(scope, 'func argc lut') * pageSize), 'read_argc' ]
|
1961
|
+
], tableBc, valtypeBinary)
|
1848
1962
|
],
|
1849
1963
|
default: internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, true)
|
1850
1964
|
});
|
1851
1965
|
}
|
1966
|
+
|
1852
1967
|
return internalThrow(scope, 'ReferenceError', `${unhackName(name)} is not defined`, true);
|
1853
1968
|
}
|
1854
1969
|
|
1855
|
-
const func = funcs.find(x => x.index === idx);
|
1856
|
-
|
1857
|
-
const userFunc = (funcIndex[name] && !importedFuncs[name] && !builtinFuncs[name] && !internalConstrs[name]) || idx === -1;
|
1970
|
+
const func = funcs[idx - importedFuncs.length]; // idx === scope.index ? scope : funcs.find(x => x.index === idx);
|
1971
|
+
const userFunc = func && !func.internal;
|
1858
1972
|
const typedParams = userFunc || builtinFuncs[name]?.typedParams;
|
1859
|
-
const typedReturns = (
|
1973
|
+
const typedReturns = (userFunc && func.returnType == null) || builtinFuncs[name]?.typedReturns;
|
1860
1974
|
const paramCount = func && (typedParams ? func.params.length / 2 : func.params.length);
|
1861
1975
|
|
1862
1976
|
let args = decl.arguments;
|
@@ -1877,6 +1991,13 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1877
1991
|
const arg = args[i];
|
1878
1992
|
out = out.concat(generate(scope, arg));
|
1879
1993
|
|
1994
|
+
// todo: this should be used instead of the too many args thing above (by removing that)
|
1995
|
+
if (i >= paramCount) {
|
1996
|
+
// over param count of func, drop arg
|
1997
|
+
out.push([ Opcodes.drop ]);
|
1998
|
+
continue;
|
1999
|
+
}
|
2000
|
+
|
1880
2001
|
if (valtypeBinary !== Valtype.i32 && (
|
1881
2002
|
(builtinFuncs[name] && builtinFuncs[name].params[i * (typedParams ? 2 : 1)] === Valtype.i32) ||
|
1882
2003
|
(importedFuncs[name] && name.startsWith('profile'))
|
@@ -1925,6 +2046,11 @@ const generateNew = (scope, decl, _global, _name) => {
|
|
1925
2046
|
}, _global, _name);
|
1926
2047
|
}
|
1927
2048
|
|
2049
|
+
if (
|
2050
|
+
(builtinFuncs[name] && !builtinFuncs[name].constr) ||
|
2051
|
+
(internalConstrs[name] && builtinFuncs[name].notConstr)
|
2052
|
+
) return internalThrow(scope, 'TypeError', `${name} is not a constructor`);
|
2053
|
+
|
1928
2054
|
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
2055
|
|
1930
2056
|
return generateCall(scope, decl, _global, _name);
|
@@ -1950,8 +2076,11 @@ const knownType = (scope, type) => {
|
|
1950
2076
|
const idx = type[0][1];
|
1951
2077
|
|
1952
2078
|
// type idx = var idx + 1
|
1953
|
-
const
|
1954
|
-
if (
|
2079
|
+
const name = Object.values(scope.locals).find(x => x.idx === idx)?.name;
|
2080
|
+
if (name) {
|
2081
|
+
const local = scope.locals[name];
|
2082
|
+
if (local.metadata?.type != null) return v.metadata.type;
|
2083
|
+
}
|
1955
2084
|
}
|
1956
2085
|
|
1957
2086
|
return null;
|
@@ -1986,16 +2115,17 @@ const brTable = (input, bc, returns) => {
|
|
1986
2115
|
}
|
1987
2116
|
|
1988
2117
|
for (let i = 0; i < count; i++) {
|
1989
|
-
if (i === 0) out.push([ Opcodes.block, returns, 'br table start' ]);
|
2118
|
+
// if (i === 0) out.push([ Opcodes.block, returns, 'br table start' ]);
|
2119
|
+
if (i === 0) out.push([ Opcodes.block, returns ]);
|
1990
2120
|
else out.push([ Opcodes.block, Blocktype.void ]);
|
1991
2121
|
}
|
1992
2122
|
|
1993
|
-
const nums = keys.filter(x => +x);
|
2123
|
+
const nums = keys.filter(x => +x >= 0);
|
1994
2124
|
const offset = Math.min(...nums);
|
1995
2125
|
const max = Math.max(...nums);
|
1996
2126
|
|
1997
2127
|
const table = [];
|
1998
|
-
let br =
|
2128
|
+
let br = 0;
|
1999
2129
|
|
2000
2130
|
for (let i = offset; i <= max; i++) {
|
2001
2131
|
// if branch for this num, go to that block
|
@@ -2035,10 +2165,9 @@ const brTable = (input, bc, returns) => {
|
|
2035
2165
|
br--;
|
2036
2166
|
}
|
2037
2167
|
|
2038
|
-
|
2039
|
-
|
2040
|
-
|
2041
|
-
];
|
2168
|
+
out.push([ Opcodes.end ]);
|
2169
|
+
|
2170
|
+
return out;
|
2042
2171
|
};
|
2043
2172
|
|
2044
2173
|
const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
|
@@ -2082,6 +2211,17 @@ const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
|
|
2082
2211
|
return out;
|
2083
2212
|
};
|
2084
2213
|
|
2214
|
+
const typeIsOneOf = (type, types, valtype = Valtype.i32) => {
|
2215
|
+
const out = [];
|
2216
|
+
|
2217
|
+
for (let i = 0; i < types.length; i++) {
|
2218
|
+
out.push(...type, ...number(types[i], valtype), valtype === Valtype.f64 ? [ Opcodes.f64_eq ] : [ Opcodes.i32_eq ]);
|
2219
|
+
if (i !== 0) out.push([ Opcodes.i32_or ]);
|
2220
|
+
}
|
2221
|
+
|
2222
|
+
return out;
|
2223
|
+
};
|
2224
|
+
|
2085
2225
|
const allocVar = (scope, name, global = false, type = true) => {
|
2086
2226
|
const target = global ? globals : scope.locals;
|
2087
2227
|
|
@@ -2098,7 +2238,7 @@ const allocVar = (scope, name, global = false, type = true) => {
|
|
2098
2238
|
|
2099
2239
|
if (type) {
|
2100
2240
|
let typeIdx = global ? globalInd++ : scope.localInd++;
|
2101
|
-
target[name + '#type'] = { idx: typeIdx, type: Valtype.i32 };
|
2241
|
+
target[name + '#type'] = { idx: typeIdx, type: Valtype.i32, name };
|
2102
2242
|
}
|
2103
2243
|
|
2104
2244
|
return idx;
|
@@ -2311,18 +2451,21 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
|
|
2311
2451
|
Opcodes.i32_to_u,
|
2312
2452
|
|
2313
2453
|
// turn into byte offset by * valtypeSize (4 for i32, 8 for i64/f64)
|
2314
|
-
...number(ValtypeSize[valtype], Valtype.i32),
|
2454
|
+
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
2315
2455
|
[ Opcodes.i32_mul ],
|
2316
2456
|
...(aotPointer ? [] : [ [ Opcodes.i32_add ] ]),
|
2317
2457
|
...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
|
2318
2458
|
|
2319
2459
|
...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
|
2320
2460
|
[ Opcodes.local_get, pointerTmp ],
|
2321
|
-
[ Opcodes.load,
|
2322
|
-
], generate(scope, decl.right),
|
2461
|
+
[ Opcodes.load, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
|
2462
|
+
], generate(scope, decl.right), [
|
2463
|
+
[ Opcodes.local_get, pointerTmp ],
|
2464
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32 + ValtypeSize[valtype]) ]
|
2465
|
+
], getNodeType(scope, decl.right), false, name, true)),
|
2323
2466
|
[ Opcodes.local_tee, newValueTmp ],
|
2324
2467
|
|
2325
|
-
[ Opcodes.store,
|
2468
|
+
[ Opcodes.store, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
|
2326
2469
|
],
|
2327
2470
|
|
2328
2471
|
default: internalThrow(scope, 'TypeError', `Cannot assign member with non-array`)
|
@@ -2430,6 +2573,11 @@ const generateUnary = (scope, decl) => {
|
|
2430
2573
|
];
|
2431
2574
|
|
2432
2575
|
case '!':
|
2576
|
+
const arg = decl.argument;
|
2577
|
+
if (arg.type === 'UnaryExpression' && arg.operator === '!') {
|
2578
|
+
// !!x -> is x truthy
|
2579
|
+
return truthy(scope, generate(scope, arg.argument), getNodeType(scope, arg.argument), false, false);
|
2580
|
+
}
|
2433
2581
|
// !=
|
2434
2582
|
return falsy(scope, generate(scope, decl.argument), getNodeType(scope, decl.argument), false, false);
|
2435
2583
|
|
@@ -2499,6 +2647,7 @@ const generateUnary = (scope, decl) => {
|
|
2499
2647
|
[TYPES.string]: makeString(scope, 'string', false, '#typeof_result'),
|
2500
2648
|
[TYPES.undefined]: makeString(scope, 'undefined', false, '#typeof_result'),
|
2501
2649
|
[TYPES.function]: makeString(scope, 'function', false, '#typeof_result'),
|
2650
|
+
[TYPES.symbol]: makeString(scope, 'symbol', false, '#typeof_result'),
|
2502
2651
|
|
2503
2652
|
[TYPES.bytestring]: makeString(scope, 'string', false, '#typeof_result'),
|
2504
2653
|
|
@@ -2575,21 +2724,16 @@ const generateConditional = (scope, decl) => {
|
|
2575
2724
|
out.push([ Opcodes.if, valtypeBinary ]);
|
2576
2725
|
depth.push('if');
|
2577
2726
|
|
2578
|
-
out.push(...generate(scope, decl.consequent));
|
2579
|
-
|
2580
|
-
// note type
|
2581
2727
|
out.push(
|
2582
|
-
...
|
2583
|
-
...setLastType(scope)
|
2728
|
+
...generate(scope, decl.consequent),
|
2729
|
+
...setLastType(scope, getNodeType(scope, decl.consequent))
|
2584
2730
|
);
|
2585
2731
|
|
2586
2732
|
out.push([ Opcodes.else ]);
|
2587
|
-
out.push(...generate(scope, decl.alternate));
|
2588
2733
|
|
2589
|
-
// note type
|
2590
2734
|
out.push(
|
2591
|
-
...
|
2592
|
-
...setLastType(scope)
|
2735
|
+
...generate(scope, decl.alternate),
|
2736
|
+
...setLastType(scope, getNodeType(scope, decl.alternate))
|
2593
2737
|
);
|
2594
2738
|
|
2595
2739
|
out.push([ Opcodes.end ]);
|
@@ -2737,12 +2881,15 @@ const generateForOf = (scope, decl) => {
|
|
2737
2881
|
// todo: optimize away counter and use end pointer
|
2738
2882
|
out.push(...typeSwitch(scope, getNodeType(scope, decl.right), {
|
2739
2883
|
[TYPES.array]: [
|
2740
|
-
...setType(scope, leftName, TYPES.number),
|
2741
|
-
|
2742
2884
|
[ Opcodes.loop, Blocktype.void ],
|
2743
2885
|
|
2744
2886
|
[ Opcodes.local_get, pointer ],
|
2745
|
-
[ Opcodes.load,
|
2887
|
+
[ Opcodes.load, 0, ...unsignedLEB128(ValtypeSize.i32) ],
|
2888
|
+
|
2889
|
+
...setType(scope, leftName, [
|
2890
|
+
[ Opcodes.local_get, pointer ],
|
2891
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32 + ValtypeSize[valtype]) ],
|
2892
|
+
]),
|
2746
2893
|
|
2747
2894
|
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
|
2748
2895
|
|
@@ -2751,9 +2898,9 @@ const generateForOf = (scope, decl) => {
|
|
2751
2898
|
...generate(scope, decl.body),
|
2752
2899
|
[ Opcodes.end ],
|
2753
2900
|
|
2754
|
-
// increment iter pointer by valtype size
|
2901
|
+
// increment iter pointer by valtype size + 1
|
2755
2902
|
[ Opcodes.local_get, pointer ],
|
2756
|
-
...number(ValtypeSize[valtype], Valtype.i32),
|
2903
|
+
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
2757
2904
|
[ Opcodes.i32_add ],
|
2758
2905
|
[ Opcodes.local_set, pointer ],
|
2759
2906
|
|
@@ -2869,6 +3016,44 @@ const generateForOf = (scope, decl) => {
|
|
2869
3016
|
[ Opcodes.end ],
|
2870
3017
|
[ Opcodes.end ]
|
2871
3018
|
],
|
3019
|
+
[TYPES.set]: [
|
3020
|
+
[ Opcodes.loop, Blocktype.void ],
|
3021
|
+
|
3022
|
+
[ Opcodes.local_get, pointer ],
|
3023
|
+
[ Opcodes.load, 0, ...unsignedLEB128(ValtypeSize.i32) ],
|
3024
|
+
|
3025
|
+
...setType(scope, leftName, [
|
3026
|
+
[ Opcodes.local_get, pointer ],
|
3027
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32 + ValtypeSize[valtype]) ],
|
3028
|
+
]),
|
3029
|
+
|
3030
|
+
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
|
3031
|
+
|
3032
|
+
[ Opcodes.block, Blocktype.void ],
|
3033
|
+
[ Opcodes.block, Blocktype.void ],
|
3034
|
+
...generate(scope, decl.body),
|
3035
|
+
[ Opcodes.end ],
|
3036
|
+
|
3037
|
+
// increment iter pointer by valtype size + 1
|
3038
|
+
[ Opcodes.local_get, pointer ],
|
3039
|
+
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
3040
|
+
[ Opcodes.i32_add ],
|
3041
|
+
[ Opcodes.local_set, pointer ],
|
3042
|
+
|
3043
|
+
// increment counter by 1
|
3044
|
+
[ Opcodes.local_get, counter ],
|
3045
|
+
...number(1, Valtype.i32),
|
3046
|
+
[ Opcodes.i32_add ],
|
3047
|
+
[ Opcodes.local_tee, counter ],
|
3048
|
+
|
3049
|
+
// loop if counter != length
|
3050
|
+
[ Opcodes.local_get, length ],
|
3051
|
+
[ Opcodes.i32_ne ],
|
3052
|
+
[ Opcodes.br_if, 1 ],
|
3053
|
+
|
3054
|
+
[ Opcodes.end ],
|
3055
|
+
[ Opcodes.end ]
|
3056
|
+
],
|
2872
3057
|
default: internalThrow(scope, 'TypeError', `Tried for..of on non-iterable type`)
|
2873
3058
|
}, Blocktype.void));
|
2874
3059
|
|
@@ -2970,14 +3155,18 @@ const generateThrow = (scope, decl) => {
|
|
2970
3155
|
};
|
2971
3156
|
|
2972
3157
|
const generateTry = (scope, decl) => {
|
2973
|
-
|
3158
|
+
// todo: handle control-flow pre-exit for finally
|
3159
|
+
// "Immediately before a control-flow statement (return, throw, break, continue) is executed in the try block or catch block."
|
2974
3160
|
|
2975
3161
|
const out = [];
|
2976
3162
|
|
3163
|
+
const finalizer = decl.finalizer ? generate(scope, decl.finalizer) : [];
|
3164
|
+
|
2977
3165
|
out.push([ Opcodes.try, Blocktype.void ]);
|
2978
3166
|
depth.push('try');
|
2979
3167
|
|
2980
3168
|
out.push(...generate(scope, decl.block));
|
3169
|
+
out.push(...finalizer);
|
2981
3170
|
|
2982
3171
|
if (decl.handler) {
|
2983
3172
|
depth.pop();
|
@@ -2985,6 +3174,7 @@ const generateTry = (scope, decl) => {
|
|
2985
3174
|
|
2986
3175
|
out.push([ Opcodes.catch_all ]);
|
2987
3176
|
out.push(...generate(scope, decl.handler.body));
|
3177
|
+
out.push(...finalizer);
|
2988
3178
|
}
|
2989
3179
|
|
2990
3180
|
out.push([ Opcodes.end ]);
|
@@ -3070,7 +3260,7 @@ const getAllocType = itemType => {
|
|
3070
3260
|
}
|
3071
3261
|
};
|
3072
3262
|
|
3073
|
-
const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false, itemType = valtype) => {
|
3263
|
+
const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false, itemType = valtype, typed = false) => {
|
3074
3264
|
const out = [];
|
3075
3265
|
|
3076
3266
|
scope.arrays ??= new Map();
|
@@ -3082,8 +3272,13 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
3082
3272
|
// todo: can we just have 1 undeclared array? probably not? but this is not really memory efficient
|
3083
3273
|
const uniqueName = name === '$undeclared' ? name + Math.random().toString().slice(2) : name;
|
3084
3274
|
|
3085
|
-
|
3086
|
-
|
3275
|
+
let page;
|
3276
|
+
if (Prefs.scopedPageNames) page = allocPage(scope, `${getAllocType(itemType)}: ${scope.name}/${uniqueName}`, itemType);
|
3277
|
+
else page = allocPage(scope, `${getAllocType(itemType)}: ${uniqueName}`, itemType);
|
3278
|
+
|
3279
|
+
// hack: use 1 for page 0 pointer for fast truthiness
|
3280
|
+
const ptr = page === 0 ? 1 : (page * pageSize);
|
3281
|
+
scope.arrays.set(name, ptr);
|
3087
3282
|
}
|
3088
3283
|
|
3089
3284
|
const pointer = scope.arrays.get(name);
|
@@ -3133,7 +3328,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
3133
3328
|
|
3134
3329
|
const pointerWasm = pointerTmp != null ? [ [ Opcodes.local_get, pointerTmp ] ] : number(pointer, Valtype.i32);
|
3135
3330
|
|
3136
|
-
// store length
|
3331
|
+
// store length
|
3137
3332
|
out.push(
|
3138
3333
|
...pointerWasm,
|
3139
3334
|
...number(length, Valtype.i32),
|
@@ -3141,14 +3336,20 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
3141
3336
|
);
|
3142
3337
|
|
3143
3338
|
const storeOp = StoreOps[itemType];
|
3144
|
-
|
3339
|
+
const sizePerEl = ValtypeSize[itemType] + (typed ? 1 : 0);
|
3145
3340
|
if (!initEmpty) for (let i = 0; i < length; i++) {
|
3146
3341
|
if (elements[i] == null) continue;
|
3147
3342
|
|
3343
|
+
const offset = ValtypeSize.i32 + i * sizePerEl;
|
3148
3344
|
out.push(
|
3149
3345
|
...pointerWasm,
|
3150
3346
|
...(useRawElements ? number(elements[i], Valtype[valtype]) : generate(scope, elements[i])),
|
3151
|
-
[ storeOp,
|
3347
|
+
[ storeOp, 0, ...unsignedLEB128(offset) ],
|
3348
|
+
...(!typed ? [] : [ // typed presumes !useRawElements
|
3349
|
+
...pointerWasm,
|
3350
|
+
...getNodeType(scope, elements[i]),
|
3351
|
+
[ Opcodes.i32_store8, 0, ...unsignedLEB128(offset + ValtypeSize[itemType]) ]
|
3352
|
+
])
|
3152
3353
|
);
|
3153
3354
|
}
|
3154
3355
|
|
@@ -3158,6 +3359,65 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
3158
3359
|
return [ out, pointer ];
|
3159
3360
|
};
|
3160
3361
|
|
3362
|
+
const storeArray = (scope, array, index, element, aotPointer = null) => {
|
3363
|
+
if (!Array.isArray(element)) element = generate(scope, element);
|
3364
|
+
if (typeof index === 'number') index = number(index);
|
3365
|
+
|
3366
|
+
const offset = localTmp(scope, '#storeArray_offset', Valtype.i32);
|
3367
|
+
|
3368
|
+
return [
|
3369
|
+
// calculate offset
|
3370
|
+
...index,
|
3371
|
+
Opcodes.i32_to_u,
|
3372
|
+
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
3373
|
+
[ Opcodes.i32_mul ],
|
3374
|
+
...(aotPointer ? [] : [
|
3375
|
+
...array,
|
3376
|
+
Opcodes.i32_to_u,
|
3377
|
+
[ Opcodes.i32_add ],
|
3378
|
+
]),
|
3379
|
+
[ Opcodes.local_set, offset ],
|
3380
|
+
|
3381
|
+
// store value
|
3382
|
+
[ Opcodes.local_get, offset ],
|
3383
|
+
...generate(scope, element),
|
3384
|
+
[ Opcodes.store, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
3385
|
+
|
3386
|
+
// store type
|
3387
|
+
[ Opcodes.local_get, offset ],
|
3388
|
+
...getNodeType(scope, element),
|
3389
|
+
[ Opcodes.i32_store8, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32 + ValtypeSize[valtype]) ]
|
3390
|
+
];
|
3391
|
+
};
|
3392
|
+
|
3393
|
+
const loadArray = (scope, array, index, aotPointer = null) => {
|
3394
|
+
if (typeof index === 'number') index = number(index);
|
3395
|
+
|
3396
|
+
const offset = localTmp(scope, '#loadArray_offset', Valtype.i32);
|
3397
|
+
|
3398
|
+
return [
|
3399
|
+
// calculate offset
|
3400
|
+
...index,
|
3401
|
+
Opcodes.i32_to_u,
|
3402
|
+
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
3403
|
+
[ Opcodes.i32_mul ],
|
3404
|
+
...(aotPointer ? [] : [
|
3405
|
+
...array,
|
3406
|
+
Opcodes.i32_to_u,
|
3407
|
+
[ Opcodes.i32_add ],
|
3408
|
+
]),
|
3409
|
+
[ Opcodes.local_set, offset ],
|
3410
|
+
|
3411
|
+
// load value
|
3412
|
+
[ Opcodes.local_get, offset ],
|
3413
|
+
[ Opcodes.load, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
3414
|
+
|
3415
|
+
// load type
|
3416
|
+
[ Opcodes.local_get, offset ],
|
3417
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32 + ValtypeSize[valtype]) ]
|
3418
|
+
];
|
3419
|
+
};
|
3420
|
+
|
3161
3421
|
const byteStringable = str => {
|
3162
3422
|
if (!Prefs.bytestring) return false;
|
3163
3423
|
|
@@ -3186,14 +3446,28 @@ const makeString = (scope, str, global = false, name = '$undeclared', forceBytes
|
|
3186
3446
|
};
|
3187
3447
|
|
3188
3448
|
const generateArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false) => {
|
3189
|
-
return makeArray(scope, decl, global, name, initEmpty, valtype)[0];
|
3449
|
+
return makeArray(scope, decl, global, name, initEmpty, valtype, true)[0];
|
3190
3450
|
};
|
3191
3451
|
|
3192
|
-
|
3452
|
+
const generateObject = (scope, decl, global = false, name = '$undeclared') => {
|
3453
|
+
if (decl.properties.length > 0) return todo(scope, 'objects are not supported yet', true);
|
3454
|
+
|
3455
|
+
return [
|
3456
|
+
...number(1),
|
3457
|
+
...setLastType(scope, TYPES.object)
|
3458
|
+
];
|
3459
|
+
};
|
3460
|
+
|
3461
|
+
const withType = (scope, wasm, type) => [
|
3462
|
+
...wasm,
|
3463
|
+
...setLastType(scope, type)
|
3464
|
+
];
|
3465
|
+
|
3466
|
+
const generateMember = (scope, decl, _global, _name) => {
|
3193
3467
|
const name = decl.object.name;
|
3194
3468
|
const pointer = scope.arrays?.get(name);
|
3195
3469
|
|
3196
|
-
const aotPointer = Prefs.aotPointerOpt && pointer
|
3470
|
+
const aotPointer = Prefs.aotPointerOpt && pointer;
|
3197
3471
|
|
3198
3472
|
// hack: .name
|
3199
3473
|
if (decl.property.name === 'name') {
|
@@ -3203,9 +3477,9 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3203
3477
|
// eg: __String_prototype_toLowerCase -> toLowerCase
|
3204
3478
|
if (nameProp.startsWith('__')) nameProp = nameProp.split('_').pop();
|
3205
3479
|
|
3206
|
-
return makeString(scope, nameProp, _global, _name, true);
|
3480
|
+
return withType(scope, makeString(scope, nameProp, _global, _name, true), TYPES.bytestring);
|
3207
3481
|
} else {
|
3208
|
-
return
|
3482
|
+
return withType(scope, number(0), TYPES.undefined);
|
3209
3483
|
}
|
3210
3484
|
}
|
3211
3485
|
|
@@ -3213,9 +3487,8 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3213
3487
|
if (decl.property.name === 'length') {
|
3214
3488
|
const func = funcs.find(x => x.name === name);
|
3215
3489
|
if (func) {
|
3216
|
-
const
|
3217
|
-
|
3218
|
-
return number(typedParams ? func.params.length / 2 : func.params.length);
|
3490
|
+
const typedParams = !func.internal || builtinFuncs[name]?.typedParams;
|
3491
|
+
return withType(scope, number(typedParams ? func.params.length / 2 : func.params.length), TYPES.number);
|
3219
3492
|
}
|
3220
3493
|
|
3221
3494
|
if (builtinFuncs[name + '$constructor']) {
|
@@ -3225,24 +3498,88 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3225
3498
|
const constructorFunc = builtinFuncs[name + '$constructor'];
|
3226
3499
|
const constructorParams = constructorFunc.typedParams ? (constructorFunc.params.length / 2) : constructorFunc.params.length;
|
3227
3500
|
|
3228
|
-
return number(Math.max(regularParams, constructorParams));
|
3501
|
+
return withType(scope, number(Math.max(regularParams, constructorParams)), TYPES.number);
|
3229
3502
|
}
|
3230
3503
|
|
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);
|
3504
|
+
if (builtinFuncs[name]) return withType(scope, number(builtinFuncs[name].typedParams ? (builtinFuncs[name].params.length / 2) : builtinFuncs[name].params.length), TYPES.number);
|
3505
|
+
if (importedFuncs[name]) return withType(scope, number(importedFuncs[name].params), TYPES.number);
|
3506
|
+
if (internalConstrs[name]) return withType(scope, number(internalConstrs[name].length ?? 0), TYPES.number);
|
3507
|
+
|
3508
|
+
if (Prefs.fastLength) {
|
3509
|
+
// presume valid length object
|
3510
|
+
return [
|
3511
|
+
...(aotPointer ? number(0, Valtype.i32) : [
|
3512
|
+
...generate(scope, decl.object),
|
3513
|
+
Opcodes.i32_to_u
|
3514
|
+
]),
|
3515
|
+
|
3516
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
|
3517
|
+
Opcodes.i32_from_u
|
3518
|
+
];
|
3519
|
+
}
|
3520
|
+
|
3521
|
+
const type = getNodeType(scope, decl.object);
|
3522
|
+
const known = knownType(scope, type);
|
3523
|
+
if (known != null) {
|
3524
|
+
if ([ TYPES.string, TYPES.bytestring, TYPES.array ].includes(known)) return [
|
3525
|
+
...(aotPointer ? number(0, Valtype.i32) : [
|
3526
|
+
...generate(scope, decl.object),
|
3527
|
+
Opcodes.i32_to_u
|
3528
|
+
]),
|
3529
|
+
|
3530
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
|
3531
|
+
Opcodes.i32_from_u
|
3532
|
+
];
|
3533
|
+
|
3534
|
+
return number(0);
|
3535
|
+
}
|
3234
3536
|
|
3235
3537
|
return [
|
3236
|
-
...(
|
3237
|
-
|
3238
|
-
|
3239
|
-
|
3538
|
+
...typeIsOneOf(getNodeType(scope, decl.object), [ TYPES.string, TYPES.bytestring, TYPES.array ]),
|
3539
|
+
[ Opcodes.if, valtypeBinary ],
|
3540
|
+
...(aotPointer ? number(0, Valtype.i32) : [
|
3541
|
+
...generate(scope, decl.object),
|
3542
|
+
Opcodes.i32_to_u
|
3543
|
+
]),
|
3240
3544
|
|
3241
|
-
|
3242
|
-
|
3545
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
|
3546
|
+
Opcodes.i32_from_u,
|
3547
|
+
|
3548
|
+
...setLastType(scope, TYPES.number),
|
3549
|
+
[ Opcodes.else ],
|
3550
|
+
...number(0),
|
3551
|
+
...setLastType(scope, TYPES.undefined),
|
3552
|
+
[ Opcodes.end ]
|
3243
3553
|
];
|
3244
3554
|
}
|
3245
3555
|
|
3556
|
+
// todo: generate this array procedurally during builtinFuncs creation
|
3557
|
+
if (['size', 'description'].includes(decl.property.name)) {
|
3558
|
+
const bc = {};
|
3559
|
+
const cands = Object.keys(builtinFuncs).filter(x => x.startsWith('__') && x.endsWith('_prototype_' + decl.property.name + '$get'));
|
3560
|
+
|
3561
|
+
if (cands.length > 0) {
|
3562
|
+
for (const x of cands) {
|
3563
|
+
const type = TYPES[x.split('_prototype_')[0].slice(2).toLowerCase()];
|
3564
|
+
if (type == null) continue;
|
3565
|
+
|
3566
|
+
bc[type] = generateCall(scope, {
|
3567
|
+
callee: {
|
3568
|
+
type: 'Identifier',
|
3569
|
+
name: x
|
3570
|
+
},
|
3571
|
+
arguments: [ decl.object ],
|
3572
|
+
_protoInternalCall: true
|
3573
|
+
});
|
3574
|
+
}
|
3575
|
+
}
|
3576
|
+
|
3577
|
+
return typeSwitch(scope, getNodeType(scope, decl.object), {
|
3578
|
+
...bc,
|
3579
|
+
default: withType(scope, number(0), TYPES.undefined)
|
3580
|
+
}, valtypeBinary);
|
3581
|
+
}
|
3582
|
+
|
3246
3583
|
const object = generate(scope, decl.object);
|
3247
3584
|
const property = generate(scope, decl.property);
|
3248
3585
|
|
@@ -3257,24 +3594,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3257
3594
|
|
3258
3595
|
return typeSwitch(scope, getNodeType(scope, decl.object), {
|
3259
3596
|
[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),
|
3597
|
+
...loadArray(scope, object, property, aotPointer),
|
3278
3598
|
...setLastType(scope)
|
3279
3599
|
],
|
3280
3600
|
|
@@ -3305,9 +3625,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3305
3625
|
|
3306
3626
|
// return new string (page)
|
3307
3627
|
...number(newPointer),
|
3308
|
-
|
3309
|
-
...number(TYPES.string, Valtype.i32),
|
3310
|
-
...setLastType(scope)
|
3628
|
+
...setLastType(scope, TYPES.string)
|
3311
3629
|
],
|
3312
3630
|
[TYPES.bytestring]: [
|
3313
3631
|
// setup new/out array
|
@@ -3333,9 +3651,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3333
3651
|
|
3334
3652
|
// return new string (page)
|
3335
3653
|
...number(newPointer),
|
3336
|
-
|
3337
|
-
...number(TYPES.bytestring, Valtype.i32),
|
3338
|
-
...setLastType(scope)
|
3654
|
+
...setLastType(scope, TYPES.bytestring)
|
3339
3655
|
],
|
3340
3656
|
|
3341
3657
|
default: internalThrow(scope, 'TypeError', 'Member expression is not supported for non-string non-array yet', true)
|
@@ -3360,7 +3676,7 @@ const objectHack = node => {
|
|
3360
3676
|
if (!objectName) objectName = objectHack(node.object)?.name?.slice?.(2);
|
3361
3677
|
|
3362
3678
|
// if .name or .length, give up (hack within a hack!)
|
3363
|
-
if (['name', 'length'].includes(node.property.name)) {
|
3679
|
+
if (['name', 'length', 'size', 'description'].includes(node.property.name)) {
|
3364
3680
|
node.object = objectHack(node.object);
|
3365
3681
|
return;
|
3366
3682
|
}
|
@@ -3397,33 +3713,39 @@ const generateFunc = (scope, decl) => {
|
|
3397
3713
|
const name = decl.id ? decl.id.name : `anonymous_${randId()}`;
|
3398
3714
|
const params = decl.params ?? [];
|
3399
3715
|
|
3400
|
-
// const innerScope = { ...scope };
|
3401
3716
|
// TODO: share scope/locals between !!!
|
3402
|
-
const
|
3717
|
+
const func = {
|
3403
3718
|
locals: {},
|
3404
3719
|
localInd: 0,
|
3405
3720
|
// value, type
|
3406
3721
|
returns: [ valtypeBinary, Valtype.i32 ],
|
3407
3722
|
throws: false,
|
3408
|
-
name
|
3723
|
+
name,
|
3724
|
+
index: currentFuncIndex++
|
3409
3725
|
};
|
3410
3726
|
|
3411
3727
|
if (typedInput && decl.returnType) {
|
3412
3728
|
const { type } = extractTypeAnnotation(decl.returnType);
|
3413
|
-
if (type != null && !Prefs.indirectCalls) {
|
3414
|
-
|
3415
|
-
|
3729
|
+
// if (type != null && !Prefs.indirectCalls) {
|
3730
|
+
if (type != null) {
|
3731
|
+
func.returnType = type;
|
3732
|
+
func.returns = [ valtypeBinary ];
|
3416
3733
|
}
|
3417
3734
|
}
|
3418
3735
|
|
3419
3736
|
for (let i = 0; i < params.length; i++) {
|
3420
|
-
|
3737
|
+
const name = params[i].name;
|
3738
|
+
// if (name == null) return todo('non-identifier args are not supported');
|
3739
|
+
|
3740
|
+
allocVar(func, name, false);
|
3421
3741
|
|
3422
3742
|
if (typedInput && params[i].typeAnnotation) {
|
3423
|
-
addVarMetadata(
|
3743
|
+
addVarMetadata(func, name, false, extractTypeAnnotation(params[i]));
|
3424
3744
|
}
|
3425
3745
|
}
|
3426
3746
|
|
3747
|
+
func.params = Object.values(func.locals).map(x => x.type);
|
3748
|
+
|
3427
3749
|
let body = objectHack(decl.body);
|
3428
3750
|
if (decl.type === 'ArrowFunctionExpression' && decl.expression) {
|
3429
3751
|
// hack: () => 0 -> () => return 0
|
@@ -3433,37 +3755,23 @@ const generateFunc = (scope, decl) => {
|
|
3433
3755
|
};
|
3434
3756
|
}
|
3435
3757
|
|
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
3758
|
funcIndex[name] = func.index;
|
3759
|
+
funcs.push(func);
|
3444
3760
|
|
3445
|
-
|
3761
|
+
const wasm = generate(func, body);
|
3762
|
+
func.wasm = wasm;
|
3446
3763
|
|
3447
|
-
|
3448
|
-
for (const inst of wasm) {
|
3449
|
-
if (inst[0] === Opcodes.call && inst[1] === -1) {
|
3450
|
-
inst[1] = func.index;
|
3451
|
-
}
|
3452
|
-
}
|
3764
|
+
if (name === 'main') func.gotLastType = true;
|
3453
3765
|
|
3454
3766
|
// add end return if not found
|
3455
3767
|
if (name !== 'main' && wasm[wasm.length - 1]?.[0] !== Opcodes.return && countLeftover(wasm) === 0) {
|
3456
3768
|
wasm.push(
|
3457
3769
|
...number(0),
|
3458
|
-
...(
|
3770
|
+
...(func.returnType != null ? [] : number(TYPES.undefined, Valtype.i32)),
|
3459
3771
|
[ Opcodes.return ]
|
3460
3772
|
);
|
3461
3773
|
}
|
3462
3774
|
|
3463
|
-
func.wasm = wasm;
|
3464
|
-
|
3465
|
-
funcs.push(func);
|
3466
|
-
|
3467
3775
|
return func;
|
3468
3776
|
};
|
3469
3777
|
|
@@ -3567,7 +3875,7 @@ const internalConstrs = {
|
|
3567
3875
|
generate: (scope, decl) => {
|
3568
3876
|
// todo: boolean object when used as constructor
|
3569
3877
|
const arg = decl.arguments[0] ?? DEFAULT_VALUE;
|
3570
|
-
return truthy(scope, generate(scope, arg), getNodeType(scope, arg));
|
3878
|
+
return truthy(scope, generate(scope, arg), getNodeType(scope, arg), false, false, 'full');
|
3571
3879
|
},
|
3572
3880
|
type: TYPES.boolean,
|
3573
3881
|
length: 1
|
@@ -3628,8 +3936,10 @@ const internalConstrs = {
|
|
3628
3936
|
}),
|
3629
3937
|
|
3630
3938
|
// print space
|
3631
|
-
...
|
3632
|
-
|
3939
|
+
...(i !== decl.arguments.length - 1 ? [
|
3940
|
+
...number(32),
|
3941
|
+
[ Opcodes.call, importedFuncs.printChar ]
|
3942
|
+
] : [])
|
3633
3943
|
);
|
3634
3944
|
}
|
3635
3945
|
|
@@ -3639,6 +3949,8 @@ const internalConstrs = {
|
|
3639
3949
|
[ Opcodes.call, importedFuncs.printChar ]
|
3640
3950
|
);
|
3641
3951
|
|
3952
|
+
out.push(...number(UNDEFINED));
|
3953
|
+
|
3642
3954
|
return out;
|
3643
3955
|
},
|
3644
3956
|
type: TYPES.undefined,
|
@@ -3708,9 +4020,8 @@ export default program => {
|
|
3708
4020
|
|
3709
4021
|
if (Prefs.astLog) console.log(JSON.stringify(program.body.body, null, 2));
|
3710
4022
|
|
3711
|
-
generateFunc(scope, program);
|
4023
|
+
const main = generateFunc(scope, program);
|
3712
4024
|
|
3713
|
-
const main = funcs[funcs.length - 1];
|
3714
4025
|
main.export = true;
|
3715
4026
|
main.returns = [ valtypeBinary, Valtype.i32 ];
|
3716
4027
|
|
@@ -3737,7 +4048,7 @@ export default program => {
|
|
3737
4048
|
}
|
3738
4049
|
|
3739
4050
|
// 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(
|
4051
|
+
if (main.wasm.length === 0 && funcs.reduce((acc, x) => acc + (x.export ? 1 : 0), 0) > 1) funcs.splice(main.index - importedFuncs.length, 1);
|
3741
4052
|
|
3742
4053
|
return { funcs, globals, tags, exceptions, pages, data };
|
3743
4054
|
};
|