| 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 |