mirror of
https://github.com/dart-lang/sdk
synced 2024-11-02 10:49:00 +00:00
[ddc] let compiler generate instanceof and == of types directly.
Change-Id: I87cb00cc4592125001d50505e9023c2ab45d95c4 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/138722 Commit-Queue: Sigmund Cherem <sigmund@google.com> Reviewed-by: Nicholas Shahan <nshahan@google.com> Reviewed-by: Mark Zhou <markzipan@google.com>
This commit is contained in:
parent
617b78a014
commit
db378a2f0d
5 changed files with 119 additions and 81 deletions
|
@ -4755,21 +4755,35 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
|
|||
if (target.isFactory) return _emitFactoryInvocation(node);
|
||||
|
||||
// Optimize some internal SDK calls.
|
||||
if (isSdkInternalRuntime(target.enclosingLibrary) &&
|
||||
node.arguments.positional.length == 1) {
|
||||
var name = target.name.name;
|
||||
var firstArg = node.arguments.positional[0];
|
||||
if (name == 'getGenericClass' && firstArg is TypeLiteral) {
|
||||
var type = firstArg.type;
|
||||
if (type is InterfaceType) {
|
||||
return _emitTopLevelNameNoInterop(type.classNode, suffix: '\$');
|
||||
if (isSdkInternalRuntime(target.enclosingLibrary)) {
|
||||
if (node.arguments.positional.length == 1) {
|
||||
var name = target.name.name;
|
||||
var firstArg = node.arguments.positional[0];
|
||||
if (name == 'getGenericClass' && firstArg is TypeLiteral) {
|
||||
var type = firstArg.type;
|
||||
if (type is InterfaceType) {
|
||||
return _emitTopLevelNameNoInterop(type.classNode, suffix: '\$');
|
||||
}
|
||||
}
|
||||
if (name == 'unwrapType' && firstArg is TypeLiteral) {
|
||||
return _emitType(firstArg.type);
|
||||
}
|
||||
if (name == 'extensionSymbol' && firstArg is StringLiteral) {
|
||||
return getExtensionSymbolInternal(firstArg.value);
|
||||
}
|
||||
} else if (node.arguments.positional.length == 2) {
|
||||
var name = target.name.name;
|
||||
var firstArg = node.arguments.positional[0];
|
||||
var secondArg = node.arguments.positional[1];
|
||||
if (name == '_jsInstanceOf' && secondArg is TypeLiteral) {
|
||||
return js.call('# instanceof #',
|
||||
[_visitExpression(firstArg), _emitType(secondArg.type)]);
|
||||
}
|
||||
|
||||
if (name == '_equalType' && secondArg is TypeLiteral) {
|
||||
return js.call('# === #',
|
||||
[_visitExpression(firstArg), _emitType(secondArg.type)]);
|
||||
}
|
||||
}
|
||||
if (name == 'unwrapType' && firstArg is TypeLiteral) {
|
||||
return _emitType(firstArg.type);
|
||||
}
|
||||
if (name == 'extensionSymbol' && firstArg is StringLiteral) {
|
||||
return getExtensionSymbolInternal(firstArg.value);
|
||||
}
|
||||
}
|
||||
if (target == _coreTypes.identicalProcedure) {
|
||||
|
|
|
@ -142,17 +142,17 @@ normalizeFutureOr(typeConstructor, setBaseClass) {
|
|||
|
||||
// FutureOr<dynamic|void|Object?|Object*> --> dynamic|void|Object?|Object*
|
||||
if (_isTop(typeArg) ||
|
||||
(_isLegacy(typeArg) &&
|
||||
(_jsInstanceOf(typeArg, LegacyType) &&
|
||||
JS<bool>('!', '#.type === #', typeArg, Object))) {
|
||||
return typeArg;
|
||||
}
|
||||
|
||||
// FutureOr<Never> --> Future<Never>
|
||||
if (typeArg == never_) {
|
||||
if (_equalType(typeArg, Never)) {
|
||||
return JS('!', '#(#)', getGenericClass(Future), typeArg);
|
||||
}
|
||||
// FutureOr<Null> --> Future<Null>?
|
||||
if (typeArg == unwrapType(Null)) {
|
||||
if (_equalType(typeArg, Null)) {
|
||||
return nullable(JS('!', '#(#)', getGenericClass(Future), typeArg));
|
||||
}
|
||||
// Otherwise, create the FutureOr<T> type as a normal generic type.
|
||||
|
@ -286,7 +286,7 @@ bool isJsInterop(obj) {
|
|||
// Note that it is still possible to call typed JS interop methods on
|
||||
// extension types but the calls must be statically typed.
|
||||
if (JS('!', '#[#] != null', obj, _extensionType)) return false;
|
||||
return JS('!', '!($obj instanceof $Object)');
|
||||
return !_jsInstanceOf(obj, Object);
|
||||
}
|
||||
|
||||
/// Get the type of a method from a type using the stored signature
|
||||
|
|
|
@ -301,7 +301,7 @@ _checkAndCall(f, ftype, obj, typeArgs, args, named, displayName) =>
|
|||
}
|
||||
|
||||
// Apply type arguments
|
||||
if ($ftype instanceof $GenericFunctionType) {
|
||||
if (${_jsInstanceOf(ftype, GenericFunctionType)}) {
|
||||
let formalCount = $ftype.formalCount;
|
||||
|
||||
if ($typeArgs == null) {
|
||||
|
@ -405,17 +405,27 @@ dindex(obj, index) => callMethod(obj, '_get', null, [index], null, '[]');
|
|||
dsetindex(obj, index, value) =>
|
||||
callMethod(obj, '_set', null, [index, value], null, '[]=');
|
||||
|
||||
/// General implementation of the Dart `is` operator.
|
||||
///
|
||||
/// Some basic cases are handled directly by the `.is` methods that are attached
|
||||
/// directly on types, but any query that requires checking subtyping relations
|
||||
/// is handled here.
|
||||
@notNull
|
||||
@JSExportName('is')
|
||||
bool instanceOf(obj, type) {
|
||||
if (obj == null) {
|
||||
return identical(type, unwrapType(Null)) ||
|
||||
return _equalType(type, Null) ||
|
||||
_isTop(type) ||
|
||||
_isNullable(type);
|
||||
_jsInstanceOf(type, NullableType);
|
||||
}
|
||||
return isSubtypeOf(getReifiedType(obj), type);
|
||||
}
|
||||
|
||||
/// General implementation of the Dart `as` operator.
|
||||
///
|
||||
/// Some basic cases are handled directly by the `.as` methods that are attached
|
||||
/// directly on types, but any query that requires checking subtyping relations
|
||||
/// is handled here.
|
||||
@JSExportName('as')
|
||||
cast(obj, type, @notNull bool isImplicit) {
|
||||
// We hoist the common case where null is checked against another type here
|
||||
|
|
|
@ -88,7 +88,7 @@ getReifiedType(obj) {
|
|||
switch (JS<String>('!', 'typeof #', obj)) {
|
||||
case "object":
|
||||
if (obj == null) return JS('', '#', Null);
|
||||
if (JS('!', '# instanceof #', obj, Object)) {
|
||||
if (_jsInstanceOf(obj, Object)) {
|
||||
return JS('', '#.constructor', obj);
|
||||
}
|
||||
var result = JS('', '#[#]', obj, _extensionType);
|
||||
|
|
|
@ -294,19 +294,20 @@ Object nullable(type) {
|
|||
|
||||
Object _computeNullable(type) {
|
||||
// *? normalizes to ?.
|
||||
if (_isLegacy(type)) {
|
||||
if (_jsInstanceOf(type, LegacyType)) {
|
||||
return nullable(JS<Object>('!', '#.type', type));
|
||||
}
|
||||
if (_isNullable(type) ||
|
||||
if (_jsInstanceOf(type, NullableType) ||
|
||||
_isTop(type) ||
|
||||
_isNullType(type) ||
|
||||
_equalType(type, Null) ||
|
||||
// Normalize FutureOr<T?>? --> FutureOr<T?>
|
||||
// All other runtime FutureOr normalization is in `normalizeFutureOr()`.
|
||||
((_isFutureOr(type)) &&
|
||||
_isNullable(JS<Object>('!', '#[0]', getGenericArgs(type))))) {
|
||||
_jsInstanceOf(
|
||||
JS<Object>('!', '#[0]', getGenericArgs(type)), NullableType))) {
|
||||
return type;
|
||||
}
|
||||
if (type == never_) return unwrapType(Null);
|
||||
if (_equalType(type, Never)) return unwrapType(Null);
|
||||
return NullableType(type);
|
||||
}
|
||||
|
||||
|
@ -331,10 +332,10 @@ Object legacy(type) {
|
|||
|
||||
Object _computeLegacy(type) {
|
||||
// Note: ?* normalizes to ?, so we cache type? at type?[_cachedLegacy].
|
||||
if (_isLegacy(type) ||
|
||||
_isNullable(type) ||
|
||||
if (_jsInstanceOf(type, LegacyType) ||
|
||||
_jsInstanceOf(type, NullableType) ||
|
||||
_isTop(type) ||
|
||||
_isNullType(type)) {
|
||||
_equalType(type, Null)) {
|
||||
return type;
|
||||
}
|
||||
return LegacyType(JS<Type>('!', '#', type));
|
||||
|
@ -383,7 +384,7 @@ class LegacyType extends DartType {
|
|||
if (obj == null) {
|
||||
// Object and Never are the only legacy types that should return true if
|
||||
// obj is `null`.
|
||||
return JS<bool>('!', '# === # || # === #', type, Object, type, never_);
|
||||
return _equalType(type, Object) || _equalType(type, Never);
|
||||
}
|
||||
return JS<bool>('!', '#.is(#)', type, obj);
|
||||
}
|
||||
|
@ -470,7 +471,7 @@ Type wrapType(type, [@notNull bool isNormalized = false]) {
|
|||
}
|
||||
var result = isNormalized
|
||||
? _Type(type)
|
||||
: (_isLegacy(type)
|
||||
: (_jsInstanceOf(type, LegacyType)
|
||||
? wrapType(JS<Object>('!', '#.type', type))
|
||||
: _canonicalizeNormalizedTypeObject(type));
|
||||
JS('', '#[#] = #', type, _typeObject, result);
|
||||
|
@ -482,7 +483,7 @@ Type wrapType(type, [@notNull bool isNormalized = false]) {
|
|||
/// Used for type object identity. Normalization requires us to return a
|
||||
/// canonicalized version of the input with all legacy wrappers removed.
|
||||
Type _canonicalizeNormalizedTypeObject(type) {
|
||||
assert(!_isLegacy(type));
|
||||
assert(!_jsInstanceOf(type, LegacyType));
|
||||
// We don't call _canonicalizeNormalizedTypeObject recursively but call wrap
|
||||
// + unwrap to handle legacy types automatically and force caching the
|
||||
// canonicalized type under the _typeObject cache property directly. This
|
||||
|
@ -491,10 +492,10 @@ Type _canonicalizeNormalizedTypeObject(type) {
|
|||
Object normalizeHelper(a) => unwrapType(wrapType(a));
|
||||
|
||||
// GenericFunctionTypeIdentifiers are implicitly normalized.
|
||||
if (JS<bool>('!', '# instanceof #', type, GenericFunctionTypeIdentifier)) {
|
||||
if (_jsInstanceOf(type, GenericFunctionTypeIdentifier)) {
|
||||
return wrapType(type, true);
|
||||
}
|
||||
if (JS<bool>('!', '# instanceof #', type, FunctionType)) {
|
||||
if (_jsInstanceOf(type, FunctionType)) {
|
||||
var normReturnType = normalizeHelper(type.returnType);
|
||||
var normArgs = type.args.map(normalizeHelper).toList();
|
||||
if (JS<bool>('!', '#.Object.keys(#).length === 0', global_, type.named) &&
|
||||
|
@ -516,7 +517,7 @@ Type _canonicalizeNormalizedTypeObject(type) {
|
|||
fnType(normReturnType, normArgs, normNamed, normRequiredNamed);
|
||||
return wrapType(normType, true);
|
||||
}
|
||||
if (JS<bool>('!', '# instanceof #', type, GenericFunctionType)) {
|
||||
if (_jsInstanceOf(type, GenericFunctionType)) {
|
||||
var formals = _getCanonicalTypeFormals(type.typeFormals.length);
|
||||
var normBounds =
|
||||
type.instantiateTypeBounds(formals).map(normalizeHelper).toList();
|
||||
|
@ -982,7 +983,7 @@ class GenericFunctionType extends AbstractFunctionType {
|
|||
|
||||
bool hasFreeFormal(t) {
|
||||
// Ignore nullability wrappers.
|
||||
if (_isLegacy(t) || _isNullable(t)) {
|
||||
if (_jsInstanceOf(t, LegacyType) || _jsInstanceOf(t, NullableType)) {
|
||||
return hasFreeFormal(JS<Object>('!', '#.type', t));
|
||||
}
|
||||
if (partials.containsKey(t)) return true;
|
||||
|
@ -1111,7 +1112,7 @@ String typeName(type) => JS('', '''(() => {
|
|||
if ($type === void 0) return "undefined type";
|
||||
if ($type === null) return "null type";
|
||||
// Non-instance types
|
||||
if ($type instanceof $DartType) {
|
||||
if (${_jsInstanceOf(type, DartType)}) {
|
||||
return $type.toString();
|
||||
}
|
||||
|
||||
|
@ -1263,27 +1264,39 @@ bool isSubtypeOf(Object t1, Object t2) {
|
|||
final _subtypeCache = JS('', 'Symbol("_subtypeCache")');
|
||||
|
||||
@notNull
|
||||
bool _isBottom(type, strictMode) => JS(
|
||||
'!', '# === # || (!# && # === #)', type, never_, strictMode, type, bottom);
|
||||
bool _isBottom(type, @notNull bool strictMode) =>
|
||||
_equalType(type, Never) || (!strictMode && _equalType(type, Null));
|
||||
|
||||
@notNull
|
||||
bool _isTop(type) {
|
||||
if (_isNullable(type)) return JS('!', '#.type === #', type, Object);
|
||||
if (_jsInstanceOf(type, NullableType))
|
||||
return JS('!', '#.type === #', type, Object);
|
||||
|
||||
return JS('!', '# === # || # === #', type, dynamic, type, void_);
|
||||
return _equalType(type, dynamic) || JS('!', '# === #', type, void_);
|
||||
}
|
||||
|
||||
/// Returns `true` if [type] represents a nullable (question, ?) type.
|
||||
/// Wraps the JavaScript `instanceof` operator returning `true` if [type] is an
|
||||
/// instance of [cls].
|
||||
///
|
||||
/// This method is equivalent to:
|
||||
///
|
||||
/// JS<bool>('!', '# instanceof #', type, cls);
|
||||
///
|
||||
/// but the code is generated by the compiler directly (a low-tech way of
|
||||
/// inlining).
|
||||
@notNull
|
||||
bool _isNullable(type) => JS<bool>('!', '# instanceof #', type, NullableType);
|
||||
external bool _jsInstanceOf(type, cls);
|
||||
|
||||
/// Returns `true` if [type] represents a legacy (star, *) type.
|
||||
/// Returns `true` if [type] is [cls].
|
||||
///
|
||||
/// This method is equivalent to:
|
||||
///
|
||||
/// JS<bool>('!', '# === #', type, unwrapType(cls));
|
||||
///
|
||||
/// but the code is generated by the compiler directly (a low-tech way of
|
||||
/// inlining).
|
||||
@notNull
|
||||
bool _isLegacy(type) => JS<bool>('!', '# instanceof #', type, LegacyType);
|
||||
|
||||
/// Returns `true` if [type] is the [Null] type.
|
||||
@notNull
|
||||
bool _isNullType(type) => JS<bool>('!', '# === #', type, unwrapType(Null));
|
||||
external bool _equalType(type, cls);
|
||||
|
||||
@notNull
|
||||
bool _isFutureOr(type) {
|
||||
|
@ -1297,10 +1310,10 @@ bool _isSubtype(t1, t2, bool strictMode) => JS<bool>('!', '''(() => {
|
|||
if (!$strictMode) {
|
||||
// Strip nullable types when performing check in weak mode.
|
||||
// TODO(nshahan) Investigate stripping off legacy types as well.
|
||||
if (${_isNullable(t1)}) {
|
||||
if (${_jsInstanceOf(t1, NullableType)}) {
|
||||
t1 = t1.type;
|
||||
}
|
||||
if (${_isNullable(t2)}) {
|
||||
if (${_jsInstanceOf(t2, NullableType)}) {
|
||||
t2 = t2.type;
|
||||
}
|
||||
}
|
||||
|
@ -1314,12 +1327,12 @@ bool _isSubtype(t1, t2, bool strictMode) => JS<bool>('!', '''(() => {
|
|||
}
|
||||
|
||||
// "Left Top".
|
||||
if ($t1 == $dynamic || $t1 == $void_) {
|
||||
if (${_equalType(t1, dynamic)} || $t1 === $void_) {
|
||||
return $_isSubtype($nullable($Object), $t2, $strictMode);
|
||||
}
|
||||
|
||||
// "Right Object".
|
||||
if ($t2 == $Object) {
|
||||
if (${_equalType(t2, Object)}) {
|
||||
// TODO(nshahan) Need to handle type variables.
|
||||
// https://github.com/dart-lang/sdk/issues/38816
|
||||
if (${_isFutureOr(t1)}) {
|
||||
|
@ -1327,11 +1340,11 @@ bool _isSubtype(t1, t2, bool strictMode) => JS<bool>('!', '''(() => {
|
|||
return $_isSubtype(t1TypeArg, $Object, $strictMode);
|
||||
}
|
||||
|
||||
if (${_isLegacy(t1)}) {
|
||||
if (${_jsInstanceOf(t1, LegacyType)}) {
|
||||
return $_isSubtype(t1.type, t2, $strictMode);
|
||||
}
|
||||
|
||||
if (${_isNullType(t1)} || ${_isNullable(t1)}) {
|
||||
if (${_equalType(t1, Null)} || ${_jsInstanceOf(t1, NullableType)}) {
|
||||
// Checks for t1 is dynamic or void already performed in "Left Top" test.
|
||||
return false;
|
||||
}
|
||||
|
@ -1339,7 +1352,7 @@ bool _isSubtype(t1, t2, bool strictMode) => JS<bool>('!', '''(() => {
|
|||
}
|
||||
|
||||
// "Left Null".
|
||||
if ($t1 == $Null) {
|
||||
if (${_equalType(t1, Null)}) {
|
||||
// TODO(nshahan) Need to handle type variables.
|
||||
// https://github.com/dart-lang/sdk/issues/38816
|
||||
if (${_isFutureOr(t2)}) {
|
||||
|
@ -1347,16 +1360,17 @@ bool _isSubtype(t1, t2, bool strictMode) => JS<bool>('!', '''(() => {
|
|||
return $_isSubtype($Null, t2TypeArg, $strictMode);
|
||||
}
|
||||
|
||||
return $t2 == $Null || ${_isLegacy(t2)} || ${_isNullable(t2)};
|
||||
return ${_equalType(t2, Null)} || ${_jsInstanceOf(t2, LegacyType)} ||
|
||||
${_jsInstanceOf(t2, NullableType)};
|
||||
}
|
||||
|
||||
// "Left Legacy".
|
||||
if (${_isLegacy(t1)}) {
|
||||
if (${_jsInstanceOf(t1, LegacyType)}) {
|
||||
return $_isSubtype(t1.type, t2, $strictMode);
|
||||
}
|
||||
|
||||
// "Right Legacy".
|
||||
if (${_isLegacy(t2)}) {
|
||||
if (${_jsInstanceOf(t2, LegacyType)}) {
|
||||
return $_isSubtype(t1, $nullable(t2.type), $strictMode);
|
||||
}
|
||||
|
||||
|
@ -1375,11 +1389,12 @@ bool _isSubtype(t1, t2, bool strictMode) => JS<bool>('!', '''(() => {
|
|||
// (Future<A> | A) <: t2 iff Future<A> <: t2 and A <: t2.
|
||||
let t1Future = ${getGenericClass(Future)}(t1TypeArg);
|
||||
// Known to handle the case FutureOr<Null> <: Future<Null>.
|
||||
return $_isSubtype(t1Future, $t2, $strictMode) && $_isSubtype(t1TypeArg, $t2, $strictMode);
|
||||
return $_isSubtype(t1Future, $t2, $strictMode) &&
|
||||
$_isSubtype(t1TypeArg, $t2, $strictMode);
|
||||
}
|
||||
|
||||
// "Left Nullable".
|
||||
if (${_isNullable(t1)}) {
|
||||
if (${_jsInstanceOf(t1, NullableType)}) {
|
||||
// TODO(nshahan) Need to handle type variables.
|
||||
// https://github.com/dart-lang/sdk/issues/38816
|
||||
return $_isSubtype(t1.type, t2, $strictMode) && $_isSubtype($Null, t2, $strictMode);
|
||||
|
@ -1396,7 +1411,7 @@ bool _isSubtype(t1, t2, bool strictMode) => JS<bool>('!', '''(() => {
|
|||
}
|
||||
|
||||
// "Right Nullable".
|
||||
if (${_isNullable(t2)}) {
|
||||
if (${_jsInstanceOf(t2, NullableType)}) {
|
||||
// TODO(nshahan) Need to handle type variables.
|
||||
// https://github.com/dart-lang/sdk/issues/38816
|
||||
return $_isSubtype(t1, t2.type, $strictMode) || $_isSubtype(t1, $Null, $strictMode);
|
||||
|
@ -1405,17 +1420,17 @@ bool _isSubtype(t1, t2, bool strictMode) => JS<bool>('!', '''(() => {
|
|||
// "Traditional" name-based subtype check. Avoid passing
|
||||
// function types to the class subtype checks, since we don't
|
||||
// currently distinguish between generic typedefs and classes.
|
||||
if (!($t2 instanceof $AbstractFunctionType)) {
|
||||
if (!${_jsInstanceOf(t2, AbstractFunctionType)}) {
|
||||
// t2 is an interface type.
|
||||
|
||||
if ($t1 instanceof $AbstractFunctionType) {
|
||||
if (${_jsInstanceOf(t1, AbstractFunctionType)}) {
|
||||
// Function types are only subtypes of interface types `Function` (and top
|
||||
// types, handled already above).
|
||||
return $t2 === $Function;
|
||||
return ${_equalType(t2, Function)};
|
||||
}
|
||||
|
||||
// All JS types are subtypes of anonymous JS types.
|
||||
if ($t1 === $jsobject && $t2 instanceof $AnonymousJSType) {
|
||||
if ($t1 === $jsobject && ${_jsInstanceOf(t2, AnonymousJSType)}) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1424,13 +1439,13 @@ bool _isSubtype(t1, t2, bool strictMode) => JS<bool>('!', '''(() => {
|
|||
}
|
||||
|
||||
// Function subtyping.
|
||||
if (!($t1 instanceof $AbstractFunctionType)) {
|
||||
if (!${_jsInstanceOf(t1, AbstractFunctionType)}) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Handle generic functions.
|
||||
if ($t1 instanceof $GenericFunctionType) {
|
||||
if (!($t2 instanceof $GenericFunctionType)) {
|
||||
if (${_jsInstanceOf(t1, GenericFunctionType)}) {
|
||||
if (!${_jsInstanceOf(t2, GenericFunctionType)}) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1474,7 +1489,7 @@ bool _isSubtype(t1, t2, bool strictMode) => JS<bool>('!', '''(() => {
|
|||
|
||||
$t1 = $t1.instantiate(fresh);
|
||||
$t2 = $t2.instantiate(fresh);
|
||||
} else if ($t2 instanceof $GenericFunctionType) {
|
||||
} else if (${_jsInstanceOf(t2, GenericFunctionType)}) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1485,24 +1500,24 @@ bool _isSubtype(t1, t2, bool strictMode) => JS<bool>('!', '''(() => {
|
|||
bool _isInterfaceSubtype(t1, t2, strictMode) => JS('', '''(() => {
|
||||
// If we have lazy JS types, unwrap them. This will effectively
|
||||
// reduce to a prototype check below.
|
||||
if ($t1 instanceof $LazyJSType) $t1 = $t1.rawJSTypeForCheck();
|
||||
if ($t2 instanceof $LazyJSType) $t2 = $t2.rawJSTypeForCheck();
|
||||
if (${_jsInstanceOf(t1, LazyJSType)}) $t1 = $t1.rawJSTypeForCheck();
|
||||
if (${_jsInstanceOf(t2, LazyJSType)}) $t2 = $t2.rawJSTypeForCheck();
|
||||
|
||||
if ($t1 === $t2) {
|
||||
return true;
|
||||
}
|
||||
if ($t1 === $Object) {
|
||||
if (${_equalType(t1, Object)}) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Classes cannot subtype `Function` or vice versa.
|
||||
if ($t1 === $Function || $t2 === $Function) {
|
||||
if (${_equalType(t1, Function)} || ${_equalType(t2, Function)}) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If t1 is a JS Object, we may not hit core.Object.
|
||||
if ($t1 == null) {
|
||||
return $t2 == $Object || $t2 == $dynamic;
|
||||
return ${_equalType(t2, Object)} || ${_equalType(t2, dynamic)};
|
||||
}
|
||||
|
||||
// Check if t1 and t2 have the same raw type. If so, check covariance on
|
||||
|
@ -1769,7 +1784,7 @@ class _TypeInferrer {
|
|||
if (_isTop(supertype)) return true;
|
||||
// `Null` is a subtype match for any type `Q` under no constraints.
|
||||
// Note that nullable types will change this.
|
||||
if (_isNullType(subtype)) return true;
|
||||
if (_equalType(subtype, Null)) return true;
|
||||
|
||||
// Handle FutureOr<T> union type.
|
||||
if (_isFutureOr(subtype)) {
|
||||
|
@ -1876,8 +1891,7 @@ class _TypeInferrer {
|
|||
// TODO(paulberry): implement this case.
|
||||
if (subtype is FunctionType) {
|
||||
if (supertype is! FunctionType) {
|
||||
if (identical(supertype, unwrapType(Function)) ||
|
||||
identical(supertype, unwrapType(Object))) {
|
||||
if (_equalType(supertype, Function) || _equalType(supertype, Object)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
|
@ -1893,7 +1907,7 @@ class _TypeInferrer {
|
|||
bool _isTop(Object type) =>
|
||||
identical(type, _dynamic) ||
|
||||
identical(type, void_) ||
|
||||
identical(type, unwrapType(Object));
|
||||
_equalType(type, Object);
|
||||
}
|
||||
|
||||
/// A constraint on a type parameter that we're inferring.
|
||||
|
@ -1943,7 +1957,7 @@ class TypeConstraint {
|
|||
/// contain different generic type arguments.
|
||||
Object? _getMatchingSupertype(Object? subtype, Object supertype) {
|
||||
if (identical(subtype, supertype)) return supertype;
|
||||
if (subtype == null || subtype == unwrapType(Object)) return null;
|
||||
if (subtype == null || _equalType(subtype, Object)) return null;
|
||||
|
||||
var subclass = getGenericClass(subtype);
|
||||
var superclass = getGenericClass(supertype);
|
||||
|
|
Loading…
Reference in a new issue