OLD | NEW |
(Empty) | |
| 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_runtime/_types', null, /* Imports */[ |
| 9 ], /* Lazy Imports */[ |
| 10 'dart/core', |
| 11 'dart_runtime/_classes', |
| 12 'dart_runtime/_rtti' |
| 13 ], function(exports, core, classes, rtti) { |
| 14 'use strict'; |
| 15 |
| 16 const getOwnPropertyNames = Object.getOwnPropertyNames; |
| 17 |
| 18 const assert = dart_utils.assert; |
| 19 const copyProperties = dart_utils.copyProperties; |
| 20 const safeGetOwnProperty = dart_utils.safeGetOwnProperty; |
| 21 |
| 22 class TypeRep extends rtti.LazyTagged(() => core.Type) { |
| 23 get name() {return this.toString();} |
| 24 } |
| 25 |
| 26 class Dynamic extends TypeRep { |
| 27 toString() { return "dynamic"; } |
| 28 } |
| 29 let dynamicR = new Dynamic(); |
| 30 exports.dynamic = dynamicR; |
| 31 |
| 32 class Void extends TypeRep { |
| 33 toString() { return "void"; } |
| 34 } |
| 35 |
| 36 let voidR = new Void(); |
| 37 exports.void = voidR; |
| 38 |
| 39 class Bottom extends TypeRep { |
| 40 toString() { return "bottom"; } |
| 41 }; |
| 42 let bottomR = new Bottom(); |
| 43 exports.bottom = bottomR; |
| 44 |
| 45 class AbstractFunctionType extends TypeRep { |
| 46 constructor() { |
| 47 super(); |
| 48 this._stringValue = null; |
| 49 } |
| 50 |
| 51 toString() { return this.name; } |
| 52 |
| 53 get name() { |
| 54 if (this._stringValue) return this._stringValue; |
| 55 |
| 56 let buffer = '('; |
| 57 for (let i = 0; i < this.args.length; ++i) { |
| 58 if (i > 0) { |
| 59 buffer += ', '; |
| 60 } |
| 61 buffer += typeName(this.args[i]); |
| 62 } |
| 63 if (this.optionals.length > 0) { |
| 64 if (this.args.length > 0) buffer += ', '; |
| 65 buffer += '['; |
| 66 for (let i = 0; i < this.optionals.length; ++i) { |
| 67 if (i > 0) { |
| 68 buffer += ', '; |
| 69 } |
| 70 buffer += typeName(this.optionals[i]); |
| 71 } |
| 72 buffer += ']'; |
| 73 } else if (Object.keys(this.named).length > 0) { |
| 74 if (this.args.length > 0) buffer += ', '; |
| 75 buffer += '{'; |
| 76 let names = getOwnPropertyNames(this.named).sort(); |
| 77 for (let i = 0; i < names.length; ++i) { |
| 78 if (i > 0) { |
| 79 buffer += ', '; |
| 80 } |
| 81 buffer += names[i] + ': ' + typeName(this.named[names[i]]); |
| 82 } |
| 83 buffer += '}'; |
| 84 } |
| 85 |
| 86 buffer += ') -> ' + typeName(this.returnType); |
| 87 this._stringValue = buffer; |
| 88 return buffer; |
| 89 } |
| 90 } |
| 91 |
| 92 class FunctionType extends AbstractFunctionType { |
| 93 constructor(returnType, args, optionals, named) { |
| 94 super(); |
| 95 this.returnType = returnType; |
| 96 this.args = args; |
| 97 this.optionals = optionals; |
| 98 this.named = named; |
| 99 } |
| 100 } |
| 101 |
| 102 class Typedef extends AbstractFunctionType { |
| 103 constructor(name, closure) { |
| 104 super(); |
| 105 this._name = name; |
| 106 this._closure = closure; |
| 107 this._functionType = null; |
| 108 } |
| 109 |
| 110 get name() { |
| 111 return this._name; |
| 112 } |
| 113 |
| 114 get functionType() { |
| 115 if (!this._functionType) { |
| 116 this._functionType = this._closure(); |
| 117 } |
| 118 return this._functionType; |
| 119 } |
| 120 |
| 121 get returnType() { |
| 122 return this.functionType.returnType; |
| 123 } |
| 124 |
| 125 get args() { |
| 126 return this.functionType.args; |
| 127 } |
| 128 |
| 129 get optionals() { |
| 130 return this.functionType.optionals; |
| 131 } |
| 132 |
| 133 get named() { |
| 134 return this.functionType.named; |
| 135 } |
| 136 } |
| 137 |
| 138 function functionType(returnType, args, extra) { |
| 139 // TODO(vsm): Cache / memomize? |
| 140 let optionals; |
| 141 let named; |
| 142 if (extra === void 0) { |
| 143 optionals = []; |
| 144 named = {}; |
| 145 } else if (extra instanceof Array) { |
| 146 optionals = extra; |
| 147 named = {}; |
| 148 } else { |
| 149 optionals = []; |
| 150 named = extra; |
| 151 } |
| 152 return new FunctionType(returnType, args, optionals, named); |
| 153 } |
| 154 exports.functionType = functionType; |
| 155 |
| 156 function typedef(name, closure) { |
| 157 return new Typedef(name, closure); |
| 158 } |
| 159 exports.typedef = typedef; |
| 160 |
| 161 function isDartType(type) { |
| 162 return rtti.read(type) === core.Type; |
| 163 } |
| 164 exports.isDartType = isDartType; |
| 165 |
| 166 function typeName(type) { |
| 167 // Non-instance types |
| 168 if (type instanceof TypeRep) return type.toString(); |
| 169 // Instance types |
| 170 let tag = rtti.read(type); |
| 171 if (tag === core.Type) { |
| 172 let name = type.name; |
| 173 let args = classes.getGenericArgs(type); |
| 174 if (args) { |
| 175 name += '<'; |
| 176 for (let i = 0; i < args.length; ++i) { |
| 177 if (i > 0) name += ', '; |
| 178 name += typeName(args[i]); |
| 179 } |
| 180 name += '>'; |
| 181 } |
| 182 return name; |
| 183 } |
| 184 if (tag) return "Not a type: " + tag.name; |
| 185 return "JSObject<" + type.name + ">"; |
| 186 } |
| 187 exports.typeName = typeName; |
| 188 |
| 189 function isFunctionType(type) { |
| 190 return type instanceof AbstractFunctionType || type == core.Function; |
| 191 } |
| 192 |
| 193 function isFunctionSubType(ft1, ft2) { |
| 194 if (ft2 == core.Function) { |
| 195 return true; |
| 196 } |
| 197 |
| 198 let ret1 = ft1.returnType; |
| 199 let ret2 = ft2.returnType; |
| 200 |
| 201 if (!isSubtype_(ret1, ret2)) { |
| 202 // Covariant return types |
| 203 // Note, void (which can only appear as a return type) is effectively |
| 204 // treated as dynamic. If the base return type is void, we allow any |
| 205 // subtype return type. |
| 206 // E.g., we allow: |
| 207 // () -> int <: () -> void |
| 208 if (ret2 != voidR) { |
| 209 return false; |
| 210 } |
| 211 } |
| 212 |
| 213 let args1 = ft1.args; |
| 214 let args2 = ft2.args; |
| 215 |
| 216 if (args1.length > args2.length) { |
| 217 return false; |
| 218 } |
| 219 |
| 220 for (let i = 0; i < args1.length; ++i) { |
| 221 if (!isSubtype_(args2[i], args1[i], true)) { |
| 222 return false; |
| 223 } |
| 224 } |
| 225 |
| 226 let optionals1 = ft1.optionals; |
| 227 let optionals2 = ft2.optionals; |
| 228 |
| 229 if (args1.length + optionals1.length < args2.length + optionals2.length) { |
| 230 return false; |
| 231 } |
| 232 |
| 233 let j = 0; |
| 234 for (let i = args1.length; i < args2.length; ++i, ++j) { |
| 235 if (!isSubtype_(args2[i], optionals1[j], true)) { |
| 236 return false; |
| 237 } |
| 238 } |
| 239 |
| 240 for (let i = 0; i < optionals2.length; ++i, ++j) { |
| 241 if (!isSubtype_(optionals2[i], optionals1[j], true)) { |
| 242 return false; |
| 243 } |
| 244 } |
| 245 |
| 246 let named1 = ft1.named; |
| 247 let named2 = ft2.named; |
| 248 |
| 249 let names = getOwnPropertyNames(named2); |
| 250 for (let i = 0; i < names.length; ++i) { |
| 251 let name = names[i]; |
| 252 let n1 = named1[name]; |
| 253 let n2 = named2[name]; |
| 254 if (n1 === void 0) { |
| 255 return false; |
| 256 } |
| 257 if (!isSubtype_(n2, n1, true)) { |
| 258 return false; |
| 259 } |
| 260 } |
| 261 |
| 262 return true; |
| 263 } |
| 264 |
| 265 /** |
| 266 * Computes the canonical type. |
| 267 * This maps JS types onto their corresponding Dart Type. |
| 268 */ |
| 269 // TODO(jmesserly): lots more needs to be done here. |
| 270 function canonicalType(t) { |
| 271 if (t === Object) return core.Object; |
| 272 if (t === Function) return core.Function; |
| 273 if (t === Array) return core.List; |
| 274 |
| 275 // We shouldn't normally get here with these types, unless something strange |
| 276 // happens like subclassing Number in JS and passing it to Dart. |
| 277 if (t === String) return core.String; |
| 278 if (t === Number) return core.double; |
| 279 if (t === Boolean) return core.bool; |
| 280 return t; |
| 281 } |
| 282 |
| 283 const subtypeMap = new Map(); |
| 284 function isSubtype(t1, t2) { |
| 285 // See if we already know the answer |
| 286 // TODO(jmesserly): general purpose memoize function? |
| 287 let map = subtypeMap.get(t1); |
| 288 let result; |
| 289 if (map) { |
| 290 result = map.get(t2); |
| 291 if (result !== void 0) return result; |
| 292 } else { |
| 293 subtypeMap.set(t1, map = new Map()); |
| 294 } |
| 295 result = isSubtype_(t1, t2) |
| 296 map.set(t2, result); |
| 297 return result; |
| 298 } |
| 299 exports.isSubtype = isSubtype; |
| 300 |
| 301 function _isBottom(type, dynamicIsBottom) { |
| 302 return (type == dynamicR && dynamicIsBottom) || type == bottomR; |
| 303 } |
| 304 |
| 305 function _isTop(type, dynamicIsBottom) { |
| 306 return type == core.Object || (type == dynamicR && !dynamicIsBottom); |
| 307 } |
| 308 |
| 309 function isSubtype_(t1, t2, opt_dynamicIsBottom) { |
| 310 let dynamicIsBottom = |
| 311 opt_dynamicIsBottom === void 0 ? false : opt_dynamicIsBottom; |
| 312 |
| 313 t1 = canonicalType(t1); |
| 314 t2 = canonicalType(t2); |
| 315 if (t1 == t2) return true; |
| 316 |
| 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. |
| 322 if (_isTop(t2, dynamicIsBottom) || _isBottom(t1, dynamicIsBottom)) { |
| 323 return true; |
| 324 } |
| 325 |
| 326 // Trivially false. |
| 327 if (_isTop(t1, dynamicIsBottom) || _isBottom(t2, dynamicIsBottom)) { |
| 328 return false; |
| 329 } |
| 330 |
| 331 // "Traditional" name-based subtype check. |
| 332 if (isClassSubType(t1, t2)) { |
| 333 return true; |
| 334 } |
| 335 |
| 336 // Function subtyping. |
| 337 // TODO(vsm): Handle Objects with call methods. Those are functions |
| 338 // even if they do not *nominally* subtype core.Function. |
| 339 if (isFunctionType(t1) && |
| 340 isFunctionType(t2)) { |
| 341 return isFunctionSubType(t1, t2); |
| 342 } |
| 343 return false; |
| 344 } |
| 345 |
| 346 function isClassSubType(t1, t2) { |
| 347 // We support Dart's covariant generics with the caveat that we do not |
| 348 // substitute bottom for dynamic in subtyping rules. |
| 349 // I.e., given T1, ..., Tn where at least one Ti != dynamic we disallow: |
| 350 // - S !<: S<T1, ..., Tn> |
| 351 // - S<dynamic, ..., dynamic> !<: S<T1, ..., Tn> |
| 352 t1 = canonicalType(t1); |
| 353 assert(t2 == canonicalType(t2)); |
| 354 if (t1 == t2) return true; |
| 355 |
| 356 if (t1 == core.Object) return false; |
| 357 |
| 358 // If t1 is a JS Object, we may not hit core.Object. |
| 359 if (t1 == null) return t2 == core.Object || t2 == dynamicR; |
| 360 |
| 361 // Check if t1 and t2 have the same raw type. If so, check covariance on |
| 362 // type parameters. |
| 363 let raw1 = classes.getGenericClass(t1); |
| 364 let raw2 = classes.getGenericClass(t2); |
| 365 if (raw1 != null && raw1 == raw2) { |
| 366 let typeArguments1 = classes.getGenericArgs(t1); |
| 367 let typeArguments2 = classes.getGenericArgs(t2); |
| 368 let length = typeArguments1.length; |
| 369 if (typeArguments2.length == 0) { |
| 370 // t2 is the raw form of t1 |
| 371 return true; |
| 372 } else if (length == 0) { |
| 373 // t1 is raw, but t2 is not |
| 374 return false; |
| 375 } |
| 376 assert(length == typeArguments2.length); |
| 377 for (let i = 0; i < length; ++i) { |
| 378 if (!isSubtype(typeArguments1[i], typeArguments2[i])) { |
| 379 return false; |
| 380 } |
| 381 } |
| 382 return true; |
| 383 } |
| 384 |
| 385 // Check superclass. |
| 386 if (isClassSubType(t1.__proto__, t2)) return true; |
| 387 |
| 388 // Check mixins. |
| 389 let mixins = classes.getMixins(t1); |
| 390 if (mixins) { |
| 391 for (let m1 of mixins) { |
| 392 // TODO(jmesserly): remove the != null check once we can load core libs. |
| 393 if (m1 != null && isClassSubType(m1, t2)) return true; |
| 394 } |
| 395 } |
| 396 |
| 397 // Check interfaces. |
| 398 let getInterfaces = classes.getImplements(t1); |
| 399 if (getInterfaces) { |
| 400 for (let i1 of getInterfaces()) { |
| 401 // TODO(jmesserly): remove the != null check once we can load core libs. |
| 402 if (i1 != null && isClassSubType(i1, t2)) return true; |
| 403 } |
| 404 } |
| 405 |
| 406 return false; |
| 407 } |
| 408 |
| 409 // 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 |
| 411 // time. |
| 412 function isGroundType(type) { |
| 413 // TODO(vsm): Cache this if we start using it at runtime. |
| 414 |
| 415 if (type instanceof AbstractFunctionType) { |
| 416 if (!_isTop(type.returnType, false)) return false; |
| 417 for (let i = 0; i < type.args.length; ++i) { |
| 418 if (!_isBottom(type.args[i], true)) return false; |
| 419 } |
| 420 for (let i = 0; i < type.optionals.length; ++i) { |
| 421 if (!_isBottom(type.optionals[i], true)) return false; |
| 422 } |
| 423 let names = getOwnPropertyNames(type.named); |
| 424 for (let i = 0; i < names.length; ++i) { |
| 425 if (!_isBottom(type.named[names[i]], true)) return false; |
| 426 } |
| 427 return true; |
| 428 } |
| 429 |
| 430 let typeArgs = classes.getGenericArgs(type); |
| 431 if (!typeArgs) return true; |
| 432 for (let t of typeArgs) { |
| 433 if (t != core.Object && t != dynamicR) return false; |
| 434 } |
| 435 return true; |
| 436 } |
| 437 exports.isGroundType = isGroundType; |
| 438 |
| 439 }); |
OLD | NEW |