porffor 0.0.0-bddcdc3 → 0.0.0-e6047ea
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 +44 -11
- 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 +366 -113
- 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 +4 -5
- 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 +1 -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 +52 -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
|
+
}
|
189
|
+
|
190
|
+
return out;
|
191
|
+
},
|
175
192
|
|
176
|
-
|
177
|
-
|
193
|
+
__internal_print_type: str => {
|
194
|
+
const type = getType(scope, str) - TYPES.number;
|
178
195
|
|
179
|
-
|
180
|
-
|
196
|
+
return [
|
197
|
+
...number(type),
|
198
|
+
[ Opcodes.call, importedFuncs.print ],
|
181
199
|
|
182
|
-
|
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,75 +588,100 @@ 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'));
|
@@ -703,21 +753,15 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
703
753
|
];
|
704
754
|
};
|
705
755
|
|
706
|
-
let binaryExpDepth = 0;
|
707
756
|
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
|
-
];
|
757
|
+
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
758
|
|
714
759
|
if (valtype !== 'i32' && ['==', '===', '!=', '!==', '>', '>=', '<', '<='].includes(decl.operator)) out.push(Opcodes.i32_from_u);
|
715
760
|
|
716
|
-
binaryExpDepth--;
|
717
761
|
return out;
|
718
762
|
};
|
719
763
|
|
720
|
-
const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits, returns, returnType,
|
764
|
+
const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits, returns, returnType, localNames = [], globalNames = [] }) => {
|
721
765
|
const existing = funcs.find(x => x.name === name);
|
722
766
|
if (existing) return existing;
|
723
767
|
|
@@ -753,7 +797,6 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
|
|
753
797
|
returns,
|
754
798
|
returnType: TYPES[returnType ?? 'number'],
|
755
799
|
wasm,
|
756
|
-
memory,
|
757
800
|
internal: true,
|
758
801
|
index: currentFuncIndex++
|
759
802
|
};
|
@@ -772,7 +815,7 @@ const includeBuiltin = (scope, builtin) => {
|
|
772
815
|
};
|
773
816
|
|
774
817
|
const generateLogicExp = (scope, decl) => {
|
775
|
-
return performLogicOp(scope, decl.operator, generate(scope, decl.left), generate(scope, decl.right));
|
818
|
+
return performLogicOp(scope, decl.operator, generate(scope, decl.left), generate(scope, decl.right), getNodeType(scope, decl.left), getNodeType(scope, decl.right));
|
776
819
|
};
|
777
820
|
|
778
821
|
const TYPES = {
|
@@ -786,7 +829,8 @@ const TYPES = {
|
|
786
829
|
bigint: 0xffffffffffff7,
|
787
830
|
|
788
831
|
// these are not "typeof" types but tracked internally
|
789
|
-
_array:
|
832
|
+
_array: 0xfffffffffff0f,
|
833
|
+
_regexp: 0xfffffffffff1f
|
790
834
|
};
|
791
835
|
|
792
836
|
const TYPE_NAMES = {
|
@@ -821,6 +865,8 @@ const getType = (scope, _name) => {
|
|
821
865
|
const getNodeType = (scope, node) => {
|
822
866
|
if (node.type === 'Literal') {
|
823
867
|
if (['number', 'boolean', 'string', 'undefined', 'object', 'function', 'symbol', 'bigint'].includes(node.value)) return TYPES.number;
|
868
|
+
if (node.regex) return TYPES._regexp;
|
869
|
+
|
824
870
|
return TYPES[typeof node.value];
|
825
871
|
}
|
826
872
|
|
@@ -854,6 +900,11 @@ const getNodeType = (scope, node) => {
|
|
854
900
|
|
855
901
|
// literal.func()
|
856
902
|
if (!name && node.callee.type === 'MemberExpression') {
|
903
|
+
if (node.callee.object.regex) {
|
904
|
+
const funcName = node.callee.property.name;
|
905
|
+
return Rhemyn[funcName] ? TYPES.boolean : TYPES.undefined;
|
906
|
+
}
|
907
|
+
|
857
908
|
const baseType = getNodeType(scope, node.callee.object);
|
858
909
|
|
859
910
|
const func = node.callee.property.name;
|
@@ -897,6 +948,11 @@ const getNodeType = (scope, node) => {
|
|
897
948
|
const generateLiteral = (scope, decl, global, name) => {
|
898
949
|
if (decl.value === null) return number(NULL);
|
899
950
|
|
951
|
+
if (decl.regex) {
|
952
|
+
scope.regex[name] = decl.regex;
|
953
|
+
return number(1);
|
954
|
+
}
|
955
|
+
|
900
956
|
switch (typeof decl.value) {
|
901
957
|
case 'number':
|
902
958
|
return number(decl.value);
|
@@ -936,7 +992,8 @@ const generateLiteral = (scope, decl, global, name) => {
|
|
936
992
|
const countLeftover = wasm => {
|
937
993
|
let count = 0, depth = 0;
|
938
994
|
|
939
|
-
for (
|
995
|
+
for (let i = 0; i < wasm.length; i++) {
|
996
|
+
const inst = wasm[i];
|
940
997
|
if (depth === 0 && (inst[0] === Opcodes.if || inst[0] === Opcodes.block || inst[0] === Opcodes.loop)) {
|
941
998
|
if (inst[0] === Opcodes.if) count--;
|
942
999
|
if (inst[1] !== Blocktype.void) count++;
|
@@ -957,6 +1014,8 @@ const countLeftover = wasm => {
|
|
957
1014
|
} else count--;
|
958
1015
|
if (func) count += func.returns.length;
|
959
1016
|
} else count--;
|
1017
|
+
|
1018
|
+
// console.log(count, decompile([ inst ]).slice(0, -1));
|
960
1019
|
}
|
961
1020
|
|
962
1021
|
return count;
|
@@ -1040,7 +1099,7 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1040
1099
|
}
|
1041
1100
|
|
1042
1101
|
let out = [];
|
1043
|
-
let protoFunc, protoName, baseType, baseName
|
1102
|
+
let protoFunc, protoName, baseType, baseName;
|
1044
1103
|
// ident.func()
|
1045
1104
|
if (name && name.startsWith('__')) {
|
1046
1105
|
const spl = name.slice(2).split('_');
|
@@ -1055,6 +1114,25 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1055
1114
|
|
1056
1115
|
// literal.func()
|
1057
1116
|
if (!name && decl.callee.type === 'MemberExpression') {
|
1117
|
+
// megahack for /regex/.func()
|
1118
|
+
if (decl.callee.object.regex) {
|
1119
|
+
const funcName = decl.callee.property.name;
|
1120
|
+
const func = Rhemyn[funcName](decl.callee.object.regex.pattern, currentFuncIndex++);
|
1121
|
+
|
1122
|
+
funcIndex[func.name] = func.index;
|
1123
|
+
funcs.push(func);
|
1124
|
+
|
1125
|
+
return [
|
1126
|
+
// make string arg
|
1127
|
+
...generate(scope, decl.arguments[0]),
|
1128
|
+
|
1129
|
+
// call regex func
|
1130
|
+
Opcodes.i32_to_u,
|
1131
|
+
[ Opcodes.call, func.index ],
|
1132
|
+
Opcodes.i32_from
|
1133
|
+
];
|
1134
|
+
}
|
1135
|
+
|
1058
1136
|
baseType = getNodeType(scope, decl.callee.object);
|
1059
1137
|
|
1060
1138
|
const func = decl.callee.property.name;
|
@@ -1063,11 +1141,36 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1063
1141
|
|
1064
1142
|
out = generate(scope, decl.callee.object);
|
1065
1143
|
out.push([ Opcodes.drop ]);
|
1144
|
+
|
1145
|
+
baseName = [...arrays.keys()].pop();
|
1066
1146
|
}
|
1067
1147
|
|
1068
|
-
if (
|
1069
|
-
|
1148
|
+
if (protoName && baseType === TYPES.string && Rhemyn[protoName]) {
|
1149
|
+
const func = Rhemyn[protoName](decl.arguments[0].regex.pattern, currentFuncIndex++);
|
1150
|
+
|
1151
|
+
funcIndex[func.name] = func.index;
|
1152
|
+
funcs.push(func);
|
1070
1153
|
|
1154
|
+
const pointer = arrays.get(baseName);
|
1155
|
+
const [ local, isGlobal ] = lookupName(scope, baseName);
|
1156
|
+
|
1157
|
+
return [
|
1158
|
+
...out,
|
1159
|
+
|
1160
|
+
...(pointer == null ? [
|
1161
|
+
[ isGlobal ? Opcodes.global_get : Opcodes.local_get, local.idx ],
|
1162
|
+
Opcodes.i32_to_u,
|
1163
|
+
] : [
|
1164
|
+
...number(pointer, Valtype.i32)
|
1165
|
+
]),
|
1166
|
+
|
1167
|
+
// call regex func
|
1168
|
+
[ Opcodes.call, func.index ],
|
1169
|
+
Opcodes.i32_from
|
1170
|
+
];
|
1171
|
+
}
|
1172
|
+
|
1173
|
+
if (protoFunc) {
|
1071
1174
|
let pointer = arrays.get(baseName);
|
1072
1175
|
|
1073
1176
|
if (pointer == null) {
|
@@ -1075,7 +1178,7 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1075
1178
|
if (codeLog) log('codegen', 'cloning unknown dynamic pointer');
|
1076
1179
|
|
1077
1180
|
// register array
|
1078
|
-
|
1181
|
+
0, [ , pointer ] = makeArray(scope, {
|
1079
1182
|
rawElements: new Array(0)
|
1080
1183
|
}, _global, baseName, true, baseType === TYPES.string ? 'i16' : valtype);
|
1081
1184
|
|
@@ -1173,7 +1276,6 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1173
1276
|
args = args.slice(0, func.params.length);
|
1174
1277
|
}
|
1175
1278
|
|
1176
|
-
if (func && func.memory) scope.memory = true;
|
1177
1279
|
if (func && func.throws) scope.throws = true;
|
1178
1280
|
|
1179
1281
|
for (const arg of args) {
|
@@ -1189,7 +1291,7 @@ const generateNew = (scope, decl, _global, _name) => {
|
|
1189
1291
|
// hack: basically treat this as a normal call for builtins for now
|
1190
1292
|
const name = mapName(decl.callee.name);
|
1191
1293
|
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)})`);
|
1294
|
+
if (!builtinFuncs[name]) return todo(`new statement is not supported yet`); // return todo(`new statement is not supported yet (new ${unhackName(name)})`);
|
1193
1295
|
|
1194
1296
|
return generateCall(scope, decl, _global, _name);
|
1195
1297
|
};
|
@@ -1297,8 +1399,6 @@ const generateAssign = (scope, decl) => {
|
|
1297
1399
|
const name = decl.left.object.name;
|
1298
1400
|
const pointer = arrays.get(name);
|
1299
1401
|
|
1300
|
-
scope.memory = true;
|
1301
|
-
|
1302
1402
|
const aotPointer = pointer != null;
|
1303
1403
|
|
1304
1404
|
const newValueTmp = localTmp(scope, '__length_setter_tmp');
|
@@ -1319,6 +1419,47 @@ const generateAssign = (scope, decl) => {
|
|
1319
1419
|
];
|
1320
1420
|
}
|
1321
1421
|
|
1422
|
+
if (decl.left.type === 'MemberExpression' && decl.left.computed) {
|
1423
|
+
// arr[i] | str[i]
|
1424
|
+
const name = decl.left.object.name;
|
1425
|
+
const pointer = arrays.get(name);
|
1426
|
+
|
1427
|
+
const aotPointer = pointer != null;
|
1428
|
+
|
1429
|
+
const newValueTmp = localTmp(scope, '__member_setter_tmp');
|
1430
|
+
|
1431
|
+
const parentType = getNodeType(scope, decl.left.object);
|
1432
|
+
|
1433
|
+
const op = decl.operator.slice(0, -1);
|
1434
|
+
return [
|
1435
|
+
...(aotPointer ? number(0, Valtype.i32) : [
|
1436
|
+
...generate(scope, decl.left.object),
|
1437
|
+
Opcodes.i32_to_u
|
1438
|
+
]),
|
1439
|
+
|
1440
|
+
// get index as valtype
|
1441
|
+
...generate(scope, decl.left.property),
|
1442
|
+
|
1443
|
+
// convert to i32 and turn into byte offset by * valtypeSize (4 for i32, 8 for i64/f64)
|
1444
|
+
Opcodes.i32_to_u,
|
1445
|
+
...number(ValtypeSize[valtype], Valtype.i32),
|
1446
|
+
[ Opcodes.i32_mul ],
|
1447
|
+
[ Opcodes.i32_add ],
|
1448
|
+
|
1449
|
+
...(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)),
|
1450
|
+
[ Opcodes.local_tee, newValueTmp ],
|
1451
|
+
|
1452
|
+
...(parentType === TYPES._array ? [
|
1453
|
+
[ Opcodes.store, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
|
1454
|
+
] : [
|
1455
|
+
Opcodes.i32_to_u,
|
1456
|
+
[ StoreOps.i16, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
|
1457
|
+
]),
|
1458
|
+
|
1459
|
+
[ Opcodes.local_get, newValueTmp ]
|
1460
|
+
];
|
1461
|
+
}
|
1462
|
+
|
1322
1463
|
const [ local, isGlobal ] = lookupName(scope, name);
|
1323
1464
|
|
1324
1465
|
if (local === undefined) {
|
@@ -1339,8 +1480,10 @@ const generateAssign = (scope, decl) => {
|
|
1339
1480
|
];
|
1340
1481
|
}
|
1341
1482
|
|
1483
|
+
typeStates[name] = getNodeType(scope, decl.right);
|
1484
|
+
|
1342
1485
|
if (decl.operator === '=') {
|
1343
|
-
typeStates[name] = getNodeType(scope, decl.right);
|
1486
|
+
// typeStates[name] = getNodeType(scope, decl.right);
|
1344
1487
|
|
1345
1488
|
return [
|
1346
1489
|
...generate(scope, decl.right, isGlobal, name),
|
@@ -1349,8 +1492,27 @@ const generateAssign = (scope, decl) => {
|
|
1349
1492
|
];
|
1350
1493
|
}
|
1351
1494
|
|
1495
|
+
const op = decl.operator.slice(0, -1);
|
1496
|
+
if (op === '||' || op === '&&' || op === '??') {
|
1497
|
+
// todo: is this needed?
|
1498
|
+
// for logical assignment ops, it is not left @= right ~= left = left @ right
|
1499
|
+
// instead, left @ (left = right)
|
1500
|
+
// eg, x &&= y ~= x && (x = y)
|
1501
|
+
|
1502
|
+
return [
|
1503
|
+
...performOp(scope, op, [
|
1504
|
+
[ isGlobal ? Opcodes.global_get : Opcodes.local_get, local.idx ]
|
1505
|
+
], [
|
1506
|
+
...generate(scope, decl.right),
|
1507
|
+
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
|
1508
|
+
[ isGlobal ? Opcodes.global_get : Opcodes.local_get, local.idx ]
|
1509
|
+
], getType(scope, name), getNodeType(scope, decl.right), isGlobal, name, true),
|
1510
|
+
[ isGlobal ? Opcodes.global_get : Opcodes.local_get, local.idx ]
|
1511
|
+
];
|
1512
|
+
}
|
1513
|
+
|
1352
1514
|
return [
|
1353
|
-
...performOp(scope,
|
1515
|
+
...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
1516
|
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
|
1355
1517
|
[ isGlobal ? Opcodes.global_get : Opcodes.local_get, local.idx ]
|
1356
1518
|
];
|
@@ -1377,13 +1539,14 @@ const generateUnary = (scope, decl) => {
|
|
1377
1539
|
|
1378
1540
|
case '!':
|
1379
1541
|
// !=
|
1380
|
-
return falsy(scope, generate(scope, decl.argument));
|
1542
|
+
return falsy(scope, generate(scope, decl.argument), getNodeType(scope, decl.argument));
|
1381
1543
|
|
1382
1544
|
case '~':
|
1545
|
+
// todo: does not handle Infinity properly (should convert to 0) (but opt const converting saves us sometimes)
|
1383
1546
|
return [
|
1384
1547
|
...generate(scope, decl.argument),
|
1385
1548
|
Opcodes.i32_to,
|
1386
|
-
[ Opcodes.i32_const, signedLEB128(-1) ],
|
1549
|
+
[ Opcodes.i32_const, ...signedLEB128(-1) ],
|
1387
1550
|
[ Opcodes.i32_xor ],
|
1388
1551
|
Opcodes.i32_from
|
1389
1552
|
];
|
@@ -1464,7 +1627,7 @@ const generateUpdate = (scope, decl) => {
|
|
1464
1627
|
};
|
1465
1628
|
|
1466
1629
|
const generateIf = (scope, decl) => {
|
1467
|
-
const out = truthy(scope, generate(scope, decl.test), decl.test);
|
1630
|
+
const out = truthy(scope, generate(scope, decl.test), getNodeType(scope, decl.test));
|
1468
1631
|
|
1469
1632
|
out.push(Opcodes.i32_to, [ Opcodes.if, Blocktype.void ]);
|
1470
1633
|
depth.push('if');
|
@@ -1557,18 +1720,106 @@ const generateWhile = (scope, decl) => {
|
|
1557
1720
|
const generateForOf = (scope, decl) => {
|
1558
1721
|
const out = [];
|
1559
1722
|
|
1723
|
+
const rightType = getNodeType(scope, decl.right);
|
1724
|
+
const valtypeSize = rightType === TYPES._array ? ValtypeSize[valtype] : ValtypeSize.i16; // presume array (:()
|
1725
|
+
|
1726
|
+
// todo: for of inside for of might fuck up?
|
1727
|
+
const pointer = localTmp(scope, 'forof_base_pointer', Valtype.i32);
|
1728
|
+
const length = localTmp(scope, 'forof_length', Valtype.i32);
|
1729
|
+
const counter = localTmp(scope, 'forof_counter', Valtype.i32);
|
1730
|
+
|
1731
|
+
out.push(
|
1732
|
+
// set pointer as right
|
1733
|
+
...generate(scope, decl.right),
|
1734
|
+
Opcodes.i32_to_u,
|
1735
|
+
[ Opcodes.local_set, pointer ],
|
1736
|
+
|
1737
|
+
// get length
|
1738
|
+
[ Opcodes.local_get, pointer ],
|
1739
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
|
1740
|
+
[ Opcodes.local_set, length ]
|
1741
|
+
);
|
1742
|
+
|
1560
1743
|
out.push([ Opcodes.loop, Blocktype.void ]);
|
1561
|
-
depth.push('
|
1744
|
+
depth.push('forof');
|
1562
1745
|
|
1563
|
-
|
1564
|
-
|
1565
|
-
depth.push('if');
|
1746
|
+
// setup local for left
|
1747
|
+
generate(scope, decl.left);
|
1566
1748
|
|
1567
|
-
|
1749
|
+
const leftName = decl.left.declarations[0].id.name;
|
1568
1750
|
|
1569
|
-
|
1570
|
-
|
1571
|
-
|
1751
|
+
// set type for local
|
1752
|
+
typeStates[leftName] = rightType === TYPES._array ? TYPES.number : TYPES.string;
|
1753
|
+
|
1754
|
+
const [ local, isGlobal ] = lookupName(scope, leftName);
|
1755
|
+
|
1756
|
+
if (rightType === TYPES._array) { // array
|
1757
|
+
out.push(
|
1758
|
+
[ Opcodes.local_get, pointer ],
|
1759
|
+
[ Opcodes.load, Math.log2(valtypeSize) - 1, ...unsignedLEB128(ValtypeSize.i32) ]
|
1760
|
+
);
|
1761
|
+
} else { // string
|
1762
|
+
const [ newOut, newPointer ] = makeArray(scope, {
|
1763
|
+
rawElements: new Array(1)
|
1764
|
+
}, isGlobal, leftName, true, 'i16');
|
1765
|
+
|
1766
|
+
out.push(
|
1767
|
+
// setup new/out array
|
1768
|
+
...newOut,
|
1769
|
+
[ Opcodes.drop ],
|
1770
|
+
|
1771
|
+
...number(0, Valtype.i32), // base 0 for store after
|
1772
|
+
|
1773
|
+
// load current string ind {arg}
|
1774
|
+
[ Opcodes.local_get, pointer ],
|
1775
|
+
[ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(ValtypeSize.i32) ],
|
1776
|
+
|
1777
|
+
// store to new string ind 0
|
1778
|
+
[ Opcodes.i32_store16, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
|
1779
|
+
|
1780
|
+
// return new string (page)
|
1781
|
+
...number(newPointer)
|
1782
|
+
);
|
1783
|
+
}
|
1784
|
+
|
1785
|
+
// set left value
|
1786
|
+
out.push([ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ]);
|
1787
|
+
|
1788
|
+
out.push(
|
1789
|
+
[ Opcodes.block, Blocktype.void ],
|
1790
|
+
[ Opcodes.block, Blocktype.void ]
|
1791
|
+
);
|
1792
|
+
depth.push('block');
|
1793
|
+
depth.push('block');
|
1794
|
+
|
1795
|
+
out.push(
|
1796
|
+
...generate(scope, decl.body),
|
1797
|
+
[ Opcodes.end ]
|
1798
|
+
);
|
1799
|
+
depth.pop();
|
1800
|
+
|
1801
|
+
out.push(
|
1802
|
+
// increment iter pointer by valtype size
|
1803
|
+
[ Opcodes.local_get, pointer ],
|
1804
|
+
...number(valtypeSize, Valtype.i32),
|
1805
|
+
[ Opcodes.i32_add ],
|
1806
|
+
[ Opcodes.local_set, pointer ],
|
1807
|
+
|
1808
|
+
// increment counter by 1
|
1809
|
+
[ Opcodes.local_get, counter ],
|
1810
|
+
...number(1, Valtype.i32),
|
1811
|
+
[ Opcodes.i32_add ],
|
1812
|
+
[ Opcodes.local_tee, counter ],
|
1813
|
+
|
1814
|
+
// loop if counter != length
|
1815
|
+
[ Opcodes.local_get, length ],
|
1816
|
+
[ Opcodes.i32_ne ],
|
1817
|
+
[ Opcodes.br_if, 1 ],
|
1818
|
+
|
1819
|
+
[ Opcodes.end ], [ Opcodes.end ]
|
1820
|
+
);
|
1821
|
+
depth.pop();
|
1822
|
+
depth.pop();
|
1572
1823
|
|
1573
1824
|
return out;
|
1574
1825
|
};
|
@@ -1659,13 +1910,22 @@ const generateAssignPat = (scope, decl) => {
|
|
1659
1910
|
};
|
1660
1911
|
|
1661
1912
|
let pages = new Map();
|
1662
|
-
const allocPage = reason => {
|
1663
|
-
if (pages.has(reason)) return pages.get(reason);
|
1913
|
+
const allocPage = (reason, type) => {
|
1914
|
+
if (pages.has(reason)) return pages.get(reason).ind;
|
1915
|
+
|
1916
|
+
const ind = pages.size;
|
1917
|
+
pages.set(reason, { ind, type });
|
1918
|
+
|
1919
|
+
if (allocLog) log('alloc', `allocated new page of memory (${ind}) | ${reason} (type: ${type})`);
|
1920
|
+
|
1921
|
+
return ind;
|
1922
|
+
};
|
1664
1923
|
|
1665
|
-
|
1666
|
-
pages.
|
1924
|
+
const freePage = reason => {
|
1925
|
+
const { ind } = pages.get(reason);
|
1926
|
+
pages.delete(reason);
|
1667
1927
|
|
1668
|
-
if (
|
1928
|
+
if (allocLog) log('alloc', `freed page of memory (${ind}) | ${reason}`);
|
1669
1929
|
|
1670
1930
|
return ind;
|
1671
1931
|
};
|
@@ -1679,7 +1939,7 @@ const itemTypeToValtype = {
|
|
1679
1939
|
i16: 'i32'
|
1680
1940
|
};
|
1681
1941
|
|
1682
|
-
const
|
1942
|
+
const StoreOps = {
|
1683
1943
|
i32: Opcodes.i32_store,
|
1684
1944
|
i64: Opcodes.i64_store,
|
1685
1945
|
f64: Opcodes.f64_store,
|
@@ -1694,7 +1954,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
1694
1954
|
if (!arrays.has(name) || name === '$undeclared') {
|
1695
1955
|
// todo: can we just have 1 undeclared array? probably not? but this is not really memory efficient
|
1696
1956
|
const uniqueName = name === '$undeclared' ? name + Math.random().toString().slice(2) : name;
|
1697
|
-
arrays.set(name, allocPage(`${itemType === 'i16' ? 'string' : 'array'}: ${uniqueName}
|
1957
|
+
arrays.set(name, allocPage(`${itemType === 'i16' ? 'string' : 'array'}: ${uniqueName}`, itemType) * pageSize);
|
1698
1958
|
}
|
1699
1959
|
|
1700
1960
|
const pointer = arrays.get(name);
|
@@ -1711,7 +1971,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
1711
1971
|
[ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(pointer) ]
|
1712
1972
|
);
|
1713
1973
|
|
1714
|
-
const storeOp =
|
1974
|
+
const storeOp = StoreOps[itemType];
|
1715
1975
|
const valtype = itemTypeToValtype[itemType];
|
1716
1976
|
|
1717
1977
|
if (!initEmpty) for (let i = 0; i < length; i++) {
|
@@ -1727,8 +1987,6 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
1727
1987
|
// local value as pointer
|
1728
1988
|
out.push(...number(pointer));
|
1729
1989
|
|
1730
|
-
scope.memory = true;
|
1731
|
-
|
1732
1990
|
return [ out, pointer ];
|
1733
1991
|
};
|
1734
1992
|
|
@@ -1747,8 +2005,6 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
1747
2005
|
const name = decl.object.name;
|
1748
2006
|
const pointer = arrays.get(name);
|
1749
2007
|
|
1750
|
-
scope.memory = true;
|
1751
|
-
|
1752
2008
|
const aotPointer = pointer != null;
|
1753
2009
|
|
1754
2010
|
return [
|
@@ -1768,8 +2024,6 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
1768
2024
|
const name = decl.object.name;
|
1769
2025
|
const pointer = arrays.get(name);
|
1770
2026
|
|
1771
|
-
scope.memory = true;
|
1772
|
-
|
1773
2027
|
const aotPointer = pointer != null;
|
1774
2028
|
|
1775
2029
|
if (type === TYPES._array) {
|
@@ -1879,7 +2133,7 @@ const generateFunc = (scope, decl) => {
|
|
1879
2133
|
locals: {},
|
1880
2134
|
localInd: 0,
|
1881
2135
|
returns: [ valtypeBinary ],
|
1882
|
-
|
2136
|
+
returnType: null,
|
1883
2137
|
throws: false,
|
1884
2138
|
name
|
1885
2139
|
};
|
@@ -1905,7 +2159,6 @@ const generateFunc = (scope, decl) => {
|
|
1905
2159
|
returns: innerScope.returns,
|
1906
2160
|
returnType: innerScope.returnType,
|
1907
2161
|
locals: innerScope.locals,
|
1908
|
-
memory: innerScope.memory,
|
1909
2162
|
throws: innerScope.throws,
|
1910
2163
|
index: currentFuncIndex++
|
1911
2164
|
};
|
@@ -1920,6 +2173,8 @@ const generateFunc = (scope, decl) => {
|
|
1920
2173
|
|
1921
2174
|
if (name !== 'main' && func.returns.length !== 0 && wasm[wasm.length - 1]?.[0] !== Opcodes.return && countLeftover(wasm) === 0) {
|
1922
2175
|
wasm.push(...number(0), [ Opcodes.return ]);
|
2176
|
+
|
2177
|
+
if (func.returnType === null) func.returnType = TYPES.undefined;
|
1923
2178
|
}
|
1924
2179
|
|
1925
2180
|
// change v128 params into many <type> (i32x4 -> i32/etc) instead as unsupported param valtype
|
@@ -1930,9 +2185,7 @@ const generateFunc = (scope, decl) => {
|
|
1930
2185
|
if (local.type === Valtype.v128) {
|
1931
2186
|
vecParams++;
|
1932
2187
|
|
1933
|
-
/*
|
1934
|
-
|
1935
|
-
wasm.unshift( // add v128 load for param
|
2188
|
+
/* wasm.unshift( // add v128 load for param
|
1936
2189
|
[ Opcodes.i32_const, 0 ],
|
1937
2190
|
[ ...Opcodes.v128_load, 0, i * 16 ],
|
1938
2191
|
[ Opcodes.local_set, local.idx ]
|