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 */ | 6 */ |
7 | 7 |
8 dart_library.library('dart_runtime/_types', null, /* Imports */[ | 8 dart_library.library('dart_runtime/_types', null, /* Imports */[ |
9 ], /* Lazy Imports */[ | 9 ], /* Lazy Imports */[ |
10 'dart/core', | 10 'dart/core', |
11 'dart_runtime/_classes', | 11 'dart_runtime/_classes', |
12 'dart_runtime/_rtti' | 12 'dart_runtime/_rtti' |
13 ], function(exports, core, classes, rtti) { | 13 ], function(exports, core, classes, rtti) { |
14 'use strict'; | 14 'use strict'; |
15 | 15 |
16 const getOwnPropertyNames = Object.getOwnPropertyNames; | 16 const getOwnPropertyNames = Object.getOwnPropertyNames; |
17 | 17 |
18 const assert = dart_utils.assert; | 18 const assert = dart_utils.assert; |
19 const copyProperties = dart_utils.copyProperties; | 19 const copyProperties = dart_utils.copyProperties; |
20 const safeGetOwnProperty = dart_utils.safeGetOwnProperty; | 20 const safeGetOwnProperty = dart_utils.safeGetOwnProperty; |
21 | 21 |
| 22 /** |
| 23 * Types in dart are represented at runtime as follows. |
| 24 * - Normal nominal types, produced from classes, are represented |
| 25 * at runtime by the JS class of which they are an instance. |
| 26 * If the type is the result of instantiating a generic class, |
| 27 * then the "classes" module manages the association between the |
| 28 * instantiated class and the original class declaration |
| 29 * and the type arguments with which it was instantiated. This |
| 30 * assocation can be queried via the "classes" module". |
| 31 * |
| 32 * - All other types are represented as instances of class TypeRep, |
| 33 * defined in this module. |
| 34 * - Dynamic, Void, and Bottom are singleton instances of sentinal |
| 35 * classes. |
| 36 * - Function types are instances of subclasses of AbstractFunctionType. |
| 37 * |
| 38 * Function types are represented in one of two ways: |
| 39 * - As an instance of FunctionType. These are eagerly computed. |
| 40 * - As an instance of TypeDef. The TypeDef representation lazily |
| 41 * computes an instance of FunctionType, and delegates to that instance. |
| 42 * |
| 43 * All types satisfy the following interface: |
| 44 * get String name; |
| 45 * String toString(); |
| 46 * |
| 47 */ |
22 class TypeRep extends rtti.LazyTagged(() => core.Type) { | 48 class TypeRep extends rtti.LazyTagged(() => core.Type) { |
23 get name() {return this.toString();} | 49 get name() {return this.toString();} |
24 } | 50 } |
25 | 51 |
26 class Dynamic extends TypeRep { | 52 class Dynamic extends TypeRep { |
27 toString() { return "dynamic"; } | 53 toString() { return "dynamic"; } |
28 } | 54 } |
29 let dynamicR = new Dynamic(); | 55 let dynamicR = new Dynamic(); |
30 exports.dynamic = dynamicR; | 56 exports.dynamic = dynamicR; |
31 | 57 |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
83 buffer += '}'; | 109 buffer += '}'; |
84 } | 110 } |
85 | 111 |
86 buffer += ') -> ' + typeName(this.returnType); | 112 buffer += ') -> ' + typeName(this.returnType); |
87 this._stringValue = buffer; | 113 this._stringValue = buffer; |
88 return buffer; | 114 return buffer; |
89 } | 115 } |
90 } | 116 } |
91 | 117 |
92 class FunctionType extends AbstractFunctionType { | 118 class FunctionType extends AbstractFunctionType { |
93 constructor(returnType, args, optionals, named) { | 119 /** |
| 120 * Construct a function type. There are two arrow constructors, |
| 121 * distinguished by the "definite" flag. |
| 122 * |
| 123 * The fuzzy arrow (definite is false) treats any arguments |
| 124 * of type dynamic as having type bottom, and will always be |
| 125 * called with a dynamic invoke. |
| 126 * |
| 127 * The definite arrow (definite is true) leaves arguments unchanged. |
| 128 * |
| 129 * We eagerly canonize the argument types to avoid having to deal with |
| 130 * this logic in multple places. |
| 131 * |
| 132 * TODO(leafp): Figure out how to present this to the user. How |
| 133 * should these be printed out? |
| 134 */ |
| 135 constructor(definite, returnType, args, optionals, named) { |
94 super(); | 136 super(); |
| 137 this.definite = definite; |
95 this.returnType = returnType; | 138 this.returnType = returnType; |
96 this.args = args; | 139 this.args = args; |
97 this.optionals = optionals; | 140 this.optionals = optionals; |
98 this.named = named; | 141 this.named = named; |
| 142 this._canonize(); |
| 143 } |
| 144 _canonize() { |
| 145 if (this.definite) return; |
| 146 |
| 147 function replace(a) { |
| 148 return (a == dynamicR) ? bottomR : a; |
| 149 } |
| 150 |
| 151 this.args = this.args.map(replace); |
| 152 |
| 153 if (this.optionals.length > 0) { |
| 154 this.optionals = this.optionals.map(replace); |
| 155 } |
| 156 |
| 157 if (Object.keys(this.named).length > 0) { |
| 158 let r = {}; |
| 159 for (let name of getOwnPropertyNames(this.named)) { |
| 160 r[name] = replace(this.named[name]); |
| 161 } |
| 162 this.named = r; |
| 163 } |
99 } | 164 } |
100 } | 165 } |
101 | 166 |
102 class Typedef extends AbstractFunctionType { | 167 class Typedef extends AbstractFunctionType { |
103 constructor(name, closure) { | 168 constructor(name, closure) { |
104 super(); | 169 super(); |
105 this._name = name; | 170 this._name = name; |
106 this._closure = closure; | 171 this._closure = closure; |
107 this._functionType = null; | 172 this._functionType = null; |
108 } | 173 } |
109 | 174 |
| 175 get definite() { |
| 176 return this._functionType.definite; |
| 177 } |
| 178 |
110 get name() { | 179 get name() { |
111 return this._name; | 180 return this._name; |
112 } | 181 } |
113 | 182 |
114 get functionType() { | 183 get functionType() { |
115 if (!this._functionType) { | 184 if (!this._functionType) { |
116 this._functionType = this._closure(); | 185 this._functionType = this._closure(); |
117 } | 186 } |
118 return this._functionType; | 187 return this._functionType; |
119 } | 188 } |
120 | 189 |
121 get returnType() { | 190 get returnType() { |
122 return this.functionType.returnType; | 191 return this.functionType.returnType; |
123 } | 192 } |
124 | 193 |
125 get args() { | 194 get args() { |
126 return this.functionType.args; | 195 return this.functionType.args; |
127 } | 196 } |
128 | 197 |
129 get optionals() { | 198 get optionals() { |
130 return this.functionType.optionals; | 199 return this.functionType.optionals; |
131 } | 200 } |
132 | 201 |
133 get named() { | 202 get named() { |
134 return this.functionType.named; | 203 return this.functionType.named; |
135 } | 204 } |
136 } | 205 } |
137 | 206 |
138 function functionType(returnType, args, extra) { | 207 function _functionType(definite, returnType, args, extra) { |
139 // TODO(vsm): Cache / memomize? | 208 // TODO(vsm): Cache / memomize? |
140 let optionals; | 209 let optionals; |
141 let named; | 210 let named; |
142 if (extra === void 0) { | 211 if (extra === void 0) { |
143 optionals = []; | 212 optionals = []; |
144 named = {}; | 213 named = {}; |
145 } else if (extra instanceof Array) { | 214 } else if (extra instanceof Array) { |
146 optionals = extra; | 215 optionals = extra; |
147 named = {}; | 216 named = {}; |
148 } else { | 217 } else { |
149 optionals = []; | 218 optionals = []; |
150 named = extra; | 219 named = extra; |
151 } | 220 } |
152 return new FunctionType(returnType, args, optionals, named); | 221 return new FunctionType(definite, returnType, args, optionals, named); |
| 222 } |
| 223 |
| 224 /** |
| 225 * Create a "fuzzy" function type. If any argumets are dynamic |
| 226 * they will be replaced with bottom. |
| 227 */ |
| 228 function functionType(returnType, args, extra) { |
| 229 return _functionType(false, returnType, args, extra); |
153 } | 230 } |
154 exports.functionType = functionType; | 231 exports.functionType = functionType; |
155 | 232 |
| 233 /** |
| 234 * Create a definite function type. No substitution of dynamic for |
| 235 * bottom occurs. |
| 236 */ |
| 237 function definiteFunctionType(returnType, args, extra) { |
| 238 return _functionType(true, returnType, args, extra); |
| 239 } |
| 240 exports.definiteFunctionType = definiteFunctionType; |
| 241 |
156 function typedef(name, closure) { | 242 function typedef(name, closure) { |
157 return new Typedef(name, closure); | 243 return new Typedef(name, closure); |
158 } | 244 } |
159 exports.typedef = typedef; | 245 exports.typedef = typedef; |
160 | 246 |
161 function isDartType(type) { | 247 function isDartType(type) { |
162 return rtti.read(type) === core.Type; | 248 return rtti.read(type) === core.Type; |
163 } | 249 } |
164 exports.isDartType = isDartType; | 250 exports.isDartType = isDartType; |
165 | 251 |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
211 } | 297 } |
212 | 298 |
213 let args1 = ft1.args; | 299 let args1 = ft1.args; |
214 let args2 = ft2.args; | 300 let args2 = ft2.args; |
215 | 301 |
216 if (args1.length > args2.length) { | 302 if (args1.length > args2.length) { |
217 return false; | 303 return false; |
218 } | 304 } |
219 | 305 |
220 for (let i = 0; i < args1.length; ++i) { | 306 for (let i = 0; i < args1.length; ++i) { |
221 if (!isSubtype_(args2[i], args1[i], true)) { | 307 if (!isSubtype_(args2[i], args1[i])) { |
222 return false; | 308 return false; |
223 } | 309 } |
224 } | 310 } |
225 | 311 |
226 let optionals1 = ft1.optionals; | 312 let optionals1 = ft1.optionals; |
227 let optionals2 = ft2.optionals; | 313 let optionals2 = ft2.optionals; |
228 | 314 |
229 if (args1.length + optionals1.length < args2.length + optionals2.length) { | 315 if (args1.length + optionals1.length < args2.length + optionals2.length) { |
230 return false; | 316 return false; |
231 } | 317 } |
232 | 318 |
233 let j = 0; | 319 let j = 0; |
234 for (let i = args1.length; i < args2.length; ++i, ++j) { | 320 for (let i = args1.length; i < args2.length; ++i, ++j) { |
235 if (!isSubtype_(args2[i], optionals1[j], true)) { | 321 if (!isSubtype_(args2[i], optionals1[j])) { |
236 return false; | 322 return false; |
237 } | 323 } |
238 } | 324 } |
239 | 325 |
240 for (let i = 0; i < optionals2.length; ++i, ++j) { | 326 for (let i = 0; i < optionals2.length; ++i, ++j) { |
241 if (!isSubtype_(optionals2[i], optionals1[j], true)) { | 327 if (!isSubtype_(optionals2[i], optionals1[j])) { |
242 return false; | 328 return false; |
243 } | 329 } |
244 } | 330 } |
245 | 331 |
246 let named1 = ft1.named; | 332 let named1 = ft1.named; |
247 let named2 = ft2.named; | 333 let named2 = ft2.named; |
248 | 334 |
249 let names = getOwnPropertyNames(named2); | 335 let names = getOwnPropertyNames(named2); |
250 for (let i = 0; i < names.length; ++i) { | 336 for (let i = 0; i < names.length; ++i) { |
251 let name = names[i]; | 337 let name = names[i]; |
252 let n1 = named1[name]; | 338 let n1 = named1[name]; |
253 let n2 = named2[name]; | 339 let n2 = named2[name]; |
254 if (n1 === void 0) { | 340 if (n1 === void 0) { |
255 return false; | 341 return false; |
256 } | 342 } |
257 if (!isSubtype_(n2, n1, true)) { | 343 if (!isSubtype_(n2, n1)) { |
258 return false; | 344 return false; |
259 } | 345 } |
260 } | 346 } |
261 | 347 |
262 return true; | 348 return true; |
263 } | 349 } |
264 | 350 |
265 /** | 351 /** |
266 * Computes the canonical type. | 352 * Computes the canonical type. |
267 * This maps JS types onto their corresponding Dart Type. | 353 * This maps JS types onto their corresponding Dart Type. |
(...skipping 23 matching lines...) Expand all Loading... |
291 if (result !== void 0) return result; | 377 if (result !== void 0) return result; |
292 } else { | 378 } else { |
293 subtypeMap.set(t1, map = new Map()); | 379 subtypeMap.set(t1, map = new Map()); |
294 } | 380 } |
295 result = isSubtype_(t1, t2) | 381 result = isSubtype_(t1, t2) |
296 map.set(t2, result); | 382 map.set(t2, result); |
297 return result; | 383 return result; |
298 } | 384 } |
299 exports.isSubtype = isSubtype; | 385 exports.isSubtype = isSubtype; |
300 | 386 |
301 function _isBottom(type, dynamicIsBottom) { | 387 function _isBottom(type) { |
302 return (type == dynamicR && dynamicIsBottom) || type == bottomR; | 388 return type == bottomR; |
303 } | 389 } |
304 | 390 |
305 function _isTop(type, dynamicIsBottom) { | 391 function _isTop(type) { |
306 return type == core.Object || (type == dynamicR && !dynamicIsBottom); | 392 return type == core.Object || (type == dynamicR); |
307 } | 393 } |
308 | 394 |
309 function isSubtype_(t1, t2, opt_dynamicIsBottom) { | 395 function isSubtype_(t1, t2) { |
310 let dynamicIsBottom = | |
311 opt_dynamicIsBottom === void 0 ? false : opt_dynamicIsBottom; | |
312 | |
313 t1 = canonicalType(t1); | 396 t1 = canonicalType(t1); |
314 t2 = canonicalType(t2); | 397 t2 = canonicalType(t2); |
315 if (t1 == t2) return true; | 398 if (t1 == t2) return true; |
316 | 399 |
317 // In Dart, dynamic is effectively both top and bottom. | |
318 // Here, we treat dynamic as one or the other depending on context, | |
319 // but not both. | |
320 | |
321 // Trivially true. | 400 // Trivially true. |
322 if (_isTop(t2, dynamicIsBottom) || _isBottom(t1, dynamicIsBottom)) { | 401 if (_isTop(t2) || _isBottom(t1)) { |
323 return true; | 402 return true; |
324 } | 403 } |
325 | 404 |
326 // Trivially false. | 405 // Trivially false. |
327 if (_isTop(t1, dynamicIsBottom) || _isBottom(t2, dynamicIsBottom)) { | 406 if (_isTop(t1) || _isBottom(t2)) { |
328 return false; | 407 return false; |
329 } | 408 } |
330 | 409 |
331 // "Traditional" name-based subtype check. | 410 // "Traditional" name-based subtype check. |
332 if (isClassSubType(t1, t2)) { | 411 if (isClassSubType(t1, t2)) { |
333 return true; | 412 return true; |
334 } | 413 } |
335 | 414 |
336 // Function subtyping. | 415 // Function subtyping. |
337 // TODO(vsm): Handle Objects with call methods. Those are functions | 416 // TODO(vsm): Handle Objects with call methods. Those are functions |
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
406 return false; | 485 return false; |
407 } | 486 } |
408 | 487 |
409 // TODO(jmesserly): this isn't currently used, but it could be if we want | 488 // TODO(jmesserly): this isn't currently used, but it could be if we want |
410 // `obj is NonGroundType<T,S>` to be rejected at runtime instead of compile | 489 // `obj is NonGroundType<T,S>` to be rejected at runtime instead of compile |
411 // time. | 490 // time. |
412 function isGroundType(type) { | 491 function isGroundType(type) { |
413 // TODO(vsm): Cache this if we start using it at runtime. | 492 // TODO(vsm): Cache this if we start using it at runtime. |
414 | 493 |
415 if (type instanceof AbstractFunctionType) { | 494 if (type instanceof AbstractFunctionType) { |
416 if (!_isTop(type.returnType, false)) return false; | 495 if (!_isTop(type.returnType)) return false; |
417 for (let i = 0; i < type.args.length; ++i) { | 496 for (let i = 0; i < type.args.length; ++i) { |
418 if (!_isBottom(type.args[i], true)) return false; | 497 if (!_isBottom(type.args[i])) return false; |
419 } | 498 } |
420 for (let i = 0; i < type.optionals.length; ++i) { | 499 for (let i = 0; i < type.optionals.length; ++i) { |
421 if (!_isBottom(type.optionals[i], true)) return false; | 500 if (!_isBottom(type.optionals[i])) return false; |
422 } | 501 } |
423 let names = getOwnPropertyNames(type.named); | 502 let names = getOwnPropertyNames(type.named); |
424 for (let i = 0; i < names.length; ++i) { | 503 for (let i = 0; i < names.length; ++i) { |
425 if (!_isBottom(type.named[names[i]], true)) return false; | 504 if (!_isBottom(type.named[names[i]])) return false; |
426 } | 505 } |
427 return true; | 506 return true; |
428 } | 507 } |
429 | 508 |
430 let typeArgs = classes.getGenericArgs(type); | 509 let typeArgs = classes.getGenericArgs(type); |
431 if (!typeArgs) return true; | 510 if (!typeArgs) return true; |
432 for (let t of typeArgs) { | 511 for (let t of typeArgs) { |
433 if (t != core.Object && t != dynamicR) return false; | 512 if (t != core.Object && t != dynamicR) return false; |
434 } | 513 } |
435 return true; | 514 return true; |
436 } | 515 } |
437 exports.isGroundType = isGroundType; | 516 exports.isGroundType = isGroundType; |
438 | 517 |
439 }); | 518 }); |
OLD | NEW |