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.
@@ -416,7 +416,7 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
416
416
  ];
417
417
  };
418
418
 
419
- const concatStrings = (scope, left, right, global, name, assign = false, bytestrings = false) => {
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 [ out, pointer ] = makeArray(scope, {
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 left
438
- ...left,
439
- Opcodes.i32_to_u,
440
- [ Opcodes.local_set, leftPointer ],
437
+ // setup pointers
438
+ ...(left.length === 0 ? [
439
+ Opcodes.i32_to_u,
440
+ [ Opcodes.local_set, rightPointer ],
441
441
 
442
- // setup right
443
- ...right,
444
- Opcodes.i32_to_u,
445
- [ Opcodes.local_set, rightPointer ],
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
- // calculate length
448
- ...out,
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, ...unsignedLEB128(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, ...unsignedLEB128(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
- ...pointer,
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
- ...pointer,
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(bytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
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(bytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
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
- ...pointer,
538
+ [ Opcodes.local_get, out ],
503
539
  Opcodes.i32_from_u
504
540
  ];
505
541
  };
506
542
 
507
- const compareStrings = (scope, left, right, bytestrings = false) => {
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 left
522
- ...left,
523
- Opcodes.i32_to_u,
524
- [ Opcodes.local_tee, leftPointer ],
559
+ // setup pointers
560
+ ...(left.length === 0 ? [
561
+ Opcodes.i32_to_u,
562
+ [ Opcodes.local_set, rightPointer ],
525
563
 
526
- // setup right
527
- ...right,
528
- Opcodes.i32_to_u,
529
- [ Opcodes.local_tee, rightPointer ],
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, ...unsignedLEB128(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, ...unsignedLEB128(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
- ...(bytestrings ? [] : [
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
- bytestrings ?
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
- bytestrings ?
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(bytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
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 concatStrings(scope, left, right, _global, _name, assign, false);
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
- ...compareStrings(scope, left, right),
826
- [ Opcodes.i32_eqz ]
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 concatStrings(scope, left, right, _global, _name, assign, true);
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
- ...compareStrings(scope, left, right, true),
853
- [ Opcodes.i32_eqz ]
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 ((op === '===' || op === '==' || op === '!==' || op === '!=') && (knownLeft == null && knownRight == null)) {
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 string
966
+ // if left is bytestring
931
967
  ...leftType,
932
- ...number(TYPES.string, Valtype.i32),
968
+ ...number(TYPES.bytestring, Valtype.i32),
933
969
  [ Opcodes.i32_eq ],
934
970
 
935
- // if right is string
971
+ // if right is bytestring
936
972
  ...rightType,
937
- ...number(TYPES.string, Valtype.i32),
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
- ...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ], false),
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 knownLeft = knownType(scope, getNodeType(scope, node.left));
1333
- const knownRight = knownType(scope, getNodeType(scope, node.right));
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 || knownRight === TYPES.bytestring) return 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
- ...generate(scope, decl.object),
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
- ...generate(scope, decl.object),
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
- ...generate(scope, decl.object),
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(scope, defaultValues[x])),
4549
+ ...setType(func, x, getNodeType(func, defaultValues[x])),
4443
4550
  [ Opcodes.end ]
4444
4551
  );
4445
4552
  }