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() { | |
60 _initialize; | |
61 } | |
62 String get name => this.toString(); | 59 String get name => this.toString(); |
| 60 |
| 61 @JSExportName('is') |
| 62 bool is_T(object) => instanceOf(object, this); |
| 63 |
| 64 @JSExportName('as') |
| 65 as_T(object) => cast(object, this); |
| 66 |
| 67 @JSExportName('_check') |
| 68 check_T(object) => check(object, this); |
63 } | 69 } |
64 | 70 |
65 class Dynamic extends TypeRep { | 71 class Dynamic extends TypeRep { |
66 toString() => 'dynamic'; | 72 toString() => 'dynamic'; |
| 73 |
| 74 @JSExportName('is') |
| 75 bool is_T(object) => true; |
| 76 |
| 77 @JSExportName('as') |
| 78 as_T(object) => object; |
| 79 |
| 80 @JSExportName('_check') |
| 81 check_T(object) => object; |
67 } | 82 } |
68 | 83 |
69 class LazyJSType extends TypeRep { | 84 class LazyJSType extends TypeRep { |
70 final _jsTypeCallback; | 85 final Function() _rawJSType; |
71 final _dartName; | 86 final String _dartName; |
72 | 87 |
73 LazyJSType(this._jsTypeCallback, this._dartName); | 88 LazyJSType(this._rawJSType, this._dartName); |
74 | 89 |
75 get _rawJSType => JS('', '#()', _jsTypeCallback); | 90 toString() => typeName(_rawJSType()); |
76 | 91 |
77 toString() => _jsTypeCallback != null ? typeName(_rawJSType) : _dartName; | 92 rawJSTypeForCheck() { |
| 93 var raw = _rawJSType(); |
| 94 if (raw != null) return raw; |
| 95 _warn('Cannot find native JavaScript type ($_dartName) for type check'); |
| 96 return _dynamic; |
| 97 } |
| 98 |
| 99 @JSExportName('is') |
| 100 bool is_T(obj) => instanceOf(obj, rawJSTypeForCheck()); |
| 101 |
| 102 @JSExportName('as') |
| 103 as_T(obj) => cast(obj, rawJSTypeForCheck()); |
| 104 |
| 105 @JSExportName('_check') |
| 106 check_T(obj) => check(obj, rawJSTypeForCheck()); |
| 107 } |
| 108 |
| 109 /// An anonymous JS type |
| 110 /// |
| 111 /// For the purposes of subtype checks, these match any JS type. |
| 112 class AnonymousJSType extends TypeRep { |
| 113 final String _dartName; |
| 114 AnonymousJSType(this._dartName); |
| 115 toString() => _dartName; |
| 116 |
| 117 @JSExportName('is') |
| 118 bool is_T(obj) => _isJSType(getReifiedType(obj)); |
| 119 |
| 120 @JSExportName('as') |
| 121 as_T(obj) => is_T(obj) ? obj : cast(obj, this); |
| 122 |
| 123 @JSExportName('_check') |
| 124 check_T(obj) => is_T(obj) ? obj : check(obj, this); |
78 } | 125 } |
79 | 126 |
80 void _warn(arg) { | 127 void _warn(arg) { |
81 JS('void', 'console.warn(#)', arg); | 128 JS('void', 'console.warn(#)', arg); |
82 } | 129 } |
83 | 130 |
84 _isInstanceOfLazyJSType(o, LazyJSType t) { | 131 bool _isJSType(t) => JS('bool', '!#[dart._runtimeType]', t); |
85 if (t._jsTypeCallback != null) { | 132 |
86 if (t._rawJSType == null) { | 133 var _lazyJSTypes = JS('', 'new Map()'); |
87 var expected = t._dartName; | 134 var _anonymousJSTypes = JS('', 'new Map()'); |
88 var actual = typeName(getReifiedType(o)); | 135 |
89 _warn('Cannot find native JavaScript type ($expected) ' | 136 lazyJSType(Function() getJSTypeCallback, String name) { |
90 'to type check $actual'); | 137 var ret = JS('', '#.get(#)', _lazyJSTypes, name); |
91 return true; | 138 if (ret == null) { |
92 } | 139 ret = new LazyJSType(getJSTypeCallback, name); |
93 return JS('bool', 'dart.is(#, #)', o, t._rawJSType); | 140 JS('', '#.set(#, #)', _lazyJSTypes, name, ret); |
94 } | 141 } |
95 if (o == null) return false; | 142 return ret; |
96 // Anonymous case: match any JS type. | |
97 return _isJSObject(o); | |
98 } | 143 } |
99 | 144 |
100 _asInstanceOfLazyJSType(o, LazyJSType t) { | 145 anonymousJSType(String name) { |
101 if (t._jsTypeCallback != null) { | 146 var ret = JS('', '#.get(#)', _anonymousJSTypes, name); |
102 if (t._rawJSType == null) { | 147 if (ret == null) { |
103 var expected = t._dartName; | 148 ret = new AnonymousJSType(name); |
104 var actual = typeName(getReifiedType(o)); | 149 JS('', '#.set(#, #)', _anonymousJSTypes, name, ret); |
105 _warn('Cannot find native JavaScript type ($expected) ' | |
106 'to type check $actual'); | |
107 return o; | |
108 } | |
109 return JS('bool', 'dart.as(#, #)', o, t._rawJSType); | |
110 } | 150 } |
111 // Anonymous case: allow any JS type. | 151 return ret; |
112 if (o == null) return null; | |
113 if (!_isJSObject(o)) _throwCastError(o, t, true); | |
114 return o; | |
115 } | 152 } |
116 | 153 |
117 bool _isJSObject(o) => | |
118 JS('bool', '!dart.getReifiedType(#)[dart._runtimeType]', o); | |
119 | |
120 bool _isJSType(t) => JS('bool', '!#[dart._runtimeType]', t); | |
121 | |
122 @JSExportName('dynamic') | 154 @JSExportName('dynamic') |
123 final _dynamic = new Dynamic(); | 155 final _dynamic = new Dynamic(); |
124 | 156 |
125 final _initialize = _initialize2(); | |
126 | |
127 _initialize2() => JS( | |
128 '', | |
129 '''(() => { | |
130 // JavaScript API forwards to runtime library. | |
131 $TypeRep.prototype.is = function is_T(object) { | |
132 return dart.is(object, this); | |
133 }; | |
134 $TypeRep.prototype.as = function as_T(object) { | |
135 return dart.as(object, this); | |
136 }; | |
137 $TypeRep.prototype._check = function check_T(object) { | |
138 return dart.check(object, this); | |
139 }; | |
140 | |
141 // Fast path for type `dynamic`. | |
142 $Dynamic.prototype.is = function is_Dynamic(object) { | |
143 return true; | |
144 }; | |
145 $Dynamic.prototype.as = function as_Dynamic(object) { | |
146 return object; | |
147 }; | |
148 $Dynamic.prototype._check = function check_Dynamic(object) { | |
149 return object; | |
150 }; | |
151 | |
152 $LazyJSType.prototype.is = function is_T(object) { | |
153 return $_isInstanceOfLazyJSType(object, this); | |
154 }; | |
155 $LazyJSType.prototype.as = function as_T(object) { | |
156 return $_asInstanceOfLazyJSType(object, this); | |
157 }; | |
158 $LazyJSType.prototype._check = function check_T(object) { | |
159 return $_asInstanceOfLazyJSType(object, this); | |
160 }; | |
161 })()'''); | |
162 | |
163 class Void extends TypeRep { | 157 class Void extends TypeRep { |
164 toString() => 'void'; | 158 toString() => 'void'; |
165 } | 159 } |
166 | 160 |
167 @JSExportName('void') | 161 @JSExportName('void') |
168 final _void = new Void(); | 162 final _void = new Void(); |
169 | 163 |
170 class Bottom extends TypeRep { | 164 class Bottom extends TypeRep { |
171 toString() => 'bottom'; | 165 toString() => 'bottom'; |
172 } | 166 } |
(...skipping 529 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
702 } | 696 } |
703 if (tag) return "Not a type: " + tag.name; | 697 if (tag) return "Not a type: " + tag.name; |
704 return "JSObject<" + $type.name + ">"; | 698 return "JSObject<" + $type.name + ">"; |
705 })()'''); | 699 })()'''); |
706 | 700 |
707 /// Returns `true` if we have a non-generic function type representation or the | 701 /// Returns `true` if we have a non-generic function type representation or the |
708 /// type for `Function`, which is a supertype of all functions in Dart. | 702 /// type for `Function`, which is a supertype of all functions in Dart. |
709 bool _isFunctionType(type) => JS('bool', '# instanceof # || # === #', type, | 703 bool _isFunctionType(type) => JS('bool', '# instanceof # || # === #', type, |
710 AbstractFunctionType, type, Function); | 704 AbstractFunctionType, type, Function); |
711 | 705 |
712 isLazyJSSubtype(t1, LazyJSType t2, isCovariant) { | |
713 // All JS types are subtypes of anonymous JS types. | |
714 if (t2._jsTypeCallback == null) { | |
715 return _isJSType(t1); | |
716 } | |
717 return _isSubtype(t1, t2._rawJSType, isCovariant); | |
718 } | |
719 | |
720 /// Returns true if [ft1] <: [ft2]. | 706 /// Returns true if [ft1] <: [ft2]. |
721 /// Returns false if [ft1] </: [ft2] in both spec and strong mode | 707 /// Returns false if [ft1] </: [ft2] in both spec and strong mode |
722 /// Returns null if [ft1] </: [ft2] in strong mode, but spec mode | 708 /// Returns null if [ft1] </: [ft2] in strong mode, but spec mode |
723 /// may differ | 709 /// may differ |
724 /// If [isCovariant] is true, then we are checking subtyping in a covariant | 710 /// If [isCovariant] is true, then we are checking subtyping in a covariant |
725 /// position, and hence the direction of the check for function types | 711 /// position, and hence the direction of the check for function types |
726 /// corresponds to the direction of the check according to the Dart spec. | 712 /// corresponds to the direction of the check according to the Dart spec. |
727 isFunctionSubtype(ft1, ft2, isCovariant) => JS( | 713 isFunctionSubtype(ft1, ft2, isCovariant) => JS( |
728 '', | 714 '', |
729 '''(() => { | 715 '''(() => { |
(...skipping 154 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
884 | 870 |
885 // "Traditional" name-based subtype check. Avoid passing | 871 // "Traditional" name-based subtype check. Avoid passing |
886 // function types to the class subtype checks, since we don't | 872 // function types to the class subtype checks, since we don't |
887 // currently distinguish between generic typedefs and classes. | 873 // currently distinguish between generic typedefs and classes. |
888 if (!($t1 instanceof $AbstractFunctionType) && | 874 if (!($t1 instanceof $AbstractFunctionType) && |
889 !($t2 instanceof $AbstractFunctionType)) { | 875 !($t2 instanceof $AbstractFunctionType)) { |
890 let result = $isClassSubType($t1, $t2, $isCovariant); | 876 let result = $isClassSubType($t1, $t2, $isCovariant); |
891 if (result === true || result === null) return result; | 877 if (result === true || result === null) return result; |
892 } | 878 } |
893 | 879 |
| 880 if ($t2 instanceof $AnonymousJSType) { |
| 881 // All JS types are subtypes of anonymous JS types. |
| 882 return $_isJSType($t1); |
| 883 } |
894 if ($t2 instanceof $LazyJSType) { | 884 if ($t2 instanceof $LazyJSType) { |
895 return $isLazyJSSubtype($t1, $t2, $isCovariant); | 885 return $_isSubtype($t1, $t2.rawJSTypeForCheck(), isCovariant); |
896 } | 886 } |
897 | 887 |
898 // Function subtyping. | 888 // Function subtyping. |
899 | 889 |
900 // Handle Objects with call methods. Those are functions | 890 // Handle Objects with call methods. Those are functions |
901 // even if they do not *nominally* subtype core.Function. | 891 // even if they do not *nominally* subtype core.Function. |
902 if (!$_isFunctionType($t1)) { | 892 if (!$_isFunctionType($t1)) { |
903 $t1 = $getMethodType($t1, 'call'); | 893 $t1 = $getMethodType($t1, 'call'); |
904 if ($t1 == null) return false; | 894 if ($t1 == null) return false; |
905 } | 895 } |
(...skipping 158 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1064 return true; | 1054 return true; |
1065 } | 1055 } |
1066 | 1056 |
1067 let typeArgs = $getGenericArgs($type); | 1057 let typeArgs = $getGenericArgs($type); |
1068 if (!typeArgs) return true; | 1058 if (!typeArgs) return true; |
1069 for (let t of typeArgs) { | 1059 for (let t of typeArgs) { |
1070 if (t != $Object && t != $dynamic) return false; | 1060 if (t != $Object && t != $dynamic) return false; |
1071 } | 1061 } |
1072 return true; | 1062 return true; |
1073 })()'''); | 1063 })()'''); |
OLD | NEW |