Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(636)

Side by Side Diff: lib/src/codegen/js_codegen.dart

Issue 1156993015: fixes #193, factory constructors as static methods (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
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
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698