77/// This library defines the representation of runtime types.
88part of dart._runtime;
99
10+ /// Sets the mode of the runtime subtype checks.
11+ ///
12+ /// Changing the mode after any calls to dart.isSubtype() is not supported.
13+ void strictSubtypeChecks (bool flag) {
14+ JS ('' , 'dart.__strictSubtypeChecks = #' , flag);
15+ }
16+
1017final metadata = JS ('' , 'Symbol("metadata")' );
1118
1219/// Types in dart are represented internally at runtime as follows.
@@ -904,7 +911,7 @@ String typeName(type) => JS('', '''(() => {
904911})()''' );
905912
906913/// Returns true if [ft1] <: [ft2] .
907- _isFunctionSubtype (ft1, ft2) => JS ('' , '''(() => {
914+ _isFunctionSubtype (ft1, ft2, bool strictMode ) => JS ('' , '''(() => {
908915 let ret1 = $ft1 .returnType;
909916 let ret2 = $ft2 .returnType;
910917
@@ -916,7 +923,7 @@ _isFunctionSubtype(ft1, ft2) => JS('', '''(() => {
916923 }
917924
918925 for (let i = 0; i < args1.length; ++i) {
919- if (!$_isSubtype (args2[i], args1[i])) {
926+ if (!$_isSubtype (args2[i], args1[i], strictMode )) {
920927 return false;
921928 }
922929 }
@@ -930,13 +937,13 @@ _isFunctionSubtype(ft1, ft2) => JS('', '''(() => {
930937
931938 let j = 0;
932939 for (let i = args1.length; i < args2.length; ++i, ++j) {
933- if (!$_isSubtype (args2[i], optionals1[j])) {
940+ if (!$_isSubtype (args2[i], optionals1[j], strictMode )) {
934941 return false;
935942 }
936943 }
937944
938945 for (let i = 0; i < optionals2.length; ++i, ++j) {
939- if (!$_isSubtype (optionals2[i], optionals1[j])) {
946+ if (!$_isSubtype (optionals2[i], optionals1[j], strictMode )) {
940947 return false;
941948 }
942949 }
@@ -966,7 +973,7 @@ _isFunctionSubtype(ft1, ft2) => JS('', '''(() => {
966973 if (n1 === void 0) {
967974 return false;
968975 }
969- if (!$_isSubtype (n2, n1)) {
976+ if (!$_isSubtype (n2, n1, strictMode )) {
970977 return false;
971978 }
972979 }
@@ -983,7 +990,7 @@ _isFunctionSubtype(ft1, ft2) => JS('', '''(() => {
983990 }
984991 }
985992
986- return $_isSubtype (ret1, ret2);
993+ return $_isSubtype (ret1, ret2, strictMode );
987994})()''' );
988995
989996/// Returns true if [t1] <: [t2] .
@@ -1000,17 +1007,27 @@ bool isSubtypeOf(Object t1, Object t2) {
10001007 bool result = JS ('' , '#.get(#)' , map, t2);
10011008 if (JS ('!' , '# !== void 0' , result)) return result;
10021009 }
1003- // TODO(nshahan): Add support for strict/weak mode.
1004- var result = _isSubtype (t1, t2);
1005- JS ('' , '#.set(#, #)' , map, t2, result);
1006- return result;
1010+ var validSubtype = _isSubtype (t1, t2, true );
1011+
1012+ if (! validSubtype && ! JS <bool >('!' , 'dart.__strictSubtypeChecks' )) {
1013+ validSubtype = _isSubtype (t1, t2, false );
1014+ if (validSubtype) {
1015+ // TODO(nshahan) Need more information to be helpful here.
1016+ // File and line number that caused the subtype check?
1017+ // Possibly break into debuger?
1018+ _warn ("$t1 is not a subtype of $t2 .\n "
1019+ "This will be a runtime failure when strict mode is enabled." );
1020+ }
1021+ }
1022+ JS ('' , '#.set(#, #)' , map, t2, validSubtype);
1023+ return validSubtype;
10071024}
10081025
10091026final _subtypeCache = JS ('' , 'Symbol("_subtypeCache")' );
10101027
1011- // TODO(nshahan): Add support for strict/weak mode.
10121028@notNull
1013- bool _isBottom (type) => JS ('!' , '# == #' , type, bottom);
1029+ bool _isBottom (type, strictMode) =>
1030+ JS ('!' , '# == # || (!# && #)' , type, bottom, strictMode, _isNullType (type));
10141031
10151032// TODO(nshahan): Add support for strict/weak mode.
10161033@notNull
@@ -1039,19 +1056,29 @@ bool _isNullType(Object type) => identical(type, unwrapType(Null));
10391056bool _isFutureOr (type) =>
10401057 identical (getGenericClass (type), getGenericClass (FutureOr ));
10411058
1042- bool _isSubtype (t1, t2) => JS ('bool' , '''(() => {
1059+ bool _isSubtype (t1, t2, bool strictMode) => JS ('bool' , '''(() => {
1060+ if (!$strictMode ) {
1061+ // Strip nullable types when performing check in weak mode.
1062+ // TODO(nshahan) Investigate stripping off legacy types as well.
1063+ if (${_isNullable (t1 )}) {
1064+ t1 = t1.type;
1065+ }
1066+ if (${_isNullable (t2 )}) {
1067+ t2 = t2.type;
1068+ }
1069+ }
10431070 if ($t1 === $t2 ) {
10441071 return true;
10451072 }
10461073
10471074 // Trivially true, "Right Top" or "Left Bottom".
1048- if (${_isTop (t2 )} || ${_isBottom (t1 )}) {
1075+ if (${_isTop (t2 )} || ${_isBottom (t1 , strictMode )}) {
10491076 return true;
10501077 }
10511078
10521079 // "Left Top".
10531080 if ($t1 == $dynamic || $t1 == $void_ ) {
1054- return $_isSubtype ($nullable ($Object ), $t2 );
1081+ return $_isSubtype ($nullable ($Object ), $t2 , $ strictMode );
10551082 }
10561083
10571084 // "Right Object".
@@ -1060,15 +1087,15 @@ bool _isSubtype(t1, t2) => JS('bool', '''(() => {
10601087 // https://github.com/dart-lang/sdk/issues/38816
10611088 if (${_isFutureOr (t1 )}) {
10621089 let t1TypeArg = ${getGenericArgs (t1 )}[0];
1063- return $_isSubtype (t1TypeArg, $Object );
1090+ return $_isSubtype (t1TypeArg, $Object , $ strictMode );
10641091 }
10651092
10661093 if (${_isLegacy (t1 )}) {
1067- return $_isSubtype (t1.type, t2);
1094+ return $_isSubtype (t1.type, t2, $ strictMode );
10681095 }
10691096
1070- if ($t1 == $ dynamic || $t1 == $ void_ || $ t1 == $ Null
1071- || ${ _isNullable ( t1 )}) {
1097+ if (${ _isNullType ( t1 )} || ${ _isNullable ( t1 )}) {
1098+ // Checks for t1 is dynamic or void already performed in "Left Top" test.
10721099 return false;
10731100 }
10741101 return true;
@@ -1080,20 +1107,20 @@ bool _isSubtype(t1, t2) => JS('bool', '''(() => {
10801107 // https://github.com/dart-lang/sdk/issues/38816
10811108 if (${_isFutureOr (t2 )}) {
10821109 let t2TypeArg = ${getGenericArgs (t2 )}[0];
1083- return $_isSubtype ($Null , t2TypeArg);
1110+ return $_isSubtype ($Null , t2TypeArg, $ strictMode );
10841111 }
10851112
10861113 return $t2 == $Null || ${_isLegacy (t2 )} || ${_isNullable (t2 )};
10871114 }
10881115
10891116 // "Left Legacy".
10901117 if (${_isLegacy (t1 )}) {
1091- return $_isSubtype (t1.type, t2);
1118+ return $_isSubtype (t1.type, t2, $ strictMode );
10921119 }
10931120
10941121 // "Right Legacy".
10951122 if (${_isLegacy (t2 )}) {
1096- return $_isSubtype (t1, $nullable (t2.type));
1123+ return $_isSubtype (t1, $nullable (t2.type), $ strictMode );
10971124 }
10981125
10991126 // Handle FutureOr<T> union type.
@@ -1104,21 +1131,21 @@ bool _isSubtype(t1, t2) => JS('bool', '''(() => {
11041131 // FutureOr<A> <: FutureOr<B> iff A <: B
11051132 // TODO(nshahan): Proven to not actually be true and needs cleanup.
11061133 // https://github.com/dart-lang/sdk/issues/38818
1107- return $_isSubtype (t1TypeArg, t2TypeArg);
1134+ return $_isSubtype (t1TypeArg, t2TypeArg, $ strictMode );
11081135 }
11091136
11101137 // given t1 is Future<A> | A, then:
11111138 // (Future<A> | A) <: t2 iff Future<A> <: t2 and A <: t2.
11121139 let t1Future = ${getGenericClass (Future )}(t1TypeArg);
11131140 // Known to handle the case FutureOr<Null> <: Future<Null>.
1114- return $_isSubtype (t1Future, $t2 ) && $_isSubtype (t1TypeArg, $t2 );
1141+ return $_isSubtype (t1Future, $t2 , $ strictMode ) && $_isSubtype (t1TypeArg, $t2 , $ strictMode );
11151142 }
11161143
11171144 // "Left Nullable".
11181145 if (${_isNullable (t1 )}) {
11191146 // TODO(nshahan) Need to handle type variables.
11201147 // https://github.com/dart-lang/sdk/issues/38816
1121- return $_isSubtype (t1.type, t2) && $_isSubtype ($Null , t2);
1148+ return $_isSubtype (t1.type, t2, $ strictMode ) && $_isSubtype ($Null , t2, $ strictMode );
11221149 }
11231150
11241151 if ($_isFutureOr ($t2 )) {
@@ -1128,14 +1155,14 @@ bool _isSubtype(t1, t2) => JS('bool', '''(() => {
11281155 let t2Future = ${getGenericClass (Future )}(t2TypeArg);
11291156 // TODO(nshahan) Need to handle type variables on the left.
11301157 // https://github.com/dart-lang/sdk/issues/38816
1131- return $_isSubtype ($t1 , t2Future) || $_isSubtype ($t1 , t2TypeArg);
1158+ return $_isSubtype ($t1 , t2Future, $ strictMode ) || $_isSubtype ($t1 , t2TypeArg, $ strictMode );
11321159 }
11331160
11341161 // "Right Nullable".
11351162 if (${_isNullable (t2 )}) {
11361163 // TODO(nshahan) Need to handle type variables.
11371164 // https://github.com/dart-lang/sdk/issues/38816
1138- return $_isSubtype (t1, t2.type) || $_isSubtype (t1, $Null );
1165+ return $_isSubtype (t1, t2.type, $ strictMode ) || $_isSubtype (t1, $Null , $ strictMode );
11391166 }
11401167
11411168 // "Traditional" name-based subtype check. Avoid passing
@@ -1156,7 +1183,7 @@ bool _isSubtype(t1, t2) => JS('bool', '''(() => {
11561183 }
11571184
11581185 // Compare two interface types.
1159- return ${_isInterfaceSubtype (t1 , t2 )};
1186+ return ${_isInterfaceSubtype (t1 , t2 , strictMode )};
11601187 }
11611188
11621189 // Function subtyping.
@@ -1214,10 +1241,10 @@ bool _isSubtype(t1, t2) => JS('bool', '''(() => {
12141241 }
12151242
12161243 // Handle non-generic functions.
1217- return ${_isFunctionSubtype (t1 , t2 )};
1244+ return ${_isFunctionSubtype (t1 , t2 , strictMode )};
12181245})()''' );
12191246
1220- bool _isInterfaceSubtype (t1, t2) => JS ('' , '''(() => {
1247+ bool _isInterfaceSubtype (t1, t2, strictMode ) => JS ('' , '''(() => {
12211248 // If we have lazy JS types, unwrap them. This will effectively
12221249 // reduce to a prototype check below.
12231250 if ($t1 instanceof $LazyJSType ) $t1 = $t1 .rawJSTypeForCheck();
@@ -1255,38 +1282,38 @@ bool _isInterfaceSubtype(t1, t2) => JS('', '''(() => {
12551282 // When using implicit variance, variances will be undefined and
12561283 // considered covariant.
12571284 if (variances === void 0 || variances[i] == ${Variance .covariant }) {
1258- if (!$_isSubtype (typeArguments1[i], typeArguments2[i])) {
1285+ if (!$_isSubtype (typeArguments1[i], typeArguments2[i], $ strictMode )) {
12591286 return false;
12601287 }
12611288 } else if (variances[i] == ${Variance .contravariant }) {
1262- if (!$_isSubtype (typeArguments2[i], typeArguments1[i])) {
1289+ if (!$_isSubtype (typeArguments2[i], typeArguments1[i], $ strictMode )) {
12631290 return false;
12641291 }
12651292 } else if (variances[i] == ${Variance .invariant }) {
1266- if (!$_isSubtype (typeArguments1[i], typeArguments2[i]) ||
1267- !$_isSubtype (typeArguments2[i], typeArguments1[i])) {
1293+ if (!$_isSubtype (typeArguments1[i], typeArguments2[i], $ strictMode ) ||
1294+ !$_isSubtype (typeArguments2[i], typeArguments1[i], $ strictMode )) {
12681295 return false;
12691296 }
12701297 }
12711298 }
12721299 return true;
12731300 }
12741301
1275- if ($_isInterfaceSubtype (t1.__proto__, $t2 )) {
1302+ if ($_isInterfaceSubtype (t1.__proto__, $t2 , $ strictMode )) {
12761303 return true;
12771304 }
12781305
12791306 // Check mixin.
12801307 let m1 = $getMixin ($t1 );
1281- if (m1 != null && $_isInterfaceSubtype (m1, $t2 )) {
1308+ if (m1 != null && $_isInterfaceSubtype (m1, $t2 , $ strictMode )) {
12821309 return true;
12831310 }
12841311
12851312 // Check interfaces.
12861313 let getInterfaces = $getImplements ($t1 );
12871314 if (getInterfaces) {
12881315 for (let i1 of getInterfaces()) {
1289- if ($_isInterfaceSubtype (i1, $t2 )) {
1316+ if ($_isInterfaceSubtype (i1, $t2 , $ strictMode )) {
12901317 return true;
12911318 }
12921319 }
0 commit comments