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 library dev_compiler.src.codegen.js_codegen; | 5 library dev_compiler.src.codegen.js_codegen; |
6 | 6 |
7 import 'dart:collection' show HashSet, HashMap; | 7 import 'dart:collection' show HashSet, HashMap; |
8 | 8 |
9 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; | 9 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; |
10 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator; | 10 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator; |
(...skipping 449 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
460 | 460 |
461 _loader.finishTopLevel(element); | 461 _loader.finishTopLevel(element); |
462 return heritage; | 462 return heritage; |
463 } | 463 } |
464 | 464 |
465 List<JS.Method> _emitClassMethods(ClassDeclaration node, | 465 List<JS.Method> _emitClassMethods(ClassDeclaration node, |
466 List<ConstructorDeclaration> ctors, List<FieldDeclaration> fields) { | 466 List<ConstructorDeclaration> ctors, List<FieldDeclaration> fields) { |
467 var element = node.element; | 467 var element = node.element; |
468 var type = element.type; | 468 var type = element.type; |
469 var isObject = type.isObject; | 469 var isObject = type.isObject; |
470 var name = node.name.name; | |
471 | 470 |
472 // Iff no constructor is specified for a class C, it implicitly has a | 471 // Iff no constructor is specified for a class C, it implicitly has a |
473 // default constructor `C() : super() {}`, unless C is class Object. | 472 // default constructor `C() : super() {}`, unless C is class Object. |
474 var jsMethods = <JS.Method>[]; | 473 var jsMethods = <JS.Method>[]; |
475 if (ctors.isEmpty && !isObject) { | 474 if (ctors.isEmpty && !isObject) { |
476 jsMethods.add(_emitImplicitConstructor(node, name, fields)); | 475 jsMethods.add(_emitImplicitConstructor(node, fields)); |
477 } | 476 } |
478 | 477 |
479 bool hasJsPeer = getAnnotationValue(node, _isJsPeerInterface) != null; | 478 bool hasJsPeer = getAnnotationValue(node, _isJsPeerInterface) != null; |
480 | 479 |
481 bool hasIterator = false; | 480 bool hasIterator = false; |
482 for (var m in node.members) { | 481 for (var m in node.members) { |
483 if (m is ConstructorDeclaration) { | 482 if (m is ConstructorDeclaration) { |
484 jsMethods.add(_emitConstructor(m, name, fields, isObject)); | 483 jsMethods.add(_emitConstructor(m, type, fields, isObject)); |
485 } else if (m is MethodDeclaration) { | 484 } else if (m is MethodDeclaration) { |
486 jsMethods.add(_emitMethodDeclaration(type, m)); | 485 jsMethods.add(_emitMethodDeclaration(type, m)); |
487 | 486 |
488 if (!hasJsPeer && m.isGetter && m.name.name == 'iterator') { | 487 if (!hasJsPeer && m.isGetter && m.name.name == 'iterator') { |
489 hasIterator = true; | 488 hasIterator = true; |
490 jsMethods.add(_emitIterable(type)); | 489 jsMethods.add(_emitIterable(type)); |
491 } | 490 } |
492 } | 491 } |
493 } | 492 } |
494 | 493 |
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
557 if (classElem.interfaces.isNotEmpty) { | 556 if (classElem.interfaces.isNotEmpty) { |
558 body.add(js.statement('#[dart.implements] = () => #;', [ | 557 body.add(js.statement('#[dart.implements] = () => #;', [ |
559 name, | 558 name, |
560 new JS.ArrayInitializer( | 559 new JS.ArrayInitializer( |
561 classElem.interfaces.map(_emitTypeName).toList()) | 560 classElem.interfaces.map(_emitTypeName).toList()) |
562 ])); | 561 ])); |
563 } | 562 } |
564 | 563 |
565 // Named constructors | 564 // Named constructors |
566 for (ConstructorDeclaration member in ctors) { | 565 for (ConstructorDeclaration member in ctors) { |
567 if (member.name != null) { | 566 if (member.name != null && member.factoryKeyword == null) { |
568 body.add(js.statement('dart.defineNamedConstructor(#, #);', [ | 567 body.add(js.statement('dart.defineNamedConstructor(#, #);', [ |
569 name, | 568 name, |
570 _emitMemberName(member.name.name, isStatic: true) | 569 _emitMemberName(member.name.name, isStatic: true) |
571 ])); | 570 ])); |
572 } | 571 } |
573 } | 572 } |
574 | 573 |
575 // Instance fields, if they override getter/setter pairs | 574 // Instance fields, if they override getter/setter pairs |
576 for (FieldDeclaration member in fields) { | 575 for (FieldDeclaration member in fields) { |
577 for (VariableDeclaration fieldDecl in member.fields.variables) { | 576 for (VariableDeclaration fieldDecl in member.fields.variables) { |
(...skipping 26 matching lines...) Expand all Loading... | |
604 var property = | 603 var property = |
605 new JS.Property(memberName, new JS.ArrayInitializer(parts)); | 604 new JS.Property(memberName, new JS.ArrayInitializer(parts)); |
606 if (node.isStatic) { | 605 if (node.isStatic) { |
607 tStatics.add(property); | 606 tStatics.add(property); |
608 sNames.add(memberName); | 607 sNames.add(memberName); |
609 } else tMethods.add(property); | 608 } else tMethods.add(property); |
610 } | 609 } |
611 } | 610 } |
612 var tCtors = []; | 611 var tCtors = []; |
613 for (ConstructorDeclaration node in ctors) { | 612 for (ConstructorDeclaration node in ctors) { |
614 var memberName = _constructorName(classElem.name, node.name); | 613 var memberName = _constructorName(node.element); |
615 var element = node.element; | 614 var element = node.element; |
616 var parts = | 615 var parts = |
617 _emitFunctionTypeParts(element.type, dynamicIsBottom: false); | 616 _emitFunctionTypeParts(element.type, dynamicIsBottom: false); |
618 var property = | 617 var property = |
619 new JS.Property(memberName, new JS.ArrayInitializer(parts)); | 618 new JS.Property(memberName, new JS.ArrayInitializer(parts)); |
620 tCtors.add(property); | 619 tCtors.add(property); |
621 } | 620 } |
622 build(name, elements) { | 621 build(name, elements) { |
623 var o = | 622 var o = |
624 new JS.ObjectInitializer(elements, vertical: elements.length > 1); | 623 new JS.ObjectInitializer(elements, vertical: elements.length > 1); |
(...skipping 25 matching lines...) Expand all Loading... | |
650 var cls = e.enclosingElement; | 649 var cls = e.enclosingElement; |
651 return js.statement('dart.virtualField(#, #)', [ | 650 return js.statement('dart.virtualField(#, #)', [ |
652 cls.name, | 651 cls.name, |
653 _emitMemberName(e.name, type: cls.type) | 652 _emitMemberName(e.name, type: cls.type) |
654 ]); | 653 ]); |
655 } | 654 } |
656 | 655 |
657 /// Generates the implicit default constructor for class C of the form | 656 /// Generates the implicit default constructor for class C of the form |
658 /// `C() : super() {}`. | 657 /// `C() : super() {}`. |
659 JS.Method _emitImplicitConstructor( | 658 JS.Method _emitImplicitConstructor( |
660 ClassDeclaration node, String name, List<FieldDeclaration> fields) { | 659 ClassDeclaration node, List<FieldDeclaration> fields) { |
661 assert(_hasUnnamedConstructor(node.element) == fields.isNotEmpty); | 660 assert(_hasUnnamedConstructor(node.element) == fields.isNotEmpty); |
662 | 661 |
663 // If we don't have a method body, skip this. | 662 // If we don't have a method body, skip this. |
664 var superCall = _superConstructorCall(node); | 663 var superCall = _superConstructorCall(node.element); |
665 if (fields.isEmpty && superCall == null) return null; | 664 if (fields.isEmpty && superCall == null) return null; |
666 | 665 |
667 dynamic body = _initializeFields(node, fields); | 666 dynamic body = _initializeFields(node, fields); |
668 if (superCall != null) body = [[body, superCall]]; | 667 if (superCall != null) body = [[body, superCall]]; |
669 return new JS.Method( | 668 var name = _constructorName(node.element.unnamedConstructor); |
670 _propertyName(name), js.call('function() { #; }', body)); | 669 return new JS.Method(name, js.call('function() { #; }', body)); |
671 } | 670 } |
672 | 671 |
673 JS.Method _emitConstructor(ConstructorDeclaration node, String className, | 672 JS.Method _emitConstructor(ConstructorDeclaration node, InterfaceType type, |
674 List<FieldDeclaration> fields, bool isObject) { | 673 List<FieldDeclaration> fields, bool isObject) { |
675 if (_externalOrNative(node)) return null; | 674 if (_externalOrNative(node)) return null; |
676 | 675 |
677 var name = _constructorName(className, node.name); | 676 var name = _constructorName(node.element); |
677 | |
678 // Wacky factory redirecting constructors: factory Foo.q(x, y) = Bar.baz; | |
Jennifer Messerly
2015/05/29 19:54:53
this code moved from _emitConstructorBody, which w
| |
679 var redirect = node.redirectedConstructor; | |
680 if (redirect != null) { | |
681 var newKeyword = redirect.staticElement.isFactory ? '' : 'new'; | |
682 // Pass along all arguments verbatim, and let the callee handle them. | |
683 // TODO(jmesserly): we'll need something different once we have | |
684 // rest/spread support, but this should work for now. | |
685 var params = _visit(node.parameters); | |
686 var fun = js.call('function(#) { return $newKeyword #(#); }', [ | |
687 params, | |
688 _visit(redirect), | |
689 params, | |
690 ]); | |
691 return new JS.Method(name, fun, isStatic: true)..sourceInformation = node; | |
692 } | |
693 | |
694 // Factory constructors are essentially static methods. | |
695 if (node.factoryKeyword != null) { | |
696 var body = <JS.Statement>[]; | |
697 var init = _emitArgumentInitializers(node, constructor: true); | |
698 if (init != null) body.add(init); | |
699 body.add(_visit(node.body)); | |
700 var fun = new JS.Fun(_visit(node.parameters), new JS.Block(body)); | |
701 return new JS.Method(name, fun, isStatic: true)..sourceInformation = node; | |
702 } | |
678 | 703 |
679 // Code generation for Object's constructor. | 704 // Code generation for Object's constructor. |
680 JS.Block body; | 705 JS.Block body; |
681 if (isObject && | 706 if (isObject && |
682 node.body is EmptyFunctionBody && | 707 node.body is EmptyFunctionBody && |
683 node.constKeyword != null && | 708 node.constKeyword != null && |
684 node.name == null) { | 709 node.name == null) { |
685 // Implements Dart constructor behavior. Because of V8 `super` | 710 // Implements Dart constructor behavior. Because of V8 `super` |
686 // [constructor restrictions] | 711 // [constructor restrictions] |
687 // (https://code.google.com/p/v8/issues/detail?id=3330#c65) | 712 // (https://code.google.com/p/v8/issues/detail?id=3330#c65) |
(...skipping 19 matching lines...) Expand all Loading... | |
707 body = _emitConstructorBody(node, fields); | 732 body = _emitConstructorBody(node, fields); |
708 } | 733 } |
709 | 734 |
710 // We generate constructors as initializer methods in the class; | 735 // We generate constructors as initializer methods in the class; |
711 // this allows use of `super` for instance methods/properties. | 736 // this allows use of `super` for instance methods/properties. |
712 // It also avoids V8 restrictions on `super` in default constructors. | 737 // It also avoids V8 restrictions on `super` in default constructors. |
713 return new JS.Method(name, new JS.Fun(_visit(node.parameters), body)) | 738 return new JS.Method(name, new JS.Fun(_visit(node.parameters), body)) |
714 ..sourceInformation = node; | 739 ..sourceInformation = node; |
715 } | 740 } |
716 | 741 |
717 JS.Expression _constructorName(String className, SimpleIdentifier name) { | 742 JS.Expression _constructorName(ConstructorElement ctor) { |
718 if (name == null) return _propertyName(className); | 743 var name = ctor.name; |
719 return _emitMemberName(name.name, isStatic: true); | 744 if (name != '') { |
745 return _emitMemberName(name, isStatic: true); | |
746 } | |
747 | |
748 // Factory default constructors use `new` as their name, for readability | |
749 // Other default constructors use the class name, as they aren't called | |
750 // from call sites, but rather from Object's constructor. | |
751 // TODO(jmesserly): revisit in the context of Dart metaclasses, and cleaning | |
752 // up constructors to integrate more closely with ES6. | |
753 return _propertyName(ctor.isFactory ? 'new' : ctor.enclosingElement.name); | |
720 } | 754 } |
721 | 755 |
722 JS.Block _emitConstructorBody( | 756 JS.Block _emitConstructorBody( |
723 ConstructorDeclaration node, List<FieldDeclaration> fields) { | 757 ConstructorDeclaration node, List<FieldDeclaration> fields) { |
724 // Wacky factory redirecting constructors: factory Foo.q(x, y) = Bar.baz; | |
725 if (node.redirectedConstructor != null) { | |
726 return js.statement('{ return new #(#); }', [ | |
727 _visit(node.redirectedConstructor), | |
728 _visit(node.parameters) | |
729 ]); | |
730 } | |
731 | |
732 var body = <JS.Statement>[]; | 758 var body = <JS.Statement>[]; |
733 | 759 |
734 // Generate optional/named argument value assignment. These can not have | 760 // Generate optional/named argument value assignment. These can not have |
735 // side effects, and may be used by the constructor's initializers, so it's | 761 // side effects, and may be used by the constructor's initializers, so it's |
736 // nice to do them first. | 762 // nice to do them first. |
737 // Also for const constructors we need to ensure default values are | 763 // Also for const constructors we need to ensure default values are |
738 // available for use by top-level constant initializers. | 764 // available for use by top-level constant initializers. |
739 ClassDeclaration cls = node.parent; | 765 ClassDeclaration cls = node.parent; |
740 if (node.constKeyword != null) _loader.startTopLevel(cls.element); | 766 if (node.constKeyword != null) _loader.startTopLevel(cls.element); |
741 var init = _emitArgumentInitializers(node, constructor: true); | 767 var init = _emitArgumentInitializers(node, constructor: true); |
742 if (node.constKeyword != null) _loader.finishTopLevel(cls.element); | 768 if (node.constKeyword != null) _loader.finishTopLevel(cls.element); |
743 if (init != null) body.add(init); | 769 if (init != null) body.add(init); |
744 | 770 |
745 // Redirecting constructors: these are not allowed to have initializers, | 771 // Redirecting constructors: these are not allowed to have initializers, |
746 // and the redirecting ctor invocation runs before field initializers. | 772 // and the redirecting ctor invocation runs before field initializers. |
747 var redirectCall = node.initializers.firstWhere( | 773 var redirectCall = node.initializers.firstWhere( |
748 (i) => i is RedirectingConstructorInvocation, orElse: () => null); | 774 (i) => i is RedirectingConstructorInvocation, orElse: () => null); |
749 | 775 |
750 if (redirectCall != null) { | 776 if (redirectCall != null) { |
751 body.add(_visit(redirectCall)); | 777 body.add(_visit(redirectCall)); |
752 return new JS.Block(body); | 778 return new JS.Block(body); |
753 } | 779 } |
754 | 780 |
755 // Initializers only run for non-factory constructors. | 781 // Generate field initializers. |
756 if (node.factoryKeyword == null) { | 782 // These are expanded into each non-redirecting constructor. |
757 // Generate field initializers. | 783 // In the future we may want to create an initializer function if we have |
758 // These are expanded into each non-redirecting constructor. | 784 // multiple constructors, but it needs to be balanced against readability. |
759 // In the future we may want to create an initializer function if we have | 785 body.add(_initializeFields(node.parent, fields, node)); |
760 // multiple constructors, but it needs to be balanced against readability. | |
761 body.add(_initializeFields(node.parent, fields, node)); | |
762 | 786 |
763 var superCall = node.initializers.firstWhere( | 787 var superCall = node.initializers.firstWhere( |
764 (i) => i is SuperConstructorInvocation, orElse: () => null); | 788 (i) => i is SuperConstructorInvocation, orElse: () => null); |
765 | 789 |
766 // If no superinitializer is provided, an implicit superinitializer of the | 790 // If no superinitializer is provided, an implicit superinitializer of the |
767 // form `super()` is added at the end of the initializer list, unless the | 791 // form `super()` is added at the end of the initializer list, unless the |
768 // enclosing class is class Object. | 792 // enclosing class is class Object. |
769 var jsSuper = _superConstructorCall(node.parent, superCall); | 793 var jsSuper = _superConstructorCall(cls.element, superCall); |
770 if (jsSuper != null) body.add(jsSuper); | 794 if (jsSuper != null) body.add(jsSuper); |
771 } | |
772 | 795 |
773 body.add(_visit(node.body)); | 796 body.add(_visit(node.body)); |
774 return new JS.Block(body)..sourceInformation = node; | 797 return new JS.Block(body)..sourceInformation = node; |
775 } | 798 } |
776 | 799 |
777 @override | 800 @override |
778 JS.Statement visitRedirectingConstructorInvocation( | 801 JS.Statement visitRedirectingConstructorInvocation( |
779 RedirectingConstructorInvocation node) { | 802 RedirectingConstructorInvocation node) { |
780 ClassDeclaration classDecl = node.parent.parent; | 803 var name = _constructorName(node.staticElement); |
781 var className = classDecl.name.name; | |
782 | |
783 var name = _constructorName(className, node.constructorName); | |
784 return js.statement('this.#(#);', [name, _visit(node.argumentList)]); | 804 return js.statement('this.#(#);', [name, _visit(node.argumentList)]); |
785 } | 805 } |
786 | 806 |
787 JS.Statement _superConstructorCall(ClassDeclaration clazz, | 807 JS.Statement _superConstructorCall(ClassElement element, |
788 [SuperConstructorInvocation node]) { | 808 [SuperConstructorInvocation node]) { |
789 var superCtorName = node != null ? node.constructorName : null; | |
790 | 809 |
791 var element = clazz.element; | 810 ConstructorElement superCtor; |
792 if (superCtorName == null && !_shouldCallUnnamedSuperCtor(element)) { | 811 if (node != null) { |
812 superCtor = node.staticElement; | |
813 } else { | |
814 // Get the supertype's unnamed constructor. | |
815 superCtor = element.supertype.element.unnamedConstructor; | |
816 if (superCtor == null) { | |
817 // This will only happen if the code has errors: | |
818 // we're trying to generate an implicit constructor for a type where | |
819 // we don't have a default constructor in the supertype. | |
820 assert(options.forceCompile); | |
821 return null; | |
822 } | |
823 } | |
824 | |
825 if (superCtor.name == '' && !_shouldCallUnnamedSuperCtor(element)) { | |
793 return null; | 826 return null; |
794 } | 827 } |
795 | 828 |
796 var supertypeName = element.supertype.name; | 829 var name = _constructorName(superCtor); |
797 var name = _constructorName(supertypeName, superCtorName); | |
798 | |
799 var args = node != null ? _visit(node.argumentList) : []; | 830 var args = node != null ? _visit(node.argumentList) : []; |
800 return js.statement('super.#(#);', [name, args])..sourceInformation = node; | 831 return js.statement('super.#(#);', [name, args])..sourceInformation = node; |
801 } | 832 } |
802 | 833 |
803 bool _shouldCallUnnamedSuperCtor(ClassElement e) { | 834 bool _shouldCallUnnamedSuperCtor(ClassElement e) { |
804 var supertype = e.supertype; | 835 var supertype = e.supertype; |
805 if (supertype == null) return false; | 836 if (supertype == null) return false; |
806 if (_hasUnnamedConstructor(supertype.element)) return true; | 837 if (_hasUnnamedConstructor(supertype.element)) return true; |
807 for (var mixin in e.mixins) { | 838 for (var mixin in e.mixins) { |
808 if (_hasUnnamedConstructor(mixin.element)) return true; | 839 if (_hasUnnamedConstructor(mixin.element)) return true; |
(...skipping 820 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1629 body.add(js.statement('dart.copyProperties(#, { # });', [ | 1660 body.add(js.statement('dart.copyProperties(#, { # });', [ |
1630 _exportsVar, | 1661 _exportsVar, |
1631 _properties.map(_emitTopLevelProperty) | 1662 _properties.map(_emitTopLevelProperty) |
1632 ])); | 1663 ])); |
1633 _properties.clear(); | 1664 _properties.clear(); |
1634 } | 1665 } |
1635 | 1666 |
1636 @override | 1667 @override |
1637 visitConstructorName(ConstructorName node) { | 1668 visitConstructorName(ConstructorName node) { |
1638 var typeName = _visit(node.type); | 1669 var typeName = _visit(node.type); |
1639 if (node.name != null) { | 1670 if (node.name != null || node.staticElement.isFactory) { |
1640 return js.call( | 1671 var namedCtor = _constructorName(node.staticElement); |
1641 '#.#', [typeName, _emitMemberName(node.name.name, isStatic: true)]); | 1672 return new JS.PropertyAccess(typeName, namedCtor); |
1642 } | 1673 } |
1643 return typeName; | 1674 return typeName; |
1644 } | 1675 } |
1645 | 1676 |
1646 @override | 1677 @override |
1647 visitInstanceCreationExpression(InstanceCreationExpression node) { | 1678 visitInstanceCreationExpression(InstanceCreationExpression node) { |
1648 emitNew() => js.call( | 1679 emitNew() { |
1649 'new #(#)', [_visit(node.constructorName), _visit(node.argumentList)]); | 1680 var ctor = _visit(node.constructorName); |
1681 var args = _visit(node.argumentList); | |
1682 var isFactory = node.staticElement.isFactory; | |
1683 return isFactory ? new JS.Call(ctor, args) : new JS.New(ctor, args); | |
1684 } | |
1650 if (node.isConst) return _emitConst(node, emitNew); | 1685 if (node.isConst) return _emitConst(node, emitNew); |
1651 return emitNew(); | 1686 return emitNew(); |
1652 } | 1687 } |
1653 | 1688 |
1654 /// True if this type is built-in to JS, and we use the values unwrapped. | 1689 /// True if this type is built-in to JS, and we use the values unwrapped. |
1655 /// For these types we generate a calling convention via static | 1690 /// For these types we generate a calling convention via static |
1656 /// "extension methods". This allows types to be extended without adding | 1691 /// "extension methods". This allows types to be extended without adding |
1657 /// extensions directly on the prototype. | 1692 /// extensions directly on the prototype. |
1658 bool _isJSBuiltinType(DartType t) => | 1693 bool _isJSBuiltinType(DartType t) => |
1659 typeIsPrimitiveInJS(t) || rules.isStringType(t); | 1694 typeIsPrimitiveInJS(t) || rules.isStringType(t); |
(...skipping 952 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2612 | 2647 |
2613 /// A special kind of element created by the compiler, signifying a temporary | 2648 /// A special kind of element created by the compiler, signifying a temporary |
2614 /// variable. These objects use instance equality, and should be shared | 2649 /// variable. These objects use instance equality, and should be shared |
2615 /// everywhere in the tree where they are treated as the same variable. | 2650 /// everywhere in the tree where they are treated as the same variable. |
2616 class TemporaryVariableElement extends LocalVariableElementImpl { | 2651 class TemporaryVariableElement extends LocalVariableElementImpl { |
2617 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); | 2652 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); |
2618 | 2653 |
2619 int get hashCode => identityHashCode(this); | 2654 int get hashCode => identityHashCode(this); |
2620 bool operator ==(Object other) => identical(this, other); | 2655 bool operator ==(Object other) => identical(this, other); |
2621 } | 2656 } |
OLD | NEW |