porffor 0.0.0-8c0bdaa → 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 +346 -92
- 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/publish.js +3 -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 +8 -13
- 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,15 +336,13 @@ 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 pointer = arrays.get(name ?? '$undeclared');
|
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);
|
335
342
|
|
336
343
|
if (assign) {
|
344
|
+
const pointer = arrays.get(name ?? '$undeclared');
|
345
|
+
|
337
346
|
return [
|
338
347
|
// setup right
|
339
348
|
...right,
|
@@ -384,15 +393,12 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
384
393
|
|
385
394
|
const leftPointer = localTmp(scope, 'concat_left_pointer', Valtype.i32);
|
386
395
|
|
387
|
-
|
396
|
+
// alloc/assign array
|
397
|
+
const [ , pointer ] = makeArray(scope, {
|
388
398
|
rawElements: new Array(0)
|
389
399
|
}, global, name, true, 'i16');
|
390
400
|
|
391
401
|
return [
|
392
|
-
// setup new/out array
|
393
|
-
...newOut,
|
394
|
-
[ Opcodes.drop ],
|
395
|
-
|
396
402
|
// setup left
|
397
403
|
...left,
|
398
404
|
Opcodes.i32_to_u,
|
@@ -458,75 +464,220 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
458
464
|
];
|
459
465
|
};
|
460
466
|
|
461
|
-
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) => {
|
462
571
|
// arrays are always truthy
|
463
572
|
if (type === TYPES._array) return [
|
464
573
|
...wasm,
|
465
574
|
[ Opcodes.drop ],
|
466
|
-
number(
|
575
|
+
...number(1)
|
467
576
|
];
|
468
577
|
|
469
578
|
if (type === TYPES.string) {
|
470
|
-
// if "" (length = 0)
|
579
|
+
// if not "" (length = 0)
|
471
580
|
return [
|
472
581
|
// pointer
|
473
582
|
...wasm,
|
583
|
+
Opcodes.i32_to_u,
|
474
584
|
|
475
585
|
// get length
|
476
586
|
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
|
477
587
|
|
478
|
-
// if length
|
479
|
-
[ Opcodes.i32_eqz ],
|
588
|
+
// if length != 0
|
589
|
+
/* [ Opcodes.i32_eqz ],
|
590
|
+
[ Opcodes.i32_eqz ], */
|
480
591
|
Opcodes.i32_from_u
|
481
592
|
]
|
482
593
|
}
|
483
594
|
|
484
|
-
// if
|
595
|
+
// if != 0
|
485
596
|
return [
|
486
597
|
...wasm,
|
487
598
|
|
488
|
-
|
489
|
-
Opcodes.
|
599
|
+
/* Opcodes.eqz,
|
600
|
+
[ Opcodes.i32_eqz ],
|
601
|
+
Opcodes.i32_from */
|
490
602
|
];
|
491
603
|
};
|
492
604
|
|
493
|
-
const
|
605
|
+
const falsy = (scope, wasm, type) => {
|
494
606
|
// arrays are always truthy
|
495
607
|
if (type === TYPES._array) return [
|
496
608
|
...wasm,
|
497
609
|
[ Opcodes.drop ],
|
498
|
-
number(
|
610
|
+
...number(0)
|
499
611
|
];
|
500
612
|
|
501
613
|
if (type === TYPES.string) {
|
502
|
-
// if
|
614
|
+
// if "" (length = 0)
|
503
615
|
return [
|
504
616
|
// pointer
|
505
617
|
...wasm,
|
618
|
+
Opcodes.i32_to_u,
|
506
619
|
|
507
620
|
// get length
|
508
621
|
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
|
509
622
|
|
510
|
-
// if length
|
511
|
-
|
512
|
-
[ Opcodes.i32_eqz ], */
|
623
|
+
// if length == 0
|
624
|
+
[ Opcodes.i32_eqz ],
|
513
625
|
Opcodes.i32_from_u
|
514
626
|
]
|
515
627
|
}
|
516
628
|
|
517
|
-
// if
|
629
|
+
// if = 0
|
518
630
|
return [
|
519
631
|
...wasm,
|
520
632
|
|
521
|
-
|
522
|
-
|
523
|
-
Opcodes.i32_from */
|
633
|
+
...Opcodes.eqz,
|
634
|
+
Opcodes.i32_from_u
|
524
635
|
];
|
525
636
|
};
|
526
637
|
|
527
|
-
const
|
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)
|
658
|
+
];
|
659
|
+
};
|
660
|
+
|
661
|
+
const performOp = (scope, op, left, right, leftType, rightType, _global = false, _name = '$undeclared', assign = false) => {
|
528
662
|
if (op === '||' || op === '&&' || op === '??') {
|
529
|
-
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
|
+
];
|
530
681
|
}
|
531
682
|
|
532
683
|
if (leftType === TYPES.string || rightType === TYPES.string) {
|
@@ -539,8 +690,20 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
539
690
|
if (!['==', '===', '!=', '!==', '>', '>=', '<', '<='].includes(op)) return number(NaN);
|
540
691
|
|
541
692
|
// else leave bool ops
|
542
|
-
// todo: convert string to number if string and number
|
543
|
-
// todo: string
|
693
|
+
// todo: convert string to number if string and number/bool
|
694
|
+
// todo: string (>|>=|<|<=) string
|
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];
|
@@ -569,17 +732,21 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
569
732
|
];
|
570
733
|
};
|
571
734
|
|
735
|
+
let binaryExpDepth = 0;
|
572
736
|
const generateBinaryExp = (scope, decl, _global, _name) => {
|
737
|
+
binaryExpDepth++;
|
738
|
+
|
573
739
|
const out = [
|
574
740
|
...performOp(scope, decl.operator, generate(scope, decl.left), generate(scope, decl.right), getNodeType(scope, decl.left), getNodeType(scope, decl.right), _global, _name)
|
575
741
|
];
|
576
742
|
|
577
743
|
if (valtype !== 'i32' && ['==', '===', '!=', '!==', '>', '>=', '<', '<='].includes(decl.operator)) out.push(Opcodes.i32_from_u);
|
578
744
|
|
745
|
+
binaryExpDepth--;
|
579
746
|
return out;
|
580
747
|
};
|
581
748
|
|
582
|
-
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 = [] }) => {
|
583
750
|
const existing = funcs.find(x => x.name === name);
|
584
751
|
if (existing) return existing;
|
585
752
|
|
@@ -615,7 +782,6 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
|
|
615
782
|
returns,
|
616
783
|
returnType: TYPES[returnType ?? 'number'],
|
617
784
|
wasm,
|
618
|
-
memory,
|
619
785
|
internal: true,
|
620
786
|
index: currentFuncIndex++
|
621
787
|
};
|
@@ -634,7 +800,7 @@ const includeBuiltin = (scope, builtin) => {
|
|
634
800
|
};
|
635
801
|
|
636
802
|
const generateLogicExp = (scope, decl) => {
|
637
|
-
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));
|
638
804
|
};
|
639
805
|
|
640
806
|
const TYPES = {
|
@@ -648,7 +814,8 @@ const TYPES = {
|
|
648
814
|
bigint: 0xffffffffffff7,
|
649
815
|
|
650
816
|
// these are not "typeof" types but tracked internally
|
651
|
-
_array:
|
817
|
+
_array: 0xfffffffffff0f,
|
818
|
+
_regexp: 0xfffffffffff1f
|
652
819
|
};
|
653
820
|
|
654
821
|
const TYPE_NAMES = {
|
@@ -682,6 +849,9 @@ const getType = (scope, _name) => {
|
|
682
849
|
|
683
850
|
const getNodeType = (scope, node) => {
|
684
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
|
+
|
685
855
|
return TYPES[typeof node.value];
|
686
856
|
}
|
687
857
|
|
@@ -696,7 +866,7 @@ const getNodeType = (scope, node) => {
|
|
696
866
|
if (node.type === 'CallExpression' || node.type === 'NewExpression') {
|
697
867
|
const name = node.callee.name;
|
698
868
|
const func = funcs.find(x => x.name === name);
|
699
|
-
if (func) return func.returnType
|
869
|
+
if (func) return func.returnType;
|
700
870
|
|
701
871
|
if (builtinFuncs[name]) return TYPES[builtinFuncs[name].returnType ?? 'number'];
|
702
872
|
if (internalConstrs[name]) return internalConstrs[name].type;
|
@@ -715,15 +885,18 @@ const getNodeType = (scope, node) => {
|
|
715
885
|
|
716
886
|
// literal.func()
|
717
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
|
+
|
718
893
|
const baseType = getNodeType(scope, node.callee.object);
|
719
894
|
|
720
895
|
const func = node.callee.property.name;
|
721
896
|
protoFunc = prototypeFuncs[baseType]?.[func];
|
722
897
|
}
|
723
898
|
|
724
|
-
if (protoFunc) return protoFunc.returnType
|
725
|
-
|
726
|
-
return TYPES.number;
|
899
|
+
if (protoFunc) return protoFunc.returnType;
|
727
900
|
}
|
728
901
|
|
729
902
|
if (node.type === 'ExpressionStatement') {
|
@@ -755,14 +928,16 @@ const getNodeType = (scope, node) => {
|
|
755
928
|
|
756
929
|
if (objectType === TYPES.string && node.computed) return TYPES.string;
|
757
930
|
}
|
758
|
-
|
759
|
-
// default to number
|
760
|
-
return TYPES.number;
|
761
931
|
};
|
762
932
|
|
763
933
|
const generateLiteral = (scope, decl, global, name) => {
|
764
934
|
if (decl.value === null) return number(NULL);
|
765
935
|
|
936
|
+
if (decl.regex) {
|
937
|
+
scope.regex[name] = decl.regex;
|
938
|
+
return number(1);
|
939
|
+
}
|
940
|
+
|
766
941
|
switch (typeof decl.value) {
|
767
942
|
case 'number':
|
768
943
|
return number(decl.value);
|
@@ -792,7 +967,7 @@ const generateLiteral = (scope, decl, global, name) => {
|
|
792
967
|
|
793
968
|
return makeArray(scope, {
|
794
969
|
rawElements
|
795
|
-
}, global, name, false, 'i16');
|
970
|
+
}, global, name, false, 'i16')[0];
|
796
971
|
|
797
972
|
default:
|
798
973
|
return todo(`cannot generate literal of type ${typeof decl.value}`);
|
@@ -802,7 +977,8 @@ const generateLiteral = (scope, decl, global, name) => {
|
|
802
977
|
const countLeftover = wasm => {
|
803
978
|
let count = 0, depth = 0;
|
804
979
|
|
805
|
-
for (
|
980
|
+
for (let i = 0; i < wasm.length; i++) {
|
981
|
+
const inst = wasm[i];
|
806
982
|
if (depth === 0 && (inst[0] === Opcodes.if || inst[0] === Opcodes.block || inst[0] === Opcodes.loop)) {
|
807
983
|
if (inst[0] === Opcodes.if) count--;
|
808
984
|
if (inst[1] !== Blocktype.void) count++;
|
@@ -906,7 +1082,7 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
906
1082
|
}
|
907
1083
|
|
908
1084
|
let out = [];
|
909
|
-
let protoFunc, protoName, baseType, baseName
|
1085
|
+
let protoFunc, protoName, baseType, baseName;
|
910
1086
|
// ident.func()
|
911
1087
|
if (name && name.startsWith('__')) {
|
912
1088
|
const spl = name.slice(2).split('_');
|
@@ -921,6 +1097,25 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
921
1097
|
|
922
1098
|
// literal.func()
|
923
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
|
+
|
924
1119
|
baseType = getNodeType(scope, decl.callee.object);
|
925
1120
|
|
926
1121
|
const func = decl.callee.property.name;
|
@@ -929,11 +1124,36 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
929
1124
|
|
930
1125
|
out = generate(scope, decl.callee.object);
|
931
1126
|
out.push([ Opcodes.drop ]);
|
1127
|
+
|
1128
|
+
baseName = [...arrays.keys()].pop();
|
932
1129
|
}
|
933
1130
|
|
934
|
-
if (
|
935
|
-
|
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);
|
936
1136
|
|
1137
|
+
const pointer = arrays.get(baseName);
|
1138
|
+
const [ local, isGlobal ] = lookupName(scope, baseName);
|
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) {
|
937
1157
|
let pointer = arrays.get(baseName);
|
938
1158
|
|
939
1159
|
if (pointer == null) {
|
@@ -941,10 +1161,9 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
941
1161
|
if (codeLog) log('codegen', 'cloning unknown dynamic pointer');
|
942
1162
|
|
943
1163
|
// register array
|
944
|
-
makeArray(scope, {
|
1164
|
+
0, [ , pointer ] = makeArray(scope, {
|
945
1165
|
rawElements: new Array(0)
|
946
1166
|
}, _global, baseName, true, baseType === TYPES.string ? 'i16' : valtype);
|
947
|
-
pointer = arrays.get(baseName);
|
948
1167
|
|
949
1168
|
const [ local, isGlobal ] = lookupName(scope, baseName);
|
950
1169
|
|
@@ -980,10 +1199,9 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
980
1199
|
set: value => arrayUtil.setLength(pointer, value),
|
981
1200
|
setI32: value => arrayUtil.setLengthI32(pointer, value)
|
982
1201
|
}, generate(scope, decl.arguments[0] ?? DEFAULT_VALUE), protoLocal, (length, itemType) => {
|
983
|
-
|
1202
|
+
return makeArray(scope, {
|
984
1203
|
rawElements: new Array(length)
|
985
1204
|
}, _global, _name, true, itemType);
|
986
|
-
return [ out, arrays.get(_name ?? '$undeclared') ];
|
987
1205
|
}),
|
988
1206
|
[ Opcodes.end ]
|
989
1207
|
];
|
@@ -1041,7 +1259,6 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1041
1259
|
args = args.slice(0, func.params.length);
|
1042
1260
|
}
|
1043
1261
|
|
1044
|
-
if (func && func.memory) scope.memory = true;
|
1045
1262
|
if (func && func.throws) scope.throws = true;
|
1046
1263
|
|
1047
1264
|
for (const arg of args) {
|
@@ -1057,7 +1274,7 @@ const generateNew = (scope, decl, _global, _name) => {
|
|
1057
1274
|
// hack: basically treat this as a normal call for builtins for now
|
1058
1275
|
const name = mapName(decl.callee.name);
|
1059
1276
|
if (internalConstrs[name]) return internalConstrs[name].generate(scope, decl, _global, _name);
|
1060
|
-
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)})`);
|
1061
1278
|
|
1062
1279
|
return generateCall(scope, decl, _global, _name);
|
1063
1280
|
};
|
@@ -1165,8 +1382,6 @@ const generateAssign = (scope, decl) => {
|
|
1165
1382
|
const name = decl.left.object.name;
|
1166
1383
|
const pointer = arrays.get(name);
|
1167
1384
|
|
1168
|
-
scope.memory = true;
|
1169
|
-
|
1170
1385
|
const aotPointer = pointer != null;
|
1171
1386
|
|
1172
1387
|
const newValueTmp = localTmp(scope, '__length_setter_tmp');
|
@@ -1217,8 +1432,27 @@ const generateAssign = (scope, decl) => {
|
|
1217
1432
|
];
|
1218
1433
|
}
|
1219
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
|
+
|
1220
1454
|
return [
|
1221
|
-
...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),
|
1222
1456
|
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
|
1223
1457
|
[ isGlobal ? Opcodes.global_get : Opcodes.local_get, local.idx ]
|
1224
1458
|
];
|
@@ -1245,13 +1479,14 @@ const generateUnary = (scope, decl) => {
|
|
1245
1479
|
|
1246
1480
|
case '!':
|
1247
1481
|
// !=
|
1248
|
-
return falsy(scope, generate(scope, decl.argument));
|
1482
|
+
return falsy(scope, generate(scope, decl.argument), getNodeType(scope, decl.argument));
|
1249
1483
|
|
1250
1484
|
case '~':
|
1485
|
+
// todo: does not handle Infinity properly (should convert to 0) (but opt const converting saves us sometimes)
|
1251
1486
|
return [
|
1252
1487
|
...generate(scope, decl.argument),
|
1253
1488
|
Opcodes.i32_to,
|
1254
|
-
[ Opcodes.i32_const, signedLEB128(-1) ],
|
1489
|
+
[ Opcodes.i32_const, ...signedLEB128(-1) ],
|
1255
1490
|
[ Opcodes.i32_xor ],
|
1256
1491
|
Opcodes.i32_from
|
1257
1492
|
];
|
@@ -1289,7 +1524,7 @@ const generateUnary = (scope, decl) => {
|
|
1289
1524
|
return out;
|
1290
1525
|
|
1291
1526
|
case 'typeof':
|
1292
|
-
const type = getNodeType(scope, decl.argument);
|
1527
|
+
const type = getNodeType(scope, decl.argument) ?? TYPES.number;
|
1293
1528
|
|
1294
1529
|
// for custom types, just return object
|
1295
1530
|
if (type > 0xffffffffffff7) return number(TYPES.object);
|
@@ -1422,9 +1657,28 @@ const generateWhile = (scope, decl) => {
|
|
1422
1657
|
return out;
|
1423
1658
|
};
|
1424
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
|
+
|
1425
1679
|
const getNearestLoop = () => {
|
1426
1680
|
for (let i = depth.length - 1; i >= 0; i--) {
|
1427
|
-
if (depth[i] === 'while' || depth[i] === 'for') return i;
|
1681
|
+
if (depth[i] === 'while' || depth[i] === 'for' || depth[i] === 'forof') return i;
|
1428
1682
|
}
|
1429
1683
|
|
1430
1684
|
return -1;
|
@@ -1508,13 +1762,22 @@ const generateAssignPat = (scope, decl) => {
|
|
1508
1762
|
};
|
1509
1763
|
|
1510
1764
|
let pages = new Map();
|
1511
|
-
const allocPage = reason => {
|
1512
|
-
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
|
+
};
|
1513
1775
|
|
1514
|
-
|
1515
|
-
pages.
|
1776
|
+
const freePage = reason => {
|
1777
|
+
const { ind } = pages.get(reason);
|
1778
|
+
pages.delete(reason);
|
1516
1779
|
|
1517
|
-
if (
|
1780
|
+
if (allocLog) log('alloc', `freed page of memory (${ind}) | ${reason}`);
|
1518
1781
|
|
1519
1782
|
return ind;
|
1520
1783
|
};
|
@@ -1543,7 +1806,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
1543
1806
|
if (!arrays.has(name) || name === '$undeclared') {
|
1544
1807
|
// todo: can we just have 1 undeclared array? probably not? but this is not really memory efficient
|
1545
1808
|
const uniqueName = name === '$undeclared' ? name + Math.random().toString().slice(2) : name;
|
1546
|
-
arrays.set(name, allocPage(`${itemType === 'i16' ? 'string' : 'array'}: ${uniqueName}
|
1809
|
+
arrays.set(name, allocPage(`${itemType === 'i16' ? 'string' : 'array'}: ${uniqueName}`, itemType) * pageSize);
|
1547
1810
|
}
|
1548
1811
|
|
1549
1812
|
const pointer = arrays.get(name);
|
@@ -1576,14 +1839,12 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
1576
1839
|
// local value as pointer
|
1577
1840
|
out.push(...number(pointer));
|
1578
1841
|
|
1579
|
-
|
1580
|
-
|
1581
|
-
return out;
|
1842
|
+
return [ out, pointer ];
|
1582
1843
|
};
|
1583
1844
|
|
1584
1845
|
let arrays = new Map();
|
1585
1846
|
const generateArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false) => {
|
1586
|
-
return makeArray(scope, decl, global, name, initEmpty, valtype);
|
1847
|
+
return makeArray(scope, decl, global, name, initEmpty, valtype)[0];
|
1587
1848
|
};
|
1588
1849
|
|
1589
1850
|
export const generateMember = (scope, decl, _global, _name) => {
|
@@ -1596,8 +1857,6 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
1596
1857
|
const name = decl.object.name;
|
1597
1858
|
const pointer = arrays.get(name);
|
1598
1859
|
|
1599
|
-
scope.memory = true;
|
1600
|
-
|
1601
1860
|
const aotPointer = pointer != null;
|
1602
1861
|
|
1603
1862
|
return [
|
@@ -1617,8 +1876,6 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
1617
1876
|
const name = decl.object.name;
|
1618
1877
|
const pointer = arrays.get(name);
|
1619
1878
|
|
1620
|
-
scope.memory = true;
|
1621
|
-
|
1622
1879
|
const aotPointer = pointer != null;
|
1623
1880
|
|
1624
1881
|
if (type === TYPES._array) {
|
@@ -1644,10 +1901,9 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
1644
1901
|
|
1645
1902
|
// string
|
1646
1903
|
|
1647
|
-
const newOut = makeArray(scope, {
|
1904
|
+
const [ newOut, newPointer ] = makeArray(scope, {
|
1648
1905
|
rawElements: new Array(1)
|
1649
1906
|
}, _global, _name, true, 'i16');
|
1650
|
-
const newPointer = arrays.get(_name ?? '$undeclared');
|
1651
1907
|
|
1652
1908
|
return [
|
1653
1909
|
// setup new/out array
|
@@ -1729,7 +1985,7 @@ const generateFunc = (scope, decl) => {
|
|
1729
1985
|
locals: {},
|
1730
1986
|
localInd: 0,
|
1731
1987
|
returns: [ valtypeBinary ],
|
1732
|
-
|
1988
|
+
returnType: null,
|
1733
1989
|
throws: false,
|
1734
1990
|
name
|
1735
1991
|
};
|
@@ -1753,9 +2009,8 @@ const generateFunc = (scope, decl) => {
|
|
1753
2009
|
name,
|
1754
2010
|
params: Object.values(innerScope.locals).slice(0, params.length).map(x => x.type),
|
1755
2011
|
returns: innerScope.returns,
|
1756
|
-
returnType: innerScope.returnType
|
2012
|
+
returnType: innerScope.returnType,
|
1757
2013
|
locals: innerScope.locals,
|
1758
|
-
memory: innerScope.memory,
|
1759
2014
|
throws: innerScope.throws,
|
1760
2015
|
index: currentFuncIndex++
|
1761
2016
|
};
|
@@ -1770,6 +2025,8 @@ const generateFunc = (scope, decl) => {
|
|
1770
2025
|
|
1771
2026
|
if (name !== 'main' && func.returns.length !== 0 && wasm[wasm.length - 1]?.[0] !== Opcodes.return && countLeftover(wasm) === 0) {
|
1772
2027
|
wasm.push(...number(0), [ Opcodes.return ]);
|
2028
|
+
|
2029
|
+
if (func.returnType === null) func.returnType = TYPES.undefined;
|
1773
2030
|
}
|
1774
2031
|
|
1775
2032
|
// change v128 params into many <type> (i32x4 -> i32/etc) instead as unsupported param valtype
|
@@ -1780,9 +2037,7 @@ const generateFunc = (scope, decl) => {
|
|
1780
2037
|
if (local.type === Valtype.v128) {
|
1781
2038
|
vecParams++;
|
1782
2039
|
|
1783
|
-
/*
|
1784
|
-
|
1785
|
-
wasm.unshift( // add v128 load for param
|
2040
|
+
/* wasm.unshift( // add v128 load for param
|
1786
2041
|
[ Opcodes.i32_const, 0 ],
|
1787
2042
|
[ ...Opcodes.v128_load, 0, i * 16 ],
|
1788
2043
|
[ Opcodes.local_set, local.idx ]
|
@@ -1912,10 +2167,9 @@ const internalConstrs = {
|
|
1912
2167
|
|
1913
2168
|
// new Array(n)
|
1914
2169
|
|
1915
|
-
makeArray(scope, {
|
2170
|
+
const [ , pointer ] = makeArray(scope, {
|
1916
2171
|
rawElements: new Array(0)
|
1917
2172
|
}, global, name, true);
|
1918
|
-
const pointer = arrays.get(name ?? '$undeclared');
|
1919
2173
|
|
1920
2174
|
const arg = decl.arguments[0] ?? DEFAULT_VALUE;
|
1921
2175
|
|