| OLD | NEW |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 /// This library defines the representation of runtime types. | 5 /// This library defines the representation of runtime types. |
| 6 part of dart._runtime; | 6 part of dart._runtime; |
| 7 | 7 |
| 8 final metadata = JS('', 'Symbol("metadata")'); | 8 final metadata = JS('', 'Symbol("metadata")'); |
| 9 | 9 |
| 10 /// The symbol used to store the cached `Type` object associated with a class. | 10 /// The symbol used to store the cached `Type` object associated with a class. |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 49 /// T.as(o): Implements 'o as T'. | 49 /// T.as(o): Implements 'o as T'. |
| 50 /// T._check(o): Implements the type assertion of 'T x = o;' | 50 /// T._check(o): Implements the type assertion of 'T x = o;' |
| 51 /// | 51 /// |
| 52 /// By convention, we used named JavaScript functions for these methods with the | 52 /// By convention, we used named JavaScript functions for these methods with the |
| 53 /// name 'is_X', 'as_X' and 'check_X' for various X to indicate the type or the | 53 /// name 'is_X', 'as_X' and 'check_X' for various X to indicate the type or the |
| 54 /// implementation strategy for the test (e.g 'is_String', 'is_G' for generic | 54 /// implementation strategy for the test (e.g 'is_String', 'is_G' for generic |
| 55 /// types, etc.) | 55 /// types, etc.) |
| 56 // TODO(jmesserly): we shouldn't implement Type here. It should be moved down | 56 // TODO(jmesserly): we shouldn't implement Type here. It should be moved down |
| 57 // to AbstractFunctionType. | 57 // to AbstractFunctionType. |
| 58 class TypeRep implements Type { | 58 class TypeRep implements Type { |
| 59 TypeRep() { _initialize; } | 59 TypeRep() { |
| 60 _initialize; |
| 61 } |
| 60 String get name => this.toString(); | 62 String get name => this.toString(); |
| 61 } | 63 } |
| 62 | 64 |
| 63 class Dynamic extends TypeRep { | 65 class Dynamic extends TypeRep { |
| 64 toString() => 'dynamic'; | 66 toString() => 'dynamic'; |
| 65 } | 67 } |
| 66 | 68 |
| 69 class LazyJSType implements Type { |
| 70 final _jsTypeCallback; |
| 71 final _dartName; |
| 72 |
| 73 LazyJSType(this._jsTypeCallback, this._dartName); |
| 74 |
| 75 get _rawJSType => JS('', '#()', _jsTypeCallback); |
| 76 |
| 77 toString() => _jsTypeCallback != null ? typeName(_rawJSType) : _dartName; |
| 78 } |
| 79 |
| 80 _isInstanceOfLazyJSType(o, LazyJSType t) { |
| 81 if (t._jsTypeCallback != null) { |
| 82 return JS('bool', 'dart.is(#, #)', o, t._rawJSType); |
| 83 } |
| 84 if (o == null) return false; |
| 85 // Anonymous case: match any JS type. |
| 86 return _isJSObject(o); |
| 87 } |
| 88 |
| 89 _asInstanceOfLazyJSType(o, LazyJSType t) { |
| 90 if (t._jsTypeCallback != null) { |
| 91 return JS('bool', 'dart.as(#, #)', o, t._rawJSType); |
| 92 } |
| 93 // Anonymous case: allow any JS type. |
| 94 if (o == null) return null; |
| 95 if (!_isJSObject(o)) _throwCastError(o, t, true); |
| 96 return o; |
| 97 } |
| 98 |
| 99 bool _isJSObject(o) => JS('bool', '!dart.getReifiedType(o)[dart._runtimeType]'); |
| 100 |
| 67 @JSExportName('dynamic') | 101 @JSExportName('dynamic') |
| 68 final _dynamic = new Dynamic(); | 102 final _dynamic = new Dynamic(); |
| 69 | 103 |
| 70 final _initialize = _initialize2(); | 104 final _initialize = _initialize2(); |
| 71 | 105 |
| 72 _initialize2() => JS('', '''(() => { | 106 _initialize2() => JS( |
| 107 '', |
| 108 '''(() => { |
| 73 // JavaScript API forwards to runtime library. | 109 // JavaScript API forwards to runtime library. |
| 74 $TypeRep.prototype.is = function is_T(object) { | 110 $TypeRep.prototype.is = function is_T(object) { |
| 75 return dart.is(object, this); | 111 return dart.is(object, this); |
| 76 }; | 112 }; |
| 77 $TypeRep.prototype.as = function as_T(object) { | 113 $TypeRep.prototype.as = function as_T(object) { |
| 78 return dart.as(object, this); | 114 return dart.as(object, this); |
| 79 }; | 115 }; |
| 80 $TypeRep.prototype._check = function check_T(object) { | 116 $TypeRep.prototype._check = function check_T(object) { |
| 81 return dart.check(object, this); | 117 return dart.check(object, this); |
| 82 }; | 118 }; |
| 83 | 119 |
| 84 // Fast path for type `dynamic`. | 120 // Fast path for type `dynamic`. |
| 85 $Dynamic.prototype.is = function is_Dynamic(object) { | 121 $Dynamic.prototype.is = function is_Dynamic(object) { |
| 86 return true; | 122 return true; |
| 87 }; | 123 }; |
| 88 $Dynamic.prototype.as = function as_Dynamic(object) { | 124 $Dynamic.prototype.as = function as_Dynamic(object) { |
| 89 return object; | 125 return object; |
| 90 }; | 126 }; |
| 91 $Dynamic.prototype._check = function check_Dynamic(object) { | 127 $Dynamic.prototype._check = function check_Dynamic(object) { |
| 92 return object; | 128 return object; |
| 93 }; | 129 }; |
| 130 |
| 131 $LazyJSType.prototype.is = function is_T(object) { |
| 132 return $_isInstanceOfLazyJSType(object, this); |
| 133 }; |
| 134 $LazyJSType.prototype.as = function as_T(object) { |
| 135 return $_asInstanceOfLazyJSType(object, this); |
| 136 }; |
| 94 })()'''); | 137 })()'''); |
| 95 | 138 |
| 96 class Void extends TypeRep { | 139 class Void extends TypeRep { |
| 97 toString() => 'void'; | 140 toString() => 'void'; |
| 98 } | 141 } |
| 99 | 142 |
| 100 @JSExportName('void') | 143 @JSExportName('void') |
| 101 final _void = new Void(); | 144 final _void = new Void(); |
| 102 | 145 |
| 103 class Bottom extends TypeRep { | 146 class Bottom extends TypeRep { |
| 104 toString() => 'bottom'; | 147 toString() => 'bottom'; |
| 105 } | 148 } |
| 149 |
| 106 final bottom = new Bottom(); | 150 final bottom = new Bottom(); |
| 107 | 151 |
| 108 class JSObject extends TypeRep { | 152 class JSObject extends TypeRep { |
| 109 toString() => 'NativeJavaScriptObject'; | 153 toString() => 'NativeJavaScriptObject'; |
| 110 } | 154 } |
| 111 | 155 |
| 112 final jsobject = new JSObject(); | 156 final jsobject = new JSObject(); |
| 113 | 157 |
| 114 class WrappedType extends Type { | 158 class WrappedType extends Type { |
| 115 final _wrappedType; | 159 final _wrappedType; |
| 116 WrappedType(this._wrappedType); | 160 WrappedType(this._wrappedType); |
| 117 toString() => typeName(_wrappedType); | 161 toString() => typeName(_wrappedType); |
| 118 } | 162 } |
| 119 | 163 |
| 120 final AbstractFunctionType = JS('', ''' | 164 final AbstractFunctionType = JS( |
| 165 '', |
| 166 ''' |
| 121 class AbstractFunctionType extends $TypeRep { | 167 class AbstractFunctionType extends $TypeRep { |
| 122 constructor() { | 168 constructor() { |
| 123 super(); | 169 super(); |
| 124 this._stringValue = null; | 170 this._stringValue = null; |
| 125 } | 171 } |
| 126 | 172 |
| 127 toString() { return this.name; } | 173 toString() { return this.name; } |
| 128 | 174 |
| 129 get name() { | 175 get name() { |
| 130 if (this._stringValue) return this._stringValue; | 176 if (this._stringValue) return this._stringValue; |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 165 } | 211 } |
| 166 } | 212 } |
| 167 '''); | 213 '''); |
| 168 | 214 |
| 169 /// Memo table for named argument groups. A named argument packet | 215 /// Memo table for named argument groups. A named argument packet |
| 170 /// {name1 : type1, ..., namen : typen} corresponds to the path | 216 /// {name1 : type1, ..., namen : typen} corresponds to the path |
| 171 /// n, name1, type1, ...., namen, typen. The element of the map | 217 /// n, name1, type1, ...., namen, typen. The element of the map |
| 172 /// reached via this path (if any) is the canonical representative | 218 /// reached via this path (if any) is the canonical representative |
| 173 /// for this packet. | 219 /// for this packet. |
| 174 final _fnTypeNamedArgMap = JS('', 'new Map()'); | 220 final _fnTypeNamedArgMap = JS('', 'new Map()'); |
| 221 |
| 175 /// Memo table for positional argument groups. A positional argument | 222 /// Memo table for positional argument groups. A positional argument |
| 176 /// packet [type1, ..., typen] (required or optional) corresponds to | 223 /// packet [type1, ..., typen] (required or optional) corresponds to |
| 177 /// the path n, type1, ...., typen. The element reached via | 224 /// the path n, type1, ...., typen. The element reached via |
| 178 /// this path (if any) is the canonical representative for this | 225 /// this path (if any) is the canonical representative for this |
| 179 /// packet. Note that required and optional parameters packages | 226 /// packet. Note that required and optional parameters packages |
| 180 /// may have the same canonical representation. | 227 /// may have the same canonical representation. |
| 181 final _fnTypeArrayArgMap = JS('', 'new Map()'); | 228 final _fnTypeArrayArgMap = JS('', 'new Map()'); |
| 229 |
| 182 /// Memo table for function types. The index path consists of the | 230 /// Memo table for function types. The index path consists of the |
| 183 /// path length - 1, the returnType, the canonical positional argument | 231 /// path length - 1, the returnType, the canonical positional argument |
| 184 /// packet, and if present, the canonical optional or named argument | 232 /// packet, and if present, the canonical optional or named argument |
| 185 /// packet. A level of indirection could be avoided here if desired. | 233 /// packet. A level of indirection could be avoided here if desired. |
| 186 final _fnTypeTypeMap = JS('', 'new Map()'); | 234 final _fnTypeTypeMap = JS('', 'new Map()'); |
| 235 |
| 187 /// Memo table for small function types with no optional or named | 236 /// Memo table for small function types with no optional or named |
| 188 /// arguments and less than a fixed n (currently 3) number of | 237 /// arguments and less than a fixed n (currently 3) number of |
| 189 /// required arguments. Indexing into this table by the number | 238 /// required arguments. Indexing into this table by the number |
| 190 /// of required arguments yields a map which is indexed by the | 239 /// of required arguments yields a map which is indexed by the |
| 191 /// argument types themselves. The element reached via this | 240 /// argument types themselves. The element reached via this |
| 192 /// index path (if present) is the canonical function type. | 241 /// index path (if present) is the canonical function type. |
| 193 final _fnTypeSmallMap = JS('', '[new Map(), new Map(), new Map()]'); | 242 final _fnTypeSmallMap = JS('', '[new Map(), new Map(), new Map()]'); |
| 194 | 243 |
| 195 final FunctionType = JS('', ''' | 244 final FunctionType = JS( |
| 245 '', |
| 246 ''' |
| 196 class FunctionType extends $AbstractFunctionType { | 247 class FunctionType extends $AbstractFunctionType { |
| 197 static _memoizeArray(map, arr, create) { | 248 static _memoizeArray(map, arr, create) { |
| 198 let len = arr.length; | 249 let len = arr.length; |
| 199 map = FunctionType._lookupNonTerminal(map, len); | 250 map = FunctionType._lookupNonTerminal(map, len); |
| 200 for (var i = 0; i < len-1; ++i) { | 251 for (var i = 0; i < len-1; ++i) { |
| 201 map = FunctionType._lookupNonTerminal(map, arr[i]); | 252 map = FunctionType._lookupNonTerminal(map, arr[i]); |
| 202 } | 253 } |
| 203 let result = map.get(arr[len-1]); | 254 let result = map.get(arr[len-1]); |
| 204 if (result !== void 0) return result; | 255 if (result !== void 0) return result; |
| 205 map.set(arr[len-1], result = create()); | 256 map.set(arr[len-1], result = create()); |
| (...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 339 } | 390 } |
| 340 return result; | 391 return result; |
| 341 } | 392 } |
| 342 this.args = process(this.args, this.metadata); | 393 this.args = process(this.args, this.metadata); |
| 343 this.optionals = process(this.optionals, this.metadata); | 394 this.optionals = process(this.optionals, this.metadata); |
| 344 // TODO(vsm): Add named arguments. | 395 // TODO(vsm): Add named arguments. |
| 345 } | 396 } |
| 346 } | 397 } |
| 347 '''); | 398 '''); |
| 348 | 399 |
| 349 | 400 final Typedef = JS( |
| 350 final Typedef = JS('', ''' | 401 '', |
| 402 ''' |
| 351 class Typedef extends $AbstractFunctionType { | 403 class Typedef extends $AbstractFunctionType { |
| 352 constructor(name, closure) { | 404 constructor(name, closure) { |
| 353 super(); | 405 super(); |
| 354 this._name = name; | 406 this._name = name; |
| 355 this._closure = closure; | 407 this._closure = closure; |
| 356 this._functionType = null; | 408 this._functionType = null; |
| 357 } | 409 } |
| 358 | 410 |
| 359 get name() { | 411 get name() { |
| 360 return this._name; | 412 return this._name; |
| (...skipping 23 matching lines...) Expand all Loading... |
| 384 } | 436 } |
| 385 | 437 |
| 386 get metadata() { | 438 get metadata() { |
| 387 return this.functionType.metadata; | 439 return this.functionType.metadata; |
| 388 } | 440 } |
| 389 } | 441 } |
| 390 '''); | 442 '''); |
| 391 | 443 |
| 392 final _typeFormalCount = JS('', 'Symbol("_typeFormalCount")'); | 444 final _typeFormalCount = JS('', 'Symbol("_typeFormalCount")'); |
| 393 | 445 |
| 394 _functionType(definite, returnType, args, extra) => JS('', '''(() => { | 446 _functionType(definite, returnType, args, extra) => JS( |
| 447 '', |
| 448 '''(() => { |
| 395 // TODO(jmesserly): this is a bit of a retrofit, to easily fit | 449 // TODO(jmesserly): this is a bit of a retrofit, to easily fit |
| 396 // generic functions into all of the existing ways we generate function | 450 // generic functions into all of the existing ways we generate function |
| 397 // signatures. Given `(T) => [T, [T]]` we'll return a function that does | 451 // signatures. Given `(T) => [T, [T]]` we'll return a function that does |
| 398 // `(T) => _functionType(definite, T, [T])` ... we could do this in the | 452 // `(T) => _functionType(definite, T, [T])` ... we could do this in the |
| 399 // compiler instead, at a slight cost to code size. | 453 // compiler instead, at a slight cost to code size. |
| 400 if ($args === void 0 && $extra === void 0) { | 454 if ($args === void 0 && $extra === void 0) { |
| 401 const fnTypeParts = $returnType; | 455 const fnTypeParts = $returnType; |
| 402 // A closure that computes the remaining arguments. | 456 // A closure that computes the remaining arguments. |
| 403 // Return a function that makes the type. | 457 // Return a function that makes the type. |
| 404 function makeGenericFnType(...types) { | 458 function makeGenericFnType(...types) { |
| (...skipping 15 matching lines...) Expand all Loading... |
| 420 | 474 |
| 421 /// | 475 /// |
| 422 /// Create a definite function type. No substitution of dynamic for | 476 /// Create a definite function type. No substitution of dynamic for |
| 423 /// bottom occurs. | 477 /// bottom occurs. |
| 424 /// | 478 /// |
| 425 definiteFunctionType(returnType, args, extra) => | 479 definiteFunctionType(returnType, args, extra) => |
| 426 _functionType(true, returnType, args, extra); | 480 _functionType(true, returnType, args, extra); |
| 427 | 481 |
| 428 typedef(name, closure) => JS('', 'new #(#, #)', Typedef, name, closure); | 482 typedef(name, closure) => JS('', 'new #(#, #)', Typedef, name, closure); |
| 429 | 483 |
| 430 typeName(type) => JS('', '''(() => { | 484 typeName(type) => JS( |
| 485 '', |
| 486 '''(() => { |
| 431 if ($type === void 0) return "undefined type"; | 487 if ($type === void 0) return "undefined type"; |
| 432 if ($type === null) return "null type"; | 488 if ($type === null) return "null type"; |
| 433 // Non-instance types | 489 // Non-instance types |
| 434 if ($type instanceof $TypeRep) { | 490 if ($type instanceof $TypeRep) { |
| 435 if ($type instanceof $Typedef) { | 491 if ($type instanceof $Typedef) { |
| 436 return $type.name + "(" + $type.functionType.toString() + ")"; | 492 return $type.name + "(" + $type.functionType.toString() + ")"; |
| 437 } | 493 } |
| 438 return $type.toString(); | 494 return $type.toString(); |
| 439 } | 495 } |
| 440 | 496 |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 473 return "JSObject<" + $type.name + ">"; | 529 return "JSObject<" + $type.name + ">"; |
| 474 })()'''); | 530 })()'''); |
| 475 | 531 |
| 476 /// Get the underlying function type, potentially from the call method | 532 /// Get the underlying function type, potentially from the call method |
| 477 /// for a class type. | 533 /// for a class type. |
| 478 getImplicitFunctionType(type) { | 534 getImplicitFunctionType(type) { |
| 479 if (isFunctionType(type)) return type; | 535 if (isFunctionType(type)) return type; |
| 480 return getMethodTypeFromType(type, 'call'); | 536 return getMethodTypeFromType(type, 'call'); |
| 481 } | 537 } |
| 482 | 538 |
| 483 bool isFunctionType(type) => | 539 bool isFunctionType(type) => JS('bool', '# instanceof # || # === #', type, |
| 484 JS('bool', '# instanceof # || # === #', | 540 AbstractFunctionType, type, Function); |
| 485 type, AbstractFunctionType, type, Function); | 541 |
| 542 isLazyJSSubtype(LazyJSType t1, LazyJSType t2, covariant) { |
| 543 if (t1 == t2) return true; |
| 544 |
| 545 // All anonymous JS types are subtypes of each other. |
| 546 if (t1._jsTypeCallback == null || t2._jsTypeCallback == null) return true; |
| 547 return isClassSubType(t1._rawJSType, t2._rawJSType, covariant); |
| 548 } |
| 486 | 549 |
| 487 /// Returns true if [ft1] <: [ft2]. | 550 /// Returns true if [ft1] <: [ft2]. |
| 488 /// Returns false if [ft1] </: [ft2] in both spec and strong mode | 551 /// Returns false if [ft1] </: [ft2] in both spec and strong mode |
| 489 /// Returns null if [ft1] </: [ft2] in strong mode, but spec mode | 552 /// Returns null if [ft1] </: [ft2] in strong mode, but spec mode |
| 490 /// may differ | 553 /// may differ |
| 491 /// If [covariant] is true, then we are checking subtyping in a covariant | 554 /// If [covariant] is true, then we are checking subtyping in a covariant |
| 492 /// position, and hence the direction of the check for function types | 555 /// position, and hence the direction of the check for function types |
| 493 /// corresponds to the direction of the check according to the Dart spec. | 556 /// corresponds to the direction of the check according to the Dart spec. |
| 494 isFunctionSubtype(ft1, ft2, covariant) => JS('', '''(() => { | 557 isFunctionSubtype(ft1, ft2, covariant) => JS( |
| 558 '', |
| 559 '''(() => { |
| 495 if ($ft2 === $Function) { | 560 if ($ft2 === $Function) { |
| 496 return true; | 561 return true; |
| 497 } | 562 } |
| 498 | 563 |
| 499 if ($ft1 === $Function) { | 564 if ($ft1 === $Function) { |
| 500 return false; | 565 return false; |
| 501 } | 566 } |
| 502 | 567 |
| 503 let ret1 = $ft1.returnType; | 568 let ret1 = $ft1.returnType; |
| 504 let ret2 = $ft2.returnType; | 569 let ret2 = $ft2.returnType; |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 564 // Dart allows void functions to subtype dynamic functions, but not | 629 // Dart allows void functions to subtype dynamic functions, but not |
| 565 // other functions. | 630 // other functions. |
| 566 if (ret1 === $_void) return (ret2 === $dynamic); | 631 if (ret1 === $_void) return (ret2 === $dynamic); |
| 567 if (!$_isSubtype(ret1, ret2, $covariant)) return null; | 632 if (!$_isSubtype(ret1, ret2, $covariant)) return null; |
| 568 return true; | 633 return true; |
| 569 })()'''); | 634 })()'''); |
| 570 | 635 |
| 571 /// TODO(leafp): This duplicates code in operations.dart. | 636 /// TODO(leafp): This duplicates code in operations.dart. |
| 572 /// I haven't found a way to factor it out that makes the | 637 /// I haven't found a way to factor it out that makes the |
| 573 /// code generator happy though. | 638 /// code generator happy though. |
| 574 _subtypeMemo(f) => JS('', '''(() => { | 639 _subtypeMemo(f) => JS( |
| 640 '', |
| 641 '''(() => { |
| 575 let memo = new Map(); | 642 let memo = new Map(); |
| 576 return (t1, t2) => { | 643 return (t1, t2) => { |
| 577 let map = memo.get(t1); | 644 let map = memo.get(t1); |
| 578 let result; | 645 let result; |
| 579 if (map) { | 646 if (map) { |
| 580 result = map.get(t2); | 647 result = map.get(t2); |
| 581 if (result !== void 0) return result; | 648 if (result !== void 0) return result; |
| 582 } else { | 649 } else { |
| 583 memo.set(t1, map = new Map()); | 650 memo.set(t1, map = new Map()); |
| 584 } | 651 } |
| 585 result = $f(t1, t2); | 652 result = $f(t1, t2); |
| 586 map.set(t2, result); | 653 map.set(t2, result); |
| 587 return result; | 654 return result; |
| 588 }; | 655 }; |
| 589 })()'''); | 656 })()'''); |
| 590 | 657 |
| 591 /// Returns true if [t1] <: [t2]. | 658 /// Returns true if [t1] <: [t2]. |
| 592 /// Returns false if [t1] </: [t2] in both spec and strong mode | 659 /// Returns false if [t1] </: [t2] in both spec and strong mode |
| 593 /// Returns undefined if [t1] </: [t2] in strong mode, but spec | 660 /// Returns undefined if [t1] </: [t2] in strong mode, but spec |
| 594 /// mode may differ | 661 /// mode may differ |
| 595 final isSubtype = | 662 final isSubtype = JS( |
| 596 JS('', '$_subtypeMemo((t1, t2) => (t1 === t2) || $_isSubtype(t1, t2, true))'
); | 663 '', '$_subtypeMemo((t1, t2) => (t1 === t2) || $_isSubtype(t1, t2, true))'); |
| 597 | 664 |
| 598 _isBottom(type) => JS('bool', '# == #', type, bottom); | 665 _isBottom(type) => JS('bool', '# == #', type, bottom); |
| 599 | 666 |
| 600 _isTop(type) => JS('bool', '# == # || # == #', type, Object, type, dynamic); | 667 _isTop(type) => JS('bool', '# == # || # == #', type, Object, type, dynamic); |
| 601 | 668 |
| 602 _isSubtype(t1, t2, covariant) => JS('', '''(() => { | 669 _isSubtype(t1, t2, covariant) => JS( |
| 670 '', |
| 671 '''(() => { |
| 603 if ($t1 === $t2) return true; | 672 if ($t1 === $t2) return true; |
| 604 | 673 |
| 605 // Trivially true. | 674 // Trivially true. |
| 606 if ($_isTop($t2) || $_isBottom($t1)) { | 675 if ($_isTop($t2) || $_isBottom($t1)) { |
| 607 return true; | 676 return true; |
| 608 } | 677 } |
| 609 | 678 |
| 610 // Trivially false. | 679 // Trivially false. |
| 611 if ($_isBottom($t2)) return null; | 680 if ($_isBottom($t2)) return null; |
| 612 if ($_isTop($t1)) { | 681 if ($_isTop($t1)) { |
| (...skipping 13 matching lines...) Expand all Loading... |
| 626 // Function subtyping. | 695 // Function subtyping. |
| 627 | 696 |
| 628 // Handle Objects with call methods. Those are functions | 697 // Handle Objects with call methods. Those are functions |
| 629 // even if they do not *nominally* subtype core.Function. | 698 // even if they do not *nominally* subtype core.Function. |
| 630 t1 = $getImplicitFunctionType(t1); | 699 t1 = $getImplicitFunctionType(t1); |
| 631 if (!t1) return false; | 700 if (!t1) return false; |
| 632 | 701 |
| 633 if ($isFunctionType($t1) && $isFunctionType($t2)) { | 702 if ($isFunctionType($t1) && $isFunctionType($t2)) { |
| 634 return $isFunctionSubtype($t1, $t2, $covariant); | 703 return $isFunctionSubtype($t1, $t2, $covariant); |
| 635 } | 704 } |
| 705 |
| 706 if ($t1 instanceof $LazyJSType && $t2 instanceof $LazyJSType) { |
| 707 return $isLazyJSSubtype($t1, $t2, $covariant); |
| 708 } |
| 709 |
| 636 return false; | 710 return false; |
| 637 })()'''); | 711 })()'''); |
| 638 | 712 |
| 639 isClassSubType(t1, t2, covariant) => JS('', '''(() => { | 713 isClassSubType(t1, t2, covariant) => JS( |
| 714 '', |
| 715 '''(() => { |
| 640 // We support Dart's covariant generics with the caveat that we do not | 716 // We support Dart's covariant generics with the caveat that we do not |
| 641 // substitute bottom for dynamic in subtyping rules. | 717 // substitute bottom for dynamic in subtyping rules. |
| 642 // I.e., given T1, ..., Tn where at least one Ti != dynamic we disallow: | 718 // I.e., given T1, ..., Tn where at least one Ti != dynamic we disallow: |
| 643 // - S !<: S<T1, ..., Tn> | 719 // - S !<: S<T1, ..., Tn> |
| 644 // - S<dynamic, ..., dynamic> !<: S<T1, ..., Tn> | 720 // - S<dynamic, ..., dynamic> !<: S<T1, ..., Tn> |
| 645 if ($t1 == $t2) return true; | 721 if ($t1 == $t2) return true; |
| 646 | 722 |
| 647 if ($t1 == $Object) return false; | 723 if ($t1 == $Object) return false; |
| 648 | 724 |
| 649 // If t1 is a JS Object, we may not hit core.Object. | 725 // If t1 is a JS Object, we may not hit core.Object. |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 710 // so the answer is indefinite. | 786 // so the answer is indefinite. |
| 711 if (indefinite) return null; | 787 if (indefinite) return null; |
| 712 // We found no definite supertypes and no indefinite supertypes, so we | 788 // We found no definite supertypes and no indefinite supertypes, so we |
| 713 // can return false. | 789 // can return false. |
| 714 return false; | 790 return false; |
| 715 })()'''); | 791 })()'''); |
| 716 | 792 |
| 717 // TODO(jmesserly): this isn't currently used, but it could be if we want | 793 // TODO(jmesserly): this isn't currently used, but it could be if we want |
| 718 // `obj is NonGroundType<T,S>` to be rejected at runtime instead of compile | 794 // `obj is NonGroundType<T,S>` to be rejected at runtime instead of compile |
| 719 // time. | 795 // time. |
| 720 isGroundType(type) => JS('', '''(() => { | 796 isGroundType(type) => JS( |
| 797 '', |
| 798 '''(() => { |
| 721 // TODO(vsm): Cache this if we start using it at runtime. | 799 // TODO(vsm): Cache this if we start using it at runtime. |
| 722 | 800 |
| 723 if ($type instanceof $AbstractFunctionType) { | 801 if ($type instanceof $AbstractFunctionType) { |
| 724 if (!$_isTop($type.returnType)) return false; | 802 if (!$_isTop($type.returnType)) return false; |
| 725 for (let i = 0; i < $type.args.length; ++i) { | 803 for (let i = 0; i < $type.args.length; ++i) { |
| 726 if (!$_isBottom($type.args[i])) return false; | 804 if (!$_isBottom($type.args[i])) return false; |
| 727 } | 805 } |
| 728 for (let i = 0; i < $type.optionals.length; ++i) { | 806 for (let i = 0; i < $type.optionals.length; ++i) { |
| 729 if (!$_isBottom($type.optionals[i])) return false; | 807 if (!$_isBottom($type.optionals[i])) return false; |
| 730 } | 808 } |
| 731 let names = $getOwnPropertyNames($type.named); | 809 let names = $getOwnPropertyNames($type.named); |
| 732 for (let i = 0; i < names.length; ++i) { | 810 for (let i = 0; i < names.length; ++i) { |
| 733 if (!$_isBottom($type.named[names[i]])) return false; | 811 if (!$_isBottom($type.named[names[i]])) return false; |
| 734 } | 812 } |
| 735 return true; | 813 return true; |
| 736 } | 814 } |
| 737 | 815 |
| 738 let typeArgs = $getGenericArgs($type); | 816 let typeArgs = $getGenericArgs($type); |
| 739 if (!typeArgs) return true; | 817 if (!typeArgs) return true; |
| 740 for (let t of typeArgs) { | 818 for (let t of typeArgs) { |
| 741 if (t != $Object && t != $dynamic) return false; | 819 if (t != $Object && t != $dynamic) return false; |
| 742 } | 820 } |
| 743 return true; | 821 return true; |
| 744 })()'''); | 822 })()'''); |
| OLD | NEW |