OLD | NEW |
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 | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 /* This library defines the representation of runtime types. | |
6 */ | |
7 | |
8 dart_library.library('dart/_types', null, /* Imports */[ | 1 dart_library.library('dart/_types', null, /* Imports */[ |
9 ], /* Lazy Imports */[ | |
10 'dart/_utils', | 2 'dart/_utils', |
| 3 'dart/_rtti' |
| 4 ], /* Lazy imports */[ |
11 'dart/core', | 5 'dart/core', |
12 'dart/_classes', | 6 'dart/_classes' |
13 'dart/_rtti' | 7 ], function(exports, utils, rtti, core, classes) { |
14 ], function(exports, dart_utils, core, classes, rtti) { | |
15 'use strict'; | 8 'use strict'; |
16 | 9 const assert = utils.assert; |
17 const getOwnPropertyNames = Object.getOwnPropertyNames; | 10 const getOwnPropertyNames = Object.getOwnPropertyNames; |
18 | 11 const TypeRep = class TypeRep extends rtti.LazyTagged(() => core.Type) { |
19 const assert = dart_utils.assert_; | 12 get name() { |
20 | 13 return this.toString(); |
21 /** | 14 } |
22 * Types in dart are represented at runtime as follows. | 15 }; |
23 * - Normal nominal types, produced from classes, are represented | 16 const Dynamic = class Dynamic extends TypeRep { |
24 * at runtime by the JS class of which they are an instance. | 17 toString() { |
25 * If the type is the result of instantiating a generic class, | 18 return "dynamic"; |
26 * then the "classes" module manages the association between the | 19 } |
27 * instantiated class and the original class declaration | 20 }; |
28 * and the type arguments with which it was instantiated. This | 21 const dynamic = new Dynamic(); |
29 * assocation can be queried via the "classes" module". | 22 const Void = class Void extends TypeRep { |
30 * | 23 toString() { |
31 * - All other types are represented as instances of class TypeRep, | 24 return "void"; |
32 * defined in this module. | 25 } |
33 * - Dynamic, Void, and Bottom are singleton instances of sentinal | 26 }; |
34 * classes. | 27 const void$ = new Void(); |
35 * - Function types are instances of subclasses of AbstractFunctionType. | 28 const Bottom = class Bottom extends TypeRep { |
36 * | 29 toString() { |
37 * Function types are represented in one of two ways: | 30 return "bottom"; |
38 * - As an instance of FunctionType. These are eagerly computed. | 31 } |
39 * - As an instance of TypeDef. The TypeDef representation lazily | 32 }; |
40 * computes an instance of FunctionType, and delegates to that instance. | 33 const bottom = new Bottom(); |
41 * | 34 const JSObject = class JSObject extends TypeRep { |
42 * All types satisfy the following interface: | 35 toString() { |
43 * get String name; | 36 return "NativeJavaScriptObject"; |
44 * String toString(); | 37 } |
45 * | 38 }; |
46 */ | 39 const jsobject = new JSObject(); |
47 class TypeRep extends rtti.LazyTagged(() => core.Type) { | 40 const AbstractFunctionType = class AbstractFunctionType extends TypeRep { |
48 get name() {return this.toString();} | |
49 } | |
50 | |
51 class Dynamic extends TypeRep { | |
52 toString() { return "dynamic"; } | |
53 } | |
54 let dynamicR = new Dynamic(); | |
55 exports.dynamic = dynamicR; | |
56 | |
57 class Void extends TypeRep { | |
58 toString() { return "void"; } | |
59 } | |
60 | |
61 let voidR = new Void(); | |
62 exports.void = voidR; | |
63 | |
64 class Bottom extends TypeRep { | |
65 toString() { return "bottom"; } | |
66 } | |
67 let bottomR = new Bottom(); | |
68 exports.bottom = bottomR; | |
69 | |
70 class JSObject extends TypeRep { | |
71 toString() { return "NativeJavaScriptObject"; } | |
72 } | |
73 let jsobjectR = new JSObject(); | |
74 exports.jsobject = jsobjectR; | |
75 | |
76 class AbstractFunctionType extends TypeRep { | |
77 constructor() { | 41 constructor() { |
78 super(); | 42 super(); |
79 this._stringValue = null; | 43 this._stringValue = null; |
80 } | 44 } |
81 | 45 toString() { |
82 toString() { return this.name; } | 46 return this.name; |
83 | 47 } |
84 get name() { | 48 get name() { |
85 if (this._stringValue) return this._stringValue; | 49 if (this._stringValue) return this._stringValue; |
86 | |
87 let buffer = '('; | 50 let buffer = '('; |
88 for (let i = 0; i < this.args.length; ++i) { | 51 for (let i = 0; i < this.args.length; ++i) { |
89 if (i > 0) { | 52 if (i > 0) { |
90 buffer += ', '; | 53 buffer += ', '; |
91 } | 54 } |
92 buffer += typeName(this.args[i]); | 55 buffer += typeName(this.args[i]); |
93 } | 56 } |
94 if (this.optionals.length > 0) { | 57 if (this.optionals.length > 0) { |
95 if (this.args.length > 0) buffer += ', '; | 58 if (this.args.length > 0) buffer += ', '; |
96 buffer += '['; | 59 buffer += '['; |
97 for (let i = 0; i < this.optionals.length; ++i) { | 60 for (let i = 0; i < this.optionals.length; ++i) { |
98 if (i > 0) { | 61 if (i > 0) { |
99 buffer += ', '; | 62 buffer += ', '; |
100 } | 63 } |
101 buffer += typeName(this.optionals[i]); | 64 buffer += typeName(this.optionals[i]); |
102 } | 65 } |
103 buffer += ']'; | 66 buffer += ']'; |
104 } else if (Object.keys(this.named).length > 0) { | 67 } else if (Object.keys(this.named).length > 0) { |
105 if (this.args.length > 0) buffer += ', '; | 68 if (this.args.length > 0) buffer += ', '; |
106 buffer += '{'; | 69 buffer += '{'; |
107 let names = getOwnPropertyNames(this.named).sort(); | 70 let names = getOwnPropertyNames(this.named).sort(); |
108 for (let i = 0; i < names.length; ++i) { | 71 for (let i = 0; i < names.length; ++i) { |
109 if (i > 0) { | 72 if (i > 0) { |
110 buffer += ', '; | 73 buffer += ', '; |
111 } | 74 } |
112 buffer += names[i] + ': ' + typeName(this.named[names[i]]); | 75 buffer += names[i] + ': ' + typeName(this.named[names[i]]); |
113 } | 76 } |
114 buffer += '}'; | 77 buffer += '}'; |
115 } | 78 } |
116 | |
117 buffer += ') -> ' + typeName(this.returnType); | 79 buffer += ') -> ' + typeName(this.returnType); |
118 this._stringValue = buffer; | 80 this._stringValue = buffer; |
119 return buffer; | 81 return buffer; |
120 } | 82 } |
121 } | 83 }; |
122 | 84 const FunctionType = class FunctionType extends AbstractFunctionType { |
123 class FunctionType extends AbstractFunctionType { | |
124 /** | |
125 * Construct a function type. There are two arrow constructors, | |
126 * distinguished by the "definite" flag. | |
127 * | |
128 * The fuzzy arrow (definite is false) treats any arguments | |
129 * of type dynamic as having type bottom, and will always be | |
130 * called with a dynamic invoke. | |
131 * | |
132 * The definite arrow (definite is true) leaves arguments unchanged. | |
133 * | |
134 * We eagerly canonize the argument types to avoid having to deal with | |
135 * this logic in multiple places. | |
136 * | |
137 * TODO(leafp): Figure out how to present this to the user. How | |
138 * should these be printed out? | |
139 */ | |
140 constructor(definite, returnType, args, optionals, named) { | 85 constructor(definite, returnType, args, optionals, named) { |
141 super(); | 86 super(); |
142 this.definite = definite; | 87 this.definite = definite; |
143 this.returnType = returnType; | 88 this.returnType = returnType; |
144 this.args = args; | 89 this.args = args; |
145 this.optionals = optionals; | 90 this.optionals = optionals; |
146 this.named = named; | 91 this.named = named; |
147 | |
148 // TODO(vsm): This is just parameter metadata for now. | |
149 this.metadata = []; | 92 this.metadata = []; |
150 function process(array, metadata) { | 93 function process(array, metadata) { |
151 var result = []; | 94 var result = []; |
152 for (var i = 0; i < array.length; ++i) { | 95 for (var i = 0; i < array.length; ++i) { |
153 var arg = array[i]; | 96 var arg = array[i]; |
154 if (arg instanceof Array) { | 97 if (arg instanceof Array) { |
155 metadata.push(arg.slice(1)); | 98 metadata.push(arg.slice(1)); |
156 result.push(arg[0]); | 99 result.push(arg[0]); |
157 } else { | 100 } else { |
158 metadata.push([]); | 101 metadata.push([]); |
159 result.push(arg); | 102 result.push(arg); |
160 } | 103 } |
161 } | 104 } |
162 return result; | 105 return result; |
163 } | 106 } |
164 this.args = process(this.args, this.metadata); | 107 this.args = process(this.args, this.metadata); |
165 this.optionals = process(this.optionals, this.metadata); | 108 this.optionals = process(this.optionals, this.metadata); |
166 // TODO(vsm): Add named arguments. | |
167 this._canonize(); | 109 this._canonize(); |
168 } | 110 } |
169 _canonize() { | 111 _canonize() { |
170 if (this.definite) return; | 112 if (this.definite) return; |
171 | |
172 function replace(a) { | 113 function replace(a) { |
173 return (a == dynamicR) ? bottomR : a; | 114 return a == dynamic ? bottom : a; |
174 } | 115 } |
175 | |
176 this.args = this.args.map(replace); | 116 this.args = this.args.map(replace); |
177 | |
178 if (this.optionals.length > 0) { | 117 if (this.optionals.length > 0) { |
179 this.optionals = this.optionals.map(replace); | 118 this.optionals = this.optionals.map(replace); |
180 } | 119 } |
181 | |
182 if (Object.keys(this.named).length > 0) { | 120 if (Object.keys(this.named).length > 0) { |
183 let r = {}; | 121 let r = {}; |
184 for (let name of getOwnPropertyNames(this.named)) { | 122 for (let name of getOwnPropertyNames(this.named)) { |
185 r[name] = replace(this.named[name]); | 123 r[name] = replace(this.named[name]); |
186 } | 124 } |
187 this.named = r; | 125 this.named = r; |
188 } | 126 } |
189 } | 127 } |
190 } | 128 }; |
191 | 129 const Typedef = class Typedef extends AbstractFunctionType { |
192 class Typedef extends AbstractFunctionType { | |
193 constructor(name, closure) { | 130 constructor(name, closure) { |
194 super(); | 131 super(); |
195 this._name = name; | 132 this._name = name; |
196 this._closure = closure; | 133 this._closure = closure; |
197 this._functionType = null; | 134 this._functionType = null; |
198 } | 135 } |
199 | |
200 get definite() { | 136 get definite() { |
201 return this._functionType.definite; | 137 return this._functionType.definite; |
202 } | 138 } |
203 | |
204 get name() { | 139 get name() { |
205 return this._name; | 140 return this._name; |
206 } | 141 } |
207 | |
208 get functionType() { | 142 get functionType() { |
209 if (!this._functionType) { | 143 if (!this._functionType) { |
210 this._functionType = this._closure(); | 144 this._functionType = this._closure(); |
211 } | 145 } |
212 return this._functionType; | 146 return this._functionType; |
213 } | 147 } |
214 | |
215 get returnType() { | 148 get returnType() { |
216 return this.functionType.returnType; | 149 return this.functionType.returnType; |
217 } | 150 } |
218 | |
219 get args() { | 151 get args() { |
220 return this.functionType.args; | 152 return this.functionType.args; |
221 } | 153 } |
222 | |
223 get optionals() { | 154 get optionals() { |
224 return this.functionType.optionals; | 155 return this.functionType.optionals; |
225 } | 156 } |
226 | |
227 get named() { | 157 get named() { |
228 return this.functionType.named; | 158 return this.functionType.named; |
229 } | 159 } |
230 | |
231 get metadata() { | 160 get metadata() { |
232 return this.functionType.metadata; | 161 return this.functionType.metadata; |
233 } | 162 } |
234 } | 163 }; |
235 | |
236 function _functionType(definite, returnType, args, extra) { | 164 function _functionType(definite, returnType, args, extra) { |
237 // TODO(vsm): Cache / memomize? | |
238 let optionals; | 165 let optionals; |
239 let named; | 166 let named; |
240 if (extra === void 0) { | 167 if (extra === void 0) { |
241 optionals = []; | 168 optionals = []; |
242 named = {}; | 169 named = {}; |
243 } else if (extra instanceof Array) { | 170 } else if (extra instanceof Array) { |
244 optionals = extra; | 171 optionals = extra; |
245 named = {}; | 172 named = {}; |
246 } else { | 173 } else { |
247 optionals = []; | 174 optionals = []; |
248 named = extra; | 175 named = extra; |
249 } | 176 } |
250 return new FunctionType(definite, returnType, args, optionals, named); | 177 return new FunctionType(definite, returnType, args, optionals, named); |
251 } | 178 } |
252 | |
253 /** | |
254 * Create a "fuzzy" function type. If any arguments are dynamic | |
255 * they will be replaced with bottom. | |
256 */ | |
257 function functionType(returnType, args, extra) { | 179 function functionType(returnType, args, extra) { |
258 return _functionType(false, returnType, args, extra); | 180 return _functionType(false, returnType, args, extra); |
259 } | 181 } |
260 exports.functionType = functionType; | |
261 | |
262 /** | |
263 * Create a definite function type. No substitution of dynamic for | |
264 * bottom occurs. | |
265 */ | |
266 function definiteFunctionType(returnType, args, extra) { | 182 function definiteFunctionType(returnType, args, extra) { |
267 return _functionType(true, returnType, args, extra); | 183 return _functionType(true, returnType, args, extra); |
268 } | 184 } |
269 exports.definiteFunctionType = definiteFunctionType; | |
270 | |
271 function typedef(name, closure) { | 185 function typedef(name, closure) { |
272 return new Typedef(name, closure); | 186 return new Typedef(name, closure); |
273 } | 187 } |
274 exports.typedef = typedef; | |
275 | |
276 function isDartType(type) { | 188 function isDartType(type) { |
277 return rtti.read(type) === core.Type; | 189 return rtti.read(type) === core.Type; |
278 } | 190 } |
279 exports.isDartType = isDartType; | |
280 | |
281 function typeName(type) { | 191 function typeName(type) { |
282 // Non-instance types | |
283 if (type instanceof TypeRep) return type.toString(); | 192 if (type instanceof TypeRep) return type.toString(); |
284 // Instance types | |
285 let tag = rtti.read(type); | 193 let tag = rtti.read(type); |
286 if (tag === core.Type) { | 194 if (tag === core.Type) { |
287 let name = type.name; | 195 let name = type.name; |
288 let args = classes.getGenericArgs(type); | 196 let args = classes.getGenericArgs(type); |
289 if (args) { | 197 if (args) { |
290 name += '<'; | 198 name += '<'; |
291 for (let i = 0; i < args.length; ++i) { | 199 for (let i = 0; i < args.length; ++i) { |
292 if (i > 0) name += ', '; | 200 if (i > 0) name += ', '; |
293 name += typeName(args[i]); | 201 name += typeName(args[i]); |
294 } | 202 } |
295 name += '>'; | 203 name += '>'; |
296 } | 204 } |
297 return name; | 205 return name; |
298 } | 206 } |
299 if (tag) return "Not a type: " + tag.name; | 207 if (tag) return "Not a type: " + tag.name; |
300 return "JSObject<" + type.name + ">"; | 208 return "JSObject<" + type.name + ">"; |
301 } | 209 } |
302 exports.typeName = typeName; | |
303 | |
304 function isFunctionType(type) { | 210 function isFunctionType(type) { |
305 return type instanceof AbstractFunctionType || type == core.Function; | 211 return type instanceof AbstractFunctionType || type == core.Function; |
306 } | 212 } |
307 | |
308 function isFunctionSubType(ft1, ft2) { | 213 function isFunctionSubType(ft1, ft2) { |
309 if (ft2 == core.Function) { | 214 if (ft2 == core.Function) { |
310 return true; | 215 return true; |
311 } | 216 } |
312 | |
313 let ret1 = ft1.returnType; | 217 let ret1 = ft1.returnType; |
314 let ret2 = ft2.returnType; | 218 let ret2 = ft2.returnType; |
315 | |
316 if (!isSubtype_(ret1, ret2)) { | 219 if (!isSubtype_(ret1, ret2)) { |
317 // Covariant return types | 220 if (ret2 != void$) { |
318 // Note, void (which can only appear as a return type) is effectively | |
319 // treated as dynamic. If the base return type is void, we allow any | |
320 // subtype return type. | |
321 // E.g., we allow: | |
322 // () -> int <: () -> void | |
323 if (ret2 != voidR) { | |
324 return false; | 221 return false; |
325 } | 222 } |
326 } | 223 } |
327 | |
328 let args1 = ft1.args; | 224 let args1 = ft1.args; |
329 let args2 = ft2.args; | 225 let args2 = ft2.args; |
330 | |
331 if (args1.length > args2.length) { | 226 if (args1.length > args2.length) { |
332 return false; | 227 return false; |
333 } | 228 } |
334 | |
335 for (let i = 0; i < args1.length; ++i) { | 229 for (let i = 0; i < args1.length; ++i) { |
336 if (!isSubtype_(args2[i], args1[i])) { | 230 if (!isSubtype_(args2[i], args1[i])) { |
337 return false; | 231 return false; |
338 } | 232 } |
339 } | 233 } |
340 | |
341 let optionals1 = ft1.optionals; | 234 let optionals1 = ft1.optionals; |
342 let optionals2 = ft2.optionals; | 235 let optionals2 = ft2.optionals; |
343 | |
344 if (args1.length + optionals1.length < args2.length + optionals2.length) { | 236 if (args1.length + optionals1.length < args2.length + optionals2.length) { |
345 return false; | 237 return false; |
346 } | 238 } |
347 | |
348 let j = 0; | 239 let j = 0; |
349 for (let i = args1.length; i < args2.length; ++i, ++j) { | 240 for (let i = args1.length; i < args2.length; ++i, ++j) { |
350 if (!isSubtype_(args2[i], optionals1[j])) { | 241 if (!isSubtype_(args2[i], optionals1[j])) { |
351 return false; | 242 return false; |
352 } | 243 } |
353 } | 244 } |
354 | |
355 for (let i = 0; i < optionals2.length; ++i, ++j) { | 245 for (let i = 0; i < optionals2.length; ++i, ++j) { |
356 if (!isSubtype_(optionals2[i], optionals1[j])) { | 246 if (!isSubtype_(optionals2[i], optionals1[j])) { |
357 return false; | 247 return false; |
358 } | 248 } |
359 } | 249 } |
360 | |
361 let named1 = ft1.named; | 250 let named1 = ft1.named; |
362 let named2 = ft2.named; | 251 let named2 = ft2.named; |
363 | |
364 let names = getOwnPropertyNames(named2); | 252 let names = getOwnPropertyNames(named2); |
365 for (let i = 0; i < names.length; ++i) { | 253 for (let i = 0; i < names.length; ++i) { |
366 let name = names[i]; | 254 let name = names[i]; |
367 let n1 = named1[name]; | 255 let n1 = named1[name]; |
368 let n2 = named2[name]; | 256 let n2 = named2[name]; |
369 if (n1 === void 0) { | 257 if (n1 === void 0) { |
370 return false; | 258 return false; |
371 } | 259 } |
372 if (!isSubtype_(n2, n1)) { | 260 if (!isSubtype_(n2, n1)) { |
373 return false; | 261 return false; |
374 } | 262 } |
375 } | 263 } |
376 | |
377 return true; | 264 return true; |
378 } | 265 } |
379 | |
380 /** | |
381 * Computes the canonical type. | |
382 * This maps JS types onto their corresponding Dart Type. | |
383 */ | |
384 // TODO(jmesserly): lots more needs to be done here. | |
385 function canonicalType(t) { | 266 function canonicalType(t) { |
386 if (t === Object) return core.Object; | 267 if (t === Object) return core.Object; |
387 if (t === Function) return core.Function; | 268 if (t === Function) return core.Function; |
388 if (t === Array) return core.List; | 269 if (t === Array) return core.List; |
389 | |
390 // We shouldn't normally get here with these types, unless something strange | |
391 // happens like subclassing Number in JS and passing it to Dart. | |
392 if (t === String) return core.String; | 270 if (t === String) return core.String; |
393 if (t === Number) return core.double; | 271 if (t === Number) return core.double; |
394 if (t === Boolean) return core.bool; | 272 if (t === Boolean) return core.bool; |
395 return t; | 273 return t; |
396 } | 274 } |
397 | |
398 const subtypeMap = new Map(); | 275 const subtypeMap = new Map(); |
399 function isSubtype(t1, t2) { | 276 function isSubtype(t1, t2) { |
400 // See if we already know the answer | |
401 // TODO(jmesserly): general purpose memoize function? | |
402 let map = subtypeMap.get(t1); | 277 let map = subtypeMap.get(t1); |
403 let result; | 278 let result; |
404 if (map) { | 279 if (map) { |
405 result = map.get(t2); | 280 result = map.get(t2); |
406 if (result !== void 0) return result; | 281 if (result !== void 0) return result; |
407 } else { | 282 } else { |
408 subtypeMap.set(t1, map = new Map()); | 283 subtypeMap.set(t1, map = new Map()); |
409 } | 284 } |
410 result = isSubtype_(t1, t2) | 285 result = isSubtype_(t1, t2); |
411 map.set(t2, result); | 286 map.set(t2, result); |
412 return result; | 287 return result; |
413 } | 288 } |
414 exports.isSubtype = isSubtype; | |
415 | |
416 function _isBottom(type) { | 289 function _isBottom(type) { |
417 return type == bottomR; | 290 return type == bottom; |
418 } | 291 } |
419 | |
420 function _isTop(type) { | 292 function _isTop(type) { |
421 return type == core.Object || (type == dynamicR); | 293 return type == core.Object || type == dynamic; |
422 } | 294 } |
423 | |
424 function isSubtype_(t1, t2) { | 295 function isSubtype_(t1, t2) { |
425 t1 = canonicalType(t1); | 296 t1 = canonicalType(t1); |
426 t2 = canonicalType(t2); | 297 t2 = canonicalType(t2); |
427 if (t1 == t2) return true; | 298 if (t1 == t2) return true; |
428 | |
429 // Trivially true. | |
430 if (_isTop(t2) || _isBottom(t1)) { | 299 if (_isTop(t2) || _isBottom(t1)) { |
431 return true; | 300 return true; |
432 } | 301 } |
433 | |
434 // Trivially false. | |
435 if (_isTop(t1) || _isBottom(t2)) { | 302 if (_isTop(t1) || _isBottom(t2)) { |
436 return false; | 303 return false; |
437 } | 304 } |
438 | |
439 // "Traditional" name-based subtype check. | |
440 if (isClassSubType(t1, t2)) { | 305 if (isClassSubType(t1, t2)) { |
441 return true; | 306 return true; |
442 } | 307 } |
443 | 308 if (isFunctionType(t1) && isFunctionType(t2)) { |
444 // Function subtyping. | |
445 // TODO(vsm): Handle Objects with call methods. Those are functions | |
446 // even if they do not *nominally* subtype core.Function. | |
447 if (isFunctionType(t1) && | |
448 isFunctionType(t2)) { | |
449 return isFunctionSubType(t1, t2); | 309 return isFunctionSubType(t1, t2); |
450 } | 310 } |
451 return false; | 311 return false; |
452 } | 312 } |
453 | |
454 function isClassSubType(t1, t2) { | 313 function isClassSubType(t1, t2) { |
455 // We support Dart's covariant generics with the caveat that we do not | |
456 // substitute bottom for dynamic in subtyping rules. | |
457 // I.e., given T1, ..., Tn where at least one Ti != dynamic we disallow: | |
458 // - S !<: S<T1, ..., Tn> | |
459 // - S<dynamic, ..., dynamic> !<: S<T1, ..., Tn> | |
460 t1 = canonicalType(t1); | 314 t1 = canonicalType(t1); |
461 assert(t2 == canonicalType(t2)); | 315 assert(t2 == canonicalType(t2)); |
462 if (t1 == t2) return true; | 316 if (t1 == t2) return true; |
463 | |
464 if (t1 == core.Object) return false; | 317 if (t1 == core.Object) return false; |
465 | 318 if (t1 == null) return t2 == core.Object || t2 == dynamic; |
466 // If t1 is a JS Object, we may not hit core.Object. | |
467 if (t1 == null) return t2 == core.Object || t2 == dynamicR; | |
468 | |
469 // Check if t1 and t2 have the same raw type. If so, check covariance on | |
470 // type parameters. | |
471 let raw1 = classes.getGenericClass(t1); | 319 let raw1 = classes.getGenericClass(t1); |
472 let raw2 = classes.getGenericClass(t2); | 320 let raw2 = classes.getGenericClass(t2); |
473 if (raw1 != null && raw1 == raw2) { | 321 if (raw1 != null && raw1 == raw2) { |
474 let typeArguments1 = classes.getGenericArgs(t1); | 322 let typeArguments1 = classes.getGenericArgs(t1); |
475 let typeArguments2 = classes.getGenericArgs(t2); | 323 let typeArguments2 = classes.getGenericArgs(t2); |
476 let length = typeArguments1.length; | 324 let length = typeArguments1.length; |
477 if (typeArguments2.length == 0) { | 325 if (typeArguments2.length == 0) { |
478 // t2 is the raw form of t1 | |
479 return true; | 326 return true; |
480 } else if (length == 0) { | 327 } else if (length == 0) { |
481 // t1 is raw, but t2 is not | |
482 return false; | 328 return false; |
483 } | 329 } |
484 assert(length == typeArguments2.length); | 330 assert(length == typeArguments2.length); |
485 for (let i = 0; i < length; ++i) { | 331 for (let i = 0; i < length; ++i) { |
486 if (!isSubtype(typeArguments1[i], typeArguments2[i])) { | 332 if (!isSubtype(typeArguments1[i], typeArguments2[i])) { |
487 return false; | 333 return false; |
488 } | 334 } |
489 } | 335 } |
490 return true; | 336 return true; |
491 } | 337 } |
492 | |
493 // Check superclass. | |
494 if (isClassSubType(t1.__proto__, t2)) return true; | 338 if (isClassSubType(t1.__proto__, t2)) return true; |
495 | |
496 // Check mixins. | |
497 let mixins = classes.getMixins(t1); | 339 let mixins = classes.getMixins(t1); |
498 if (mixins) { | 340 if (mixins) { |
499 for (let m1 of mixins) { | 341 for (let m1 of mixins) { |
500 // TODO(jmesserly): remove the != null check once we can load core libs. | |
501 if (m1 != null && isClassSubType(m1, t2)) return true; | 342 if (m1 != null && isClassSubType(m1, t2)) return true; |
502 } | 343 } |
503 } | 344 } |
504 | |
505 // Check interfaces. | |
506 let getInterfaces = classes.getImplements(t1); | 345 let getInterfaces = classes.getImplements(t1); |
507 if (getInterfaces) { | 346 if (getInterfaces) { |
508 for (let i1 of getInterfaces()) { | 347 for (let i1 of getInterfaces()) { |
509 // TODO(jmesserly): remove the != null check once we can load core libs. | |
510 if (i1 != null && isClassSubType(i1, t2)) return true; | 348 if (i1 != null && isClassSubType(i1, t2)) return true; |
511 } | 349 } |
512 } | 350 } |
513 | |
514 return false; | 351 return false; |
515 } | 352 } |
516 | |
517 // TODO(jmesserly): this isn't currently used, but it could be if we want | |
518 // `obj is NonGroundType<T,S>` to be rejected at runtime instead of compile | |
519 // time. | |
520 function isGroundType(type) { | 353 function isGroundType(type) { |
521 // TODO(vsm): Cache this if we start using it at runtime. | |
522 | |
523 if (type instanceof AbstractFunctionType) { | 354 if (type instanceof AbstractFunctionType) { |
524 if (!_isTop(type.returnType)) return false; | 355 if (!_isTop(type.returnType)) return false; |
525 for (let i = 0; i < type.args.length; ++i) { | 356 for (let i = 0; i < type.args.length; ++i) { |
526 if (!_isBottom(type.args[i])) return false; | 357 if (!_isBottom(type.args[i])) return false; |
527 } | 358 } |
528 for (let i = 0; i < type.optionals.length; ++i) { | 359 for (let i = 0; i < type.optionals.length; ++i) { |
529 if (!_isBottom(type.optionals[i])) return false; | 360 if (!_isBottom(type.optionals[i])) return false; |
530 } | 361 } |
531 let names = getOwnPropertyNames(type.named); | 362 let names = getOwnPropertyNames(type.named); |
532 for (let i = 0; i < names.length; ++i) { | 363 for (let i = 0; i < names.length; ++i) { |
533 if (!_isBottom(type.named[names[i]])) return false; | 364 if (!_isBottom(type.named[names[i]])) return false; |
534 } | 365 } |
535 return true; | 366 return true; |
536 } | 367 } |
537 | |
538 let typeArgs = classes.getGenericArgs(type); | 368 let typeArgs = classes.getGenericArgs(type); |
539 if (!typeArgs) return true; | 369 if (!typeArgs) return true; |
540 for (let t of typeArgs) { | 370 for (let t of typeArgs) { |
541 if (t != core.Object && t != dynamicR) return false; | 371 if (t != core.Object && t != dynamic) return false; |
542 } | 372 } |
543 return true; | 373 return true; |
544 } | 374 } |
| 375 // Exports: |
| 376 exports.assert = assert; |
| 377 exports.getOwnPropertyNames = getOwnPropertyNames; |
| 378 exports.TypeRep = TypeRep; |
| 379 exports.Dynamic = Dynamic; |
| 380 exports.dynamic = dynamic; |
| 381 exports.Void = Void; |
| 382 exports.void = void$; |
| 383 exports.Bottom = Bottom; |
| 384 exports.bottom = bottom; |
| 385 exports.JSObject = JSObject; |
| 386 exports.jsobject = jsobject; |
| 387 exports.AbstractFunctionType = AbstractFunctionType; |
| 388 exports.FunctionType = FunctionType; |
| 389 exports.Typedef = Typedef; |
| 390 exports.functionType = functionType; |
| 391 exports.definiteFunctionType = definiteFunctionType; |
| 392 exports.typedef = typedef; |
| 393 exports.isDartType = isDartType; |
| 394 exports.typeName = typeName; |
| 395 exports.isFunctionType = isFunctionType; |
| 396 exports.isFunctionSubType = isFunctionSubType; |
| 397 exports.canonicalType = canonicalType; |
| 398 exports.subtypeMap = subtypeMap; |
| 399 exports.isSubtype = isSubtype; |
| 400 exports.isSubtype_ = isSubtype_; |
| 401 exports.isClassSubType = isClassSubType; |
545 exports.isGroundType = isGroundType; | 402 exports.isGroundType = isGroundType; |
546 | |
547 }); | 403 }); |
OLD | NEW |