porffor 0.18.13 → 0.18.14
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/compiler/builtins/array.ts +2 -2
- package/compiler/builtins/base64.ts +2 -2
- package/compiler/builtins/boolean.ts +1 -1
- package/compiler/builtins/date.ts +10 -29
- package/compiler/builtins/escape.ts +2 -2
- package/compiler/builtins/number.ts +3 -3
- package/compiler/builtins/string.ts +22 -22
- package/compiler/builtins/symbol.ts +1 -1
- package/compiler/builtins/z_ecma262.ts +1 -1
- package/compiler/builtins.js +61 -4
- package/compiler/codegen.js +221 -114
- package/compiler/generated_builtins.js +452 -452
- package/compiler/types.js +14 -13
- package/compiler/wrap.js +7 -9
- package/package.json +1 -1
- package/runner/index.js +1 -1
package/compiler/codegen.js
CHANGED
@@ -416,7 +416,7 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
416
416
|
];
|
417
417
|
};
|
418
418
|
|
419
|
-
const concatStrings = (scope, left, right,
|
419
|
+
const concatStrings = (scope, left, right, leftType, rightType, allBytestrings = false, skipTypeCheck = false) => {
|
420
420
|
// todo: this should be rewritten into a built-in/func: String.prototype.concat
|
421
421
|
// todo: convert left and right to strings if not
|
422
422
|
// todo: optimize by looking up names in arrays and using that if exists?
|
@@ -429,30 +429,42 @@ const concatStrings = (scope, left, right, global, name, assign = false, bytestr
|
|
429
429
|
const leftPointer = localTmp(scope, 'concat_left_pointer', Valtype.i32);
|
430
430
|
|
431
431
|
// alloc/assign array
|
432
|
-
const
|
433
|
-
rawElements: new Array(0)
|
434
|
-
}, assign ? false : global, assign ? undefined : name, true, 'i16', true);
|
432
|
+
const out = localTmp(scope, 'concat_out_pointer', Valtype.i32);
|
435
433
|
|
434
|
+
if (!skipTypeCheck && !allBytestrings) includeBuiltin(scope, '__Porffor_bytestringToString');
|
435
|
+
includeBuiltin(scope, '__Porffor_print');
|
436
436
|
return [
|
437
|
-
// setup
|
438
|
-
...left
|
439
|
-
|
440
|
-
|
437
|
+
// setup pointers
|
438
|
+
...(left.length === 0 ? [
|
439
|
+
Opcodes.i32_to_u,
|
440
|
+
[ Opcodes.local_set, rightPointer ],
|
441
441
|
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
442
|
+
Opcodes.i32_to_u,
|
443
|
+
[ Opcodes.local_set, leftPointer ],
|
444
|
+
] : [
|
445
|
+
...left,
|
446
|
+
Opcodes.i32_to_u,
|
447
|
+
[ Opcodes.local_set, leftPointer ],
|
446
448
|
|
447
|
-
|
448
|
-
|
449
|
+
...right,
|
450
|
+
Opcodes.i32_to_u,
|
451
|
+
[ Opcodes.local_set, rightPointer ],
|
452
|
+
]),
|
449
453
|
|
454
|
+
// setup out
|
455
|
+
[ Opcodes.i32_const, 1 ],
|
456
|
+
[ Opcodes.memory_grow, 0 ],
|
457
|
+
[ Opcodes.i32_const, ...signedLEB128(65536) ],
|
458
|
+
[ Opcodes.i32_mul ],
|
459
|
+
[ Opcodes.local_tee, out ],
|
460
|
+
|
461
|
+
// calculate length
|
450
462
|
[ Opcodes.local_get, leftPointer ],
|
451
|
-
[ Opcodes.i32_load, 0,
|
463
|
+
[ Opcodes.i32_load, 0, 0 ],
|
452
464
|
[ Opcodes.local_tee, leftLength ],
|
453
465
|
|
454
466
|
[ Opcodes.local_get, rightPointer ],
|
455
|
-
[ Opcodes.i32_load, 0,
|
467
|
+
[ Opcodes.i32_load, 0, 0 ],
|
456
468
|
[ Opcodes.local_tee, rightLength ],
|
457
469
|
|
458
470
|
[ Opcodes.i32_add ],
|
@@ -460,9 +472,31 @@ const concatStrings = (scope, left, right, global, name, assign = false, bytestr
|
|
460
472
|
// store length
|
461
473
|
[ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, 0 ],
|
462
474
|
|
475
|
+
...(skipTypeCheck || allBytestrings ? [] : [
|
476
|
+
...leftType,
|
477
|
+
...number(TYPES.bytestring, Valtype.i32),
|
478
|
+
[ Opcodes.i32_eq ],
|
479
|
+
[ Opcodes.if, Blocktype.void ],
|
480
|
+
[ Opcodes.local_get, leftPointer ],
|
481
|
+
[ Opcodes.local_get, leftLength ],
|
482
|
+
[ Opcodes.call, funcIndex.__Porffor_bytestringToString ],
|
483
|
+
[ Opcodes.local_set, leftPointer ],
|
484
|
+
[ Opcodes.end ],
|
485
|
+
|
486
|
+
...rightType,
|
487
|
+
...number(TYPES.bytestring, Valtype.i32),
|
488
|
+
[ Opcodes.i32_eq ],
|
489
|
+
[ Opcodes.if, Blocktype.void ],
|
490
|
+
[ Opcodes.local_get, rightPointer ],
|
491
|
+
[ Opcodes.local_get, rightLength ],
|
492
|
+
[ Opcodes.call, funcIndex.__Porffor_bytestringToString ],
|
493
|
+
[ Opcodes.local_set, rightPointer ],
|
494
|
+
[ Opcodes.end ]
|
495
|
+
]),
|
496
|
+
|
463
497
|
// copy left
|
464
498
|
// dst = out pointer + length size
|
465
|
-
|
499
|
+
[ Opcodes.local_get, out ],
|
466
500
|
...number(ValtypeSize.i32, Valtype.i32),
|
467
501
|
[ Opcodes.i32_add ],
|
468
502
|
|
@@ -477,12 +511,12 @@ const concatStrings = (scope, left, right, global, name, assign = false, bytestr
|
|
477
511
|
|
478
512
|
// copy right
|
479
513
|
// dst = out pointer + length size + left length * sizeof valtype
|
480
|
-
|
514
|
+
[ Opcodes.local_get, out ],
|
481
515
|
...number(ValtypeSize.i32, Valtype.i32),
|
482
516
|
[ Opcodes.i32_add ],
|
483
517
|
|
484
518
|
[ Opcodes.local_get, leftLength ],
|
485
|
-
...number(
|
519
|
+
...number(allBytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
|
486
520
|
[ Opcodes.i32_mul ],
|
487
521
|
[ Opcodes.i32_add ],
|
488
522
|
|
@@ -493,18 +527,20 @@ const concatStrings = (scope, left, right, global, name, assign = false, bytestr
|
|
493
527
|
|
494
528
|
// size = right length * sizeof valtype
|
495
529
|
[ Opcodes.local_get, rightLength ],
|
496
|
-
...number(
|
530
|
+
...number(allBytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
|
497
531
|
[ Opcodes.i32_mul ],
|
498
532
|
|
499
533
|
[ ...Opcodes.memory_copy, 0x00, 0x00 ],
|
500
534
|
|
535
|
+
...setLastType(scope, allBytestrings ? TYPES.bytestring : TYPES.string),
|
536
|
+
|
501
537
|
// return new string (page)
|
502
|
-
|
538
|
+
[ Opcodes.local_get, out ],
|
503
539
|
Opcodes.i32_from_u
|
504
540
|
];
|
505
541
|
};
|
506
542
|
|
507
|
-
const compareStrings = (scope, left, right,
|
543
|
+
const compareStrings = (scope, left, right, leftType, rightType, allBytestrings = false, skipTypeCheck = false) => {
|
508
544
|
// todo: this should be rewritten into a func
|
509
545
|
// todo: convert left and right to strings if not
|
510
546
|
// todo: optimize by looking up names in arrays and using that if exists?
|
@@ -517,16 +553,28 @@ const compareStrings = (scope, left, right, bytestrings = false) => {
|
|
517
553
|
const index = localTmp(scope, 'compare_index', Valtype.i32);
|
518
554
|
const indexEnd = localTmp(scope, 'compare_index_end', Valtype.i32);
|
519
555
|
|
556
|
+
if (!skipTypeCheck && !allBytestrings) includeBuiltin(scope, '__Porffor_bytestringToString');
|
557
|
+
includeBuiltin(scope, '__Porffor_print');
|
520
558
|
return [
|
521
|
-
// setup
|
522
|
-
...left
|
523
|
-
|
524
|
-
|
559
|
+
// setup pointers
|
560
|
+
...(left.length === 0 ? [
|
561
|
+
Opcodes.i32_to_u,
|
562
|
+
[ Opcodes.local_set, rightPointer ],
|
525
563
|
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
564
|
+
Opcodes.i32_to_u,
|
565
|
+
[ Opcodes.local_set, leftPointer ],
|
566
|
+
|
567
|
+
[ Opcodes.local_get, leftPointer ],
|
568
|
+
[ Opcodes.local_get, rightPointer ],
|
569
|
+
] : [
|
570
|
+
...left,
|
571
|
+
Opcodes.i32_to_u,
|
572
|
+
[ Opcodes.local_tee, leftPointer ],
|
573
|
+
|
574
|
+
...right,
|
575
|
+
Opcodes.i32_to_u,
|
576
|
+
[ Opcodes.local_tee, rightPointer ],
|
577
|
+
]),
|
530
578
|
|
531
579
|
// fast path: check leftPointer == rightPointer
|
532
580
|
// use if (block) for everything after to "return" a value early
|
@@ -535,11 +583,34 @@ const compareStrings = (scope, left, right, bytestrings = false) => {
|
|
535
583
|
|
536
584
|
// get lengths
|
537
585
|
[ Opcodes.local_get, leftPointer ],
|
538
|
-
[ Opcodes.i32_load, 0,
|
586
|
+
[ Opcodes.i32_load, 0, 0 ],
|
539
587
|
[ Opcodes.local_tee, leftLength ],
|
540
588
|
|
541
589
|
[ Opcodes.local_get, rightPointer ],
|
542
|
-
[ Opcodes.i32_load, 0,
|
590
|
+
[ Opcodes.i32_load, 0, 0 ],
|
591
|
+
|
592
|
+
...(skipTypeCheck || allBytestrings ? [] : [
|
593
|
+
...leftType,
|
594
|
+
...number(TYPES.bytestring, Valtype.i32),
|
595
|
+
[ Opcodes.i32_eq ],
|
596
|
+
[ Opcodes.if, Blocktype.void ],
|
597
|
+
[ Opcodes.local_get, leftPointer ],
|
598
|
+
[ Opcodes.local_get, leftLength ],
|
599
|
+
[ Opcodes.call, funcIndex.__Porffor_bytestringToString ],
|
600
|
+
[ Opcodes.local_set, leftPointer ],
|
601
|
+
[ Opcodes.end ],
|
602
|
+
|
603
|
+
...rightType,
|
604
|
+
...number(TYPES.bytestring, Valtype.i32),
|
605
|
+
[ Opcodes.i32_eq ],
|
606
|
+
[ Opcodes.if, Blocktype.void ],
|
607
|
+
[ Opcodes.local_get, rightPointer ],
|
608
|
+
[ Opcodes.local_get, rightPointer ],
|
609
|
+
[ Opcodes.i32_load, 0, 0 ],
|
610
|
+
[ Opcodes.call, funcIndex.__Porffor_bytestringToString ],
|
611
|
+
[ Opcodes.local_set, rightPointer ],
|
612
|
+
[ Opcodes.end ]
|
613
|
+
]),
|
543
614
|
|
544
615
|
// fast path: check leftLength != rightLength
|
545
616
|
[ Opcodes.i32_ne ],
|
@@ -557,7 +628,7 @@ const compareStrings = (scope, left, right, bytestrings = false) => {
|
|
557
628
|
// setup index end as length * sizeof valtype (1 for bytestring, 2 for string)
|
558
629
|
// we do this instead of having to do mul/div each iter for perf™
|
559
630
|
[ Opcodes.local_get, leftLength ],
|
560
|
-
...(
|
631
|
+
...(allBytestrings ? [] : [
|
561
632
|
...number(ValtypeSize.i16, Valtype.i32),
|
562
633
|
[ Opcodes.i32_mul ],
|
563
634
|
]),
|
@@ -570,7 +641,7 @@ const compareStrings = (scope, left, right, bytestrings = false) => {
|
|
570
641
|
[ Opcodes.local_get, index ],
|
571
642
|
[ Opcodes.local_get, leftPointer ],
|
572
643
|
[ Opcodes.i32_add ],
|
573
|
-
|
644
|
+
allBytestrings ?
|
574
645
|
[ Opcodes.i32_load8_u, 0, ValtypeSize.i32 ] :
|
575
646
|
[ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ValtypeSize.i32 ],
|
576
647
|
|
@@ -578,7 +649,7 @@ const compareStrings = (scope, left, right, bytestrings = false) => {
|
|
578
649
|
[ Opcodes.local_get, index ],
|
579
650
|
[ Opcodes.local_get, rightPointer ],
|
580
651
|
[ Opcodes.i32_add ],
|
581
|
-
|
652
|
+
allBytestrings ?
|
582
653
|
[ Opcodes.i32_load8_u, 0, ValtypeSize.i32 ] :
|
583
654
|
[ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ValtypeSize.i32 ],
|
584
655
|
|
@@ -591,7 +662,7 @@ const compareStrings = (scope, left, right, bytestrings = false) => {
|
|
591
662
|
|
592
663
|
// index += sizeof valtype (1 for bytestring, 2 for string)
|
593
664
|
[ Opcodes.local_get, index ],
|
594
|
-
...number(
|
665
|
+
...number(allBytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
|
595
666
|
[ Opcodes.i32_add ],
|
596
667
|
[ Opcodes.local_tee, index ],
|
597
668
|
|
@@ -787,7 +858,11 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
787
858
|
if (strictOp) {
|
788
859
|
endOut.push(
|
789
860
|
...leftType,
|
861
|
+
...number(TYPE_FLAGS.parity, Valtype.i32),
|
862
|
+
[ Opcodes.i32_or ],
|
790
863
|
...rightType,
|
864
|
+
...number(TYPE_FLAGS.parity, Valtype.i32),
|
865
|
+
[ Opcodes.i32_or ],
|
791
866
|
...(op === '===' ? [
|
792
867
|
[ Opcodes.i32_eq ],
|
793
868
|
[ Opcodes.i32_and ]
|
@@ -803,9 +878,12 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
803
878
|
|
804
879
|
if (knownLeft === TYPES.string || knownRight === TYPES.string) {
|
805
880
|
if (op === '+') {
|
806
|
-
// todo: this should be dynamic too but for now only static
|
807
881
|
// string concat (a + b)
|
808
|
-
return
|
882
|
+
return [
|
883
|
+
...left,
|
884
|
+
...right,
|
885
|
+
...concatStrings(scope, [], [], leftType, rightType, false, knownLeft === TYPES.string && knownRight === TYPES.string)
|
886
|
+
];
|
809
887
|
}
|
810
888
|
|
811
889
|
// not an equality op, NaN
|
@@ -816,23 +894,24 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
816
894
|
// todo: string (>|>=|<|<=) string
|
817
895
|
|
818
896
|
// string comparison
|
819
|
-
if (op === '===' || op === '==') {
|
820
|
-
return compareStrings(scope, left, right);
|
821
|
-
}
|
822
|
-
|
823
|
-
if (op === '!==' || op === '!=') {
|
897
|
+
if (op === '===' || op === '==' || op === '!==' || op === '!=') {
|
824
898
|
return [
|
825
|
-
...
|
826
|
-
|
899
|
+
...left,
|
900
|
+
...right,
|
901
|
+
...compareStrings(scope, [], [], leftType, rightType, false, knownLeft === TYPES.string && knownRight === TYPES.string),
|
902
|
+
...(op === '!==' || op === '!=' ? [ [ Opcodes.i32_eqz ] ] : [])
|
827
903
|
];
|
828
904
|
}
|
829
905
|
}
|
830
906
|
|
831
907
|
if (knownLeft === TYPES.bytestring || knownRight === TYPES.bytestring) {
|
832
908
|
if (op === '+') {
|
833
|
-
// todo: this should be dynamic too but for now only static
|
834
909
|
// string concat (a + b)
|
835
|
-
return
|
910
|
+
return [
|
911
|
+
...left,
|
912
|
+
...right,
|
913
|
+
...concatStrings(scope, [], [], leftType, rightType, knownLeft === TYPES.bytestring && knownRight === TYPES.bytestring)
|
914
|
+
];
|
836
915
|
}
|
837
916
|
|
838
917
|
// not an equality op, NaN
|
@@ -843,14 +922,12 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
843
922
|
// todo: string (>|>=|<|<=) string
|
844
923
|
|
845
924
|
// string comparison
|
846
|
-
if (op === '===' || op === '==') {
|
847
|
-
return compareStrings(scope, left, right, true);
|
848
|
-
}
|
849
|
-
|
850
|
-
if (op === '!==' || op === '!=') {
|
925
|
+
if (op === '===' || op === '==' || op === '!==' || op === '!=') {
|
851
926
|
return [
|
852
|
-
...
|
853
|
-
|
927
|
+
...left,
|
928
|
+
...right,
|
929
|
+
...compareStrings(scope, [], [], leftType, rightType, knownLeft === TYPES.bytestring && knownRight === TYPES.bytestring),
|
930
|
+
...(op === '!==' || op === '!=' ? [ [ Opcodes.i32_eqz ] ] : [])
|
854
931
|
];
|
855
932
|
}
|
856
933
|
}
|
@@ -881,70 +958,63 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
881
958
|
// if neither known are string, stop this madness
|
882
959
|
// we already do known checks earlier, so don't need to recheck
|
883
960
|
|
884
|
-
if (
|
961
|
+
if (op === '+' && (knownLeft == null && knownRight == null)) {
|
885
962
|
tmpLeft = localTmp(scope, '__tmpop_left');
|
886
963
|
tmpRight = localTmp(scope, '__tmpop_right');
|
887
964
|
|
888
|
-
// returns false for one string, one not - but more ops/slower
|
889
|
-
// ops.unshift(...stringOnly([
|
890
|
-
// // if left is string
|
891
|
-
// ...leftType,
|
892
|
-
// ...number(TYPES.string, Valtype.i32),
|
893
|
-
// [ Opcodes.i32_eq ],
|
894
|
-
|
895
|
-
// // if right is string
|
896
|
-
// ...rightType,
|
897
|
-
// ...number(TYPES.string, Valtype.i32),
|
898
|
-
// [ Opcodes.i32_eq ],
|
899
|
-
|
900
|
-
// // if either are true
|
901
|
-
// [ Opcodes.i32_or ],
|
902
|
-
// [ Opcodes.if, Blocktype.void ],
|
903
|
-
|
904
|
-
// // todo: convert non-strings to strings, for now fail immediately if one is not
|
905
|
-
// // if left is not string
|
906
|
-
// ...leftType,
|
907
|
-
// ...number(TYPES.string, Valtype.i32),
|
908
|
-
// [ Opcodes.i32_ne ],
|
909
|
-
|
910
|
-
// // if right is not string
|
911
|
-
// ...rightType,
|
912
|
-
// ...number(TYPES.string, Valtype.i32),
|
913
|
-
// [ Opcodes.i32_ne ],
|
914
|
-
|
915
|
-
// // if either are true
|
916
|
-
// [ Opcodes.i32_or ],
|
917
|
-
// [ Opcodes.if, Blocktype.void ],
|
918
|
-
// ...number(0, Valtype.i32),
|
919
|
-
// [ Opcodes.br, 2 ],
|
920
|
-
// [ Opcodes.end ],
|
921
|
-
|
922
|
-
// ...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ]),
|
923
|
-
// ...(op === '!==' || op === '!=' ? [ [ Opcodes.i32_eqz ] ] : []),
|
924
|
-
// [ Opcodes.br, 1 ],
|
925
|
-
// [ Opcodes.end ],
|
926
|
-
// ]));
|
927
|
-
|
928
|
-
// does not handle one string, one not (such cases go past)
|
929
965
|
ops.unshift(...stringOnly([
|
930
|
-
// if left is
|
966
|
+
// if left is bytestring
|
931
967
|
...leftType,
|
932
|
-
...number(TYPES.
|
968
|
+
...number(TYPES.bytestring, Valtype.i32),
|
933
969
|
[ Opcodes.i32_eq ],
|
934
970
|
|
935
|
-
// if right is
|
971
|
+
// if right is bytestring
|
936
972
|
...rightType,
|
937
|
-
...number(TYPES.
|
973
|
+
...number(TYPES.bytestring, Valtype.i32),
|
938
974
|
[ Opcodes.i32_eq ],
|
939
975
|
|
940
976
|
// if both are true
|
941
977
|
[ Opcodes.i32_and ],
|
942
978
|
[ Opcodes.if, Blocktype.void ],
|
943
|
-
...
|
979
|
+
...concatStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ], leftType, rightType, true),
|
944
980
|
...(op === '!==' || op === '!=' ? [ [ Opcodes.i32_eqz ] ] : []),
|
945
981
|
[ Opcodes.br, 1 ],
|
946
982
|
[ Opcodes.end ],
|
947
983
|
|
984
|
+
// if left is string or bytestring
|
985
|
+
...leftType,
|
986
|
+
...number(TYPE_FLAGS.parity, Valtype.i32),
|
987
|
+
[ Opcodes.i32_or ],
|
988
|
+
...number(TYPES.bytestring, Valtype.i32),
|
989
|
+
[ Opcodes.i32_eq ],
|
990
|
+
|
991
|
+
// if right is string or bytestring
|
992
|
+
...rightType,
|
993
|
+
...number(TYPE_FLAGS.parity, Valtype.i32),
|
994
|
+
[ Opcodes.i32_or ],
|
995
|
+
...number(TYPES.bytestring, Valtype.i32),
|
996
|
+
[ Opcodes.i32_eq ],
|
997
|
+
|
998
|
+
// if either
|
999
|
+
[ Opcodes.i32_or ],
|
1000
|
+
[ Opcodes.if, Blocktype.void ],
|
1001
|
+
...concatStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ], leftType, rightType),
|
1002
|
+
[ Opcodes.br, 1 ],
|
1003
|
+
[ Opcodes.end ],
|
1004
|
+
|
1005
|
+
...setLastType(scope, TYPES.number)
|
1006
|
+
]));
|
1007
|
+
|
1008
|
+
// add a surrounding block
|
1009
|
+
startOut.push(stringOnly([ Opcodes.block, Valtype.f64 ]));
|
1010
|
+
endOut.unshift(stringOnly([ Opcodes.end ]));
|
1011
|
+
}
|
1012
|
+
|
1013
|
+
if ((op === '===' || op === '==' || op === '!==' || op === '!=') && (knownLeft == null && knownRight == null)) {
|
1014
|
+
tmpLeft = localTmp(scope, '__tmpop_left');
|
1015
|
+
tmpRight = localTmp(scope, '__tmpop_right');
|
1016
|
+
|
1017
|
+
ops.unshift(...stringOnly([
|
948
1018
|
// if left is bytestring
|
949
1019
|
...leftType,
|
950
1020
|
...number(TYPES.bytestring, Valtype.i32),
|
@@ -958,10 +1028,36 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
958
1028
|
// if both are true
|
959
1029
|
[ Opcodes.i32_and ],
|
960
1030
|
[ Opcodes.if, Blocktype.void ],
|
961
|
-
...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ], true),
|
1031
|
+
...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ], leftType, rightType, true),
|
962
1032
|
...(op === '!==' || op === '!=' ? [ [ Opcodes.i32_eqz ] ] : []),
|
963
1033
|
[ Opcodes.br, 1 ],
|
964
1034
|
[ Opcodes.end ],
|
1035
|
+
|
1036
|
+
// if left is string or bytestring
|
1037
|
+
...leftType,
|
1038
|
+
...number(TYPES.string, Valtype.i32),
|
1039
|
+
[ Opcodes.i32_eq ],
|
1040
|
+
...leftType,
|
1041
|
+
...number(TYPES.bytestring, Valtype.i32),
|
1042
|
+
[ Opcodes.i32_eq ],
|
1043
|
+
[ Opcodes.i32_or ],
|
1044
|
+
|
1045
|
+
// if right is string or bytestring
|
1046
|
+
...rightType,
|
1047
|
+
...number(TYPES.string, Valtype.i32),
|
1048
|
+
[ Opcodes.i32_eq ],
|
1049
|
+
...rightType,
|
1050
|
+
...number(TYPES.bytestring, Valtype.i32),
|
1051
|
+
[ Opcodes.i32_eq ],
|
1052
|
+
[ Opcodes.i32_or ],
|
1053
|
+
|
1054
|
+
// if either
|
1055
|
+
[ Opcodes.i32_or ],
|
1056
|
+
[ Opcodes.if, Blocktype.void ],
|
1057
|
+
...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ], leftType, rightType),
|
1058
|
+
...(op === '!==' || op === '!=' ? [ [ Opcodes.i32_eqz ] ] : []),
|
1059
|
+
[ Opcodes.br, 1 ],
|
1060
|
+
[ Opcodes.end ]
|
965
1061
|
]));
|
966
1062
|
|
967
1063
|
// add a surrounding block
|
@@ -1329,13 +1425,20 @@ const getNodeType = (scope, node) => {
|
|
1329
1425
|
if (['==', '===', '!=', '!==', '>', '>=', '<', '<=', 'instanceof'].includes(node.operator)) return TYPES.boolean;
|
1330
1426
|
if (node.operator !== '+') return TYPES.number;
|
1331
1427
|
|
1332
|
-
const
|
1333
|
-
const
|
1428
|
+
const leftType = getNodeType(scope, node.left);
|
1429
|
+
const rightType = getNodeType(scope, node.right);
|
1430
|
+
const knownLeft = knownType(scope, leftType);
|
1431
|
+
const knownRight = knownType(scope, rightType);
|
1334
1432
|
|
1335
|
-
// todo: this should be dynamic but for now only static
|
1336
1433
|
if (knownLeft === TYPES.string || knownRight === TYPES.string) return TYPES.string;
|
1337
|
-
if (knownLeft === TYPES.bytestring
|
1434
|
+
if (knownLeft === TYPES.bytestring && knownRight === TYPES.bytestring) return TYPES.bytestring;
|
1435
|
+
if (knownLeft === TYPES.bytestring || knownRight === TYPES.bytestring) return TYPES.string;
|
1436
|
+
|
1437
|
+
if (knownLeft != null || knownRight != null) return TYPES.number;
|
1438
|
+
|
1439
|
+
if (scope.locals['#last_type']) return getLastType(scope);
|
1338
1440
|
|
1441
|
+
// presume
|
1339
1442
|
return TYPES.number;
|
1340
1443
|
}
|
1341
1444
|
|
@@ -4076,11 +4179,15 @@ const generateMember = (scope, decl, _global, _name) => {
|
|
4076
4179
|
if (importedFuncs[name]) return withType(scope, number(importedFuncs[name].params.length ?? importedFuncs[name].params), TYPES.number);
|
4077
4180
|
if (internalConstrs[name]) return withType(scope, number(internalConstrs[name].length ?? 0), TYPES.number);
|
4078
4181
|
|
4182
|
+
const out = [
|
4183
|
+
...generate(scope, decl.object),
|
4184
|
+
Opcodes.i32_to_u,
|
4185
|
+
];
|
4186
|
+
|
4079
4187
|
if (Prefs.fastLength) {
|
4080
4188
|
// presume valid length object
|
4081
4189
|
return [
|
4082
|
-
...
|
4083
|
-
Opcodes.i32_to_u,
|
4190
|
+
...out,
|
4084
4191
|
|
4085
4192
|
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
|
4086
4193
|
Opcodes.i32_from_u
|
@@ -4091,8 +4198,7 @@ const generateMember = (scope, decl, _global, _name) => {
|
|
4091
4198
|
const known = knownType(scope, type);
|
4092
4199
|
if (known != null) {
|
4093
4200
|
if (typeHasFlag(known, TYPE_FLAGS.length)) return [
|
4094
|
-
...
|
4095
|
-
Opcodes.i32_to_u,
|
4201
|
+
...out,
|
4096
4202
|
|
4097
4203
|
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
|
4098
4204
|
Opcodes.i32_from_u
|
@@ -4102,13 +4208,14 @@ const generateMember = (scope, decl, _global, _name) => {
|
|
4102
4208
|
}
|
4103
4209
|
|
4104
4210
|
return [
|
4211
|
+
...out,
|
4212
|
+
[ Opcodes.local_set, localTmp(scope, '#length_tmp', Valtype.i32) ],
|
4213
|
+
|
4105
4214
|
...getNodeType(scope, decl.object),
|
4106
4215
|
...number(TYPE_FLAGS.length, Valtype.i32),
|
4107
4216
|
[ Opcodes.i32_and ],
|
4108
4217
|
[ Opcodes.if, valtypeBinary ],
|
4109
|
-
|
4110
|
-
Opcodes.i32_to_u,
|
4111
|
-
|
4218
|
+
[ Opcodes.local_get, localTmp(scope, '#length_tmp', Valtype.i32) ],
|
4112
4219
|
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
|
4113
4220
|
Opcodes.i32_from_u,
|
4114
4221
|
|
@@ -4439,7 +4546,7 @@ const generateFunc = (scope, decl) => {
|
|
4439
4546
|
...generate(func, defaultValues[x], false, x),
|
4440
4547
|
[ Opcodes.local_set, func.locals[x].idx ],
|
4441
4548
|
|
4442
|
-
...setType(func, x, getNodeType(
|
4549
|
+
...setType(func, x, getNodeType(func, defaultValues[x])),
|
4443
4550
|
[ Opcodes.end ]
|
4444
4551
|
);
|
4445
4552
|
}
|