| 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 /// | |
| 24 mixin(base, @rest mixins) => JS( | 23 mixin(base, @rest mixins) => JS( |
| 25 '', | 24 '', |
| 26 '''(() => { | 25 '''(() => { |
| 27 // Create an initializer for the mixin, so when derived constructor calls | 26 // Create an initializer for the mixin, so when derived constructor calls |
| 28 // super, we can correctly initialize base and mixins. | 27 // super, we can correctly initialize base and mixins. |
| 29 | 28 |
| 30 // Create a class that will hold all of the mixin methods. | 29 // Create a class that will hold all of the mixin methods. |
| 31 class Mixin extends $base {} | 30 class Mixin extends $base {} |
| 32 // Save the original constructor. For ClassTypeAlias definitions, this | 31 // Save the original constructor. For ClassTypeAlias definitions, this |
| 33 // is the concrete type. We embed metadata (e.g., implemented interfaces) | 32 // is the concrete type. We embed metadata (e.g., implemented interfaces) |
| 34 // on this constructor and need to access that from runtime instances. | 33 // on this constructor and need to access that from runtime instances. |
| 35 let constructor = Mixin.prototype.constructor; | 34 let constructor = Mixin.prototype.constructor; |
| 36 // Copy each mixin's methods, with later ones overwriting earlier entries. | 35 // Copy each mixin's methods, with later ones overwriting earlier entries. |
| 37 for (let m of $mixins) { | 36 for (let m of $mixins) { |
| 38 $copyProperties(Mixin.prototype, m.prototype); | 37 $copyProperties(Mixin.prototype, m.prototype); |
| 39 } | 38 } |
| 40 // Restore original Mixin constructor. | 39 // Restore original Mixin JS constructor. |
| 41 Mixin.prototype.constructor = constructor; | 40 Mixin.prototype.constructor = constructor; |
| 42 // Initializer methods: run mixin initializers, then the base. | 41 // Dart constructors: run mixin constructors, then the base constructors. |
| 43 Mixin.prototype.new = function(...args) { | 42 for (let memberName of $getOwnNamesAndSymbols($base)) { |
| 44 // Run mixin initializers. They cannot have arguments. | 43 let member = $safeGetOwnProperty($base, memberName); |
| 45 // Run them backwards so most-derived mixin is initialized first. | 44 if (typeof member == "function" && member.prototype === base.prototype) { |
| 46 for (let i = $mixins.length - 1; i >= 0; i--) { | 45 $defineValue(Mixin, memberName, function(...args) { |
| 47 $mixins[i].prototype.new.call(this); | |
| 48 } | |
| 49 // Run base initializer. | |
| 50 $base.prototype.new.apply(this, args); | |
| 51 }; | |
| 52 let namedCtors = ${safeGetOwnProperty(base, _namedConstructors)}; | |
| 53 if ($base[$_namedConstructors] != null) { | |
| 54 for (let namedCtor of $base[$_namedConstructors]) { | |
| 55 Mixin.prototype[namedCtor] = function(...args) { | |
| 56 // Run mixin initializers. They cannot have arguments. | 46 // Run mixin initializers. They cannot have arguments. |
| 57 // Run them backwards so most-derived mixin is initialized first. | 47 // Run them backwards so most-derived mixin is initialized first. |
| 58 for (let i = $mixins.length - 1; i >= 0; i--) { | 48 for (let i = $mixins.length - 1; i >= 0; i--) { |
| 59 $mixins[i].prototype.new.call(this); | 49 $mixins[i].new.call(this); |
| 60 } | 50 } |
| 61 // Run base initializer. | 51 // Run base initializer. |
| 62 $base.prototype[namedCtor].apply(this, args); | 52 $base[memberName].apply(this, args); |
| 63 }; | 53 }).prototype = Mixin.prototype; |
| 64 $defineNamedConstructor(Mixin, namedCtor); | |
| 65 } | 54 } |
| 66 } | 55 } |
| 67 | 56 |
| 68 // Set the signature of the Mixin class to be the composition | 57 // Set the signature of the Mixin class to be the composition |
| 69 // of the signatures of the mixins. | 58 // of the signatures of the mixins. |
| 70 $setSignature(Mixin, { | 59 $setSignature(Mixin, { |
| 71 methods: () => { | 60 methods: () => { |
| 72 let s = {}; | 61 let s = {}; |
| 73 for (let m of $mixins) { | 62 for (let m of $mixins) { |
| 74 if (m[$_methodSig]) $copyProperties(s, m[$_methodSig]); | 63 if (m[$_methodSig]) $copyProperties(s, m[$_methodSig]); |
| (...skipping 315 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 390 let sigObj = $type[$sigF]; | 379 let sigObj = $type[$sigF]; |
| 391 if (sigObj === void 0) return false; | 380 if (sigObj === void 0) return false; |
| 392 return $name in sigObj; | 381 return $name in sigObj; |
| 393 })()'''); | 382 })()'''); |
| 394 | 383 |
| 395 hasMethod(type, name) => _hasSigEntry(type, _methodSig, name); | 384 hasMethod(type, name) => _hasSigEntry(type, _methodSig, name); |
| 396 hasGetter(type, name) => _hasSigEntry(type, _getterSig, name); | 385 hasGetter(type, name) => _hasSigEntry(type, _getterSig, name); |
| 397 hasSetter(type, name) => _hasSigEntry(type, _setterSig, name); | 386 hasSetter(type, name) => _hasSigEntry(type, _setterSig, name); |
| 398 hasField(type, name) => _hasSigEntry(type, _fieldSig, name); | 387 hasField(type, name) => _hasSigEntry(type, _fieldSig, name); |
| 399 | 388 |
| 400 /// Given a class and an initializer method name, creates a constructor | |
| 401 /// function with the same name. | |
| 402 /// | |
| 403 /// After we define the named constructor, the class can be constructed with | |
| 404 /// `new SomeClass.name(args)`. | |
| 405 defineNamedConstructor(clazz, name) => JS( | |
| 406 '', | |
| 407 '''(() => { | |
| 408 let proto = $clazz.prototype; | |
| 409 let initMethod = proto[$name]; | |
| 410 let ctor = function(...args) { initMethod.apply(this, args); }; | |
| 411 ctor.prototype = proto; | |
| 412 // Use defineProperty so we don't hit a property defined on Function, | |
| 413 // like `caller` and `arguments`. | |
| 414 $defineProperty($clazz, $name, { value: ctor, configurable: true }); | |
| 415 | |
| 416 let namedCtors = ${safeGetOwnProperty(clazz, _namedConstructors)}; | |
| 417 if (namedCtors == null) $clazz[$_namedConstructors] = namedCtors = []; | |
| 418 namedCtors.push($name); | |
| 419 })()'''); | |
| 420 | |
| 421 final _namedConstructors = JS('', 'Symbol("_namedConstructors")'); | |
| 422 | |
| 423 final _extensionType = JS('', 'Symbol("extensionType")'); | 389 final _extensionType = JS('', 'Symbol("extensionType")'); |
| 424 | 390 |
| 425 getExtensionType(obj) => JS('', '#[#]', obj, _extensionType); | 391 getExtensionType(obj) => JS('', '#[#]', obj, _extensionType); |
| 426 | 392 |
| 427 final dartx = JS('', 'dartx'); | 393 final dartx = JS('', 'dartx'); |
| 428 | 394 |
| 429 getExtensionSymbol(name) { | 395 getExtensionSymbol(name) { |
| 430 var sym = JS('', 'dartx[#]', name); | 396 var sym = JS('', 'dartx[#]', name); |
| 431 if (sym == null) { | 397 if (sym == null) { |
| 432 sym = JS('', 'Symbol("dartx." + #.toString())', name); | 398 sym = JS('', 'Symbol("dartx." + #.toString())', name); |
| (...skipping 159 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 592 return JS( | 558 return JS( |
| 593 '', | 559 '', |
| 594 '''(() => { | 560 '''(() => { |
| 595 if ($base) { | 561 if ($base) { |
| 596 $derived.prototype[$_extensionType] = $derived; | 562 $derived.prototype[$_extensionType] = $derived; |
| 597 $derived.prototype.__proto__ = $base.prototype | 563 $derived.prototype.__proto__ = $base.prototype |
| 598 } | 564 } |
| 599 })()'''); | 565 })()'''); |
| 600 } | 566 } |
| 601 | 567 |
| 602 /// Given a special constructor function that creates a function instances, | 568 defineEnumValues(enumClass, names) { |
| 603 /// and a class with a `call` method, merge them so the constructor function | 569 var values = []; |
| 604 /// will have the correct methods and prototype. | 570 for (var i = 0; i < JS('int', '#.length', names); i++) { |
| 605 /// | 571 var value = const_(JS('', 'new #.new(#)', enumClass, i)); |
| 606 /// For example: | 572 JS('', '#.push(#)', values, value); |
| 607 /// | 573 defineValue(enumClass, JS('', '#[#]', names, i), value); |
| 608 /// lib.Foo = dart.callableClass( | 574 } |
| 609 /// function Foo { function call(...args) { ... } ... return call; }, | 575 JS('', '#.values = #', enumClass, constList(values, enumClass)); |
| 610 /// class Foo { call(x) { ... } }); | |
| 611 /// ... | |
| 612 /// let f = new lib.Foo(); | |
| 613 /// f(42); | |
| 614 callableClass(callableCtor, classExpr) { | |
| 615 JS('', '#.prototype = #.prototype', callableCtor, classExpr); | |
| 616 // We're not going to use the original class, so we can safely replace it to | |
| 617 // point at this constructor for the runtime type information. | |
| 618 JS('', '#.prototype.constructor = #', callableCtor, callableCtor); | |
| 619 JS('', '#.__proto__ = #', callableCtor, classExpr); | |
| 620 return callableCtor; | |
| 621 } | 576 } |
| 622 | |
| 623 /// Given a class and an initializer method name and a call method, creates a | |
| 624 /// constructor function with the same name. | |
| 625 /// | |
| 626 /// For example it can be called with `new SomeClass.name(args)`. | |
| 627 /// | |
| 628 /// The constructor | |
| 629 defineNamedConstructorCallable(clazz, name, ctor) => JS( | |
| 630 '', | |
| 631 '''(() => { | |
| 632 ctor.prototype = $clazz.prototype; | |
| 633 // Use defineProperty so we don't hit a property defined on Function, | |
| 634 // like `caller` and `arguments`. | |
| 635 $defineProperty($clazz, $name, { value: ctor, configurable: true }); | |
| 636 | |
| 637 let namedCtors = ${safeGetOwnProperty(clazz, _namedConstructors)}; | |
| 638 if (namedCtors == null) $clazz[$_namedConstructors] = namedCtors = []; | |
| 639 namedCtors.push($name); | |
| 640 })()'''); | |
| 641 | |
| 642 defineEnumValues(enumClass, names) => JS( | |
| 643 '', | |
| 644 '''(() => { | |
| 645 let values = []; | |
| 646 for (var i = 0; i < $names.length; i++) { | |
| 647 let value = $const_(new $enumClass(i)); | |
| 648 values.push(value); | |
| 649 Object.defineProperty($enumClass, $names[i], | |
| 650 { value: value, configurable: true }); | |
| 651 } | |
| 652 $enumClass.values = $constList(values, $enumClass); | |
| 653 })()'''); | |
| OLD | NEW |