porffor 0.0.0-bddcdc3 → 0.0.0-d650361
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/README.md +45 -12
- package/c +0 -0
- package/c.exe +0 -0
- package/compiler/2c.js +350 -0
- package/compiler/builtins.js +6 -1
- package/compiler/codeGen.js +397 -128
- package/compiler/decompile.js +3 -3
- package/compiler/encoding.js +4 -2
- package/compiler/index.js +53 -2
- package/compiler/opt.js +35 -5
- package/compiler/parse.js +1 -0
- package/compiler/prototype.js +92 -33
- package/compiler/sections.js +28 -2
- package/compiler/wrap.js +21 -3
- package/cool.exe +0 -0
- package/g +0 -0
- package/g.exe +0 -0
- package/hi.c +37 -0
- package/out +0 -0
- package/out.exe +0 -0
- package/package.json +1 -1
- package/r.js +39 -0
- package/rhemyn/README.md +37 -0
- package/rhemyn/compile.js +214 -0
- package/rhemyn/parse.js +321 -0
- package/rhemyn/test/parse.js +59 -0
- package/runner/index.js +54 -34
- package/runner/repl.js +3 -10
- package/runner/transform.js +2 -1
- package/runner/version.js +10 -0
- package/tmp.c +58 -0
package/compiler/codeGen.js
CHANGED
@@ -5,6 +5,7 @@ import { BuiltinFuncs, BuiltinVars, importedFuncs, NULL, UNDEFINED } from "./bui
|
|
5
5
|
import { PrototypeFuncs } from "./prototype.js";
|
6
6
|
import { number, i32x4 } from "./embedding.js";
|
7
7
|
import parse from "./parse.js";
|
8
|
+
import * as Rhemyn from "../rhemyn/compile.js";
|
8
9
|
|
9
10
|
let globals = {};
|
10
11
|
let globalInd = 0;
|
@@ -35,7 +36,14 @@ const debug = str => {
|
|
35
36
|
};
|
36
37
|
|
37
38
|
const todo = msg => {
|
38
|
-
|
39
|
+
class TodoError extends Error {
|
40
|
+
constructor(message) {
|
41
|
+
super(message);
|
42
|
+
this.name = 'TodoError';
|
43
|
+
}
|
44
|
+
}
|
45
|
+
|
46
|
+
throw new TodoError(`todo: ${msg}`);
|
39
47
|
|
40
48
|
const code = [];
|
41
49
|
|
@@ -101,8 +109,8 @@ const generate = (scope, decl, global = false, name = undefined) => {
|
|
101
109
|
case 'WhileStatement':
|
102
110
|
return generateWhile(scope, decl);
|
103
111
|
|
104
|
-
|
105
|
-
return generateForOf(scope, decl);
|
112
|
+
case 'ForOfStatement':
|
113
|
+
return generateForOf(scope, decl);
|
106
114
|
|
107
115
|
case 'BreakStatement':
|
108
116
|
return generateBreak(scope, decl);
|
@@ -144,45 +152,65 @@ const generate = (scope, decl, global = false, name = undefined) => {
|
|
144
152
|
|
145
153
|
return [];
|
146
154
|
|
147
|
-
case 'TaggedTemplateExpression':
|
148
|
-
|
149
|
-
|
155
|
+
case 'TaggedTemplateExpression': {
|
156
|
+
const funcs = {
|
157
|
+
asm: str => {
|
158
|
+
let out = [];
|
150
159
|
|
151
|
-
|
152
|
-
|
160
|
+
for (const line of str.split('\n')) {
|
161
|
+
const asm = line.trim().split(';;')[0].split(' ');
|
162
|
+
if (asm[0] === '') continue; // blank
|
153
163
|
|
154
|
-
|
155
|
-
|
156
|
-
|
164
|
+
if (asm[0] === 'local') {
|
165
|
+
const [ name, idx, type ] = asm.slice(1);
|
166
|
+
scope.locals[name] = { idx: parseInt(idx), type: Valtype[type] };
|
167
|
+
continue;
|
168
|
+
}
|
157
169
|
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
}
|
170
|
+
if (asm[0] === 'returns') {
|
171
|
+
scope.returns = asm.slice(1).map(x => Valtype[x]);
|
172
|
+
continue;
|
173
|
+
}
|
163
174
|
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
175
|
+
if (asm[0] === 'memory') {
|
176
|
+
allocPage('asm instrinsic');
|
177
|
+
// todo: add to store/load offset insts
|
178
|
+
continue;
|
179
|
+
}
|
168
180
|
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
181
|
+
let inst = Opcodes[asm[0].replace('.', '_')];
|
182
|
+
if (!inst) throw new Error(`inline asm: inst ${asm[0]} not found`);
|
183
|
+
|
184
|
+
if (!Array.isArray(inst)) inst = [ inst ];
|
185
|
+
const immediates = asm.slice(1).map(x => parseInt(x));
|
186
|
+
|
187
|
+
out.push([ ...inst, ...immediates ]);
|
188
|
+
}
|
175
189
|
|
176
|
-
|
177
|
-
|
190
|
+
return out;
|
191
|
+
},
|
178
192
|
|
179
|
-
|
180
|
-
|
193
|
+
__internal_print_type: str => {
|
194
|
+
const type = getType(scope, str) - TYPES.number;
|
181
195
|
|
182
|
-
|
196
|
+
return [
|
197
|
+
...number(type),
|
198
|
+
[ Opcodes.call, importedFuncs.print ],
|
199
|
+
|
200
|
+
// newline
|
201
|
+
...number(10),
|
202
|
+
[ Opcodes.call, importedFuncs.printChar ]
|
203
|
+
];
|
204
|
+
}
|
183
205
|
}
|
184
206
|
|
185
|
-
|
207
|
+
const name = decl.tag.name;
|
208
|
+
// hack for inline asm
|
209
|
+
if (!funcs[name]) return todo('tagged template expressions not implemented');
|
210
|
+
|
211
|
+
const str = decl.quasi.quasis[0].value.raw;
|
212
|
+
return funcs[name](str);
|
213
|
+
}
|
186
214
|
|
187
215
|
default:
|
188
216
|
return todo(`no generation for ${decl.type}!`);
|
@@ -281,7 +309,7 @@ const generateReturn = (scope, decl) => {
|
|
281
309
|
];
|
282
310
|
}
|
283
311
|
|
284
|
-
|
312
|
+
scope.returnType = getNodeType(scope, decl.argument);
|
285
313
|
|
286
314
|
return [
|
287
315
|
...generate(scope, decl.argument),
|
@@ -298,11 +326,11 @@ const localTmp = (scope, name, type = valtypeBinary) => {
|
|
298
326
|
return idx;
|
299
327
|
};
|
300
328
|
|
301
|
-
const performLogicOp = (scope, op, left, right) => {
|
329
|
+
const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
302
330
|
const checks = {
|
303
|
-
'||':
|
304
|
-
'&&':
|
305
|
-
|
331
|
+
'||': falsy,
|
332
|
+
'&&': truthy,
|
333
|
+
'??': nullish
|
306
334
|
};
|
307
335
|
|
308
336
|
if (!checks[op]) return todo(`logic operator ${op} not implemented yet`);
|
@@ -313,7 +341,8 @@ const performLogicOp = (scope, op, left, right) => {
|
|
313
341
|
return [
|
314
342
|
...left,
|
315
343
|
[ Opcodes.local_tee, localTmp(scope, 'logictmp') ],
|
316
|
-
...checks[op],
|
344
|
+
...checks[op](scope, [], leftType),
|
345
|
+
Opcodes.i32_to,
|
317
346
|
[ Opcodes.if, valtypeBinary ],
|
318
347
|
...right,
|
319
348
|
[ Opcodes.else ],
|
@@ -328,8 +357,6 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
328
357
|
// todo: optimize by looking up names in arrays and using that if exists?
|
329
358
|
// todo: optimize this if using literals/known lengths?
|
330
359
|
|
331
|
-
scope.memory = true;
|
332
|
-
|
333
360
|
const rightPointer = localTmp(scope, 'concat_right_pointer', Valtype.i32);
|
334
361
|
const rightLength = localTmp(scope, 'concat_right_length', Valtype.i32);
|
335
362
|
const leftLength = localTmp(scope, 'concat_left_length', Valtype.i32);
|
@@ -464,8 +491,6 @@ const compareStrings = (scope, left, right) => {
|
|
464
491
|
// todo: optimize by looking up names in arrays and using that if exists?
|
465
492
|
// todo: optimize this if using literals/known lengths?
|
466
493
|
|
467
|
-
scope.memory = true;
|
468
|
-
|
469
494
|
const leftPointer = localTmp(scope, 'compare_left_pointer', Valtype.i32);
|
470
495
|
const leftLength = localTmp(scope, 'compare_left_length', Valtype.i32);
|
471
496
|
const rightPointer = localTmp(scope, 'compare_right_pointer', Valtype.i32);
|
@@ -563,102 +588,142 @@ const compareStrings = (scope, left, right) => {
|
|
563
588
|
];
|
564
589
|
};
|
565
590
|
|
566
|
-
const
|
591
|
+
const truthy = (scope, wasm, type) => {
|
567
592
|
// arrays are always truthy
|
568
593
|
if (type === TYPES._array) return [
|
569
594
|
...wasm,
|
570
595
|
[ Opcodes.drop ],
|
571
|
-
number(
|
596
|
+
...number(1)
|
572
597
|
];
|
573
598
|
|
574
599
|
if (type === TYPES.string) {
|
575
|
-
// if "" (length = 0)
|
600
|
+
// if not "" (length = 0)
|
576
601
|
return [
|
577
602
|
// pointer
|
578
603
|
...wasm,
|
604
|
+
Opcodes.i32_to_u,
|
579
605
|
|
580
606
|
// get length
|
581
607
|
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
|
582
608
|
|
583
|
-
// if length
|
584
|
-
[ Opcodes.i32_eqz ],
|
609
|
+
// if length != 0
|
610
|
+
/* [ Opcodes.i32_eqz ],
|
611
|
+
[ Opcodes.i32_eqz ], */
|
585
612
|
Opcodes.i32_from_u
|
586
613
|
]
|
587
614
|
}
|
588
615
|
|
589
|
-
// if
|
616
|
+
// if != 0
|
590
617
|
return [
|
591
618
|
...wasm,
|
592
619
|
|
593
|
-
|
594
|
-
Opcodes.
|
620
|
+
/* Opcodes.eqz,
|
621
|
+
[ Opcodes.i32_eqz ],
|
622
|
+
Opcodes.i32_from */
|
595
623
|
];
|
596
624
|
};
|
597
625
|
|
598
|
-
const
|
626
|
+
const falsy = (scope, wasm, type) => {
|
599
627
|
// arrays are always truthy
|
600
628
|
if (type === TYPES._array) return [
|
601
629
|
...wasm,
|
602
630
|
[ Opcodes.drop ],
|
603
|
-
number(
|
631
|
+
...number(0)
|
604
632
|
];
|
605
633
|
|
606
634
|
if (type === TYPES.string) {
|
607
|
-
// if
|
635
|
+
// if "" (length = 0)
|
608
636
|
return [
|
609
637
|
// pointer
|
610
638
|
...wasm,
|
639
|
+
Opcodes.i32_to_u,
|
611
640
|
|
612
641
|
// get length
|
613
642
|
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
|
614
643
|
|
615
|
-
// if length
|
616
|
-
|
617
|
-
[ Opcodes.i32_eqz ], */
|
644
|
+
// if length == 0
|
645
|
+
[ Opcodes.i32_eqz ],
|
618
646
|
Opcodes.i32_from_u
|
619
647
|
]
|
620
648
|
}
|
621
649
|
|
622
|
-
// if
|
650
|
+
// if = 0
|
623
651
|
return [
|
624
652
|
...wasm,
|
625
653
|
|
626
|
-
|
627
|
-
|
628
|
-
|
654
|
+
...Opcodes.eqz,
|
655
|
+
Opcodes.i32_from_u
|
656
|
+
];
|
657
|
+
};
|
658
|
+
|
659
|
+
const nullish = (scope, wasm, type) => {
|
660
|
+
// undefined
|
661
|
+
if (type === TYPES.undefined) return [
|
662
|
+
...wasm,
|
663
|
+
[ Opcodes.drop ],
|
664
|
+
...number(1)
|
665
|
+
];
|
666
|
+
|
667
|
+
// null (if object and = "0")
|
668
|
+
if (type === TYPES.object) return [
|
669
|
+
...wasm,
|
670
|
+
...Opcodes.eqz,
|
671
|
+
Opcodes.i32_from_u
|
672
|
+
];
|
673
|
+
|
674
|
+
// not
|
675
|
+
return [
|
676
|
+
...wasm,
|
677
|
+
[ Opcodes.drop ],
|
678
|
+
...number(0)
|
629
679
|
];
|
630
680
|
};
|
631
681
|
|
632
682
|
const performOp = (scope, op, left, right, leftType, rightType, _global = false, _name = '$undeclared', assign = false) => {
|
633
683
|
if (op === '||' || op === '&&' || op === '??') {
|
634
|
-
return performLogicOp(scope, op, left, right);
|
684
|
+
return performLogicOp(scope, op, left, right, leftType, rightType);
|
635
685
|
}
|
636
686
|
|
637
687
|
if (codeLog && (!leftType || !rightType)) log('codegen', 'untracked type in op', op, _name, '\n' + new Error().stack.split('\n').slice(1).join('\n'));
|
638
688
|
|
639
|
-
|
640
|
-
|
689
|
+
const eqOp = ['==', '===', '!=', '!==', '>', '>=', '<', '<='].includes(op);
|
690
|
+
|
691
|
+
if (leftType && rightType && (
|
692
|
+
// if strict (in)equal and known types mismatch, return false (===)/true (!==)
|
693
|
+
((op === '===' || op === '!==') && leftType !== rightType) ||
|
694
|
+
|
695
|
+
// if equality op and an operand is undefined, return false
|
696
|
+
(eqOp && leftType === TYPES.undefined ^ rightType === TYPES.undefined)
|
697
|
+
)) {
|
698
|
+
// undefined == null
|
699
|
+
if (((leftType === TYPES.undefined && rightType === TYPES.object) || (leftType === TYPES.object && rightType === TYPES.undefined)) && (op === '==' || op === '!=')) return [
|
700
|
+
...(leftType === TYPES.object ? left : right),
|
701
|
+
...Opcodes.eqz,
|
702
|
+
...(op === '!=' ? [ [ Opcodes.i32_eqz ] ] : [])
|
703
|
+
];
|
704
|
+
|
641
705
|
return [
|
642
706
|
...left,
|
643
|
-
...right,
|
644
|
-
|
645
|
-
// drop values
|
646
707
|
[ Opcodes.drop ],
|
708
|
+
|
709
|
+
...right,
|
647
710
|
[ Opcodes.drop ],
|
648
711
|
|
649
|
-
// return
|
650
|
-
...number(op === '===' ?
|
712
|
+
// return true (!=/!==) or false (else)
|
713
|
+
...number(op === '!=' || op === '!==' ? 1 : 0, Valtype.i32)
|
651
714
|
];
|
652
715
|
}
|
653
716
|
|
717
|
+
// todo: niche null hell with 0
|
718
|
+
|
654
719
|
if (leftType === TYPES.string || rightType === TYPES.string) {
|
655
720
|
if (op === '+') {
|
656
721
|
// string concat (a + b)
|
657
722
|
return concatStrings(scope, left, right, _global, _name, assign);
|
658
723
|
}
|
659
724
|
|
660
|
-
//
|
661
|
-
if (!
|
725
|
+
// not an equality op, NaN
|
726
|
+
if (!eqOp) return number(NaN);
|
662
727
|
|
663
728
|
// else leave bool ops
|
664
729
|
// todo: convert string to number if string and number/bool
|
@@ -703,21 +768,15 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
703
768
|
];
|
704
769
|
};
|
705
770
|
|
706
|
-
let binaryExpDepth = 0;
|
707
771
|
const generateBinaryExp = (scope, decl, _global, _name) => {
|
708
|
-
|
709
|
-
|
710
|
-
const out = [
|
711
|
-
...performOp(scope, decl.operator, generate(scope, decl.left), generate(scope, decl.right), getNodeType(scope, decl.left), getNodeType(scope, decl.right), _global, _name)
|
712
|
-
];
|
772
|
+
const out = performOp(scope, decl.operator, generate(scope, decl.left), generate(scope, decl.right), getNodeType(scope, decl.left), getNodeType(scope, decl.right), _global, _name);
|
713
773
|
|
714
774
|
if (valtype !== 'i32' && ['==', '===', '!=', '!==', '>', '>=', '<', '<='].includes(decl.operator)) out.push(Opcodes.i32_from_u);
|
715
775
|
|
716
|
-
binaryExpDepth--;
|
717
776
|
return out;
|
718
777
|
};
|
719
778
|
|
720
|
-
const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits, returns, returnType,
|
779
|
+
const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits, returns, returnType, localNames = [], globalNames = [] }) => {
|
721
780
|
const existing = funcs.find(x => x.name === name);
|
722
781
|
if (existing) return existing;
|
723
782
|
|
@@ -753,7 +812,6 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
|
|
753
812
|
returns,
|
754
813
|
returnType: TYPES[returnType ?? 'number'],
|
755
814
|
wasm,
|
756
|
-
memory,
|
757
815
|
internal: true,
|
758
816
|
index: currentFuncIndex++
|
759
817
|
};
|
@@ -772,7 +830,7 @@ const includeBuiltin = (scope, builtin) => {
|
|
772
830
|
};
|
773
831
|
|
774
832
|
const generateLogicExp = (scope, decl) => {
|
775
|
-
return performLogicOp(scope, decl.operator, generate(scope, decl.left), generate(scope, decl.right));
|
833
|
+
return performLogicOp(scope, decl.operator, generate(scope, decl.left), generate(scope, decl.right), getNodeType(scope, decl.left), getNodeType(scope, decl.right));
|
776
834
|
};
|
777
835
|
|
778
836
|
const TYPES = {
|
@@ -786,7 +844,8 @@ const TYPES = {
|
|
786
844
|
bigint: 0xffffffffffff7,
|
787
845
|
|
788
846
|
// these are not "typeof" types but tracked internally
|
789
|
-
_array:
|
847
|
+
_array: 0xfffffffffff0f,
|
848
|
+
_regexp: 0xfffffffffff1f
|
790
849
|
};
|
791
850
|
|
792
851
|
const TYPE_NAMES = {
|
@@ -821,6 +880,8 @@ const getType = (scope, _name) => {
|
|
821
880
|
const getNodeType = (scope, node) => {
|
822
881
|
if (node.type === 'Literal') {
|
823
882
|
if (['number', 'boolean', 'string', 'undefined', 'object', 'function', 'symbol', 'bigint'].includes(node.value)) return TYPES.number;
|
883
|
+
if (node.regex) return TYPES._regexp;
|
884
|
+
|
824
885
|
return TYPES[typeof node.value];
|
825
886
|
}
|
826
887
|
|
@@ -854,6 +915,11 @@ const getNodeType = (scope, node) => {
|
|
854
915
|
|
855
916
|
// literal.func()
|
856
917
|
if (!name && node.callee.type === 'MemberExpression') {
|
918
|
+
if (node.callee.object.regex) {
|
919
|
+
const funcName = node.callee.property.name;
|
920
|
+
return Rhemyn[funcName] ? TYPES.boolean : TYPES.undefined;
|
921
|
+
}
|
922
|
+
|
857
923
|
const baseType = getNodeType(scope, node.callee.object);
|
858
924
|
|
859
925
|
const func = node.callee.property.name;
|
@@ -897,6 +963,11 @@ const getNodeType = (scope, node) => {
|
|
897
963
|
const generateLiteral = (scope, decl, global, name) => {
|
898
964
|
if (decl.value === null) return number(NULL);
|
899
965
|
|
966
|
+
if (decl.regex) {
|
967
|
+
scope.regex[name] = decl.regex;
|
968
|
+
return number(1);
|
969
|
+
}
|
970
|
+
|
900
971
|
switch (typeof decl.value) {
|
901
972
|
case 'number':
|
902
973
|
return number(decl.value);
|
@@ -936,7 +1007,8 @@ const generateLiteral = (scope, decl, global, name) => {
|
|
936
1007
|
const countLeftover = wasm => {
|
937
1008
|
let count = 0, depth = 0;
|
938
1009
|
|
939
|
-
for (
|
1010
|
+
for (let i = 0; i < wasm.length; i++) {
|
1011
|
+
const inst = wasm[i];
|
940
1012
|
if (depth === 0 && (inst[0] === Opcodes.if || inst[0] === Opcodes.block || inst[0] === Opcodes.loop)) {
|
941
1013
|
if (inst[0] === Opcodes.if) count--;
|
942
1014
|
if (inst[1] !== Blocktype.void) count++;
|
@@ -957,6 +1029,8 @@ const countLeftover = wasm => {
|
|
957
1029
|
} else count--;
|
958
1030
|
if (func) count += func.returns.length;
|
959
1031
|
} else count--;
|
1032
|
+
|
1033
|
+
// console.log(count, decompile([ inst ]).slice(0, -1));
|
960
1034
|
}
|
961
1035
|
|
962
1036
|
return count;
|
@@ -1040,7 +1114,7 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1040
1114
|
}
|
1041
1115
|
|
1042
1116
|
let out = [];
|
1043
|
-
let protoFunc, protoName, baseType, baseName
|
1117
|
+
let protoFunc, protoName, baseType, baseName;
|
1044
1118
|
// ident.func()
|
1045
1119
|
if (name && name.startsWith('__')) {
|
1046
1120
|
const spl = name.slice(2).split('_');
|
@@ -1055,6 +1129,25 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1055
1129
|
|
1056
1130
|
// literal.func()
|
1057
1131
|
if (!name && decl.callee.type === 'MemberExpression') {
|
1132
|
+
// megahack for /regex/.func()
|
1133
|
+
if (decl.callee.object.regex) {
|
1134
|
+
const funcName = decl.callee.property.name;
|
1135
|
+
const func = Rhemyn[funcName](decl.callee.object.regex.pattern, currentFuncIndex++);
|
1136
|
+
|
1137
|
+
funcIndex[func.name] = func.index;
|
1138
|
+
funcs.push(func);
|
1139
|
+
|
1140
|
+
return [
|
1141
|
+
// make string arg
|
1142
|
+
...generate(scope, decl.arguments[0]),
|
1143
|
+
|
1144
|
+
// call regex func
|
1145
|
+
Opcodes.i32_to_u,
|
1146
|
+
[ Opcodes.call, func.index ],
|
1147
|
+
Opcodes.i32_from
|
1148
|
+
];
|
1149
|
+
}
|
1150
|
+
|
1058
1151
|
baseType = getNodeType(scope, decl.callee.object);
|
1059
1152
|
|
1060
1153
|
const func = decl.callee.property.name;
|
@@ -1063,11 +1156,36 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1063
1156
|
|
1064
1157
|
out = generate(scope, decl.callee.object);
|
1065
1158
|
out.push([ Opcodes.drop ]);
|
1159
|
+
|
1160
|
+
baseName = [...arrays.keys()].pop();
|
1066
1161
|
}
|
1067
1162
|
|
1068
|
-
if (
|
1069
|
-
|
1163
|
+
if (protoName && baseType === TYPES.string && Rhemyn[protoName]) {
|
1164
|
+
const func = Rhemyn[protoName](decl.arguments[0].regex.pattern, currentFuncIndex++);
|
1165
|
+
|
1166
|
+
funcIndex[func.name] = func.index;
|
1167
|
+
funcs.push(func);
|
1168
|
+
|
1169
|
+
const pointer = arrays.get(baseName);
|
1170
|
+
const [ local, isGlobal ] = lookupName(scope, baseName);
|
1171
|
+
|
1172
|
+
return [
|
1173
|
+
...out,
|
1174
|
+
|
1175
|
+
...(pointer == null ? [
|
1176
|
+
[ isGlobal ? Opcodes.global_get : Opcodes.local_get, local.idx ],
|
1177
|
+
Opcodes.i32_to_u,
|
1178
|
+
] : [
|
1179
|
+
...number(pointer, Valtype.i32)
|
1180
|
+
]),
|
1181
|
+
|
1182
|
+
// call regex func
|
1183
|
+
[ Opcodes.call, func.index ],
|
1184
|
+
Opcodes.i32_from
|
1185
|
+
];
|
1186
|
+
}
|
1070
1187
|
|
1188
|
+
if (protoFunc) {
|
1071
1189
|
let pointer = arrays.get(baseName);
|
1072
1190
|
|
1073
1191
|
if (pointer == null) {
|
@@ -1075,7 +1193,7 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1075
1193
|
if (codeLog) log('codegen', 'cloning unknown dynamic pointer');
|
1076
1194
|
|
1077
1195
|
// register array
|
1078
|
-
|
1196
|
+
0, [ , pointer ] = makeArray(scope, {
|
1079
1197
|
rawElements: new Array(0)
|
1080
1198
|
}, _global, baseName, true, baseType === TYPES.string ? 'i16' : valtype);
|
1081
1199
|
|
@@ -1107,9 +1225,10 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1107
1225
|
|
1108
1226
|
[ Opcodes.block, valtypeBinary ],
|
1109
1227
|
...protoFunc(pointer, {
|
1110
|
-
|
1111
|
-
|
1112
|
-
|
1228
|
+
getCachedI32: () => [ [ Opcodes.local_get, lengthLocal ] ],
|
1229
|
+
setCachedI32: () => [ [ Opcodes.local_set, lengthLocal ] ],
|
1230
|
+
get: () => arrayUtil.getLength(pointer),
|
1231
|
+
getI32: () => arrayUtil.getLengthI32(pointer),
|
1113
1232
|
set: value => arrayUtil.setLength(pointer, value),
|
1114
1233
|
setI32: value => arrayUtil.setLengthI32(pointer, value)
|
1115
1234
|
}, generate(scope, decl.arguments[0] ?? DEFAULT_VALUE), protoLocal, (length, itemType) => {
|
@@ -1173,11 +1292,10 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1173
1292
|
args = args.slice(0, func.params.length);
|
1174
1293
|
}
|
1175
1294
|
|
1176
|
-
if (func && func.memory) scope.memory = true;
|
1177
1295
|
if (func && func.throws) scope.throws = true;
|
1178
1296
|
|
1179
1297
|
for (const arg of args) {
|
1180
|
-
out.
|
1298
|
+
out = out.concat(generate(scope, arg));
|
1181
1299
|
}
|
1182
1300
|
|
1183
1301
|
out.push([ Opcodes.call, idx ]);
|
@@ -1189,7 +1307,7 @@ const generateNew = (scope, decl, _global, _name) => {
|
|
1189
1307
|
// hack: basically treat this as a normal call for builtins for now
|
1190
1308
|
const name = mapName(decl.callee.name);
|
1191
1309
|
if (internalConstrs[name]) return internalConstrs[name].generate(scope, decl, _global, _name);
|
1192
|
-
if (!builtinFuncs[name]) return todo(`new statement is not supported yet (new ${unhackName(name)})`);
|
1310
|
+
if (!builtinFuncs[name]) return todo(`new statement is not supported yet`); // return todo(`new statement is not supported yet (new ${unhackName(name)})`);
|
1193
1311
|
|
1194
1312
|
return generateCall(scope, decl, _global, _name);
|
1195
1313
|
};
|
@@ -1297,8 +1415,6 @@ const generateAssign = (scope, decl) => {
|
|
1297
1415
|
const name = decl.left.object.name;
|
1298
1416
|
const pointer = arrays.get(name);
|
1299
1417
|
|
1300
|
-
scope.memory = true;
|
1301
|
-
|
1302
1418
|
const aotPointer = pointer != null;
|
1303
1419
|
|
1304
1420
|
const newValueTmp = localTmp(scope, '__length_setter_tmp');
|
@@ -1319,6 +1435,47 @@ const generateAssign = (scope, decl) => {
|
|
1319
1435
|
];
|
1320
1436
|
}
|
1321
1437
|
|
1438
|
+
if (decl.left.type === 'MemberExpression' && decl.left.computed) {
|
1439
|
+
// arr[i] | str[i]
|
1440
|
+
const name = decl.left.object.name;
|
1441
|
+
const pointer = arrays.get(name);
|
1442
|
+
|
1443
|
+
const aotPointer = pointer != null;
|
1444
|
+
|
1445
|
+
const newValueTmp = localTmp(scope, '__member_setter_tmp');
|
1446
|
+
|
1447
|
+
const parentType = getNodeType(scope, decl.left.object);
|
1448
|
+
|
1449
|
+
const op = decl.operator.slice(0, -1);
|
1450
|
+
return [
|
1451
|
+
...(aotPointer ? number(0, Valtype.i32) : [
|
1452
|
+
...generate(scope, decl.left.object),
|
1453
|
+
Opcodes.i32_to_u
|
1454
|
+
]),
|
1455
|
+
|
1456
|
+
// get index as valtype
|
1457
|
+
...generate(scope, decl.left.property),
|
1458
|
+
|
1459
|
+
// convert to i32 and turn into byte offset by * valtypeSize (4 for i32, 8 for i64/f64)
|
1460
|
+
Opcodes.i32_to_u,
|
1461
|
+
...number(ValtypeSize[valtype], Valtype.i32),
|
1462
|
+
[ Opcodes.i32_mul ],
|
1463
|
+
[ Opcodes.i32_add ],
|
1464
|
+
|
1465
|
+
...(op === '' ? generate(scope, decl.right, false, name) : performOp(scope, op, generate(scope, decl.left), generate(scope, decl.right), parentType === TYPES._array ? TYPES.number : TYPES.string, getNodeType(scope, decl.right), false, name, true)),
|
1466
|
+
[ Opcodes.local_tee, newValueTmp ],
|
1467
|
+
|
1468
|
+
...(parentType === TYPES._array ? [
|
1469
|
+
[ Opcodes.store, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
|
1470
|
+
] : [
|
1471
|
+
Opcodes.i32_to_u,
|
1472
|
+
[ StoreOps.i16, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
|
1473
|
+
]),
|
1474
|
+
|
1475
|
+
[ Opcodes.local_get, newValueTmp ]
|
1476
|
+
];
|
1477
|
+
}
|
1478
|
+
|
1322
1479
|
const [ local, isGlobal ] = lookupName(scope, name);
|
1323
1480
|
|
1324
1481
|
if (local === undefined) {
|
@@ -1339,8 +1496,10 @@ const generateAssign = (scope, decl) => {
|
|
1339
1496
|
];
|
1340
1497
|
}
|
1341
1498
|
|
1499
|
+
typeStates[name] = getNodeType(scope, decl.right);
|
1500
|
+
|
1342
1501
|
if (decl.operator === '=') {
|
1343
|
-
typeStates[name] = getNodeType(scope, decl.right);
|
1502
|
+
// typeStates[name] = getNodeType(scope, decl.right);
|
1344
1503
|
|
1345
1504
|
return [
|
1346
1505
|
...generate(scope, decl.right, isGlobal, name),
|
@@ -1349,8 +1508,27 @@ const generateAssign = (scope, decl) => {
|
|
1349
1508
|
];
|
1350
1509
|
}
|
1351
1510
|
|
1511
|
+
const op = decl.operator.slice(0, -1);
|
1512
|
+
if (op === '||' || op === '&&' || op === '??') {
|
1513
|
+
// todo: is this needed?
|
1514
|
+
// for logical assignment ops, it is not left @= right ~= left = left @ right
|
1515
|
+
// instead, left @ (left = right)
|
1516
|
+
// eg, x &&= y ~= x && (x = y)
|
1517
|
+
|
1518
|
+
return [
|
1519
|
+
...performOp(scope, op, [
|
1520
|
+
[ isGlobal ? Opcodes.global_get : Opcodes.local_get, local.idx ]
|
1521
|
+
], [
|
1522
|
+
...generate(scope, decl.right),
|
1523
|
+
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
|
1524
|
+
[ isGlobal ? Opcodes.global_get : Opcodes.local_get, local.idx ]
|
1525
|
+
], getType(scope, name), getNodeType(scope, decl.right), isGlobal, name, true),
|
1526
|
+
[ isGlobal ? Opcodes.global_get : Opcodes.local_get, local.idx ]
|
1527
|
+
];
|
1528
|
+
}
|
1529
|
+
|
1352
1530
|
return [
|
1353
|
-
...performOp(scope,
|
1531
|
+
...performOp(scope, op, [ [ isGlobal ? Opcodes.global_get : Opcodes.local_get, local.idx ] ], generate(scope, decl.right), getType(scope, name), getNodeType(scope, decl.right), isGlobal, name, true),
|
1354
1532
|
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
|
1355
1533
|
[ isGlobal ? Opcodes.global_get : Opcodes.local_get, local.idx ]
|
1356
1534
|
];
|
@@ -1377,13 +1555,14 @@ const generateUnary = (scope, decl) => {
|
|
1377
1555
|
|
1378
1556
|
case '!':
|
1379
1557
|
// !=
|
1380
|
-
return falsy(scope, generate(scope, decl.argument));
|
1558
|
+
return falsy(scope, generate(scope, decl.argument), getNodeType(scope, decl.argument));
|
1381
1559
|
|
1382
1560
|
case '~':
|
1561
|
+
// todo: does not handle Infinity properly (should convert to 0) (but opt const converting saves us sometimes)
|
1383
1562
|
return [
|
1384
1563
|
...generate(scope, decl.argument),
|
1385
1564
|
Opcodes.i32_to,
|
1386
|
-
[ Opcodes.i32_const, signedLEB128(-1) ],
|
1565
|
+
[ Opcodes.i32_const, ...signedLEB128(-1) ],
|
1387
1566
|
[ Opcodes.i32_xor ],
|
1388
1567
|
Opcodes.i32_from
|
1389
1568
|
];
|
@@ -1464,7 +1643,7 @@ const generateUpdate = (scope, decl) => {
|
|
1464
1643
|
};
|
1465
1644
|
|
1466
1645
|
const generateIf = (scope, decl) => {
|
1467
|
-
const out = truthy(scope, generate(scope, decl.test), decl.test);
|
1646
|
+
const out = truthy(scope, generate(scope, decl.test), getNodeType(scope, decl.test));
|
1468
1647
|
|
1469
1648
|
out.push(Opcodes.i32_to, [ Opcodes.if, Blocktype.void ]);
|
1470
1649
|
depth.push('if');
|
@@ -1557,18 +1736,106 @@ const generateWhile = (scope, decl) => {
|
|
1557
1736
|
const generateForOf = (scope, decl) => {
|
1558
1737
|
const out = [];
|
1559
1738
|
|
1739
|
+
const rightType = getNodeType(scope, decl.right);
|
1740
|
+
const valtypeSize = rightType === TYPES._array ? ValtypeSize[valtype] : ValtypeSize.i16; // presume array (:()
|
1741
|
+
|
1742
|
+
// todo: for of inside for of might fuck up?
|
1743
|
+
const pointer = localTmp(scope, 'forof_base_pointer', Valtype.i32);
|
1744
|
+
const length = localTmp(scope, 'forof_length', Valtype.i32);
|
1745
|
+
const counter = localTmp(scope, 'forof_counter', Valtype.i32);
|
1746
|
+
|
1747
|
+
out.push(
|
1748
|
+
// set pointer as right
|
1749
|
+
...generate(scope, decl.right),
|
1750
|
+
Opcodes.i32_to_u,
|
1751
|
+
[ Opcodes.local_set, pointer ],
|
1752
|
+
|
1753
|
+
// get length
|
1754
|
+
[ Opcodes.local_get, pointer ],
|
1755
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
|
1756
|
+
[ Opcodes.local_set, length ]
|
1757
|
+
);
|
1758
|
+
|
1560
1759
|
out.push([ Opcodes.loop, Blocktype.void ]);
|
1561
|
-
depth.push('
|
1760
|
+
depth.push('forof');
|
1562
1761
|
|
1563
|
-
|
1564
|
-
|
1565
|
-
depth.push('if');
|
1762
|
+
// setup local for left
|
1763
|
+
generate(scope, decl.left);
|
1566
1764
|
|
1567
|
-
|
1765
|
+
const leftName = decl.left.declarations[0].id.name;
|
1568
1766
|
|
1569
|
-
|
1570
|
-
|
1571
|
-
|
1767
|
+
// set type for local
|
1768
|
+
typeStates[leftName] = rightType === TYPES._array ? TYPES.number : TYPES.string;
|
1769
|
+
|
1770
|
+
const [ local, isGlobal ] = lookupName(scope, leftName);
|
1771
|
+
|
1772
|
+
if (rightType === TYPES._array) { // array
|
1773
|
+
out.push(
|
1774
|
+
[ Opcodes.local_get, pointer ],
|
1775
|
+
[ Opcodes.load, Math.log2(valtypeSize) - 1, ...unsignedLEB128(ValtypeSize.i32) ]
|
1776
|
+
);
|
1777
|
+
} else { // string
|
1778
|
+
const [ newOut, newPointer ] = makeArray(scope, {
|
1779
|
+
rawElements: new Array(1)
|
1780
|
+
}, isGlobal, leftName, true, 'i16');
|
1781
|
+
|
1782
|
+
out.push(
|
1783
|
+
// setup new/out array
|
1784
|
+
...newOut,
|
1785
|
+
[ Opcodes.drop ],
|
1786
|
+
|
1787
|
+
...number(0, Valtype.i32), // base 0 for store after
|
1788
|
+
|
1789
|
+
// load current string ind {arg}
|
1790
|
+
[ Opcodes.local_get, pointer ],
|
1791
|
+
[ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(ValtypeSize.i32) ],
|
1792
|
+
|
1793
|
+
// store to new string ind 0
|
1794
|
+
[ Opcodes.i32_store16, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
|
1795
|
+
|
1796
|
+
// return new string (page)
|
1797
|
+
...number(newPointer)
|
1798
|
+
);
|
1799
|
+
}
|
1800
|
+
|
1801
|
+
// set left value
|
1802
|
+
out.push([ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ]);
|
1803
|
+
|
1804
|
+
out.push(
|
1805
|
+
[ Opcodes.block, Blocktype.void ],
|
1806
|
+
[ Opcodes.block, Blocktype.void ]
|
1807
|
+
);
|
1808
|
+
depth.push('block');
|
1809
|
+
depth.push('block');
|
1810
|
+
|
1811
|
+
out.push(
|
1812
|
+
...generate(scope, decl.body),
|
1813
|
+
[ Opcodes.end ]
|
1814
|
+
);
|
1815
|
+
depth.pop();
|
1816
|
+
|
1817
|
+
out.push(
|
1818
|
+
// increment iter pointer by valtype size
|
1819
|
+
[ Opcodes.local_get, pointer ],
|
1820
|
+
...number(valtypeSize, Valtype.i32),
|
1821
|
+
[ Opcodes.i32_add ],
|
1822
|
+
[ Opcodes.local_set, pointer ],
|
1823
|
+
|
1824
|
+
// increment counter by 1
|
1825
|
+
[ Opcodes.local_get, counter ],
|
1826
|
+
...number(1, Valtype.i32),
|
1827
|
+
[ Opcodes.i32_add ],
|
1828
|
+
[ Opcodes.local_tee, counter ],
|
1829
|
+
|
1830
|
+
// loop if counter != length
|
1831
|
+
[ Opcodes.local_get, length ],
|
1832
|
+
[ Opcodes.i32_ne ],
|
1833
|
+
[ Opcodes.br_if, 1 ],
|
1834
|
+
|
1835
|
+
[ Opcodes.end ], [ Opcodes.end ]
|
1836
|
+
);
|
1837
|
+
depth.pop();
|
1838
|
+
depth.pop();
|
1572
1839
|
|
1573
1840
|
return out;
|
1574
1841
|
};
|
@@ -1659,13 +1926,22 @@ const generateAssignPat = (scope, decl) => {
|
|
1659
1926
|
};
|
1660
1927
|
|
1661
1928
|
let pages = new Map();
|
1662
|
-
const allocPage = reason => {
|
1663
|
-
if (pages.has(reason)) return pages.get(reason);
|
1929
|
+
const allocPage = (reason, type) => {
|
1930
|
+
if (pages.has(reason)) return pages.get(reason).ind;
|
1664
1931
|
|
1665
|
-
|
1666
|
-
pages.set(reason, ind);
|
1932
|
+
const ind = pages.size;
|
1933
|
+
pages.set(reason, { ind, type });
|
1667
1934
|
|
1668
|
-
if (
|
1935
|
+
if (allocLog) log('alloc', `allocated new page of memory (${ind}) | ${reason} (type: ${type})`);
|
1936
|
+
|
1937
|
+
return ind;
|
1938
|
+
};
|
1939
|
+
|
1940
|
+
const freePage = reason => {
|
1941
|
+
const { ind } = pages.get(reason);
|
1942
|
+
pages.delete(reason);
|
1943
|
+
|
1944
|
+
if (allocLog) log('alloc', `freed page of memory (${ind}) | ${reason}`);
|
1669
1945
|
|
1670
1946
|
return ind;
|
1671
1947
|
};
|
@@ -1679,7 +1955,7 @@ const itemTypeToValtype = {
|
|
1679
1955
|
i16: 'i32'
|
1680
1956
|
};
|
1681
1957
|
|
1682
|
-
const
|
1958
|
+
const StoreOps = {
|
1683
1959
|
i32: Opcodes.i32_store,
|
1684
1960
|
i64: Opcodes.i64_store,
|
1685
1961
|
f64: Opcodes.f64_store,
|
@@ -1694,7 +1970,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
1694
1970
|
if (!arrays.has(name) || name === '$undeclared') {
|
1695
1971
|
// todo: can we just have 1 undeclared array? probably not? but this is not really memory efficient
|
1696
1972
|
const uniqueName = name === '$undeclared' ? name + Math.random().toString().slice(2) : name;
|
1697
|
-
arrays.set(name, allocPage(`${itemType === 'i16' ? 'string' : 'array'}: ${uniqueName}
|
1973
|
+
arrays.set(name, allocPage(`${itemType === 'i16' ? 'string' : 'array'}: ${uniqueName}`, itemType) * pageSize);
|
1698
1974
|
}
|
1699
1975
|
|
1700
1976
|
const pointer = arrays.get(name);
|
@@ -1711,7 +1987,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
1711
1987
|
[ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(pointer) ]
|
1712
1988
|
);
|
1713
1989
|
|
1714
|
-
const storeOp =
|
1990
|
+
const storeOp = StoreOps[itemType];
|
1715
1991
|
const valtype = itemTypeToValtype[itemType];
|
1716
1992
|
|
1717
1993
|
if (!initEmpty) for (let i = 0; i < length; i++) {
|
@@ -1727,8 +2003,6 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
1727
2003
|
// local value as pointer
|
1728
2004
|
out.push(...number(pointer));
|
1729
2005
|
|
1730
|
-
scope.memory = true;
|
1731
|
-
|
1732
2006
|
return [ out, pointer ];
|
1733
2007
|
};
|
1734
2008
|
|
@@ -1747,8 +2021,6 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
1747
2021
|
const name = decl.object.name;
|
1748
2022
|
const pointer = arrays.get(name);
|
1749
2023
|
|
1750
|
-
scope.memory = true;
|
1751
|
-
|
1752
2024
|
const aotPointer = pointer != null;
|
1753
2025
|
|
1754
2026
|
return [
|
@@ -1768,8 +2040,6 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
1768
2040
|
const name = decl.object.name;
|
1769
2041
|
const pointer = arrays.get(name);
|
1770
2042
|
|
1771
|
-
scope.memory = true;
|
1772
|
-
|
1773
2043
|
const aotPointer = pointer != null;
|
1774
2044
|
|
1775
2045
|
if (type === TYPES._array) {
|
@@ -1879,7 +2149,7 @@ const generateFunc = (scope, decl) => {
|
|
1879
2149
|
locals: {},
|
1880
2150
|
localInd: 0,
|
1881
2151
|
returns: [ valtypeBinary ],
|
1882
|
-
|
2152
|
+
returnType: null,
|
1883
2153
|
throws: false,
|
1884
2154
|
name
|
1885
2155
|
};
|
@@ -1905,7 +2175,6 @@ const generateFunc = (scope, decl) => {
|
|
1905
2175
|
returns: innerScope.returns,
|
1906
2176
|
returnType: innerScope.returnType,
|
1907
2177
|
locals: innerScope.locals,
|
1908
|
-
memory: innerScope.memory,
|
1909
2178
|
throws: innerScope.throws,
|
1910
2179
|
index: currentFuncIndex++
|
1911
2180
|
};
|
@@ -1920,6 +2189,8 @@ const generateFunc = (scope, decl) => {
|
|
1920
2189
|
|
1921
2190
|
if (name !== 'main' && func.returns.length !== 0 && wasm[wasm.length - 1]?.[0] !== Opcodes.return && countLeftover(wasm) === 0) {
|
1922
2191
|
wasm.push(...number(0), [ Opcodes.return ]);
|
2192
|
+
|
2193
|
+
if (func.returnType === null) func.returnType = TYPES.undefined;
|
1923
2194
|
}
|
1924
2195
|
|
1925
2196
|
// change v128 params into many <type> (i32x4 -> i32/etc) instead as unsupported param valtype
|
@@ -1930,9 +2201,7 @@ const generateFunc = (scope, decl) => {
|
|
1930
2201
|
if (local.type === Valtype.v128) {
|
1931
2202
|
vecParams++;
|
1932
2203
|
|
1933
|
-
/*
|
1934
|
-
|
1935
|
-
wasm.unshift( // add v128 load for param
|
2204
|
+
/* wasm.unshift( // add v128 load for param
|
1936
2205
|
[ Opcodes.i32_const, 0 ],
|
1937
2206
|
[ ...Opcodes.v128_load, 0, i * 16 ],
|
1938
2207
|
[ Opcodes.local_set, local.idx ]
|
@@ -2043,10 +2312,10 @@ const generateFunc = (scope, decl) => {
|
|
2043
2312
|
};
|
2044
2313
|
|
2045
2314
|
const generateCode = (scope, decl) => {
|
2046
|
-
|
2315
|
+
let out = [];
|
2047
2316
|
|
2048
2317
|
for (const x of decl.body) {
|
2049
|
-
out.
|
2318
|
+
out = out.concat(generate(scope, x));
|
2050
2319
|
}
|
2051
2320
|
|
2052
2321
|
return out;
|