@@ -567,17 +567,19 @@ class Types {
567
567
/// Emit code for testing a value against a Dart type. Expects the value on
568
568
/// the stack as a (ref null #Top) and leaves the result on the stack as an
569
569
/// i32.
570
- void emitTypeCheck (CodeGenerator codeGen, DartType type, DartType operandType,
570
+ void emitIsTest (CodeGenerator codeGen, DartType type, DartType operandType,
571
571
[TreeNode ? node]) {
572
572
final b = codeGen.b;
573
573
b.comment ("Type check against $type " );
574
574
w.Local ? operandTemp;
575
575
if (translator.options.verifyTypeChecks) {
576
- operandTemp = codeGen.addLocal (translator.topInfo.nullableType);
576
+ operandTemp =
577
+ b.addLocal (translator.topInfo.nullableType, isParameter: false );
577
578
b.local_tee (operandTemp);
578
579
}
579
- if (! _emitOptimizedTypeCheck (codeGen, type, operandType)) {
580
- // General fallback path
580
+ if (_canUseIsCheckHelper (codeGen, type, operandType)) {
581
+ b.call (_generateIsChecker (type as InterfaceType , operandType));
582
+ } else {
581
583
makeType (codeGen, type);
582
584
codeGen.call (translator.isSubtype.reference);
583
585
}
@@ -597,15 +599,36 @@ class Types {
597
599
}
598
600
}
599
601
600
- /// Emit optimized code for testing a value against a Dart type. If the type
601
- /// to be tested against is of a shape where we can generate more efficient
602
- /// code than the general fallback path, generate such code and return `true` .
603
- /// Otherwise, return `false` to indicate that the general path should be
604
- /// taken.
605
- bool _emitOptimizedTypeCheck (
602
+ w.ValueType emitAsCheck (CodeGenerator codeGen, DartType type,
603
+ DartType operandType, w.RefType boxedOperandType,
604
+ [TreeNode ? node]) {
605
+ final b = codeGen.b;
606
+
607
+ if (_canUseAsCheckHelper (codeGen, type, operandType)) {
608
+ b.call (_generateAsChecker (type as InterfaceType , operandType));
609
+ return translator.translateType (type);
610
+ }
611
+
612
+ w.Local operand = b.addLocal (boxedOperandType, isParameter: false );
613
+ b.local_tee (operand);
614
+ w.Label asCheckBlock = b.block ();
615
+ b.local_get (operand);
616
+ emitIsTest (codeGen, type, operandType, node);
617
+ b.br_if (asCheckBlock);
618
+ b.local_get (operand);
619
+ makeType (codeGen, type);
620
+ codeGen.call (translator.stackTraceCurrent.reference);
621
+ codeGen.call (translator.throwAsCheckError.reference);
622
+ b.unreachable ();
623
+ b.end ();
624
+ return operand.type;
625
+ }
626
+
627
+ bool _canUseIsCheckHelper (
606
628
CodeGenerator codeGen, DartType type, DartType operandType) {
607
- if (type is ! InterfaceType ) return false ;
629
+ if (_canUseAsCheckHelper (codeGen, type, operandType)) return true ;
608
630
631
+ if (type is ! InterfaceType ) return false ;
609
632
if (type.typeArguments.any ((t) => t is ! DynamicType )) {
610
633
// Type has at least one type argument that is not `dynamic`.
611
634
//
@@ -627,43 +650,124 @@ class Types {
627
650
return false ;
628
651
}
629
652
}
653
+ return true ;
654
+ }
630
655
631
- final b = codeGen.b;
632
- bool isPotentiallyNullable = operandType.isPotentiallyNullable;
633
- w.Label ? resultLabel;
634
- if (isPotentiallyNullable) {
635
- // Store operand in a temporary variable, since Binaryen does not support
636
- // block inputs.
637
- w.Local operand = codeGen.addLocal (translator.topInfo.nullableType);
638
- b.local_set (operand);
639
- resultLabel = b.block (const [], const [w.NumType .i32]);
640
- w.Label nullLabel = b.block (const [], const []);
641
- b.local_get (operand);
642
- b.br_on_null (nullLabel);
643
- }
656
+ bool _canUseAsCheckHelper (
657
+ CodeGenerator codeGen, DartType type, DartType operandType) {
658
+ if (type is ! InterfaceType ) return false ;
659
+ // TODO: Should be rather defaults to bounds instead of `dynamic`.
660
+ // (assuming omitting bound will make it dynamic rather than void/Object?)
661
+ return type.typeArguments.every ((t) => t is DynamicType );
662
+ }
663
+
664
+ final Map <DartType , w.BaseFunction > _nullableIsCheckers = {};
665
+ final Map <DartType , w.BaseFunction > _isCheckers = {};
644
666
667
+ w.BaseFunction _generateIsChecker (InterfaceType type, DartType operandType) {
668
+ final operandIsNullable = operandType.isPotentiallyNullable;
645
669
final interfaceClass = type.classNode;
646
670
647
- if (interfaceClass == coreTypes.objectClass) {
648
- b.drop ();
649
- b.i32_const (1 );
650
- } else if (interfaceClass == coreTypes.functionClass) {
651
- b.ref_test (translator.closureInfo.nonNullableType);
652
- } else {
653
- final ranges =
654
- translator.classIdNumbering.getConcreteClassIdRanges (interfaceClass);
655
- b.struct_get (translator.topInfo.struct, FieldIndex .classId);
656
- b.emitClassIdRangeCheck (codeGen, ranges);
657
- }
671
+ final cachedIsCheckers =
672
+ operandIsNullable ? _nullableIsCheckers : _isCheckers;
673
+
674
+ return cachedIsCheckers.putIfAbsent (type, () {
675
+ final argumentType = operandIsNullable
676
+ ? translator.topInfo.nullableType
677
+ : translator.topInfo.nonNullableType;
678
+ final function = translator.m.functions.define (
679
+ translator.m.types.defineFunction (
680
+ [argumentType],
681
+ [w.NumType .i32],
682
+ ),
683
+ '<obj> is <$type >' );
684
+
685
+ final b = function.body;
686
+ b.local_get (b.locals[0 ]);
687
+
688
+ w.Label ? resultLabel;
689
+ if (operandIsNullable) {
690
+ // Store operand in a temporary variable, since Binaryen does not support
691
+ // block inputs.
692
+ w.Local operand = function.addLocal (translator.topInfo.nullableType);
693
+ b.local_set (operand);
694
+ resultLabel = b.block (const [], const [w.NumType .i32]);
695
+ w.Label nullLabel = b.block (const [], const []);
696
+ b.local_get (operand);
697
+ b.br_on_null (nullLabel);
698
+ }
658
699
659
- if (isPotentiallyNullable) {
660
- b.br (resultLabel! );
661
- b.end (); // nullLabel
662
- b.i32_const (encodedNullability (type));
663
- b.end (); // resultLabel
664
- }
700
+ if (interfaceClass == coreTypes.objectClass) {
701
+ b.drop ();
702
+ b.i32_const (1 );
703
+ } else if (interfaceClass == coreTypes.functionClass) {
704
+ b.ref_test (translator.closureInfo.nonNullableType);
705
+ } else {
706
+ final ranges = translator.classIdNumbering
707
+ .getConcreteClassIdRanges (interfaceClass);
708
+ b.struct_get (translator.topInfo.struct, FieldIndex .classId);
709
+ b.emitClassIdRangeCheck (ranges);
710
+ }
665
711
666
- return true ;
712
+ if (operandIsNullable) {
713
+ b.br (resultLabel! );
714
+ b.end (); // nullLabel
715
+ b.i32_const (encodedNullability (type));
716
+ b.end (); // resultLabel
717
+ }
718
+
719
+ b.return_ ();
720
+ b.end ();
721
+
722
+ return function;
723
+ });
724
+ }
725
+
726
+ final Map <DartType , w.BaseFunction > _nullableAsCheckers = {};
727
+ final Map <DartType , w.BaseFunction > _asCheckers = {};
728
+
729
+ w.BaseFunction _generateAsChecker (InterfaceType type, DartType operandType) {
730
+ final operandIsNullable = operandType.isPotentiallyNullable;
731
+ final cachedAsCheckers =
732
+ operandIsNullable ? _nullableAsCheckers : _asCheckers;
733
+
734
+ final returnType = translator.translateType (type);
735
+
736
+ return cachedAsCheckers.putIfAbsent (type, () {
737
+ final argumentType = operandIsNullable
738
+ ? translator.topInfo.nullableType
739
+ : translator.topInfo.nonNullableType;
740
+ final function = translator.m.functions.define (
741
+ translator.m.types.defineFunction (
742
+ [argumentType],
743
+ [returnType],
744
+ ),
745
+ '<obj> as <$type >' );
746
+
747
+ final b = function.body;
748
+ w.Label asCheckBlock = b.block ();
749
+ b.local_get (b.locals[0 ]);
750
+ b.call (_generateIsChecker (type, operandType));
751
+ b.br_if (asCheckBlock);
752
+
753
+ b.local_get (b.locals[0 ]);
754
+ translator.constants.instantiateConstant (
755
+ function, b, TypeLiteralConstant (type), nonNullableTypeType);
756
+ b.call (translator.functions
757
+ .getFunction (translator.stackTraceCurrent.reference));
758
+ b.call (translator.functions
759
+ .getFunction (translator.throwAsCheckError.reference));
760
+ b.unreachable ();
761
+
762
+ b.end ();
763
+
764
+ b.local_get (b.locals[0 ]);
765
+ translator.convertType (function, argumentType, returnType);
766
+ b.return_ ();
767
+ b.end ();
768
+
769
+ return function;
770
+ });
667
771
}
668
772
669
773
int encodedNullability (DartType type) =>
0 commit comments