| 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 operations that define and manipulate Dart | 5 /// This library defines the operations that define and manipulate Dart |
| 6 /// classes. Included in this are: | 6 /// classes. Included in this are: |
| 7 /// - Generics | 7 /// - Generics |
| 8 /// - Class metadata | 8 /// - Class metadata |
| 9 /// - Extension methods | 9 /// - Extension methods |
| 10 /// | 10 /// |
| 11 | 11 |
| 12 // TODO(leafp): Consider splitting some of this out. | 12 // TODO(leafp): Consider splitting some of this out. |
| 13 part of dart._runtime; | 13 part of dart._runtime; |
| 14 | 14 |
| 15 /// | 15 /// |
| 16 /// Returns a new type that mixes members from base and all mixins. | 16 /// Returns a new type that mixes members from base and all mixins. |
| 17 /// | 17 /// |
| 18 /// Each mixin applies in sequence, with further to the right ones overriding | 18 /// Each mixin applies in sequence, with further to the right ones overriding |
| 19 /// previous entries. | 19 /// previous entries. |
| 20 /// | 20 /// |
| 21 /// For each mixin, we only take its own properties, not anything from its | 21 /// For each mixin, we only take its own properties, not anything from its |
| 22 /// superclass (prototype). | 22 /// superclass (prototype). |
| 23 mixin(base, @rest mixins) => JS( | 23 mixin(base, @rest mixins) => JS('', '''(() => { |
| 24 '', | |
| 25 '''(() => { | |
| 26 // Create an initializer for the mixin, so when derived constructor calls | 24 // Create an initializer for the mixin, so when derived constructor calls |
| 27 // super, we can correctly initialize base and mixins. | 25 // super, we can correctly initialize base and mixins. |
| 28 | 26 |
| 29 // Create a class that will hold all of the mixin methods. | 27 // Create a class that will hold all of the mixin methods. |
| 30 class Mixin extends $base {} | 28 class Mixin extends $base {} |
| 31 // Save the original constructor. For ClassTypeAlias definitions, this | 29 // Save the original constructor. For ClassTypeAlias definitions, this |
| 32 // is the concrete type. We embed metadata (e.g., implemented interfaces) | 30 // is the concrete type. We embed metadata (e.g., implemented interfaces) |
| 33 // on this constructor and need to access that from runtime instances. | 31 // on this constructor and need to access that from runtime instances. |
| 34 let constructor = Mixin.prototype.constructor; | 32 let constructor = Mixin.prototype.constructor; |
| 35 // Copy each mixin's methods, with later ones overwriting earlier entries. | 33 // Copy each mixin's methods, with later ones overwriting earlier entries. |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 106 clazz, _implements, clazz, _implements); | 104 clazz, _implements, clazz, _implements); |
| 107 | 105 |
| 108 /// The Symbol for storing type arguments on a specialized generic type. | 106 /// The Symbol for storing type arguments on a specialized generic type. |
| 109 final _typeArguments = JS('', 'Symbol("typeArguments")'); | 107 final _typeArguments = JS('', 'Symbol("typeArguments")'); |
| 110 | 108 |
| 111 final _originalDeclaration = JS('', 'Symbol("originalDeclaration")'); | 109 final _originalDeclaration = JS('', 'Symbol("originalDeclaration")'); |
| 112 | 110 |
| 113 final mixinNew = JS('', 'Symbol("dart.mixinNew")'); | 111 final mixinNew = JS('', 'Symbol("dart.mixinNew")'); |
| 114 | 112 |
| 115 /// Wrap a generic class builder function with future flattening. | 113 /// Wrap a generic class builder function with future flattening. |
| 116 flattenFutures(builder) => JS( | 114 flattenFutures(builder) => JS('', '''(() => { |
| 117 '', | |
| 118 '''(() => { | |
| 119 function flatten(T) { | 115 function flatten(T) { |
| 120 if (!T) return $builder($dynamic); | 116 if (!T) return $builder($dynamic); |
| 121 let futureClass = $getGenericClass($Future); | 117 let futureClass = $getGenericClass($Future); |
| 122 //TODO(leafp): This only handles the direct flattening case. | 118 //TODO(leafp): This only handles the direct flattening case. |
| 123 // It would probably be good to at least search up the class | 119 // It would probably be good to at least search up the class |
| 124 // hierarchy. If we keep doing flattening long term, we may | 120 // hierarchy. If we keep doing flattening long term, we may |
| 125 // want to implement the full future flattening per spec. | 121 // want to implement the full future flattening per spec. |
| 126 if ($getGenericClass(T) == futureClass) { | 122 if ($getGenericClass(T) == futureClass) { |
| 127 let args = $getGenericArgs(T); | 123 let args = $getGenericArgs(T); |
| 128 if (args) return $builder(args[0]); | 124 if (args) return $builder(args[0]); |
| 129 } | 125 } |
| 130 return $builder(T); | 126 return $builder(T); |
| 131 } | 127 } |
| 132 return flatten; | 128 return flatten; |
| 133 })()'''); | 129 })()'''); |
| 134 | 130 |
| 135 /// Memoize a generic type constructor function. | 131 /// Memoize a generic type constructor function. |
| 136 generic(typeConstructor, [setBaseClass]) => JS( | 132 generic(typeConstructor, [setBaseClass]) => JS('', '''(() => { |
| 137 '', | |
| 138 '''(() => { | |
| 139 let length = $typeConstructor.length; | 133 let length = $typeConstructor.length; |
| 140 if (length < 1) { | 134 if (length < 1) { |
| 141 $throwInternalError('must have at least one generic type argument'); | 135 $throwInternalError('must have at least one generic type argument'); |
| 142 } | 136 } |
| 143 let resultMap = new Map(); | 137 let resultMap = new Map(); |
| 144 function makeGenericType(...args) { | 138 function makeGenericType(...args) { |
| 145 if (args.length != length && args.length != 0) { | 139 if (args.length != length && args.length != 0) { |
| 146 $throwInternalError('requires ' + length + ' or 0 type arguments'); | 140 $throwInternalError('requires ' + length + ' or 0 type arguments'); |
| 147 } | 141 } |
| 148 while (args.length < length) args.push($dynamic); | 142 while (args.length < length) args.push($dynamic); |
| (...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 258 | 252 |
| 259 finalFieldType(type, metadata) => | 253 finalFieldType(type, metadata) => |
| 260 JS('', '{ type: #, isFinal: true, metadata: # }', type, metadata); | 254 JS('', '{ type: #, isFinal: true, metadata: # }', type, metadata); |
| 261 | 255 |
| 262 fieldType(type, metadata) => | 256 fieldType(type, metadata) => |
| 263 JS('', '{ type: #, isFinal: false, metadata: # }', type, metadata); | 257 JS('', '{ type: #, isFinal: false, metadata: # }', type, metadata); |
| 264 | 258 |
| 265 /// Get the type of a constructor from a class using the stored signature | 259 /// Get the type of a constructor from a class using the stored signature |
| 266 /// If name is undefined, returns the type of the default constructor | 260 /// If name is undefined, returns the type of the default constructor |
| 267 /// Returns undefined if the constructor is not found. | 261 /// Returns undefined if the constructor is not found. |
| 268 classGetConstructorType(cls, name) => JS( | 262 classGetConstructorType(cls, name) => JS('', '''(() => { |
| 269 '', | |
| 270 '''(() => { | |
| 271 if(!$name) $name = 'new'; | 263 if(!$name) $name = 'new'; |
| 272 if ($cls === void 0) return void 0; | 264 if ($cls === void 0) return void 0; |
| 273 if ($cls == null) return void 0; | 265 if ($cls == null) return void 0; |
| 274 let sigCtor = $cls[$_constructorSig]; | 266 let sigCtor = $cls[$_constructorSig]; |
| 275 if (sigCtor === void 0) return void 0; | 267 if (sigCtor === void 0) return void 0; |
| 276 return sigCtor[$name]; | 268 return sigCtor[$name]; |
| 277 })()'''); | 269 })()'''); |
| 278 | 270 |
| 279 // Set up the method signature field on the constructor | 271 // Set up the method signature field on the constructor |
| 280 _setInstanceSignature(f, sigF, kind) => defineMemoizedGetter( | 272 _setInstanceSignature(f, sigF, kind) => defineMemoizedGetter( |
| (...skipping 29 matching lines...) Expand all Loading... |
| 310 _setStaticFieldSignature(f, sigF) => | 302 _setStaticFieldSignature(f, sigF) => |
| 311 JS('', '$defineMemoizedGetter($f, $_staticFieldSig, $sigF)'); | 303 JS('', '$defineMemoizedGetter($f, $_staticFieldSig, $sigF)'); |
| 312 | 304 |
| 313 _setStaticGetterSignature(f, sigF) => | 305 _setStaticGetterSignature(f, sigF) => |
| 314 JS('', '$defineMemoizedGetter($f, $_staticGetterSig, $sigF)'); | 306 JS('', '$defineMemoizedGetter($f, $_staticGetterSig, $sigF)'); |
| 315 | 307 |
| 316 _setStaticSetterSignature(f, sigF) => | 308 _setStaticSetterSignature(f, sigF) => |
| 317 JS('', '$defineMemoizedGetter($f, $_staticSetterSig, $sigF)'); | 309 JS('', '$defineMemoizedGetter($f, $_staticSetterSig, $sigF)'); |
| 318 | 310 |
| 319 // Set the lazily computed runtime type field on static methods | 311 // Set the lazily computed runtime type field on static methods |
| 320 _setStaticTypes(f, names) => JS( | 312 _setStaticTypes(f, names) => JS('', '''(() => { |
| 321 '', | |
| 322 '''(() => { | |
| 323 for (let name of $names) { | 313 for (let name of $names) { |
| 324 // TODO(vsm): Need to generate static methods. | 314 // TODO(vsm): Need to generate static methods. |
| 325 if (!$f[name]) continue; | 315 if (!$f[name]) continue; |
| 326 $tagLazy($f[name], function() { | 316 $tagLazy($f[name], function() { |
| 327 return $f[$_staticSig][name]; | 317 return $f[$_staticSig][name]; |
| 328 }) | 318 }) |
| 329 } | 319 } |
| 330 })()'''); | 320 })()'''); |
| 331 | 321 |
| 332 /// Set up the type signature of a class (constructor object) | 322 /// Set up the type signature of a class (constructor object) |
| 333 /// f is a constructor object | 323 /// f is a constructor object |
| 334 /// signature is an object containing optional properties as follows: | 324 /// signature is an object containing optional properties as follows: |
| 335 /// methods: A function returning an object mapping method names | 325 /// methods: A function returning an object mapping method names |
| 336 /// to method types. The function is evaluated lazily and cached. | 326 /// to method types. The function is evaluated lazily and cached. |
| 337 /// statics: A function returning an object mapping static method | 327 /// statics: A function returning an object mapping static method |
| 338 /// names to types. The function is evaluated lazily and cached. | 328 /// names to types. The function is evaluated lazily and cached. |
| 339 /// names: An array of the names of the static methods. Used to | 329 /// names: An array of the names of the static methods. Used to |
| 340 /// permit eagerly setting the runtimeType field on the methods | 330 /// permit eagerly setting the runtimeType field on the methods |
| 341 /// while still lazily computing the type descriptor object. | 331 /// while still lazily computing the type descriptor object. |
| 342 /// fields: A function returning an object mapping instance field | 332 /// fields: A function returning an object mapping instance field |
| 343 /// names to types. | 333 /// names to types. |
| 344 setSignature(f, signature) => JS( | 334 setSignature(f, signature) => JS('', '''(() => { |
| 345 '', | |
| 346 '''(() => { | |
| 347 // TODO(ochafik): Deconstruct these when supported by Chrome. | 335 // TODO(ochafik): Deconstruct these when supported by Chrome. |
| 348 let constructors = | 336 let constructors = |
| 349 ('constructors' in signature) ? signature.constructors : () => ({}); | 337 ('constructors' in signature) ? signature.constructors : () => ({}); |
| 350 let methods = | 338 let methods = |
| 351 ('methods' in signature) ? signature.methods : () => ({}); | 339 ('methods' in signature) ? signature.methods : () => ({}); |
| 352 let fields = | 340 let fields = |
| 353 ('fields' in signature) ? signature.fields : () => ({}); | 341 ('fields' in signature) ? signature.fields : () => ({}); |
| 354 let getters = | 342 let getters = |
| 355 ('getters' in signature) ? signature.getters : () => ({}); | 343 ('getters' in signature) ? signature.getters : () => ({}); |
| 356 let setters = | 344 let setters = |
| (...skipping 13 matching lines...) Expand all Loading... |
| 370 $_setFieldSignature($f, fields); | 358 $_setFieldSignature($f, fields); |
| 371 $_setGetterSignature($f, getters); | 359 $_setGetterSignature($f, getters); |
| 372 $_setSetterSignature($f, setters); | 360 $_setSetterSignature($f, setters); |
| 373 $_setStaticSignature($f, statics); | 361 $_setStaticSignature($f, statics); |
| 374 $_setStaticFieldSignature($f, staticFields); | 362 $_setStaticFieldSignature($f, staticFields); |
| 375 $_setStaticGetterSignature($f, staticGetters); | 363 $_setStaticGetterSignature($f, staticGetters); |
| 376 $_setStaticSetterSignature($f, staticSetters); | 364 $_setStaticSetterSignature($f, staticSetters); |
| 377 $_setStaticTypes($f, names); | 365 $_setStaticTypes($f, names); |
| 378 })()'''); | 366 })()'''); |
| 379 | 367 |
| 380 bool _hasSigEntry(type, sigF, name) => JS( | 368 bool _hasSigEntry(type, sigF, name) => JS('bool', '''(() => { |
| 381 'bool', | |
| 382 '''(() => { | |
| 383 let sigObj = $type[$sigF]; | 369 let sigObj = $type[$sigF]; |
| 384 if (sigObj === void 0) return false; | 370 if (sigObj === void 0) return false; |
| 385 return $name in sigObj; | 371 return $name in sigObj; |
| 386 })()'''); | 372 })()'''); |
| 387 | 373 |
| 388 bool hasMethod(type, name) => _hasSigEntry(type, _methodSig, name); | 374 bool hasMethod(type, name) => _hasSigEntry(type, _methodSig, name); |
| 389 bool hasGetter(type, name) => _hasSigEntry(type, _getterSig, name); | 375 bool hasGetter(type, name) => _hasSigEntry(type, _getterSig, name); |
| 390 bool hasSetter(type, name) => _hasSigEntry(type, _setterSig, name); | 376 bool hasSetter(type, name) => _hasSigEntry(type, _setterSig, name); |
| 391 bool hasField(type, name) => _hasSigEntry(type, _fieldSig, name); | 377 bool hasField(type, name) => _hasSigEntry(type, _fieldSig, name); |
| 392 | 378 |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 434 var names = getOwnPropertyNames(coreObjProto); | 420 var names = getOwnPropertyNames(coreObjProto); |
| 435 for (int i = 0; i < JS('int', '#.length', names); ++i) { | 421 for (int i = 0; i < JS('int', '#.length', names); ++i) { |
| 436 var name = JS('', '#[#]', names, i); | 422 var name = JS('', '#[#]', names, i); |
| 437 var desc = getOwnPropertyDescriptor(coreObjProto, name); | 423 var desc = getOwnPropertyDescriptor(coreObjProto, name); |
| 438 defineProperty(jsProto, getExtensionSymbol(name), desc); | 424 defineProperty(jsProto, getExtensionSymbol(name), desc); |
| 439 } | 425 } |
| 440 } | 426 } |
| 441 | 427 |
| 442 final _extensionMap = JS('', 'new Map()'); | 428 final _extensionMap = JS('', 'new Map()'); |
| 443 | 429 |
| 444 _applyExtension(jsType, dartExtType) => JS( | 430 _applyExtension(jsType, dartExtType) => JS('', '''(() => { |
| 445 '', | |
| 446 '''(() => { | |
| 447 // TODO(vsm): Not all registered js types are real. | 431 // TODO(vsm): Not all registered js types are real. |
| 448 if (!$jsType) return; | 432 if (!$jsType) return; |
| 449 | 433 |
| 450 let jsProto = $jsType.prototype; | 434 let jsProto = $jsType.prototype; |
| 451 | 435 |
| 452 // TODO(vsm): This sometimes doesn't exist on FF. These types will be | 436 // TODO(vsm): This sometimes doesn't exist on FF. These types will be |
| 453 // broken. | 437 // broken. |
| 454 if (!jsProto) return; | 438 if (!jsProto) return; |
| 455 | 439 |
| 456 $_installProperties(jsProto, $dartExtType, jsProto[$_extensionType]); | 440 $_installProperties(jsProto, $dartExtType, jsProto[$_extensionType]); |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 498 /// | 482 /// |
| 499 /// Results in: | 483 /// Results in: |
| 500 /// | 484 /// |
| 501 /// MyType.prototype[dartx.add] = MyType.prototype.add; | 485 /// MyType.prototype[dartx.add] = MyType.prototype.add; |
| 502 /// MyType.prototype[dartx.remove] = MyType.prototype.remove; | 486 /// MyType.prototype[dartx.remove] = MyType.prototype.remove; |
| 503 /// | 487 /// |
| 504 // TODO(jmesserly): essentially this gives two names to the same method. | 488 // TODO(jmesserly): essentially this gives two names to the same method. |
| 505 // This benefit is roughly equivalent call performance either way, but the | 489 // This benefit is roughly equivalent call performance either way, but the |
| 506 // cost is we need to call defineExtensionMembers any time a subclass | 490 // cost is we need to call defineExtensionMembers any time a subclass |
| 507 // overrides one of these methods. | 491 // overrides one of these methods. |
| 508 defineExtensionMembers(type, methodNames) => JS( | 492 defineExtensionMembers(type, methodNames) => JS('', '''(() => { |
| 509 '', | |
| 510 '''(() => { | |
| 511 let proto = $type.prototype; | 493 let proto = $type.prototype; |
| 512 for (let name of $methodNames) { | 494 for (let name of $methodNames) { |
| 513 let method = $getOwnPropertyDescriptor(proto, name); | 495 let method = $getOwnPropertyDescriptor(proto, name); |
| 514 $defineProperty(proto, $getExtensionSymbol(name), method); | 496 $defineProperty(proto, $getExtensionSymbol(name), method); |
| 515 } | 497 } |
| 516 // Ensure the signature is available too. | 498 // Ensure the signature is available too. |
| 517 // TODO(jmesserly): not sure if we can do this in a cleaner way. Essentially | 499 // TODO(jmesserly): not sure if we can do this in a cleaner way. Essentially |
| 518 // we need to copy the signature (and in the future, other data like | 500 // we need to copy the signature (and in the future, other data like |
| 519 // annotations) any time we copy a method as part of our metaprogramming. | 501 // annotations) any time we copy a method as part of our metaprogramming. |
| 520 // It might be more friendly to JS metaprogramming if we include this info | 502 // It might be more friendly to JS metaprogramming if we include this info |
| (...skipping 19 matching lines...) Expand all Loading... |
| 540 upgradeSig($_getterSig); | 522 upgradeSig($_getterSig); |
| 541 upgradeSig($_setterSig); | 523 upgradeSig($_setterSig); |
| 542 })()'''); | 524 })()'''); |
| 543 | 525 |
| 544 /// Sets the type of `obj` to be `type` | 526 /// Sets the type of `obj` to be `type` |
| 545 setType(obj, type) { | 527 setType(obj, type) { |
| 546 JS('', '#.__proto__ = #.prototype', obj, type); | 528 JS('', '#.__proto__ = #.prototype', obj, type); |
| 547 return obj; | 529 return obj; |
| 548 } | 530 } |
| 549 | 531 |
| 550 /// Sets the element type of a list literal. | |
| 551 list(obj, elementType) => | |
| 552 setType(obj, JS('', '#(#)', getGenericClass(JSArray), elementType)); | |
| 553 | |
| 554 /// Link the extension to the type it's extending as a base class. | 532 /// Link the extension to the type it's extending as a base class. |
| 555 setBaseClass(derived, base) { | 533 setBaseClass(derived, base) { |
| 556 JS('', '#.prototype.__proto__ = #.prototype', derived, base); | 534 JS('', '#.prototype.__proto__ = #.prototype', derived, base); |
| 557 // We use __proto__ to track the superclass hierarchy (see isSubtype). | 535 // We use __proto__ to track the superclass hierarchy (see isSubtype). |
| 558 JS('', '#.__proto__ = #', derived, base); | 536 JS('', '#.__proto__ = #', derived, base); |
| 559 } | 537 } |
| 560 | 538 |
| 561 /// Like [setBaseClass], but for generic extension types such as `JSArray<E>`. | 539 /// Like [setBaseClass], but for generic extension types such as `JSArray<E>`. |
| 562 setExtensionBaseClass(dartType, jsType) { | 540 setExtensionBaseClass(dartType, jsType) { |
| 563 // Mark the generic type as an extension type and link the prototype objects. | 541 // Mark the generic type as an extension type and link the prototype objects. |
| 564 var dartProto = JS('', '#.prototype', dartType); | 542 var dartProto = JS('', '#.prototype', dartType); |
| 565 JS('', '#[#] = #', dartProto, _extensionType, dartType); | 543 JS('', '#[#] = #', dartProto, _extensionType, dartType); |
| 566 JS('', '#.__proto__ = #.prototype', dartProto, jsType); | 544 JS('', '#.__proto__ = #.prototype', dartProto, jsType); |
| 567 } | 545 } |
| 568 | 546 |
| 569 defineEnumValues(enumClass, names) { | 547 defineEnumValues(enumClass, names) { |
| 570 var values = []; | 548 var values = []; |
| 571 for (var i = 0; i < JS('int', '#.length', names); i++) { | 549 for (var i = 0; i < JS('int', '#.length', names); i++) { |
| 572 var value = const_(JS('', 'new #.new(#)', enumClass, i)); | 550 var value = const_(JS('', 'new #.new(#)', enumClass, i)); |
| 573 JS('', '#.push(#)', values, value); | 551 JS('', '#.push(#)', values, value); |
| 574 defineValue(enumClass, JS('', '#[#]', names, i), value); | 552 defineValue(enumClass, JS('', '#[#]', names, i), value); |
| 575 } | 553 } |
| 576 JS('', '#.values = #', enumClass, constList(values, enumClass)); | 554 JS('', '#.values = #', enumClass, constList(values, enumClass)); |
| 577 } | 555 } |
| OLD | NEW |