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 |