porffor 0.14.0-7bef6473d → 0.14.0-83cb6bb87
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CONTRIBUTING.md +9 -3
- package/README.md +9 -13
- package/asur/index.js +1 -1
- package/compiler/2c.js +65 -2
- 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/crypto.ts +1 -0
- package/compiler/builtins/date.ts +2 -0
- package/compiler/builtins/error.js +22 -0
- package/compiler/builtins/escape.ts +1 -2
- package/compiler/builtins/function.ts +2 -0
- package/compiler/builtins/int.ts +2 -0
- package/compiler/builtins/math.ts +410 -0
- package/compiler/builtins/number.ts +2 -0
- package/compiler/builtins/object.ts +2 -0
- package/compiler/builtins/set.ts +19 -7
- package/compiler/builtins/string.ts +1 -0
- package/compiler/builtins/symbol.ts +62 -0
- package/compiler/builtins.js +37 -8
- package/compiler/codegen.js +635 -263
- package/compiler/decompile.js +1 -1
- package/compiler/generated_builtins.js +545 -60
- package/compiler/index.js +5 -9
- package/compiler/parse.js +1 -1
- package/compiler/precompile.js +5 -4
- package/compiler/prefs.js +6 -2
- package/compiler/prototype.js +180 -157
- package/compiler/wrap.js +91 -44
- 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':
|
@@ -58,10 +58,11 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
58
58
|
|
59
59
|
case 'ArrowFunctionExpression':
|
60
60
|
case 'FunctionDeclaration':
|
61
|
+
case 'FunctionExpression':
|
61
62
|
const func = generateFunc(scope, decl);
|
62
63
|
|
63
64
|
if (decl.type.endsWith('Expression')) {
|
64
|
-
return number(func.index);
|
65
|
+
return number(func.index - importedFuncs.length);
|
65
66
|
}
|
66
67
|
|
67
68
|
return [];
|
@@ -139,18 +140,23 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
139
140
|
case 'ArrayExpression':
|
140
141
|
return generateArray(scope, decl, global, name);
|
141
142
|
|
143
|
+
case 'ObjectExpression':
|
144
|
+
return generateObject(scope, decl, global, name);
|
145
|
+
|
142
146
|
case 'MemberExpression':
|
143
147
|
return generateMember(scope, decl, global, name);
|
144
148
|
|
145
149
|
case 'ExportNamedDeclaration':
|
146
|
-
|
147
|
-
const funcsBefore = funcs.length;
|
150
|
+
const funcsBefore = funcs.map(x => x.name);
|
148
151
|
generate(scope, decl.declaration);
|
149
152
|
|
150
|
-
|
151
|
-
|
152
|
-
const
|
153
|
-
|
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
|
+
}
|
154
160
|
}
|
155
161
|
|
156
162
|
return [];
|
@@ -187,7 +193,7 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
187
193
|
if (!Array.isArray(inst)) inst = [ inst ];
|
188
194
|
const immediates = asm.slice(1).map(x => {
|
189
195
|
const int = parseInt(x);
|
190
|
-
if (Number.isNaN(int)) return scope.locals[x]?.idx;
|
196
|
+
if (Number.isNaN(int)) return scope.locals[x]?.idx ?? globals[x].idx;
|
191
197
|
return int;
|
192
198
|
});
|
193
199
|
|
@@ -199,19 +205,11 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
199
205
|
|
200
206
|
__Porffor_bs: str => [
|
201
207
|
...makeString(scope, str, global, name, true),
|
202
|
-
|
203
|
-
...(name ? setType(scope, name, TYPES.bytestring) : [
|
204
|
-
...number(TYPES.bytestring, Valtype.i32),
|
205
|
-
...setLastType(scope)
|
206
|
-
])
|
208
|
+
...(name ? setType(scope, name, TYPES.bytestring) : setLastType(scope, TYPES.bytestring))
|
207
209
|
],
|
208
210
|
__Porffor_s: str => [
|
209
211
|
...makeString(scope, str, global, name, false),
|
210
|
-
|
211
|
-
...(name ? setType(scope, name, TYPES.string) : [
|
212
|
-
...number(TYPES.string, Valtype.i32),
|
213
|
-
...setLastType(scope)
|
214
|
-
])
|
212
|
+
...(name ? setType(scope, name, TYPES.string) : setLastType(scope, TYPES.string))
|
215
213
|
],
|
216
214
|
};
|
217
215
|
|
@@ -313,10 +311,10 @@ const generateIdent = (scope, decl) => {
|
|
313
311
|
|
314
312
|
if (local?.idx === undefined) {
|
315
313
|
// no local var with name
|
316
|
-
if (Object.hasOwn(importedFuncs, name)) return number(importedFuncs[name]);
|
317
|
-
if (Object.hasOwn(funcIndex, name)) return number(funcIndex[name]);
|
318
|
-
|
319
314
|
if (Object.hasOwn(globals, name)) return [ [ Opcodes.global_get, globals[name].idx ] ];
|
315
|
+
|
316
|
+
if (Object.hasOwn(importedFuncs, name)) return number(importedFuncs[name] - importedFuncs.length);
|
317
|
+
if (Object.hasOwn(funcIndex, name)) return number(funcIndex[name] - importedFuncs.length);
|
320
318
|
}
|
321
319
|
|
322
320
|
if (local?.idx === undefined && rawName.startsWith('__')) {
|
@@ -350,9 +348,7 @@ const generateReturn = (scope, decl) => {
|
|
350
348
|
|
351
349
|
return [
|
352
350
|
...generate(scope, decl.argument),
|
353
|
-
...(scope.returnType != null ? [] :
|
354
|
-
...getNodeType(scope, decl.argument)
|
355
|
-
]),
|
351
|
+
...(scope.returnType != null ? [] : getNodeType(scope, decl.argument)),
|
356
352
|
[ Opcodes.return ]
|
357
353
|
];
|
358
354
|
};
|
@@ -367,7 +363,7 @@ const localTmp = (scope, name, type = valtypeBinary) => {
|
|
367
363
|
};
|
368
364
|
|
369
365
|
const isIntOp = op => op && ((op[0] >= 0x45 && op[0] <= 0x4f) || (op[0] >= 0x67 && op[0] <= 0x78) || op[0] === 0x41);
|
370
|
-
const
|
366
|
+
const isIntToFloatOp = op => op && (op[0] >= 0xb7 && op[0] <= 0xba);
|
371
367
|
|
372
368
|
const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
373
369
|
const checks = {
|
@@ -384,10 +380,10 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
384
380
|
|
385
381
|
// if we can, use int tmp and convert at the end to help prevent unneeded conversions
|
386
382
|
// (like if we are in an if condition - very common)
|
387
|
-
const
|
388
|
-
const
|
383
|
+
const leftWasInt = isIntToFloatOp(left[left.length - 1]);
|
384
|
+
const rightWasInt = isIntToFloatOp(right[right.length - 1]);
|
389
385
|
|
390
|
-
const canInt =
|
386
|
+
const canInt = leftWasInt && rightWasInt;
|
391
387
|
|
392
388
|
if (canInt) {
|
393
389
|
// remove int -> float conversions from left and right
|
@@ -401,13 +397,11 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
401
397
|
[ Opcodes.if, Valtype.i32 ],
|
402
398
|
...right,
|
403
399
|
// note type
|
404
|
-
...rightType,
|
405
|
-
...setLastType(scope),
|
400
|
+
...setLastType(scope, rightType),
|
406
401
|
[ Opcodes.else ],
|
407
402
|
[ Opcodes.local_get, localTmp(scope, 'logictmpi', Valtype.i32) ],
|
408
403
|
// note type
|
409
|
-
...leftType,
|
410
|
-
...setLastType(scope),
|
404
|
+
...setLastType(scope, leftType),
|
411
405
|
[ Opcodes.end ],
|
412
406
|
Opcodes.i32_from
|
413
407
|
];
|
@@ -420,13 +414,11 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
420
414
|
[ Opcodes.if, valtypeBinary ],
|
421
415
|
...right,
|
422
416
|
// note type
|
423
|
-
...rightType,
|
424
|
-
...setLastType(scope),
|
417
|
+
...setLastType(scope, rightType),
|
425
418
|
[ Opcodes.else ],
|
426
419
|
[ Opcodes.local_get, localTmp(scope, 'logictmp') ],
|
427
420
|
// note type
|
428
|
-
...leftType,
|
429
|
-
...setLastType(scope),
|
421
|
+
...setLastType(scope, leftType),
|
430
422
|
[ Opcodes.end ]
|
431
423
|
];
|
432
424
|
};
|
@@ -454,11 +446,11 @@ const concatStrings = (scope, left, right, global, name, assign = false, bytestr
|
|
454
446
|
...number(0, Valtype.i32), // base 0 for store later
|
455
447
|
|
456
448
|
...number(pointer, Valtype.i32),
|
457
|
-
[ Opcodes.i32_load,
|
449
|
+
[ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
|
458
450
|
[ Opcodes.local_tee, leftLength ],
|
459
451
|
|
460
452
|
[ Opcodes.local_get, rightPointer ],
|
461
|
-
[ Opcodes.i32_load,
|
453
|
+
[ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
|
462
454
|
[ Opcodes.local_tee, rightLength ],
|
463
455
|
|
464
456
|
[ Opcodes.i32_add ],
|
@@ -514,11 +506,11 @@ const concatStrings = (scope, left, right, global, name, assign = false, bytestr
|
|
514
506
|
...number(0, Valtype.i32), // base 0 for store later
|
515
507
|
|
516
508
|
[ Opcodes.local_get, leftPointer ],
|
517
|
-
[ Opcodes.i32_load,
|
509
|
+
[ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
|
518
510
|
[ Opcodes.local_tee, leftLength ],
|
519
511
|
|
520
512
|
[ Opcodes.local_get, rightPointer ],
|
521
|
-
[ Opcodes.i32_load,
|
513
|
+
[ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
|
522
514
|
[ Opcodes.local_tee, rightLength ],
|
523
515
|
|
524
516
|
[ Opcodes.i32_add ],
|
@@ -596,11 +588,11 @@ const compareStrings = (scope, left, right, bytestrings = false) => {
|
|
596
588
|
|
597
589
|
// get lengths
|
598
590
|
[ Opcodes.local_get, leftPointer ],
|
599
|
-
[ Opcodes.i32_load,
|
591
|
+
[ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
|
600
592
|
[ Opcodes.local_tee, leftLength ],
|
601
593
|
|
602
594
|
[ Opcodes.local_get, rightPointer ],
|
603
|
-
[ Opcodes.i32_load,
|
595
|
+
[ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
|
604
596
|
|
605
597
|
// fast path: check leftLength != rightLength
|
606
598
|
[ Opcodes.i32_ne ],
|
@@ -656,9 +648,9 @@ const compareStrings = (scope, left, right, bytestrings = false) => {
|
|
656
648
|
[ Opcodes.i32_add ],
|
657
649
|
[ Opcodes.local_tee, index ],
|
658
650
|
|
659
|
-
// if index
|
651
|
+
// if index < index end (length * sizeof valtype), loop
|
660
652
|
[ Opcodes.local_get, indexEnd ],
|
661
|
-
[ Opcodes.
|
653
|
+
[ Opcodes.i32_lt_s ],
|
662
654
|
[ Opcodes.br_if, 0 ],
|
663
655
|
[ Opcodes.end ],
|
664
656
|
|
@@ -676,38 +668,50 @@ const compareStrings = (scope, left, right, bytestrings = false) => {
|
|
676
668
|
];
|
677
669
|
};
|
678
670
|
|
679
|
-
const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
680
|
-
if (
|
671
|
+
const truthy = (scope, wasm, type, intIn = false, intOut = false, forceTruthyMode = undefined) => {
|
672
|
+
if (isIntToFloatOp(wasm[wasm.length - 1])) return [
|
681
673
|
...wasm,
|
682
674
|
...(!intIn && intOut ? [ Opcodes.i32_to_u ] : [])
|
683
675
|
];
|
684
676
|
// if (isIntOp(wasm[wasm.length - 1])) return [ ...wasm ];
|
685
677
|
|
678
|
+
// todo/perf: use knownType and custom bytecode here instead of typeSwitch
|
679
|
+
|
686
680
|
const useTmp = knownType(scope, type) == null;
|
687
681
|
const tmp = useTmp && localTmp(scope, `#logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
|
688
682
|
|
689
|
-
const def =
|
690
|
-
|
691
|
-
|
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 ]),
|
692
688
|
|
693
|
-
|
694
|
-
|
689
|
+
[ Opcodes.i32_eqz ],
|
690
|
+
[ Opcodes.i32_eqz ],
|
695
691
|
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
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');
|
700
709
|
|
701
710
|
return [
|
702
711
|
...wasm,
|
703
712
|
...(!useTmp ? [] : [ [ Opcodes.local_set, tmp ] ]),
|
704
713
|
|
705
714
|
...typeSwitch(scope, type, {
|
706
|
-
// [TYPES.number]: def,
|
707
|
-
[TYPES.array]: [
|
708
|
-
// arrays are always truthy
|
709
|
-
...number(1, intOut ? Valtype.i32 : valtypeBinary)
|
710
|
-
],
|
711
715
|
[TYPES.string]: [
|
712
716
|
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
713
717
|
...(intIn ? [] : [ Opcodes.i32_to_u ]),
|
@@ -743,10 +747,6 @@ const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
743
747
|
...(!useTmp ? [] : [ [ Opcodes.local_set, tmp ] ]),
|
744
748
|
|
745
749
|
...typeSwitch(scope, type, {
|
746
|
-
[TYPES.array]: [
|
747
|
-
// arrays are always truthy
|
748
|
-
...number(0, intOut ? Valtype.i32 : valtypeBinary)
|
749
|
-
],
|
750
750
|
[TYPES.string]: [
|
751
751
|
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
752
752
|
...(intIn ? [] : [ Opcodes.i32_to_u ]),
|
@@ -990,7 +990,7 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
990
990
|
// if both are true
|
991
991
|
[ Opcodes.i32_and ],
|
992
992
|
[ Opcodes.if, Blocktype.void ],
|
993
|
-
...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ]),
|
993
|
+
...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ], false),
|
994
994
|
...(op === '!==' || op === '!=' ? [ [ Opcodes.i32_eqz ] ] : []),
|
995
995
|
[ Opcodes.br, 1 ],
|
996
996
|
[ Opcodes.end ],
|
@@ -1039,14 +1039,14 @@ const generateBinaryExp = (scope, decl, _global, _name) => {
|
|
1039
1039
|
return out;
|
1040
1040
|
};
|
1041
1041
|
|
1042
|
-
const asmFuncToAsm = (func,
|
1043
|
-
return func(
|
1042
|
+
const asmFuncToAsm = (func, scope) => {
|
1043
|
+
return func(scope, {
|
1044
1044
|
TYPES, TYPE_NAMES, typeSwitch, makeArray, makeString, allocPage, internalThrow,
|
1045
|
-
builtin:
|
1046
|
-
let idx = funcIndex[
|
1047
|
-
if (idx
|
1048
|
-
includeBuiltin(null,
|
1049
|
-
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];
|
1050
1050
|
}
|
1051
1051
|
|
1052
1052
|
return idx;
|
@@ -1054,7 +1054,7 @@ const asmFuncToAsm = (func, { name = '#unknown_asm_func', params = [], locals =
|
|
1054
1054
|
});
|
1055
1055
|
};
|
1056
1056
|
|
1057
|
-
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 }) => {
|
1058
1058
|
const existing = funcs.find(x => x.name === name);
|
1059
1059
|
if (existing) return existing;
|
1060
1060
|
|
@@ -1072,7 +1072,22 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
|
|
1072
1072
|
data.push(copy);
|
1073
1073
|
}
|
1074
1074
|
|
1075
|
-
|
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);
|
1076
1091
|
|
1077
1092
|
let baseGlobalIdx, i = 0;
|
1078
1093
|
for (const type of globalTypes) {
|
@@ -1091,19 +1106,14 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
|
|
1091
1106
|
}
|
1092
1107
|
}
|
1093
1108
|
|
1094
|
-
const
|
1095
|
-
|
1096
|
-
|
1097
|
-
|
1098
|
-
|
1099
|
-
|
1100
|
-
wasm,
|
1101
|
-
internal: true,
|
1102
|
-
index: currentFuncIndex++
|
1103
|
-
};
|
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
|
+
}
|
1104
1115
|
|
1105
|
-
|
1106
|
-
funcIndex[name] = func.index;
|
1116
|
+
func.wasm = wasm;
|
1107
1117
|
|
1108
1118
|
return func;
|
1109
1119
|
};
|
@@ -1195,9 +1205,10 @@ const getLastType = scope => {
|
|
1195
1205
|
return [ [ Opcodes.local_get, localTmp(scope, '#last_type', Valtype.i32) ] ];
|
1196
1206
|
};
|
1197
1207
|
|
1198
|
-
const setLastType = scope =>
|
1199
|
-
|
1200
|
-
|
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
|
+
];
|
1201
1212
|
|
1202
1213
|
const getNodeType = (scope, node) => {
|
1203
1214
|
const ret = (() => {
|
@@ -1258,7 +1269,17 @@ const getNodeType = (scope, node) => {
|
|
1258
1269
|
|
1259
1270
|
const func = spl[spl.length - 1];
|
1260
1271
|
const protoFuncs = Object.keys(prototypeFuncs).filter(x => x != TYPES.bytestring && prototypeFuncs[x][func] != null);
|
1261
|
-
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
|
+
}
|
1262
1283
|
}
|
1263
1284
|
|
1264
1285
|
if (name.startsWith('__Porffor_wasm_')) {
|
@@ -1353,22 +1374,27 @@ const getNodeType = (scope, node) => {
|
|
1353
1374
|
}
|
1354
1375
|
|
1355
1376
|
if (node.type === 'MemberExpression') {
|
1356
|
-
|
1357
|
-
|
1358
|
-
|
1359
|
-
|
1360
|
-
|
1361
|
-
return TYPES.undefined;
|
1362
|
-
}
|
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;
|
1363
1382
|
}
|
1364
1383
|
|
1365
|
-
// hack: if something.length, number type
|
1366
|
-
if (node.property.name === 'length') return TYPES.number;
|
1367
1384
|
|
1368
|
-
|
1369
|
-
if (
|
1370
|
-
|
1371
|
-
|
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
|
+
}
|
1372
1398
|
|
1373
1399
|
if (scope.locals['#last_type']) return getLastType(scope);
|
1374
1400
|
|
@@ -1439,17 +1465,16 @@ const countLeftover = wasm => {
|
|
1439
1465
|
else if (inst[0] === Opcodes.return) count = 0;
|
1440
1466
|
else if (inst[0] === Opcodes.call) {
|
1441
1467
|
let func = funcs.find(x => x.index === inst[1]);
|
1442
|
-
if (inst[1]
|
1443
|
-
|
1444
|
-
|
1445
|
-
count -= importedFuncs[inst[1]].params;
|
1446
|
-
count += importedFuncs[inst[1]].returns;
|
1468
|
+
if (inst[1] < importedFuncs.length) {
|
1469
|
+
func = importedFuncs[inst[1]];
|
1470
|
+
count = count - func.params + func.returns;
|
1447
1471
|
} else {
|
1448
|
-
|
1449
|
-
count -= func.params.length;
|
1450
|
-
} else count--;
|
1451
|
-
if (func) count += func.returns.length;
|
1472
|
+
count = count - func.params.length + func.returns.length;
|
1452
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)
|
1453
1478
|
} else count--;
|
1454
1479
|
|
1455
1480
|
// console.log(count, decompile([ inst ]).slice(0, -1));
|
@@ -1467,7 +1492,7 @@ const disposeLeftover = wasm => {
|
|
1467
1492
|
const generateExp = (scope, decl) => {
|
1468
1493
|
const expression = decl.expression;
|
1469
1494
|
|
1470
|
-
const out = generate(scope, expression, undefined, undefined,
|
1495
|
+
const out = generate(scope, expression, undefined, undefined, Prefs.optUnused);
|
1471
1496
|
disposeLeftover(out);
|
1472
1497
|
|
1473
1498
|
return out;
|
@@ -1558,16 +1583,10 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1558
1583
|
out.splice(out.length - 1, 1);
|
1559
1584
|
|
1560
1585
|
const finalStatement = parsed.body[parsed.body.length - 1];
|
1561
|
-
out.push(
|
1562
|
-
...getNodeType(scope, finalStatement),
|
1563
|
-
...setLastType(scope)
|
1564
|
-
);
|
1586
|
+
out.push(...setLastType(scope, getNodeType(scope, finalStatement)));
|
1565
1587
|
} else if (countLeftover(out) === 0) {
|
1566
1588
|
out.push(...number(UNDEFINED));
|
1567
|
-
out.push(
|
1568
|
-
...number(TYPES.undefined, Valtype.i32),
|
1569
|
-
...setLastType(scope)
|
1570
|
-
);
|
1589
|
+
out.push(...setLastType(scope, TYPES.undefined));
|
1571
1590
|
}
|
1572
1591
|
|
1573
1592
|
// if (lastInst && lastInst[0] === Opcodes.drop) {
|
@@ -1603,6 +1622,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1603
1622
|
|
1604
1623
|
if (!funcIndex[rhemynName]) {
|
1605
1624
|
const func = Rhemyn[funcName](regex, currentFuncIndex++, rhemynName);
|
1625
|
+
func.internal = true;
|
1606
1626
|
|
1607
1627
|
funcIndex[func.name] = func.index;
|
1608
1628
|
funcs.push(func);
|
@@ -1619,8 +1639,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1619
1639
|
[ Opcodes.call, idx ],
|
1620
1640
|
Opcodes.i32_from_u,
|
1621
1641
|
|
1622
|
-
...
|
1623
|
-
...setLastType(scope)
|
1642
|
+
...setLastType(scope, TYPES.boolean)
|
1624
1643
|
];
|
1625
1644
|
}
|
1626
1645
|
|
@@ -1692,9 +1711,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1692
1711
|
if (protoFunc.noArgRetLength && decl.arguments.length === 0) {
|
1693
1712
|
protoBC[x] = [
|
1694
1713
|
...RTArrayUtil.getLength(getPointer),
|
1695
|
-
|
1696
|
-
...number(TYPES.number, Valtype.i32),
|
1697
|
-
...setLastType(scope)
|
1714
|
+
...setLastType(scope, TYPES.number)
|
1698
1715
|
];
|
1699
1716
|
continue;
|
1700
1717
|
}
|
@@ -1713,7 +1730,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1713
1730
|
getI32: () => RTArrayUtil.getLengthI32(getPointer),
|
1714
1731
|
set: value => RTArrayUtil.setLength(getPointer, value),
|
1715
1732
|
setI32: value => RTArrayUtil.setLengthI32(getPointer, value)
|
1716
|
-
}, 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) => {
|
1717
1734
|
return makeArray(scope, {
|
1718
1735
|
rawElements: new Array(length)
|
1719
1736
|
}, _global, _name, true, itemType);
|
@@ -1727,9 +1744,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1727
1744
|
protoBC[x] = [
|
1728
1745
|
[ Opcodes.block, unusedValue && optUnused ? Blocktype.void : valtypeBinary ],
|
1729
1746
|
...protoOut,
|
1730
|
-
|
1731
|
-
...number(protoFunc.returnType ?? TYPES.number, Valtype.i32),
|
1732
|
-
...setLastType(scope),
|
1747
|
+
...(unusedValue && optUnused ? [] : (protoFunc.returnType != null ? setLastType(scope, protoFunc.returnType) : setLastType(scope))),
|
1733
1748
|
[ Opcodes.end ]
|
1734
1749
|
];
|
1735
1750
|
}
|
@@ -1779,11 +1794,6 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1779
1794
|
|
1780
1795
|
if (idx === undefined && internalConstrs[name]) return internalConstrs[name].generate(scope, decl, _global, _name);
|
1781
1796
|
|
1782
|
-
if (idx === undefined && name === scope.name) {
|
1783
|
-
// hack: calling self, func generator will fix later
|
1784
|
-
idx = -1;
|
1785
|
-
}
|
1786
|
-
|
1787
1797
|
if (idx === undefined && name.startsWith('__Porffor_wasm_')) {
|
1788
1798
|
const wasmOps = {
|
1789
1799
|
// pointer, align, offset
|
@@ -1831,15 +1841,144 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1831
1841
|
}
|
1832
1842
|
|
1833
1843
|
if (idx === undefined) {
|
1834
|
-
if (scope.locals[name] !== undefined || globals[name] !== undefined || builtinVars[name] !== undefined)
|
1844
|
+
if (scope.locals[name] !== undefined || globals[name] !== undefined || builtinVars[name] !== undefined) {
|
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
|
+
|
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
|
+
}
|
1951
|
+
|
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), {
|
1955
|
+
[TYPES.function]: [
|
1956
|
+
...out,
|
1957
|
+
|
1958
|
+
[ global ? Opcodes.global_get : Opcodes.local_get, local.idx ],
|
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)
|
1970
|
+
],
|
1971
|
+
default: internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, true)
|
1972
|
+
});
|
1973
|
+
}
|
1974
|
+
|
1835
1975
|
return internalThrow(scope, 'ReferenceError', `${unhackName(name)} is not defined`, true);
|
1836
1976
|
}
|
1837
1977
|
|
1838
|
-
const func = funcs.find(x => x.index === idx);
|
1839
|
-
|
1840
|
-
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;
|
1841
1980
|
const typedParams = userFunc || builtinFuncs[name]?.typedParams;
|
1842
|
-
const typedReturns = (func
|
1981
|
+
const typedReturns = (func && func.returnType == null) || builtinFuncs[name]?.typedReturns;
|
1843
1982
|
const paramCount = func && (typedParams ? func.params.length / 2 : func.params.length);
|
1844
1983
|
|
1845
1984
|
let args = decl.arguments;
|
@@ -1860,11 +1999,17 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1860
1999
|
const arg = args[i];
|
1861
2000
|
out = out.concat(generate(scope, arg));
|
1862
2001
|
|
1863
|
-
|
1864
|
-
|
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;
|
1865
2007
|
}
|
1866
2008
|
|
1867
|
-
if (
|
2009
|
+
if (valtypeBinary !== Valtype.i32 && (
|
2010
|
+
(builtinFuncs[name] && builtinFuncs[name].params[i * (typedParams ? 2 : 1)] === Valtype.i32) ||
|
2011
|
+
(importedFuncs[name] && name.startsWith('profile'))
|
2012
|
+
)) {
|
1868
2013
|
out.push(Opcodes.i32_to);
|
1869
2014
|
}
|
1870
2015
|
|
@@ -1909,6 +2054,11 @@ const generateNew = (scope, decl, _global, _name) => {
|
|
1909
2054
|
}, _global, _name);
|
1910
2055
|
}
|
1911
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
|
+
|
1912
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)})`);
|
1913
2063
|
|
1914
2064
|
return generateCall(scope, decl, _global, _name);
|
@@ -1934,8 +2084,11 @@ const knownType = (scope, type) => {
|
|
1934
2084
|
const idx = type[0][1];
|
1935
2085
|
|
1936
2086
|
// type idx = var idx + 1
|
1937
|
-
const
|
1938
|
-
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
|
+
}
|
1939
2092
|
}
|
1940
2093
|
|
1941
2094
|
return null;
|
@@ -1970,16 +2123,17 @@ const brTable = (input, bc, returns) => {
|
|
1970
2123
|
}
|
1971
2124
|
|
1972
2125
|
for (let i = 0; i < count; i++) {
|
1973
|
-
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 ]);
|
1974
2128
|
else out.push([ Opcodes.block, Blocktype.void ]);
|
1975
2129
|
}
|
1976
2130
|
|
1977
|
-
const nums = keys.filter(x => +x);
|
2131
|
+
const nums = keys.filter(x => +x >= 0);
|
1978
2132
|
const offset = Math.min(...nums);
|
1979
2133
|
const max = Math.max(...nums);
|
1980
2134
|
|
1981
2135
|
const table = [];
|
1982
|
-
let br =
|
2136
|
+
let br = 0;
|
1983
2137
|
|
1984
2138
|
for (let i = offset; i <= max; i++) {
|
1985
2139
|
// if branch for this num, go to that block
|
@@ -2019,10 +2173,9 @@ const brTable = (input, bc, returns) => {
|
|
2019
2173
|
br--;
|
2020
2174
|
}
|
2021
2175
|
|
2022
|
-
|
2023
|
-
|
2024
|
-
|
2025
|
-
];
|
2176
|
+
out.push([ Opcodes.end ]);
|
2177
|
+
|
2178
|
+
return out;
|
2026
2179
|
};
|
2027
2180
|
|
2028
2181
|
const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
|
@@ -2066,6 +2219,17 @@ const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
|
|
2066
2219
|
return out;
|
2067
2220
|
};
|
2068
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
|
+
|
2069
2233
|
const allocVar = (scope, name, global = false, type = true) => {
|
2070
2234
|
const target = global ? globals : scope.locals;
|
2071
2235
|
|
@@ -2082,7 +2246,7 @@ const allocVar = (scope, name, global = false, type = true) => {
|
|
2082
2246
|
|
2083
2247
|
if (type) {
|
2084
2248
|
let typeIdx = global ? globalInd++ : scope.localInd++;
|
2085
|
-
target[name + '#type'] = { idx: typeIdx, type: Valtype.i32 };
|
2249
|
+
target[name + '#type'] = { idx: typeIdx, type: Valtype.i32, name };
|
2086
2250
|
}
|
2087
2251
|
|
2088
2252
|
return idx;
|
@@ -2174,6 +2338,22 @@ const generateVar = (scope, decl) => {
|
|
2174
2338
|
}
|
2175
2339
|
|
2176
2340
|
if (x.init) {
|
2341
|
+
// if (isFuncType(x.init.type)) {
|
2342
|
+
// // let a = function () { ... }
|
2343
|
+
// x.init.id = { name };
|
2344
|
+
|
2345
|
+
// const func = generateFunc(scope, x.init);
|
2346
|
+
|
2347
|
+
// out.push(
|
2348
|
+
// ...number(func.index - importedFuncs.length),
|
2349
|
+
// [ global ? Opcodes.global_set : Opcodes.local_set, idx ],
|
2350
|
+
|
2351
|
+
// ...setType(scope, name, TYPES.function)
|
2352
|
+
// );
|
2353
|
+
|
2354
|
+
// continue;
|
2355
|
+
// }
|
2356
|
+
|
2177
2357
|
const generated = generate(scope, x.init, global, name);
|
2178
2358
|
if (scope.arrays?.get(name) != null) {
|
2179
2359
|
// hack to set local as pointer before
|
@@ -2185,6 +2365,7 @@ const generateVar = (scope, decl) => {
|
|
2185
2365
|
out = out.concat(generated);
|
2186
2366
|
out.push([ global ? Opcodes.global_set : Opcodes.local_set, idx ]);
|
2187
2367
|
}
|
2368
|
+
|
2188
2369
|
out.push(...setType(scope, name, getNodeType(scope, x.init)));
|
2189
2370
|
}
|
2190
2371
|
|
@@ -2198,6 +2379,7 @@ const generateVar = (scope, decl) => {
|
|
2198
2379
|
// todo: optimize this func for valueUnused
|
2199
2380
|
const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
|
2200
2381
|
const { type, name } = decl.left;
|
2382
|
+
const [ local, isGlobal ] = lookupName(scope, name);
|
2201
2383
|
|
2202
2384
|
if (type === 'ObjectPattern') {
|
2203
2385
|
// hack: ignore object parts of `var a = {} = 2`
|
@@ -2207,8 +2389,18 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
|
|
2207
2389
|
if (isFuncType(decl.right.type)) {
|
2208
2390
|
// hack for a = function () { ... }
|
2209
2391
|
decl.right.id = { name };
|
2210
|
-
|
2211
|
-
|
2392
|
+
|
2393
|
+
const func = generateFunc(scope, decl.right);
|
2394
|
+
|
2395
|
+
return [
|
2396
|
+
...number(func.index - importedFuncs.length),
|
2397
|
+
...(local != null ? [
|
2398
|
+
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
|
2399
|
+
[ isGlobal ? Opcodes.global_get : Opcodes.local_get, local.idx ],
|
2400
|
+
|
2401
|
+
...setType(scope, name, TYPES.function)
|
2402
|
+
] : [])
|
2403
|
+
];
|
2212
2404
|
}
|
2213
2405
|
|
2214
2406
|
const op = decl.operator.slice(0, -1) || '=';
|
@@ -2267,18 +2459,21 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
|
|
2267
2459
|
Opcodes.i32_to_u,
|
2268
2460
|
|
2269
2461
|
// turn into byte offset by * valtypeSize (4 for i32, 8 for i64/f64)
|
2270
|
-
...number(ValtypeSize[valtype], Valtype.i32),
|
2462
|
+
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
2271
2463
|
[ Opcodes.i32_mul ],
|
2272
2464
|
...(aotPointer ? [] : [ [ Opcodes.i32_add ] ]),
|
2273
2465
|
...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
|
2274
2466
|
|
2275
2467
|
...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
|
2276
2468
|
[ Opcodes.local_get, pointerTmp ],
|
2277
|
-
[ Opcodes.load,
|
2278
|
-
], 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)),
|
2279
2474
|
[ Opcodes.local_tee, newValueTmp ],
|
2280
2475
|
|
2281
|
-
[ Opcodes.store,
|
2476
|
+
[ Opcodes.store, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
|
2282
2477
|
],
|
2283
2478
|
|
2284
2479
|
default: internalThrow(scope, 'TypeError', `Cannot assign member with non-array`)
|
@@ -2307,8 +2502,6 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
|
|
2307
2502
|
|
2308
2503
|
if (!name) return todo(scope, 'destructuring is not supported yet', true);
|
2309
2504
|
|
2310
|
-
const [ local, isGlobal ] = lookupName(scope, name);
|
2311
|
-
|
2312
2505
|
if (local === undefined) {
|
2313
2506
|
// todo: this should be a sloppy mode only thing
|
2314
2507
|
|
@@ -2388,6 +2581,11 @@ const generateUnary = (scope, decl) => {
|
|
2388
2581
|
];
|
2389
2582
|
|
2390
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
|
+
}
|
2391
2589
|
// !=
|
2392
2590
|
return falsy(scope, generate(scope, decl.argument), getNodeType(scope, decl.argument), false, false);
|
2393
2591
|
|
@@ -2457,6 +2655,7 @@ const generateUnary = (scope, decl) => {
|
|
2457
2655
|
[TYPES.string]: makeString(scope, 'string', false, '#typeof_result'),
|
2458
2656
|
[TYPES.undefined]: makeString(scope, 'undefined', false, '#typeof_result'),
|
2459
2657
|
[TYPES.function]: makeString(scope, 'function', false, '#typeof_result'),
|
2658
|
+
[TYPES.symbol]: makeString(scope, 'symbol', false, '#typeof_result'),
|
2460
2659
|
|
2461
2660
|
[TYPES.bytestring]: makeString(scope, 'string', false, '#typeof_result'),
|
2462
2661
|
|
@@ -2533,21 +2732,16 @@ const generateConditional = (scope, decl) => {
|
|
2533
2732
|
out.push([ Opcodes.if, valtypeBinary ]);
|
2534
2733
|
depth.push('if');
|
2535
2734
|
|
2536
|
-
out.push(...generate(scope, decl.consequent));
|
2537
|
-
|
2538
|
-
// note type
|
2539
2735
|
out.push(
|
2540
|
-
...
|
2541
|
-
...setLastType(scope)
|
2736
|
+
...generate(scope, decl.consequent),
|
2737
|
+
...setLastType(scope, getNodeType(scope, decl.consequent))
|
2542
2738
|
);
|
2543
2739
|
|
2544
2740
|
out.push([ Opcodes.else ]);
|
2545
|
-
out.push(...generate(scope, decl.alternate));
|
2546
2741
|
|
2547
|
-
// note type
|
2548
2742
|
out.push(
|
2549
|
-
...
|
2550
|
-
...setLastType(scope)
|
2743
|
+
...generate(scope, decl.alternate),
|
2744
|
+
...setLastType(scope, getNodeType(scope, decl.alternate))
|
2551
2745
|
);
|
2552
2746
|
|
2553
2747
|
out.push([ Opcodes.end ]);
|
@@ -2695,12 +2889,15 @@ const generateForOf = (scope, decl) => {
|
|
2695
2889
|
// todo: optimize away counter and use end pointer
|
2696
2890
|
out.push(...typeSwitch(scope, getNodeType(scope, decl.right), {
|
2697
2891
|
[TYPES.array]: [
|
2698
|
-
...setType(scope, leftName, TYPES.number),
|
2699
|
-
|
2700
2892
|
[ Opcodes.loop, Blocktype.void ],
|
2701
2893
|
|
2702
2894
|
[ Opcodes.local_get, pointer ],
|
2703
|
-
[ 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
|
+
]),
|
2704
2901
|
|
2705
2902
|
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
|
2706
2903
|
|
@@ -2709,9 +2906,9 @@ const generateForOf = (scope, decl) => {
|
|
2709
2906
|
...generate(scope, decl.body),
|
2710
2907
|
[ Opcodes.end ],
|
2711
2908
|
|
2712
|
-
// increment iter pointer by valtype size
|
2909
|
+
// increment iter pointer by valtype size + 1
|
2713
2910
|
[ Opcodes.local_get, pointer ],
|
2714
|
-
...number(ValtypeSize[valtype], Valtype.i32),
|
2911
|
+
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
2715
2912
|
[ Opcodes.i32_add ],
|
2716
2913
|
[ Opcodes.local_set, pointer ],
|
2717
2914
|
|
@@ -2827,6 +3024,44 @@ const generateForOf = (scope, decl) => {
|
|
2827
3024
|
[ Opcodes.end ],
|
2828
3025
|
[ Opcodes.end ]
|
2829
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
|
+
],
|
2830
3065
|
default: internalThrow(scope, 'TypeError', `Tried for..of on non-iterable type`)
|
2831
3066
|
}, Blocktype.void));
|
2832
3067
|
|
@@ -2928,14 +3163,18 @@ const generateThrow = (scope, decl) => {
|
|
2928
3163
|
};
|
2929
3164
|
|
2930
3165
|
const generateTry = (scope, decl) => {
|
2931
|
-
|
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."
|
2932
3168
|
|
2933
3169
|
const out = [];
|
2934
3170
|
|
3171
|
+
const finalizer = decl.finalizer ? generate(scope, decl.finalizer) : [];
|
3172
|
+
|
2935
3173
|
out.push([ Opcodes.try, Blocktype.void ]);
|
2936
3174
|
depth.push('try');
|
2937
3175
|
|
2938
3176
|
out.push(...generate(scope, decl.block));
|
3177
|
+
out.push(...finalizer);
|
2939
3178
|
|
2940
3179
|
if (decl.handler) {
|
2941
3180
|
depth.pop();
|
@@ -2943,6 +3182,7 @@ const generateTry = (scope, decl) => {
|
|
2943
3182
|
|
2944
3183
|
out.push([ Opcodes.catch_all ]);
|
2945
3184
|
out.push(...generate(scope, decl.handler.body));
|
3185
|
+
out.push(...finalizer);
|
2946
3186
|
}
|
2947
3187
|
|
2948
3188
|
out.push([ Opcodes.end ]);
|
@@ -3028,7 +3268,7 @@ const getAllocType = itemType => {
|
|
3028
3268
|
}
|
3029
3269
|
};
|
3030
3270
|
|
3031
|
-
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) => {
|
3032
3272
|
const out = [];
|
3033
3273
|
|
3034
3274
|
scope.arrays ??= new Map();
|
@@ -3040,8 +3280,13 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
3040
3280
|
// todo: can we just have 1 undeclared array? probably not? but this is not really memory efficient
|
3041
3281
|
const uniqueName = name === '$undeclared' ? name + Math.random().toString().slice(2) : name;
|
3042
3282
|
|
3043
|
-
|
3044
|
-
|
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);
|
3045
3290
|
}
|
3046
3291
|
|
3047
3292
|
const pointer = scope.arrays.get(name);
|
@@ -3091,7 +3336,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
3091
3336
|
|
3092
3337
|
const pointerWasm = pointerTmp != null ? [ [ Opcodes.local_get, pointerTmp ] ] : number(pointer, Valtype.i32);
|
3093
3338
|
|
3094
|
-
// store length
|
3339
|
+
// store length
|
3095
3340
|
out.push(
|
3096
3341
|
...pointerWasm,
|
3097
3342
|
...number(length, Valtype.i32),
|
@@ -3099,14 +3344,20 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
3099
3344
|
);
|
3100
3345
|
|
3101
3346
|
const storeOp = StoreOps[itemType];
|
3102
|
-
|
3347
|
+
const sizePerEl = ValtypeSize[itemType] + (typed ? 1 : 0);
|
3103
3348
|
if (!initEmpty) for (let i = 0; i < length; i++) {
|
3104
3349
|
if (elements[i] == null) continue;
|
3105
3350
|
|
3351
|
+
const offset = ValtypeSize.i32 + i * sizePerEl;
|
3106
3352
|
out.push(
|
3107
3353
|
...pointerWasm,
|
3108
3354
|
...(useRawElements ? number(elements[i], Valtype[valtype]) : generate(scope, elements[i])),
|
3109
|
-
[ 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
|
+
])
|
3110
3361
|
);
|
3111
3362
|
}
|
3112
3363
|
|
@@ -3116,6 +3367,65 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
3116
3367
|
return [ out, pointer ];
|
3117
3368
|
};
|
3118
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
|
+
|
3119
3429
|
const byteStringable = str => {
|
3120
3430
|
if (!Prefs.bytestring) return false;
|
3121
3431
|
|
@@ -3144,14 +3454,39 @@ const makeString = (scope, str, global = false, name = '$undeclared', forceBytes
|
|
3144
3454
|
};
|
3145
3455
|
|
3146
3456
|
const generateArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false) => {
|
3147
|
-
return makeArray(scope, decl, global, name, initEmpty, valtype)[0];
|
3457
|
+
return makeArray(scope, decl, global, name, initEmpty, valtype, true)[0];
|
3148
3458
|
};
|
3149
3459
|
|
3150
|
-
|
3460
|
+
const generateObject = (scope, decl, global = false, name = '$undeclared') => {
|
3461
|
+
if (decl.properties.length > 0) return todo(scope, 'objects are not supported yet', true);
|
3462
|
+
|
3463
|
+
return [
|
3464
|
+
...number(1),
|
3465
|
+
...setLastType(scope, TYPES.object)
|
3466
|
+
];
|
3467
|
+
};
|
3468
|
+
|
3469
|
+
const withType = (scope, wasm, type) => [
|
3470
|
+
...wasm,
|
3471
|
+
...setLastType(scope, type)
|
3472
|
+
];
|
3473
|
+
|
3474
|
+
const generateMember = (scope, decl, _global, _name) => {
|
3151
3475
|
const name = decl.object.name;
|
3152
|
-
const pointer = scope.arrays?.get(name);
|
3153
3476
|
|
3154
|
-
|
3477
|
+
// hack: process.argv[n]
|
3478
|
+
if (name === '__process_argv') {
|
3479
|
+
const setPointer = scope.arrays?.get(_name);
|
3480
|
+
|
3481
|
+
return [
|
3482
|
+
...number(decl.property.value - 1),
|
3483
|
+
...(setPointer ? number(setPointer) : makeArray(scope, { elements: [] }, undefined, undefined, true, 'i8')[0]),
|
3484
|
+
[ Opcodes.call, importedFuncs.__Porffor_readArgv ]
|
3485
|
+
];
|
3486
|
+
}
|
3487
|
+
|
3488
|
+
const pointer = scope.arrays?.get(name);
|
3489
|
+
const aotPointer = Prefs.aotPointerOpt && pointer;
|
3155
3490
|
|
3156
3491
|
// hack: .name
|
3157
3492
|
if (decl.property.name === 'name') {
|
@@ -3161,9 +3496,9 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3161
3496
|
// eg: __String_prototype_toLowerCase -> toLowerCase
|
3162
3497
|
if (nameProp.startsWith('__')) nameProp = nameProp.split('_').pop();
|
3163
3498
|
|
3164
|
-
return makeString(scope, nameProp, _global, _name, true);
|
3499
|
+
return withType(scope, makeString(scope, nameProp, _global, _name, true), TYPES.bytestring);
|
3165
3500
|
} else {
|
3166
|
-
return
|
3501
|
+
return withType(scope, number(0), TYPES.undefined);
|
3167
3502
|
}
|
3168
3503
|
}
|
3169
3504
|
|
@@ -3171,9 +3506,8 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3171
3506
|
if (decl.property.name === 'length') {
|
3172
3507
|
const func = funcs.find(x => x.name === name);
|
3173
3508
|
if (func) {
|
3174
|
-
const
|
3175
|
-
|
3176
|
-
return number(typedParams ? func.params.length / 2 : func.params.length);
|
3509
|
+
const typedParams = !func.internal || builtinFuncs[name]?.typedParams;
|
3510
|
+
return withType(scope, number(typedParams ? func.params.length / 2 : func.params.length), TYPES.number);
|
3177
3511
|
}
|
3178
3512
|
|
3179
3513
|
if (builtinFuncs[name + '$constructor']) {
|
@@ -3183,24 +3517,88 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3183
3517
|
const constructorFunc = builtinFuncs[name + '$constructor'];
|
3184
3518
|
const constructorParams = constructorFunc.typedParams ? (constructorFunc.params.length / 2) : constructorFunc.params.length;
|
3185
3519
|
|
3186
|
-
return number(Math.max(regularParams, constructorParams));
|
3520
|
+
return withType(scope, number(Math.max(regularParams, constructorParams)), TYPES.number);
|
3521
|
+
}
|
3522
|
+
|
3523
|
+
if (builtinFuncs[name]) return withType(scope, number(builtinFuncs[name].typedParams ? (builtinFuncs[name].params.length / 2) : builtinFuncs[name].params.length), TYPES.number);
|
3524
|
+
if (importedFuncs[name]) return withType(scope, number(importedFuncs[name].params), TYPES.number);
|
3525
|
+
if (internalConstrs[name]) return withType(scope, number(internalConstrs[name].length ?? 0), TYPES.number);
|
3526
|
+
|
3527
|
+
if (Prefs.fastLength) {
|
3528
|
+
// presume valid length object
|
3529
|
+
return [
|
3530
|
+
...(aotPointer ? number(0, Valtype.i32) : [
|
3531
|
+
...generate(scope, decl.object),
|
3532
|
+
Opcodes.i32_to_u
|
3533
|
+
]),
|
3534
|
+
|
3535
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
|
3536
|
+
Opcodes.i32_from_u
|
3537
|
+
];
|
3187
3538
|
}
|
3188
3539
|
|
3189
|
-
|
3190
|
-
|
3191
|
-
if (
|
3540
|
+
const type = getNodeType(scope, decl.object);
|
3541
|
+
const known = knownType(scope, type);
|
3542
|
+
if (known != null) {
|
3543
|
+
if ([ TYPES.string, TYPES.bytestring, TYPES.array ].includes(known)) return [
|
3544
|
+
...(aotPointer ? number(0, Valtype.i32) : [
|
3545
|
+
...generate(scope, decl.object),
|
3546
|
+
Opcodes.i32_to_u
|
3547
|
+
]),
|
3548
|
+
|
3549
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
|
3550
|
+
Opcodes.i32_from_u
|
3551
|
+
];
|
3552
|
+
|
3553
|
+
return number(0);
|
3554
|
+
}
|
3192
3555
|
|
3193
3556
|
return [
|
3194
|
-
...(
|
3195
|
-
|
3196
|
-
|
3197
|
-
|
3557
|
+
...typeIsOneOf(getNodeType(scope, decl.object), [ TYPES.string, TYPES.bytestring, TYPES.array ]),
|
3558
|
+
[ Opcodes.if, valtypeBinary ],
|
3559
|
+
...(aotPointer ? number(0, Valtype.i32) : [
|
3560
|
+
...generate(scope, decl.object),
|
3561
|
+
Opcodes.i32_to_u
|
3562
|
+
]),
|
3198
3563
|
|
3199
|
-
|
3200
|
-
|
3564
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
|
3565
|
+
Opcodes.i32_from_u,
|
3566
|
+
|
3567
|
+
...setLastType(scope, TYPES.number),
|
3568
|
+
[ Opcodes.else ],
|
3569
|
+
...number(0),
|
3570
|
+
...setLastType(scope, TYPES.undefined),
|
3571
|
+
[ Opcodes.end ]
|
3201
3572
|
];
|
3202
3573
|
}
|
3203
3574
|
|
3575
|
+
// todo: generate this array procedurally during builtinFuncs creation
|
3576
|
+
if (['size', 'description'].includes(decl.property.name)) {
|
3577
|
+
const bc = {};
|
3578
|
+
const cands = Object.keys(builtinFuncs).filter(x => x.startsWith('__') && x.endsWith('_prototype_' + decl.property.name + '$get'));
|
3579
|
+
|
3580
|
+
if (cands.length > 0) {
|
3581
|
+
for (const x of cands) {
|
3582
|
+
const type = TYPES[x.split('_prototype_')[0].slice(2).toLowerCase()];
|
3583
|
+
if (type == null) continue;
|
3584
|
+
|
3585
|
+
bc[type] = generateCall(scope, {
|
3586
|
+
callee: {
|
3587
|
+
type: 'Identifier',
|
3588
|
+
name: x
|
3589
|
+
},
|
3590
|
+
arguments: [ decl.object ],
|
3591
|
+
_protoInternalCall: true
|
3592
|
+
});
|
3593
|
+
}
|
3594
|
+
}
|
3595
|
+
|
3596
|
+
return typeSwitch(scope, getNodeType(scope, decl.object), {
|
3597
|
+
...bc,
|
3598
|
+
default: withType(scope, number(0), TYPES.undefined)
|
3599
|
+
}, valtypeBinary);
|
3600
|
+
}
|
3601
|
+
|
3204
3602
|
const object = generate(scope, decl.object);
|
3205
3603
|
const property = generate(scope, decl.property);
|
3206
3604
|
|
@@ -3215,24 +3613,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3215
3613
|
|
3216
3614
|
return typeSwitch(scope, getNodeType(scope, decl.object), {
|
3217
3615
|
[TYPES.array]: [
|
3218
|
-
|
3219
|
-
...property,
|
3220
|
-
|
3221
|
-
// convert to i32 and turn into byte offset by * valtypeSize (4 for i32, 8 for i64/f64)
|
3222
|
-
Opcodes.i32_to_u,
|
3223
|
-
...number(ValtypeSize[valtype], Valtype.i32),
|
3224
|
-
[ Opcodes.i32_mul ],
|
3225
|
-
|
3226
|
-
...(aotPointer ? [] : [
|
3227
|
-
...object,
|
3228
|
-
Opcodes.i32_to_u,
|
3229
|
-
[ Opcodes.i32_add ]
|
3230
|
-
]),
|
3231
|
-
|
3232
|
-
// read from memory
|
3233
|
-
[ Opcodes.load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
3234
|
-
|
3235
|
-
...number(TYPES.number, Valtype.i32),
|
3616
|
+
...loadArray(scope, object, property, aotPointer),
|
3236
3617
|
...setLastType(scope)
|
3237
3618
|
],
|
3238
3619
|
|
@@ -3263,9 +3644,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3263
3644
|
|
3264
3645
|
// return new string (page)
|
3265
3646
|
...number(newPointer),
|
3266
|
-
|
3267
|
-
...number(TYPES.string, Valtype.i32),
|
3268
|
-
...setLastType(scope)
|
3647
|
+
...setLastType(scope, TYPES.string)
|
3269
3648
|
],
|
3270
3649
|
[TYPES.bytestring]: [
|
3271
3650
|
// setup new/out array
|
@@ -3291,9 +3670,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3291
3670
|
|
3292
3671
|
// return new string (page)
|
3293
3672
|
...number(newPointer),
|
3294
|
-
|
3295
|
-
...number(TYPES.bytestring, Valtype.i32),
|
3296
|
-
...setLastType(scope)
|
3673
|
+
...setLastType(scope, TYPES.bytestring)
|
3297
3674
|
],
|
3298
3675
|
|
3299
3676
|
default: internalThrow(scope, 'TypeError', 'Member expression is not supported for non-string non-array yet', true)
|
@@ -3318,7 +3695,7 @@ const objectHack = node => {
|
|
3318
3695
|
if (!objectName) objectName = objectHack(node.object)?.name?.slice?.(2);
|
3319
3696
|
|
3320
3697
|
// if .name or .length, give up (hack within a hack!)
|
3321
|
-
if (['name', 'length'].includes(node.property.name)) {
|
3698
|
+
if (['name', 'length', 'size', 'description'].includes(node.property.name)) {
|
3322
3699
|
node.object = objectHack(node.object);
|
3323
3700
|
return;
|
3324
3701
|
}
|
@@ -3355,33 +3732,39 @@ const generateFunc = (scope, decl) => {
|
|
3355
3732
|
const name = decl.id ? decl.id.name : `anonymous_${randId()}`;
|
3356
3733
|
const params = decl.params ?? [];
|
3357
3734
|
|
3358
|
-
// const innerScope = { ...scope };
|
3359
3735
|
// TODO: share scope/locals between !!!
|
3360
|
-
const
|
3736
|
+
const func = {
|
3361
3737
|
locals: {},
|
3362
3738
|
localInd: 0,
|
3363
3739
|
// value, type
|
3364
3740
|
returns: [ valtypeBinary, Valtype.i32 ],
|
3365
3741
|
throws: false,
|
3366
|
-
name
|
3742
|
+
name,
|
3743
|
+
index: currentFuncIndex++
|
3367
3744
|
};
|
3368
3745
|
|
3369
3746
|
if (typedInput && decl.returnType) {
|
3370
3747
|
const { type } = extractTypeAnnotation(decl.returnType);
|
3748
|
+
// if (type != null && !Prefs.indirectCalls) {
|
3371
3749
|
if (type != null) {
|
3372
|
-
|
3373
|
-
|
3750
|
+
func.returnType = type;
|
3751
|
+
func.returns = [ valtypeBinary ];
|
3374
3752
|
}
|
3375
3753
|
}
|
3376
3754
|
|
3377
3755
|
for (let i = 0; i < params.length; i++) {
|
3378
|
-
|
3756
|
+
const name = params[i].name;
|
3757
|
+
// if (name == null) return todo('non-identifier args are not supported');
|
3758
|
+
|
3759
|
+
allocVar(func, name, false);
|
3379
3760
|
|
3380
3761
|
if (typedInput && params[i].typeAnnotation) {
|
3381
|
-
addVarMetadata(
|
3762
|
+
addVarMetadata(func, name, false, extractTypeAnnotation(params[i]));
|
3382
3763
|
}
|
3383
3764
|
}
|
3384
3765
|
|
3766
|
+
func.params = Object.values(func.locals).map(x => x.type);
|
3767
|
+
|
3385
3768
|
let body = objectHack(decl.body);
|
3386
3769
|
if (decl.type === 'ArrowFunctionExpression' && decl.expression) {
|
3387
3770
|
// hack: () => 0 -> () => return 0
|
@@ -3391,37 +3774,23 @@ const generateFunc = (scope, decl) => {
|
|
3391
3774
|
};
|
3392
3775
|
}
|
3393
3776
|
|
3394
|
-
const wasm = generate(innerScope, body);
|
3395
|
-
const func = {
|
3396
|
-
name,
|
3397
|
-
params: Object.values(innerScope.locals).slice(0, params.length * 2).map(x => x.type),
|
3398
|
-
index: currentFuncIndex++,
|
3399
|
-
...innerScope
|
3400
|
-
};
|
3401
3777
|
funcIndex[name] = func.index;
|
3778
|
+
funcs.push(func);
|
3402
3779
|
|
3403
|
-
|
3780
|
+
const wasm = generate(func, body);
|
3781
|
+
func.wasm = wasm;
|
3404
3782
|
|
3405
|
-
|
3406
|
-
for (const inst of wasm) {
|
3407
|
-
if (inst[0] === Opcodes.call && inst[1] === -1) {
|
3408
|
-
inst[1] = func.index;
|
3409
|
-
}
|
3410
|
-
}
|
3783
|
+
if (name === 'main') func.gotLastType = true;
|
3411
3784
|
|
3412
3785
|
// add end return if not found
|
3413
3786
|
if (name !== 'main' && wasm[wasm.length - 1]?.[0] !== Opcodes.return && countLeftover(wasm) === 0) {
|
3414
3787
|
wasm.push(
|
3415
3788
|
...number(0),
|
3416
|
-
...(
|
3789
|
+
...(func.returnType != null ? [] : number(TYPES.undefined, Valtype.i32)),
|
3417
3790
|
[ Opcodes.return ]
|
3418
3791
|
);
|
3419
3792
|
}
|
3420
3793
|
|
3421
|
-
func.wasm = wasm;
|
3422
|
-
|
3423
|
-
funcs.push(func);
|
3424
|
-
|
3425
3794
|
return func;
|
3426
3795
|
};
|
3427
3796
|
|
@@ -3525,7 +3894,7 @@ const internalConstrs = {
|
|
3525
3894
|
generate: (scope, decl) => {
|
3526
3895
|
// todo: boolean object when used as constructor
|
3527
3896
|
const arg = decl.arguments[0] ?? DEFAULT_VALUE;
|
3528
|
-
return truthy(scope, generate(scope, arg), getNodeType(scope, arg));
|
3897
|
+
return truthy(scope, generate(scope, arg), getNodeType(scope, arg), false, false, 'full');
|
3529
3898
|
},
|
3530
3899
|
type: TYPES.boolean,
|
3531
3900
|
length: 1
|
@@ -3586,8 +3955,10 @@ const internalConstrs = {
|
|
3586
3955
|
}),
|
3587
3956
|
|
3588
3957
|
// print space
|
3589
|
-
...
|
3590
|
-
|
3958
|
+
...(i !== decl.arguments.length - 1 ? [
|
3959
|
+
...number(32),
|
3960
|
+
[ Opcodes.call, importedFuncs.printChar ]
|
3961
|
+
] : [])
|
3591
3962
|
);
|
3592
3963
|
}
|
3593
3964
|
|
@@ -3597,6 +3968,8 @@ const internalConstrs = {
|
|
3597
3968
|
[ Opcodes.call, importedFuncs.printChar ]
|
3598
3969
|
);
|
3599
3970
|
|
3971
|
+
out.push(...number(UNDEFINED));
|
3972
|
+
|
3600
3973
|
return out;
|
3601
3974
|
},
|
3602
3975
|
type: TYPES.undefined,
|
@@ -3666,9 +4039,8 @@ export default program => {
|
|
3666
4039
|
|
3667
4040
|
if (Prefs.astLog) console.log(JSON.stringify(program.body.body, null, 2));
|
3668
4041
|
|
3669
|
-
generateFunc(scope, program);
|
4042
|
+
const main = generateFunc(scope, program);
|
3670
4043
|
|
3671
|
-
const main = funcs[funcs.length - 1];
|
3672
4044
|
main.export = true;
|
3673
4045
|
main.returns = [ valtypeBinary, Valtype.i32 ];
|
3674
4046
|
|
@@ -3695,7 +4067,7 @@ export default program => {
|
|
3695
4067
|
}
|
3696
4068
|
|
3697
4069
|
// if blank main func and other exports, remove it
|
3698
|
-
if (main.wasm.length === 0 && funcs.reduce((acc, x) => acc + (x.export ? 1 : 0), 0) > 1) funcs.splice(
|
4070
|
+
if (main.wasm.length === 0 && funcs.reduce((acc, x) => acc + (x.export ? 1 : 0), 0) > 1) funcs.splice(main.index - importedFuncs.length, 1);
|
3699
4071
|
|
3700
4072
|
return { funcs, globals, tags, exceptions, pages, data };
|
3701
4073
|
};
|