Chromium Code Reviews| 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 var dart, _js_helper; | 5 var dart, _js_helper; |
| 6 (function (dart) { | 6 (function (dart) { |
| 7 'use strict'; | 7 'use strict'; |
| 8 | 8 |
| 9 let defineProperty = Object.defineProperty; | 9 let defineProperty = Object.defineProperty; |
| 10 let getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; | 10 let getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; |
| (...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 119 case "boolean": | 119 case "boolean": |
| 120 return core.bool; | 120 return core.bool; |
| 121 case "string": | 121 case "string": |
| 122 return core.String; | 122 return core.String; |
| 123 case "symbol": | 123 case "symbol": |
| 124 return Symbol; | 124 return Symbol; |
| 125 } | 125 } |
| 126 // Undefined is handled above. For historical reasons, | 126 // Undefined is handled above. For historical reasons, |
| 127 // typeof null == "object" in JS. | 127 // typeof null == "object" in JS. |
| 128 if (obj === null) return core.Null; | 128 if (obj === null) return core.Null; |
| 129 return obj.constructor; | 129 // TODO(vsm): Should we explicitly check if this is a core.Object? |
|
Jennifer Messerly
2015/04/09 18:44:28
I wondered this too, but since we didn't need it y
vsm
2015/04/09 19:52:25
Changed the comment a bit. I think my real questi
| |
| 130 var result = obj[dart.runtimeType]; | |
|
Jennifer Messerly
2015/04/09 18:44:28
what does this do? are you trying to call the Obje
vsm
2015/04/09 19:52:24
It's only used by tests are the moment.
I was usi
| |
| 131 if (result) return result; | |
| 132 result = obj.constructor; | |
| 133 if (result == Function) { | |
| 134 return getFunctionType(obj); | |
| 135 } | |
| 136 return result; | |
| 130 } | 137 } |
| 131 dart.getRuntimeType = getRuntimeType; | 138 dart.getRuntimeType = getRuntimeType; |
| 132 | 139 |
| 133 function instanceOf(obj, type) { | 140 function instanceOf(obj, type) { |
| 134 return isSubtype(getRuntimeType(obj), type); | 141 return isSubtype(getRuntimeType(obj), type); |
| 135 } | 142 } |
| 136 dart.is = instanceOf; | 143 dart.is = instanceOf; |
| 137 | 144 |
| 138 /** | 145 /** |
| 139 * Computes the canonical type. | 146 * Computes the canonical type. |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 163 result = map.get(t2); | 170 result = map.get(t2); |
| 164 if (result !== void 0) return result; | 171 if (result !== void 0) return result; |
| 165 } else { | 172 } else { |
| 166 subtypeMap.set(t1, map = new Map()); | 173 subtypeMap.set(t1, map = new Map()); |
| 167 } | 174 } |
| 168 map.set(t2, result = isSubtype_(t1, t2)); | 175 map.set(t2, result = isSubtype_(t1, t2)); |
| 169 return result; | 176 return result; |
| 170 } | 177 } |
| 171 dart.isSubtype = isSubtype; | 178 dart.isSubtype = isSubtype; |
| 172 | 179 |
| 173 function isSubtype_(t1, t2) { | 180 function _isBottom(type, dynamicIsBottom) { |
| 181 return (type == dart.dynamic && dynamicIsBottom) || type == dart.bottom; | |
| 182 } | |
| 183 | |
| 184 function _isTop(type, dynamicIsBottom) { | |
| 185 return type == core.Object || (type == dart.dynamic && !dynamicIsBottom); | |
| 186 } | |
| 187 | |
| 188 function isSubtype_(t1, t2, opt_dynamicIsBottom) { | |
| 189 let dynamicIsBottom = | |
| 190 opt_dynamicIsBottom === void 0 ? false : opt_dynamicIsBottom; | |
| 191 | |
| 174 t1 = canonicalType(t1); | 192 t1 = canonicalType(t1); |
| 175 t2 = canonicalType(t2); | 193 t2 = canonicalType(t2); |
| 176 if (t1 == t2) return true; | 194 if (t1 == t2) return true; |
| 177 | 195 |
| 178 // In Dart, dynamic is effectively both top and bottom. | 196 // In Dart, dynamic is effectively both top and bottom. |
| 179 // Here, we treat dynamic as top - the base type of everything. | 197 // Here, we treat dynamic as one or the other depending on context, |
| 180 if (t1 == dart.dynamic) return false; | 198 // but not both. |
| 181 if (t2 == dart.dynamic) return true; | |
| 182 | 199 |
| 183 if (t2 == core.Object) return true; | 200 // Trivially true. |
| 184 if (t1 == core.Object) return false; | 201 if (_isTop(t2, dynamicIsBottom) || _isBottom(t1, dynamicIsBottom)) return tr ue; |
|
Jennifer Messerly
2015/04/09 18:44:28
some long lines here
vsm
2015/04/09 19:52:24
Done. The dart formatter has made me lazy on this
| |
| 202 | |
| 203 // Trivially false. | |
| 204 if (_isTop(t1, dynamicIsBottom) || _isBottom(t2, dynamicIsBottom)) return fa lse; | |
| 185 | 205 |
| 186 // "Traditional" name-based subtype check. | 206 // "Traditional" name-based subtype check. |
| 187 if (isClassSubType(t1, t2)) { | 207 if (isClassSubType(t1, t2)) { |
| 188 return true; | 208 return true; |
| 189 } | 209 } |
| 190 | 210 |
| 191 // Function subtyping. | 211 // Function subtyping. |
| 192 // TODO(jmesserly): implement this properly. | 212 // TODO(vsm): Handle Objects with call methods. Those are functions |
|
Jennifer Messerly
2015/04/09 18:44:28
does dart_runtime.dart handle this case?
it would
vsm
2015/04/09 19:52:25
Yes.
| |
| 193 if (isClassSubType(t1, core.Function) && | 213 // even if they do not *nominally* subtype core.Function. |
| 194 isClassSubType(t2, core.Function)) { | 214 if (isFunctionType(t1) && |
| 195 return true; | 215 isFunctionType(t2)) { |
| 216 return isFunctionSubType(t1, t2); | |
| 196 } | 217 } |
| 197 return false; | 218 return false; |
| 198 } | 219 } |
| 199 | 220 |
| 200 function safeGetOwnProperty(obj, name) { | 221 function safeGetOwnProperty(obj, name) { |
| 201 var desc = getOwnPropertyDescriptor(obj, name); | 222 var desc = getOwnPropertyDescriptor(obj, name); |
| 202 if (desc) return desc.value; | 223 if (desc) return desc.value; |
| 203 } | 224 } |
| 204 | 225 |
| 205 function isClassSubType(t1, t2) { | 226 function isClassSubType(t1, t2) { |
| 206 // We support Dart's covariant generics with the caveat that we do not | 227 // We support Dart's covariant generics with the caveat that we do not |
| 207 // substitute bottom for dynamic in subtyping rules. | 228 // substitute bottom for dynamic in subtyping rules. |
| 208 // I.e., given T1, ..., Tn where at least one Ti != dynamic we disallow: | 229 // I.e., given T1, ..., Tn where at least one Ti != dynamic we disallow: |
| 209 // - S !<: S<T1, ..., Tn> | 230 // - S !<: S<T1, ..., Tn> |
| 210 // - S<dynamic, ..., dynamic> !<: S<T1, ..., Tn> | 231 // - S<dynamic, ..., dynamic> !<: S<T1, ..., Tn> |
| 211 if (t1 == t2) return true; | 232 if (t1 == t2) return true; |
| 212 | 233 |
| 213 if (t1 == core.Object) return false; | 234 if (t1 == core.Object) return false; |
| 214 | 235 |
| 236 // If t1 is a JS Object, we may not hit core.Object. | |
| 237 if (t1 == null) return t2 == core.Object || t2 == dart.dynamic; | |
|
Jennifer Messerly
2015/04/09 18:44:29
Hmmm, canonicalType was supposed to be handling th
vsm
2015/04/09 19:52:25
Yeah, I was hitting this at runtime.
| |
| 238 | |
| 215 // Check if t1 and t2 have the same raw type. If so, check covariance on | 239 // Check if t1 and t2 have the same raw type. If so, check covariance on |
| 216 // type parameters. | 240 // type parameters. |
| 217 let raw1 = safeGetOwnProperty(t1, dart.originalDeclaration); | 241 let raw1 = safeGetOwnProperty(t1, dart.originalDeclaration); |
| 218 let raw2 = safeGetOwnProperty(t2, dart.originalDeclaration); | 242 let raw2 = safeGetOwnProperty(t2, dart.originalDeclaration); |
| 219 if (raw1 != null && raw1 == raw2) { | 243 if (raw1 != null && raw1 == raw2) { |
| 220 let typeArguments1 = safeGetOwnProperty(t1, dart.typeArguments); | 244 let typeArguments1 = safeGetOwnProperty(t1, dart.typeArguments); |
| 221 let typeArguments2 = safeGetOwnProperty(t2, dart.typeArguments); | 245 let typeArguments2 = safeGetOwnProperty(t2, dart.typeArguments); |
| 222 let length = typeArguments1.length; | 246 let length = typeArguments1.length; |
| 223 if (typeArguments2.length == 0) { | 247 if (typeArguments2.length == 0) { |
| 224 // t2 is the raw form of t1 | 248 // t2 is the raw form of t1 |
| (...skipping 28 matching lines...) Expand all Loading... | |
| 253 if (getInterfaces) { | 277 if (getInterfaces) { |
| 254 for (let i1 of getInterfaces()) { | 278 for (let i1 of getInterfaces()) { |
| 255 // TODO(jmesserly): remove the != null check once we can load core libs. | 279 // TODO(jmesserly): remove the != null check once we can load core libs. |
| 256 if (i1 != null && isClassSubType(i1, t2)) return true; | 280 if (i1 != null && isClassSubType(i1, t2)) return true; |
| 257 } | 281 } |
| 258 } | 282 } |
| 259 | 283 |
| 260 return false; | 284 return false; |
| 261 } | 285 } |
| 262 | 286 |
| 263 function closureWrap(obj, type) { | |
| 264 // TODO(vsm): Remove this once we handle in the checker. | |
| 265 return obj; | |
| 266 } | |
| 267 dart.closureWrap = closureWrap; | |
| 268 | |
| 269 | |
| 270 // TODO(jmesserly): this isn't currently used, but it could be if we want | 287 // TODO(jmesserly): this isn't currently used, but it could be if we want |
| 271 // `obj is NonGroundType<T,S>` to be rejected at runtime instead of compile | 288 // `obj is NonGroundType<T,S>` to be rejected at runtime instead of compile |
| 272 // time. Also TODO: update this to handle functions. | 289 // time. |
| 273 function isGroundType(type) { | 290 function isGroundType(type) { |
| 291 // TODO(vsm): Cache this if we start using it at runtime. | |
| 292 | |
| 293 if (type instanceof AbstractFunctionType) { | |
| 294 if (!_isTop(type.returnType, false)) return false; | |
| 295 for (var i = 0; i < type.args.length; ++i) { | |
| 296 if (!_isBottom(type.args[i], true)) return false; | |
| 297 } | |
| 298 for (var i = 0; i < type.optionals.length; ++i) { | |
| 299 if (!_isBottom(type.optionals[i], true)) return false; | |
| 300 } | |
| 301 var names = Object.getOwnPropertyNames(type.named); | |
| 302 for (var i = 0; i < names.length; ++i) { | |
| 303 if (!_isBottom(type.named[names[i]], true)) return false; | |
| 304 } | |
| 305 return true; | |
| 306 } | |
| 307 | |
| 274 let typeArgs = safeGetOwnProperty(type, dart.typeArguments); | 308 let typeArgs = safeGetOwnProperty(type, dart.typeArguments); |
| 275 if (!typeArgs) return true; | 309 if (!typeArgs) return true; |
| 276 for (let t of typeArgs) { | 310 for (let t of typeArgs) { |
| 277 if (t != core.Object && t != dart.dynamic) return false; | 311 if (t != core.Object && t != dart.dynamic) return false; |
| 278 } | 312 } |
| 279 return true; | 313 return true; |
| 280 } | 314 } |
| 281 dart.isGroundType = isGroundType; | 315 dart.isGroundType = isGroundType; |
| 282 | 316 |
| 283 function arity(f) { | 317 function arity(f) { |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 294 } | 328 } |
| 295 dart.equals = equals; | 329 dart.equals = equals; |
| 296 | 330 |
| 297 /** Checks that `x` is not null or undefined. */ | 331 /** Checks that `x` is not null or undefined. */ |
| 298 function notNull(x) { | 332 function notNull(x) { |
| 299 if (x == null) throw 'expected not-null value'; | 333 if (x == null) throw 'expected not-null value'; |
| 300 return x; | 334 return x; |
| 301 } | 335 } |
| 302 dart.notNull = notNull; | 336 dart.notNull = notNull; |
| 303 | 337 |
| 338 function _typeName(type) { | |
| 339 // TODO(vsm): Common type object? | |
|
Jennifer Messerly
2015/04/09 18:44:28
we could, it's kind of nice to reuse the JS type o
Jennifer Messerly
2015/04/09 18:47:24
having said that, we can certainly have a differen
vsm
2015/04/09 19:52:25
That's a good point.
| |
| 340 if (type instanceof AbstractFunctionType) return type.toString(); | |
|
Jennifer Messerly
2015/04/09 18:44:29
could you just set a `name` property on this?
vsm
2015/04/09 19:52:25
Done.
| |
| 341 var name = type.name; | |
| 342 if (name) return name; | |
| 343 return type.toString(); | |
|
Jennifer Messerly
2015/04/09 18:44:28
does this happen?
vsm
2015/04/09 19:52:25
Doesn't seem to. Changed the code to throw.
| |
| 344 } | |
| 345 | |
| 346 class AbstractFunctionType { | |
| 347 constructor() { | |
| 348 this._stringValue = null; | |
| 349 } | |
| 350 | |
| 351 toString() { | |
| 352 if (this._stringValue) return this._stringValue; | |
| 353 | |
| 354 var buffer = '('; | |
| 355 for (var i = 0; i < this.args.length; ++i) { | |
| 356 if (i > 0) { | |
| 357 buffer += ', '; | |
| 358 } | |
| 359 buffer += _typeName(this.args[i]); | |
| 360 } | |
| 361 if (this.optionals.length > 0) { | |
| 362 if (this.args.length > 0) buffer += ', '; | |
| 363 buffer += '['; | |
| 364 for (var i = 0; i < this.optionals.length; ++i) { | |
| 365 if (i > 0) { | |
| 366 buffer += ', '; | |
| 367 } | |
| 368 buffer += _typeName(this.optionals[i]); | |
| 369 } | |
| 370 buffer += ']'; | |
| 371 } else if (this.named.length > 0) { | |
| 372 if (this.args.length > 0) buffer += ', '; | |
| 373 buffer += '{'; | |
| 374 let names = Object.getOwnPropertyNames(this.named).sort(); | |
| 375 for (var i = 0; i < names.length; ++i) { | |
| 376 if (i > 0) { | |
| 377 buffer += ', '; | |
| 378 } | |
| 379 buffer += names[i] + ': ' + _typeName(this.named[names[i]]); | |
| 380 } | |
| 381 buffer += '}'; | |
| 382 } | |
| 383 | |
| 384 buffer += ') -> ' + _typeName(this.returnType); | |
| 385 this._stringValue = buffer; | |
| 386 return buffer; | |
| 387 } | |
| 388 } | |
| 389 | |
| 390 class FunctionType extends AbstractFunctionType { | |
| 391 constructor(returnType, args, optionals, named) { | |
| 392 super(); | |
| 393 this.returnType = returnType; | |
| 394 this.args = args; | |
| 395 this.optionals = optionals; | |
| 396 this.named = named; | |
| 397 } | |
| 398 } | |
| 399 | |
| 400 function functionType(returnType, args, extra) { | |
| 401 // TODO(vsm): Cache / memomize? | |
| 402 var optionals; | |
| 403 var named; | |
| 404 if (extra === void 0) { | |
| 405 optionals = []; | |
| 406 named = {}; | |
| 407 } else if (extra instanceof Array) { | |
| 408 optionals = extra; | |
| 409 named = {}; | |
| 410 } else { | |
| 411 optionals = []; | |
| 412 named = extra; | |
| 413 } | |
| 414 return new FunctionType(returnType, args, optionals, named); | |
| 415 } | |
| 416 dart.functionType = functionType; | |
| 417 | |
| 418 class Typedef extends AbstractFunctionType { | |
| 419 constructor(name, closure) { | |
| 420 super(); | |
| 421 this._name = name; | |
| 422 this._closure = closure; | |
| 423 this._functionType = null; | |
| 424 } | |
| 425 | |
| 426 toString() { | |
| 427 return this._name; | |
| 428 } | |
| 429 | |
| 430 get functionType() { | |
| 431 if (!this._functionType) { | |
| 432 this._functionType = this._closure(); | |
| 433 } | |
| 434 return this._functionType; | |
| 435 } | |
| 436 | |
| 437 get returnType() { | |
| 438 return this.functionType.returnType; | |
| 439 } | |
| 440 | |
| 441 get args() { | |
| 442 return this.functionType.args; | |
| 443 } | |
| 444 | |
| 445 get optionals() { | |
| 446 return this.functionType.optionals; | |
| 447 } | |
| 448 | |
| 449 get named() { | |
| 450 return this.functionType.named; | |
| 451 } | |
| 452 } | |
| 453 | |
| 454 function typedef(name, closure) { | |
| 455 return new Typedef(name, closure); | |
| 456 } | |
| 457 dart.typedef = typedef; | |
| 458 | |
| 459 function isFunctionType(type) { | |
| 460 return isClassSubType(type, core.Function) || type instanceof AbstractFuncti onType; | |
| 461 } | |
| 462 | |
| 463 function getFunctionType(obj) { | |
| 464 // TODO(vsm): Encode this properly on the function for Dart-generated code. | |
| 465 var args = Array.apply(null, new Array(obj.length)).map(function(){return co re.Object}); | |
| 466 return functionType(dart.bottom, args); | |
| 467 } | |
| 468 | |
| 469 function isFunctionSubType(ft1, ft2) { | |
| 470 if (ft2 == core.Function) { | |
| 471 return true; | |
| 472 } | |
| 473 | |
| 474 let ret1 = ft1.returnType; | |
| 475 let ret2 = ft2.returnType; | |
| 476 | |
| 477 if (!isSubtype_(ret1, ret2)) { | |
| 478 // Covariant return types | |
| 479 // Note, void (which can only appear as a return type) is effectively | |
| 480 // treated as dynamic. If the base return type is void, we allow any | |
| 481 // subtype return type. | |
| 482 // E.g., we allow: | |
| 483 // () -> int <: () -> void | |
| 484 if (ret2 != dart.void) { | |
| 485 return false; | |
| 486 } | |
| 487 } | |
| 488 | |
| 489 let args1 = ft1.args; | |
| 490 let args2 = ft2.args; | |
| 491 | |
| 492 if (args1.length > args2.length) { | |
| 493 return false; | |
| 494 } | |
| 495 | |
| 496 for (var i = 0; i < args1.length; ++i) { | |
| 497 if (!isSubtype_(args2[i], args1[i], true)) { | |
| 498 return false; | |
| 499 } | |
| 500 } | |
| 501 | |
| 502 let optionals1 = ft1.optionals; | |
| 503 let optionals2 = ft2.optionals; | |
| 504 | |
| 505 if (args1.length + optionals1.length < args2.length + optionals2.length) { | |
| 506 return false; | |
| 507 } | |
| 508 | |
| 509 var j = 0; | |
| 510 for (var i = args1.length; i < args2.length; ++i, ++j) { | |
| 511 if (!isSubtype_(args2[i], optionals1[j], true)) { | |
| 512 return false; | |
| 513 } | |
| 514 } | |
| 515 | |
| 516 for (var i = 0; i < optionals2.length; ++i, ++j) { | |
| 517 if (!isSubtype_(optionals2[i], optionals1[j], true)) { | |
| 518 return false; | |
| 519 } | |
| 520 } | |
| 521 | |
| 522 let named1 = ft1.named; | |
| 523 let named2 = ft2.named; | |
| 524 | |
| 525 let names = Object.getOwnPropertyNames(named2); | |
| 526 for (var i = 0; i < names.length; ++i) { | |
| 527 let name = names[i]; | |
| 528 let n1 = named1[name] | |
| 529 let n2 = named2[name]; | |
| 530 if (n1 === void 0) { | |
| 531 return false; | |
| 532 } | |
| 533 if (!isSubtype_(n2, n1, true)) { | |
| 534 return false; | |
| 535 } | |
| 536 } | |
| 537 | |
| 538 return true; | |
| 539 } | |
| 540 | |
| 304 /** | 541 /** |
| 305 * Defines a lazy property. | 542 * Defines a lazy property. |
| 306 * After initial get or set, it will replace itself with a value property. | 543 * After initial get or set, it will replace itself with a value property. |
| 307 */ | 544 */ |
| 308 // TODO(jmesserly): is this the best implementation for JS engines? | 545 // TODO(jmesserly): is this the best implementation for JS engines? |
| 309 // TODO(jmesserly): reusing descriptor objects has been shown to improve | 546 // TODO(jmesserly): reusing descriptor objects has been shown to improve |
| 310 // performance in other projects (e.g. webcomponents.js ShadowDOM polyfill). | 547 // performance in other projects (e.g. webcomponents.js ShadowDOM polyfill). |
| 311 function defineLazyProperty(to, name, desc) { | 548 function defineLazyProperty(to, name, desc) { |
| 312 let init = desc.get; | 549 let init = desc.get; |
| 313 let writable = !!desc.set; | 550 let writable = !!desc.set; |
| (...skipping 187 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 501 } | 738 } |
| 502 return value; | 739 return value; |
| 503 } | 740 } |
| 504 return makeGenericType; | 741 return makeGenericType; |
| 505 } | 742 } |
| 506 dart.generic = generic; | 743 dart.generic = generic; |
| 507 | 744 |
| 508 // TODO(jmesserly): right now this is a sentinel. It should be a type object | 745 // TODO(jmesserly): right now this is a sentinel. It should be a type object |
| 509 // of some sort, assuming we keep around `dynamic` at runtime. | 746 // of some sort, assuming we keep around `dynamic` at runtime. |
| 510 dart.dynamic = { toString() { return 'dynamic'; } }; | 747 dart.dynamic = { toString() { return 'dynamic'; } }; |
| 748 dart.void = { toString() { return 'void'; } }; | |
| 749 dart.bottom = { toString() { return 'bottom'; } }; | |
| 750 | |
| 751 // TODO(vsm): How should we encode the runtime type? | |
| 752 dart.runtimeType = Symbol('runtimeType'); | |
| 511 | 753 |
| 512 dart.JsSymbol = Symbol; | 754 dart.JsSymbol = Symbol; |
| 513 | 755 |
| 514 // TODO(jmesserly): hack to bootstrap the SDK | 756 // TODO(jmesserly): hack to bootstrap the SDK |
| 515 _js_helper = _js_helper || {}; | 757 _js_helper = _js_helper || {}; |
| 516 _js_helper.checkNum = notNull; | 758 _js_helper.checkNum = notNull; |
| 517 | 759 |
| 518 })(dart || (dart = {})); | 760 })(dart || (dart = {})); |
| OLD | NEW |