Chromium Code Reviews| 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 loader.library('dart/types', null, /* Imports */[ | |
| 9 ], /* Lazy Imports */[ | |
| 10 'dart/classes', | |
| 11 'dart/core', | |
| 12 'dart/rtti' | |
| 13 ], function(exports, classes, core, rtti) { | |
| 14 'use strict'; | |
| 15 | |
| 16 const assert = js_utils.assert; | |
| 17 const copyProperties = js_utils.copyProperties; | |
| 18 const getOwnPropertyNames = js_utils.getOwnPropertyNames; | |
| 19 const safeGetOwnProperty = js_utils.safeGetOwnProperty; | |
| 20 const throwRuntimeError = js_utils.throwRuntimeError; | |
| 21 | |
| 22 class TypeRep extends rtti.LazyTagged(() => core.Type) { | |
| 23 get name() {return this.toString();} | |
| 24 } | |
| 25 | |
| 26 class Dynamic extends TypeRep { | |
|
Jennifer Messerly
2015/06/12 17:32:25
awesome!
| |
| 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 += typeToString(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 += typeToString(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] + ': ' + typeToString(this.named[names[i]]); | |
| 82 } | |
| 83 buffer += '}'; | |
| 84 } | |
| 85 | |
| 86 buffer += ') -> ' + typeToString(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 typeToString(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 += typeToString(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.typeToString = typeToString; | |
| 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 |