porffor 0.0.0-828ee15 → 0.0.0-ba812f2
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 +257 -0
- package/compiler/builtins.js +0 -1
- package/compiler/codeGen.js +330 -76
- 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 +2 -1
- package/compiler/prototype.js +4 -5
- package/compiler/sections.js +28 -2
- package/compiler/wrap.js +14 -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/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 +319 -0
- package/rhemyn/test/parse.js +59 -0
- package/runner/index.js +52 -33
- package/runner/repl.js +6 -11
- package/runner/transform.js +5 -26
- package/runner/version.js +10 -0
- package/t.js +31 -0
- package/tmp.c +37 -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,6 +109,9 @@ const generate = (scope, decl, global = false, name = undefined) => {
|
|
101
109
|
case 'WhileStatement':
|
102
110
|
return generateWhile(scope, decl);
|
103
111
|
|
112
|
+
/* case 'ForOfStatement':
|
113
|
+
return generateForOf(scope, decl); */
|
114
|
+
|
104
115
|
case 'BreakStatement':
|
105
116
|
return generateBreak(scope, decl);
|
106
117
|
|
@@ -164,7 +175,6 @@ const generate = (scope, decl, global = false, name = undefined) => {
|
|
164
175
|
}
|
165
176
|
|
166
177
|
if (asm[0] === 'memory') {
|
167
|
-
scope.memory = true;
|
168
178
|
allocPage('asm instrinsic');
|
169
179
|
// todo: add to store/load offset insts
|
170
180
|
continue;
|
@@ -278,7 +288,7 @@ const generateReturn = (scope, decl) => {
|
|
278
288
|
];
|
279
289
|
}
|
280
290
|
|
281
|
-
|
291
|
+
scope.returnType = getNodeType(scope, decl.argument);
|
282
292
|
|
283
293
|
return [
|
284
294
|
...generate(scope, decl.argument),
|
@@ -295,11 +305,11 @@ const localTmp = (scope, name, type = valtypeBinary) => {
|
|
295
305
|
return idx;
|
296
306
|
};
|
297
307
|
|
298
|
-
const performLogicOp = (scope, op, left, right) => {
|
308
|
+
const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
299
309
|
const checks = {
|
300
|
-
'||':
|
301
|
-
'&&':
|
302
|
-
|
310
|
+
'||': falsy,
|
311
|
+
'&&': truthy,
|
312
|
+
'??': nullish
|
303
313
|
};
|
304
314
|
|
305
315
|
if (!checks[op]) return todo(`logic operator ${op} not implemented yet`);
|
@@ -310,7 +320,8 @@ const performLogicOp = (scope, op, left, right) => {
|
|
310
320
|
return [
|
311
321
|
...left,
|
312
322
|
[ Opcodes.local_tee, localTmp(scope, 'logictmp') ],
|
313
|
-
...checks[op],
|
323
|
+
...checks[op](scope, [], leftType),
|
324
|
+
Opcodes.i32_to,
|
314
325
|
[ Opcodes.if, valtypeBinary ],
|
315
326
|
...right,
|
316
327
|
[ Opcodes.else ],
|
@@ -325,10 +336,6 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
325
336
|
// todo: optimize by looking up names in arrays and using that if exists?
|
326
337
|
// todo: optimize this if using literals/known lengths?
|
327
338
|
|
328
|
-
scope.memory = true;
|
329
|
-
|
330
|
-
const getLocalTmp = name => localTmp(scope, name + binaryExpDepth);
|
331
|
-
|
332
339
|
const rightPointer = localTmp(scope, 'concat_right_pointer', Valtype.i32);
|
333
340
|
const rightLength = localTmp(scope, 'concat_right_length', Valtype.i32);
|
334
341
|
const leftLength = localTmp(scope, 'concat_left_length', Valtype.i32);
|
@@ -457,75 +464,220 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
457
464
|
];
|
458
465
|
};
|
459
466
|
|
460
|
-
const
|
467
|
+
const compareStrings = (scope, left, right) => {
|
468
|
+
// todo: this should be rewritten into a built-in/func: String.prototype.concat
|
469
|
+
// todo: convert left and right to strings if not
|
470
|
+
// todo: optimize by looking up names in arrays and using that if exists?
|
471
|
+
// todo: optimize this if using literals/known lengths?
|
472
|
+
|
473
|
+
const leftPointer = localTmp(scope, 'compare_left_pointer', Valtype.i32);
|
474
|
+
const leftLength = localTmp(scope, 'compare_left_length', Valtype.i32);
|
475
|
+
const rightPointer = localTmp(scope, 'compare_right_pointer', Valtype.i32);
|
476
|
+
const rightLength = localTmp(scope, 'compare_right_length', Valtype.i32);
|
477
|
+
|
478
|
+
const index = localTmp(scope, 'compare_index', Valtype.i32);
|
479
|
+
const indexEnd = localTmp(scope, 'compare_index_end', Valtype.i32);
|
480
|
+
|
481
|
+
return [
|
482
|
+
// setup left
|
483
|
+
...left,
|
484
|
+
Opcodes.i32_to_u,
|
485
|
+
[ Opcodes.local_tee, leftPointer ],
|
486
|
+
|
487
|
+
// setup right
|
488
|
+
...right,
|
489
|
+
Opcodes.i32_to_u,
|
490
|
+
[ Opcodes.local_tee, rightPointer ],
|
491
|
+
|
492
|
+
// fast path: check leftPointer == rightPointer
|
493
|
+
// use if (block) for everything after to "return" a value early
|
494
|
+
[ Opcodes.i32_ne ],
|
495
|
+
[ Opcodes.if, Valtype.i32 ],
|
496
|
+
|
497
|
+
// get lengths
|
498
|
+
[ Opcodes.local_get, leftPointer ],
|
499
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128(0) ],
|
500
|
+
[ Opcodes.local_tee, leftLength ],
|
501
|
+
|
502
|
+
[ Opcodes.local_get, rightPointer ],
|
503
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128(0) ],
|
504
|
+
[ Opcodes.local_tee, rightLength ],
|
505
|
+
|
506
|
+
// fast path: check leftLength != rightLength
|
507
|
+
[ Opcodes.i32_ne ],
|
508
|
+
[ Opcodes.if, Blocktype.void ],
|
509
|
+
...number(0, Valtype.i32),
|
510
|
+
[ Opcodes.br, 1 ],
|
511
|
+
[ Opcodes.end ],
|
512
|
+
|
513
|
+
// no fast path for length = 0 as it would probably be slower for most of the time?
|
514
|
+
|
515
|
+
// setup index end as length * sizeof i16 (2)
|
516
|
+
// we do this instead of having to do mul/div each iter for perf™
|
517
|
+
[ Opcodes.local_get, leftLength ],
|
518
|
+
...number(ValtypeSize.i16, Valtype.i32),
|
519
|
+
[ Opcodes.i32_mul ],
|
520
|
+
[ Opcodes.local_set, indexEnd ],
|
521
|
+
|
522
|
+
// iterate over each char and check if eq
|
523
|
+
[ Opcodes.loop, Blocktype.void ],
|
524
|
+
|
525
|
+
// fetch left
|
526
|
+
[ Opcodes.local_get, index ],
|
527
|
+
[ Opcodes.local_get, leftPointer ],
|
528
|
+
[ Opcodes.i32_add ],
|
529
|
+
[ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(ValtypeSize.i32) ],
|
530
|
+
|
531
|
+
// fetch right
|
532
|
+
[ Opcodes.local_get, index ],
|
533
|
+
[ Opcodes.local_get, rightPointer ],
|
534
|
+
[ Opcodes.i32_add ],
|
535
|
+
[ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(ValtypeSize.i32) ],
|
536
|
+
|
537
|
+
// not equal, "return" false
|
538
|
+
[ Opcodes.i32_ne ],
|
539
|
+
[ Opcodes.if, Blocktype.void ],
|
540
|
+
...number(0, Valtype.i32),
|
541
|
+
[ Opcodes.br, 2 ],
|
542
|
+
[ Opcodes.end ],
|
543
|
+
|
544
|
+
// index += sizeof i16 (2)
|
545
|
+
[ Opcodes.local_get, index ],
|
546
|
+
...number(ValtypeSize.i16, Valtype.i32),
|
547
|
+
[ Opcodes.i32_add ],
|
548
|
+
[ Opcodes.local_tee, index ],
|
549
|
+
|
550
|
+
// if index != index end (length * sizeof 16), loop
|
551
|
+
[ Opcodes.local_get, indexEnd ],
|
552
|
+
[ Opcodes.i32_ne ],
|
553
|
+
[ Opcodes.br_if, 0 ],
|
554
|
+
[ Opcodes.end ],
|
555
|
+
|
556
|
+
// no failed checks, so true!
|
557
|
+
...number(1, Valtype.i32),
|
558
|
+
|
559
|
+
// pointers match, so true
|
560
|
+
[ Opcodes.else ],
|
561
|
+
...number(1, Valtype.i32),
|
562
|
+
[ Opcodes.end ],
|
563
|
+
|
564
|
+
// convert i32 result to valtype
|
565
|
+
// do not do as automatically added by binary exp gen for equality ops
|
566
|
+
// Opcodes.i32_from_u
|
567
|
+
];
|
568
|
+
};
|
569
|
+
|
570
|
+
const truthy = (scope, wasm, type) => {
|
461
571
|
// arrays are always truthy
|
462
572
|
if (type === TYPES._array) return [
|
463
573
|
...wasm,
|
464
574
|
[ Opcodes.drop ],
|
465
|
-
number(
|
575
|
+
...number(1)
|
466
576
|
];
|
467
577
|
|
468
578
|
if (type === TYPES.string) {
|
469
|
-
// if "" (length = 0)
|
579
|
+
// if not "" (length = 0)
|
470
580
|
return [
|
471
581
|
// pointer
|
472
582
|
...wasm,
|
583
|
+
Opcodes.i32_to_u,
|
473
584
|
|
474
585
|
// get length
|
475
586
|
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
|
476
587
|
|
477
|
-
// if length
|
478
|
-
[ Opcodes.i32_eqz ],
|
588
|
+
// if length != 0
|
589
|
+
/* [ Opcodes.i32_eqz ],
|
590
|
+
[ Opcodes.i32_eqz ], */
|
479
591
|
Opcodes.i32_from_u
|
480
592
|
]
|
481
593
|
}
|
482
594
|
|
483
|
-
// if
|
595
|
+
// if != 0
|
484
596
|
return [
|
485
597
|
...wasm,
|
486
598
|
|
487
|
-
|
488
|
-
Opcodes.
|
599
|
+
/* Opcodes.eqz,
|
600
|
+
[ Opcodes.i32_eqz ],
|
601
|
+
Opcodes.i32_from */
|
489
602
|
];
|
490
603
|
};
|
491
604
|
|
492
|
-
const
|
605
|
+
const falsy = (scope, wasm, type) => {
|
493
606
|
// arrays are always truthy
|
494
607
|
if (type === TYPES._array) return [
|
495
608
|
...wasm,
|
496
609
|
[ Opcodes.drop ],
|
497
|
-
number(
|
610
|
+
...number(0)
|
498
611
|
];
|
499
612
|
|
500
613
|
if (type === TYPES.string) {
|
501
|
-
// if
|
614
|
+
// if "" (length = 0)
|
502
615
|
return [
|
503
616
|
// pointer
|
504
617
|
...wasm,
|
618
|
+
Opcodes.i32_to_u,
|
505
619
|
|
506
620
|
// get length
|
507
621
|
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
|
508
622
|
|
509
|
-
// if length
|
510
|
-
|
511
|
-
[ Opcodes.i32_eqz ], */
|
623
|
+
// if length == 0
|
624
|
+
[ Opcodes.i32_eqz ],
|
512
625
|
Opcodes.i32_from_u
|
513
626
|
]
|
514
627
|
}
|
515
628
|
|
516
|
-
// if
|
629
|
+
// if = 0
|
517
630
|
return [
|
518
631
|
...wasm,
|
519
632
|
|
520
|
-
|
521
|
-
|
522
|
-
|
633
|
+
...Opcodes.eqz,
|
634
|
+
Opcodes.i32_from_u
|
635
|
+
];
|
636
|
+
};
|
637
|
+
|
638
|
+
const nullish = (scope, wasm, type) => {
|
639
|
+
// undefined
|
640
|
+
if (type === TYPES.undefined) return [
|
641
|
+
...wasm,
|
642
|
+
[ Opcodes.drop ],
|
643
|
+
...number(1)
|
644
|
+
];
|
645
|
+
|
646
|
+
// null (if object and = "0")
|
647
|
+
if (type === TYPES.object) return [
|
648
|
+
...wasm,
|
649
|
+
...Opcodes.eqz,
|
650
|
+
Opcodes.i32_from_u
|
651
|
+
];
|
652
|
+
|
653
|
+
// not
|
654
|
+
return [
|
655
|
+
...wasm,
|
656
|
+
[ Opcodes.drop ],
|
657
|
+
...number(0)
|
523
658
|
];
|
524
659
|
};
|
525
660
|
|
526
661
|
const performOp = (scope, op, left, right, leftType, rightType, _global = false, _name = '$undeclared', assign = false) => {
|
527
662
|
if (op === '||' || op === '&&' || op === '??') {
|
528
|
-
return performLogicOp(scope, op, left, right);
|
663
|
+
return performLogicOp(scope, op, left, right, leftType, rightType);
|
664
|
+
}
|
665
|
+
|
666
|
+
if (codeLog && (!leftType || !rightType)) log('codegen', 'untracked type in op', op, _name, '\n' + new Error().stack.split('\n').slice(1).join('\n'));
|
667
|
+
|
668
|
+
// if strict (in)equal and known types mismatch, return false (===)/true (!==)
|
669
|
+
if ((op === '===' || op === '!==') && leftType && rightType && leftType !== rightType) {
|
670
|
+
return [
|
671
|
+
...left,
|
672
|
+
...right,
|
673
|
+
|
674
|
+
// drop values
|
675
|
+
[ Opcodes.drop ],
|
676
|
+
[ Opcodes.drop ],
|
677
|
+
|
678
|
+
// return false (===)/true (!==)
|
679
|
+
...number(op === '===' ? 0 : 1, Valtype.i32)
|
680
|
+
];
|
529
681
|
}
|
530
682
|
|
531
683
|
if (leftType === TYPES.string || rightType === TYPES.string) {
|
@@ -538,9 +690,20 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
538
690
|
if (!['==', '===', '!=', '!==', '>', '>=', '<', '<='].includes(op)) return number(NaN);
|
539
691
|
|
540
692
|
// else leave bool ops
|
541
|
-
// todo: convert string to number if string and number
|
693
|
+
// todo: convert string to number if string and number/bool
|
542
694
|
// todo: string (>|>=|<|<=) string
|
543
|
-
|
695
|
+
|
696
|
+
// string comparison
|
697
|
+
if (op === '===' || op === '==') {
|
698
|
+
return compareStrings(scope, left, right);
|
699
|
+
}
|
700
|
+
|
701
|
+
if (op === '!==' || op === '!=') {
|
702
|
+
return [
|
703
|
+
...compareStrings(scope, left, right),
|
704
|
+
[ Opcodes.i32_eqz ]
|
705
|
+
];
|
706
|
+
}
|
544
707
|
}
|
545
708
|
|
546
709
|
let ops = operatorOpcode[valtype][op];
|
@@ -583,7 +746,7 @@ const generateBinaryExp = (scope, decl, _global, _name) => {
|
|
583
746
|
return out;
|
584
747
|
};
|
585
748
|
|
586
|
-
const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits, returns, returnType,
|
749
|
+
const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits, returns, returnType, localNames = [], globalNames = [] }) => {
|
587
750
|
const existing = funcs.find(x => x.name === name);
|
588
751
|
if (existing) return existing;
|
589
752
|
|
@@ -619,7 +782,6 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
|
|
619
782
|
returns,
|
620
783
|
returnType: TYPES[returnType ?? 'number'],
|
621
784
|
wasm,
|
622
|
-
memory,
|
623
785
|
internal: true,
|
624
786
|
index: currentFuncIndex++
|
625
787
|
};
|
@@ -638,7 +800,7 @@ const includeBuiltin = (scope, builtin) => {
|
|
638
800
|
};
|
639
801
|
|
640
802
|
const generateLogicExp = (scope, decl) => {
|
641
|
-
return performLogicOp(scope, decl.operator, generate(scope, decl.left), generate(scope, decl.right));
|
803
|
+
return performLogicOp(scope, decl.operator, generate(scope, decl.left), generate(scope, decl.right), getNodeType(scope, decl.left), getNodeType(scope, decl.right));
|
642
804
|
};
|
643
805
|
|
644
806
|
const TYPES = {
|
@@ -652,7 +814,8 @@ const TYPES = {
|
|
652
814
|
bigint: 0xffffffffffff7,
|
653
815
|
|
654
816
|
// these are not "typeof" types but tracked internally
|
655
|
-
_array:
|
817
|
+
_array: 0xfffffffffff0f,
|
818
|
+
_regexp: 0xfffffffffff1f
|
656
819
|
};
|
657
820
|
|
658
821
|
const TYPE_NAMES = {
|
@@ -686,6 +849,9 @@ const getType = (scope, _name) => {
|
|
686
849
|
|
687
850
|
const getNodeType = (scope, node) => {
|
688
851
|
if (node.type === 'Literal') {
|
852
|
+
if (['number', 'boolean', 'string', 'undefined', 'object', 'function', 'symbol', 'bigint'].includes(node.value)) return TYPES.number;
|
853
|
+
if (node.regex) return TYPES._regexp;
|
854
|
+
|
689
855
|
return TYPES[typeof node.value];
|
690
856
|
}
|
691
857
|
|
@@ -700,7 +866,7 @@ const getNodeType = (scope, node) => {
|
|
700
866
|
if (node.type === 'CallExpression' || node.type === 'NewExpression') {
|
701
867
|
const name = node.callee.name;
|
702
868
|
const func = funcs.find(x => x.name === name);
|
703
|
-
if (func) return func.returnType
|
869
|
+
if (func) return func.returnType;
|
704
870
|
|
705
871
|
if (builtinFuncs[name]) return TYPES[builtinFuncs[name].returnType ?? 'number'];
|
706
872
|
if (internalConstrs[name]) return internalConstrs[name].type;
|
@@ -719,15 +885,18 @@ const getNodeType = (scope, node) => {
|
|
719
885
|
|
720
886
|
// literal.func()
|
721
887
|
if (!name && node.callee.type === 'MemberExpression') {
|
888
|
+
if (node.callee.object.regex) {
|
889
|
+
const funcName = node.callee.property.name;
|
890
|
+
return Rhemyn[funcName] ? TYPES.boolean : TYPES.undefined;
|
891
|
+
}
|
892
|
+
|
722
893
|
const baseType = getNodeType(scope, node.callee.object);
|
723
894
|
|
724
895
|
const func = node.callee.property.name;
|
725
896
|
protoFunc = prototypeFuncs[baseType]?.[func];
|
726
897
|
}
|
727
898
|
|
728
|
-
if (protoFunc) return protoFunc.returnType
|
729
|
-
|
730
|
-
return TYPES.number;
|
899
|
+
if (protoFunc) return protoFunc.returnType;
|
731
900
|
}
|
732
901
|
|
733
902
|
if (node.type === 'ExpressionStatement') {
|
@@ -759,14 +928,16 @@ const getNodeType = (scope, node) => {
|
|
759
928
|
|
760
929
|
if (objectType === TYPES.string && node.computed) return TYPES.string;
|
761
930
|
}
|
762
|
-
|
763
|
-
// default to number
|
764
|
-
return TYPES.number;
|
765
931
|
};
|
766
932
|
|
767
933
|
const generateLiteral = (scope, decl, global, name) => {
|
768
934
|
if (decl.value === null) return number(NULL);
|
769
935
|
|
936
|
+
if (decl.regex) {
|
937
|
+
scope.regex[name] = decl.regex;
|
938
|
+
return number(1);
|
939
|
+
}
|
940
|
+
|
770
941
|
switch (typeof decl.value) {
|
771
942
|
case 'number':
|
772
943
|
return number(decl.value);
|
@@ -806,7 +977,8 @@ const generateLiteral = (scope, decl, global, name) => {
|
|
806
977
|
const countLeftover = wasm => {
|
807
978
|
let count = 0, depth = 0;
|
808
979
|
|
809
|
-
for (
|
980
|
+
for (let i = 0; i < wasm.length; i++) {
|
981
|
+
const inst = wasm[i];
|
810
982
|
if (depth === 0 && (inst[0] === Opcodes.if || inst[0] === Opcodes.block || inst[0] === Opcodes.loop)) {
|
811
983
|
if (inst[0] === Opcodes.if) count--;
|
812
984
|
if (inst[1] !== Blocktype.void) count++;
|
@@ -910,7 +1082,7 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
910
1082
|
}
|
911
1083
|
|
912
1084
|
let out = [];
|
913
|
-
let protoFunc, protoName, baseType, baseName
|
1085
|
+
let protoFunc, protoName, baseType, baseName;
|
914
1086
|
// ident.func()
|
915
1087
|
if (name && name.startsWith('__')) {
|
916
1088
|
const spl = name.slice(2).split('_');
|
@@ -925,6 +1097,25 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
925
1097
|
|
926
1098
|
// literal.func()
|
927
1099
|
if (!name && decl.callee.type === 'MemberExpression') {
|
1100
|
+
// megahack for /regex/.func()
|
1101
|
+
if (decl.callee.object.regex) {
|
1102
|
+
const funcName = decl.callee.property.name;
|
1103
|
+
const func = Rhemyn[funcName](decl.callee.object.regex.pattern, currentFuncIndex++);
|
1104
|
+
|
1105
|
+
funcIndex[func.name] = func.index;
|
1106
|
+
funcs.push(func);
|
1107
|
+
|
1108
|
+
return [
|
1109
|
+
// make string arg
|
1110
|
+
...generate(scope, decl.arguments[0]),
|
1111
|
+
|
1112
|
+
// call regex func
|
1113
|
+
Opcodes.i32_to_u,
|
1114
|
+
[ Opcodes.call, func.index ],
|
1115
|
+
Opcodes.i32_from
|
1116
|
+
];
|
1117
|
+
}
|
1118
|
+
|
928
1119
|
baseType = getNodeType(scope, decl.callee.object);
|
929
1120
|
|
930
1121
|
const func = decl.callee.property.name;
|
@@ -933,11 +1124,36 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
933
1124
|
|
934
1125
|
out = generate(scope, decl.callee.object);
|
935
1126
|
out.push([ Opcodes.drop ]);
|
1127
|
+
|
1128
|
+
baseName = [...arrays.keys()].pop();
|
936
1129
|
}
|
937
1130
|
|
938
|
-
if (
|
939
|
-
|
1131
|
+
if (protoName && baseType === TYPES.string && Rhemyn[protoName]) {
|
1132
|
+
const func = Rhemyn[protoName](decl.arguments[0].regex.pattern, currentFuncIndex++);
|
1133
|
+
|
1134
|
+
funcIndex[func.name] = func.index;
|
1135
|
+
funcs.push(func);
|
1136
|
+
|
1137
|
+
const pointer = arrays.get(baseName);
|
1138
|
+
const [ local, isGlobal ] = lookupName(scope, baseName);
|
940
1139
|
|
1140
|
+
return [
|
1141
|
+
...out,
|
1142
|
+
|
1143
|
+
...(pointer == null ? [
|
1144
|
+
[ isGlobal ? Opcodes.global_get : Opcodes.local_get, local.idx ],
|
1145
|
+
Opcodes.i32_to_u,
|
1146
|
+
] : [
|
1147
|
+
...number(pointer, Valtype.i32)
|
1148
|
+
]),
|
1149
|
+
|
1150
|
+
// call regex func
|
1151
|
+
[ Opcodes.call, func.index ],
|
1152
|
+
Opcodes.i32_from
|
1153
|
+
];
|
1154
|
+
}
|
1155
|
+
|
1156
|
+
if (protoFunc) {
|
941
1157
|
let pointer = arrays.get(baseName);
|
942
1158
|
|
943
1159
|
if (pointer == null) {
|
@@ -945,7 +1161,7 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
945
1161
|
if (codeLog) log('codegen', 'cloning unknown dynamic pointer');
|
946
1162
|
|
947
1163
|
// register array
|
948
|
-
|
1164
|
+
0, [ , pointer ] = makeArray(scope, {
|
949
1165
|
rawElements: new Array(0)
|
950
1166
|
}, _global, baseName, true, baseType === TYPES.string ? 'i16' : valtype);
|
951
1167
|
|
@@ -1043,7 +1259,6 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1043
1259
|
args = args.slice(0, func.params.length);
|
1044
1260
|
}
|
1045
1261
|
|
1046
|
-
if (func && func.memory) scope.memory = true;
|
1047
1262
|
if (func && func.throws) scope.throws = true;
|
1048
1263
|
|
1049
1264
|
for (const arg of args) {
|
@@ -1059,7 +1274,7 @@ const generateNew = (scope, decl, _global, _name) => {
|
|
1059
1274
|
// hack: basically treat this as a normal call for builtins for now
|
1060
1275
|
const name = mapName(decl.callee.name);
|
1061
1276
|
if (internalConstrs[name]) return internalConstrs[name].generate(scope, decl, _global, _name);
|
1062
|
-
if (!builtinFuncs[name]) return todo(`new statement is not supported yet (new ${unhackName(name)})`);
|
1277
|
+
if (!builtinFuncs[name]) return todo(`new statement is not supported yet`); // return todo(`new statement is not supported yet (new ${unhackName(name)})`);
|
1063
1278
|
|
1064
1279
|
return generateCall(scope, decl, _global, _name);
|
1065
1280
|
};
|
@@ -1167,8 +1382,6 @@ const generateAssign = (scope, decl) => {
|
|
1167
1382
|
const name = decl.left.object.name;
|
1168
1383
|
const pointer = arrays.get(name);
|
1169
1384
|
|
1170
|
-
scope.memory = true;
|
1171
|
-
|
1172
1385
|
const aotPointer = pointer != null;
|
1173
1386
|
|
1174
1387
|
const newValueTmp = localTmp(scope, '__length_setter_tmp');
|
@@ -1219,8 +1432,27 @@ const generateAssign = (scope, decl) => {
|
|
1219
1432
|
];
|
1220
1433
|
}
|
1221
1434
|
|
1435
|
+
const op = decl.operator.slice(0, -1);
|
1436
|
+
if (op === '||' || op === '&&' || op === '??') {
|
1437
|
+
// todo: is this needed?
|
1438
|
+
// for logical assignment ops, it is not left @= right ~= left = left @ right
|
1439
|
+
// instead, left @ (left = right)
|
1440
|
+
// eg, x &&= y ~= x && (x = y)
|
1441
|
+
|
1442
|
+
return [
|
1443
|
+
...performOp(scope, op, [
|
1444
|
+
[ isGlobal ? Opcodes.global_get : Opcodes.local_get, local.idx ]
|
1445
|
+
], [
|
1446
|
+
...generate(scope, decl.right),
|
1447
|
+
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
|
1448
|
+
[ isGlobal ? Opcodes.global_get : Opcodes.local_get, local.idx ]
|
1449
|
+
], getType(scope, name), getNodeType(scope, decl.right), isGlobal, name, true),
|
1450
|
+
[ isGlobal ? Opcodes.global_get : Opcodes.local_get, local.idx ]
|
1451
|
+
];
|
1452
|
+
}
|
1453
|
+
|
1222
1454
|
return [
|
1223
|
-
...performOp(scope,
|
1455
|
+
...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),
|
1224
1456
|
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
|
1225
1457
|
[ isGlobal ? Opcodes.global_get : Opcodes.local_get, local.idx ]
|
1226
1458
|
];
|
@@ -1247,13 +1479,14 @@ const generateUnary = (scope, decl) => {
|
|
1247
1479
|
|
1248
1480
|
case '!':
|
1249
1481
|
// !=
|
1250
|
-
return falsy(scope, generate(scope, decl.argument));
|
1482
|
+
return falsy(scope, generate(scope, decl.argument), getNodeType(scope, decl.argument));
|
1251
1483
|
|
1252
1484
|
case '~':
|
1485
|
+
// todo: does not handle Infinity properly (should convert to 0) (but opt const converting saves us sometimes)
|
1253
1486
|
return [
|
1254
1487
|
...generate(scope, decl.argument),
|
1255
1488
|
Opcodes.i32_to,
|
1256
|
-
[ Opcodes.i32_const, signedLEB128(-1) ],
|
1489
|
+
[ Opcodes.i32_const, ...signedLEB128(-1) ],
|
1257
1490
|
[ Opcodes.i32_xor ],
|
1258
1491
|
Opcodes.i32_from
|
1259
1492
|
];
|
@@ -1291,7 +1524,7 @@ const generateUnary = (scope, decl) => {
|
|
1291
1524
|
return out;
|
1292
1525
|
|
1293
1526
|
case 'typeof':
|
1294
|
-
const type = getNodeType(scope, decl.argument);
|
1527
|
+
const type = getNodeType(scope, decl.argument) ?? TYPES.number;
|
1295
1528
|
|
1296
1529
|
// for custom types, just return object
|
1297
1530
|
if (type > 0xffffffffffff7) return number(TYPES.object);
|
@@ -1424,9 +1657,28 @@ const generateWhile = (scope, decl) => {
|
|
1424
1657
|
return out;
|
1425
1658
|
};
|
1426
1659
|
|
1660
|
+
const generateForOf = (scope, decl) => {
|
1661
|
+
const out = [];
|
1662
|
+
|
1663
|
+
out.push([ Opcodes.loop, Blocktype.void ]);
|
1664
|
+
depth.push('while');
|
1665
|
+
|
1666
|
+
out.push(...generate(scope, decl.test));
|
1667
|
+
out.push(Opcodes.i32_to, [ Opcodes.if, Blocktype.void ]);
|
1668
|
+
depth.push('if');
|
1669
|
+
|
1670
|
+
out.push(...generate(scope, decl.body));
|
1671
|
+
|
1672
|
+
out.push([ Opcodes.br, 1 ]);
|
1673
|
+
out.push([ Opcodes.end ], [ Opcodes.end ]);
|
1674
|
+
depth.pop(); depth.pop();
|
1675
|
+
|
1676
|
+
return out;
|
1677
|
+
};
|
1678
|
+
|
1427
1679
|
const getNearestLoop = () => {
|
1428
1680
|
for (let i = depth.length - 1; i >= 0; i--) {
|
1429
|
-
if (depth[i] === 'while' || depth[i] === 'for') return i;
|
1681
|
+
if (depth[i] === 'while' || depth[i] === 'for' || depth[i] === 'forof') return i;
|
1430
1682
|
}
|
1431
1683
|
|
1432
1684
|
return -1;
|
@@ -1510,13 +1762,22 @@ const generateAssignPat = (scope, decl) => {
|
|
1510
1762
|
};
|
1511
1763
|
|
1512
1764
|
let pages = new Map();
|
1513
|
-
const allocPage = reason => {
|
1514
|
-
if (pages.has(reason)) return pages.get(reason);
|
1765
|
+
const allocPage = (reason, type) => {
|
1766
|
+
if (pages.has(reason)) return pages.get(reason).ind;
|
1767
|
+
|
1768
|
+
const ind = pages.size;
|
1769
|
+
pages.set(reason, { ind, type });
|
1770
|
+
|
1771
|
+
if (allocLog) log('alloc', `allocated new page of memory (${ind}) | ${reason} (type: ${type})`);
|
1772
|
+
|
1773
|
+
return ind;
|
1774
|
+
};
|
1515
1775
|
|
1516
|
-
|
1517
|
-
pages.
|
1776
|
+
const freePage = reason => {
|
1777
|
+
const { ind } = pages.get(reason);
|
1778
|
+
pages.delete(reason);
|
1518
1779
|
|
1519
|
-
if (
|
1780
|
+
if (allocLog) log('alloc', `freed page of memory (${ind}) | ${reason}`);
|
1520
1781
|
|
1521
1782
|
return ind;
|
1522
1783
|
};
|
@@ -1545,7 +1806,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
1545
1806
|
if (!arrays.has(name) || name === '$undeclared') {
|
1546
1807
|
// todo: can we just have 1 undeclared array? probably not? but this is not really memory efficient
|
1547
1808
|
const uniqueName = name === '$undeclared' ? name + Math.random().toString().slice(2) : name;
|
1548
|
-
arrays.set(name, allocPage(`${itemType === 'i16' ? 'string' : 'array'}: ${uniqueName}
|
1809
|
+
arrays.set(name, allocPage(`${itemType === 'i16' ? 'string' : 'array'}: ${uniqueName}`, itemType) * pageSize);
|
1549
1810
|
}
|
1550
1811
|
|
1551
1812
|
const pointer = arrays.get(name);
|
@@ -1578,8 +1839,6 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
1578
1839
|
// local value as pointer
|
1579
1840
|
out.push(...number(pointer));
|
1580
1841
|
|
1581
|
-
scope.memory = true;
|
1582
|
-
|
1583
1842
|
return [ out, pointer ];
|
1584
1843
|
};
|
1585
1844
|
|
@@ -1598,8 +1857,6 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
1598
1857
|
const name = decl.object.name;
|
1599
1858
|
const pointer = arrays.get(name);
|
1600
1859
|
|
1601
|
-
scope.memory = true;
|
1602
|
-
|
1603
1860
|
const aotPointer = pointer != null;
|
1604
1861
|
|
1605
1862
|
return [
|
@@ -1619,8 +1876,6 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
1619
1876
|
const name = decl.object.name;
|
1620
1877
|
const pointer = arrays.get(name);
|
1621
1878
|
|
1622
|
-
scope.memory = true;
|
1623
|
-
|
1624
1879
|
const aotPointer = pointer != null;
|
1625
1880
|
|
1626
1881
|
if (type === TYPES._array) {
|
@@ -1730,7 +1985,7 @@ const generateFunc = (scope, decl) => {
|
|
1730
1985
|
locals: {},
|
1731
1986
|
localInd: 0,
|
1732
1987
|
returns: [ valtypeBinary ],
|
1733
|
-
|
1988
|
+
returnType: null,
|
1734
1989
|
throws: false,
|
1735
1990
|
name
|
1736
1991
|
};
|
@@ -1754,9 +2009,8 @@ const generateFunc = (scope, decl) => {
|
|
1754
2009
|
name,
|
1755
2010
|
params: Object.values(innerScope.locals).slice(0, params.length).map(x => x.type),
|
1756
2011
|
returns: innerScope.returns,
|
1757
|
-
returnType: innerScope.returnType
|
2012
|
+
returnType: innerScope.returnType,
|
1758
2013
|
locals: innerScope.locals,
|
1759
|
-
memory: innerScope.memory,
|
1760
2014
|
throws: innerScope.throws,
|
1761
2015
|
index: currentFuncIndex++
|
1762
2016
|
};
|
@@ -1771,6 +2025,8 @@ const generateFunc = (scope, decl) => {
|
|
1771
2025
|
|
1772
2026
|
if (name !== 'main' && func.returns.length !== 0 && wasm[wasm.length - 1]?.[0] !== Opcodes.return && countLeftover(wasm) === 0) {
|
1773
2027
|
wasm.push(...number(0), [ Opcodes.return ]);
|
2028
|
+
|
2029
|
+
if (func.returnType === null) func.returnType = TYPES.undefined;
|
1774
2030
|
}
|
1775
2031
|
|
1776
2032
|
// change v128 params into many <type> (i32x4 -> i32/etc) instead as unsupported param valtype
|
@@ -1781,9 +2037,7 @@ const generateFunc = (scope, decl) => {
|
|
1781
2037
|
if (local.type === Valtype.v128) {
|
1782
2038
|
vecParams++;
|
1783
2039
|
|
1784
|
-
/*
|
1785
|
-
|
1786
|
-
wasm.unshift( // add v128 load for param
|
2040
|
+
/* wasm.unshift( // add v128 load for param
|
1787
2041
|
[ Opcodes.i32_const, 0 ],
|
1788
2042
|
[ ...Opcodes.v128_load, 0, i * 16 ],
|
1789
2043
|
[ Opcodes.local_set, local.idx ]
|