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 651 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
662 _visit(node.redirectedConstructor), | 662 _visit(node.redirectedConstructor), |
663 _visit(node.parameters) | 663 _visit(node.parameters) |
664 ]); | 664 ]); |
665 } | 665 } |
666 | 666 |
667 var body = <JS.Statement>[]; | 667 var body = <JS.Statement>[]; |
668 | 668 |
669 // Generate optional/named argument value assignment. These can not have | 669 // Generate optional/named argument value assignment. These can not have |
670 // side effects, and may be used by the constructor's initializers, so it's | 670 // side effects, and may be used by the constructor's initializers, so it's |
671 // nice to do them first. | 671 // nice to do them first. |
| 672 // Also for const constructors we need to ensure default values are |
| 673 // available for use by top-level constant initializers. |
| 674 ClassDeclaration cls = node.parent; |
| 675 if (node.constKeyword != null) _loader.startTopLevel(cls.element); |
672 var init = _emitArgumentInitializers(node, constructor: true); | 676 var init = _emitArgumentInitializers(node, constructor: true); |
| 677 if (node.constKeyword != null) _loader.finishTopLevel(cls.element); |
673 if (init != null) body.add(init); | 678 if (init != null) body.add(init); |
674 | 679 |
675 // Redirecting constructors: these are not allowed to have initializers, | 680 // Redirecting constructors: these are not allowed to have initializers, |
676 // and the redirecting ctor invocation runs before field initializers. | 681 // and the redirecting ctor invocation runs before field initializers. |
677 var redirectCall = node.initializers.firstWhere( | 682 var redirectCall = node.initializers.firstWhere( |
678 (i) => i is RedirectingConstructorInvocation, orElse: () => null); | 683 (i) => i is RedirectingConstructorInvocation, orElse: () => null); |
679 | 684 |
680 if (redirectCall != null) { | 685 if (redirectCall != null) { |
681 body.add(_visit(redirectCall)); | 686 body.add(_visit(redirectCall)); |
682 return new JS.Block(body); | 687 return new JS.Block(body); |
683 } | 688 } |
684 | 689 |
685 // Initializers only run for non-factory constructors. | 690 // Initializers only run for non-factory constructors. |
686 if (node.factoryKeyword == null) { | 691 if (node.factoryKeyword == null) { |
687 // Generate field initializers. | 692 // Generate field initializers. |
688 // These are expanded into each non-redirecting constructor. | 693 // These are expanded into each non-redirecting constructor. |
689 // In the future we may want to create an initializer function if we have | 694 // In the future we may want to create an initializer function if we have |
690 // multiple constructors, but it needs to be balanced against readability. | 695 // multiple constructors, but it needs to be balanced against readability. |
691 body.add(_initializeFields(node, fields)); | 696 body.add(_initializeFields(node.parent, fields, node)); |
692 | 697 |
693 var superCall = node.initializers.firstWhere( | 698 var superCall = node.initializers.firstWhere( |
694 (i) => i is SuperConstructorInvocation, orElse: () => null); | 699 (i) => i is SuperConstructorInvocation, orElse: () => null); |
695 | 700 |
696 // If no superinitializer is provided, an implicit superinitializer of the | 701 // If no superinitializer is provided, an implicit superinitializer of the |
697 // form `super()` is added at the end of the initializer list, unless the | 702 // form `super()` is added at the end of the initializer list, unless the |
698 // enclosing class is class Object. | 703 // enclosing class is class Object. |
699 var jsSuper = _superConstructorCall(node.parent, superCall); | 704 var jsSuper = _superConstructorCall(node.parent, superCall); |
700 if (jsSuper != null) body.add(jsSuper); | 705 if (jsSuper != null) body.add(jsSuper); |
701 } | 706 } |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
746 return e.fields.any((f) => !f.isStatic && !f.isSynthetic); | 751 return e.fields.any((f) => !f.isStatic && !f.isSynthetic); |
747 } | 752 } |
748 | 753 |
749 /// Initialize fields. They follow the sequence: | 754 /// Initialize fields. They follow the sequence: |
750 /// | 755 /// |
751 /// 1. field declaration initializer if non-const, | 756 /// 1. field declaration initializer if non-const, |
752 /// 2. field initializing parameters, | 757 /// 2. field initializing parameters, |
753 /// 3. constructor field initializers, | 758 /// 3. constructor field initializers, |
754 /// 4. initialize fields not covered in 1-3 | 759 /// 4. initialize fields not covered in 1-3 |
755 JS.Statement _initializeFields( | 760 JS.Statement _initializeFields( |
756 AstNode node, List<FieldDeclaration> fieldDecls) { | 761 ClassDeclaration cls, List<FieldDeclaration> fieldDecls, |
757 var unit = node.getAncestor((a) => a is CompilationUnit); | 762 [ConstructorDeclaration ctor]) { |
| 763 var unit = cls.getAncestor((a) => a is CompilationUnit); |
758 var constField = new ConstFieldVisitor(types, unit); | 764 var constField = new ConstFieldVisitor(types, unit); |
| 765 bool isConst = ctor != null && ctor.constKeyword != null; |
| 766 if (isConst) _loader.startTopLevel(cls.element); |
759 | 767 |
760 // Run field initializers if they can have side-effects. | 768 // Run field initializers if they can have side-effects. |
761 var fields = new Map<FieldElement, JS.Expression>(); | 769 var fields = new Map<FieldElement, JS.Expression>(); |
762 var unsetFields = new Map<FieldElement, VariableDeclaration>(); | 770 var unsetFields = new Map<FieldElement, VariableDeclaration>(); |
763 for (var declaration in fieldDecls) { | 771 for (var declaration in fieldDecls) { |
764 for (var fieldNode in declaration.fields.variables) { | 772 for (var fieldNode in declaration.fields.variables) { |
765 var element = fieldNode.element; | 773 var element = fieldNode.element; |
766 if (constField.isFieldInitConstant(fieldNode)) { | 774 if (constField.isFieldInitConstant(fieldNode)) { |
767 unsetFields[element] = fieldNode; | 775 unsetFields[element] = fieldNode; |
768 } else { | 776 } else { |
769 fields[element] = _visitInitializer(fieldNode); | 777 fields[element] = _visitInitializer(fieldNode); |
770 } | 778 } |
771 } | 779 } |
772 } | 780 } |
773 | 781 |
774 // Initialize fields from `this.fieldName` parameters. | 782 // Initialize fields from `this.fieldName` parameters. |
775 if (node is ConstructorDeclaration) { | 783 if (ctor != null) { |
776 var parameters = node.parameters; | 784 for (var p in ctor.parameters.parameters) { |
777 var initializers = node.initializers; | |
778 | |
779 for (var p in parameters.parameters) { | |
780 var element = p.element; | 785 var element = p.element; |
781 if (element is FieldFormalParameterElement) { | 786 if (element is FieldFormalParameterElement) { |
782 fields[element.field] = _visit(p); | 787 fields[element.field] = _visit(p); |
783 } | 788 } |
784 } | 789 } |
785 | 790 |
786 // Run constructor field initializers such as `: foo = bar.baz` | 791 // Run constructor field initializers such as `: foo = bar.baz` |
787 for (var init in initializers) { | 792 for (var init in ctor.initializers) { |
788 if (init is ConstructorFieldInitializer) { | 793 if (init is ConstructorFieldInitializer) { |
789 fields[init.fieldName.staticElement] = _visit(init.expression); | 794 fields[init.fieldName.staticElement] = _visit(init.expression); |
790 } | 795 } |
791 } | 796 } |
792 } | 797 } |
793 | 798 |
794 for (var f in fields.keys) unsetFields.remove(f); | 799 for (var f in fields.keys) unsetFields.remove(f); |
795 | 800 |
796 // Initialize all remaining fields | 801 // Initialize all remaining fields |
797 unsetFields.forEach((element, fieldNode) { | 802 unsetFields.forEach((element, fieldNode) { |
798 JS.Expression value; | 803 JS.Expression value; |
799 if (fieldNode.initializer != null) { | 804 if (fieldNode.initializer != null) { |
800 value = _visit(fieldNode.initializer); | 805 value = _visit(fieldNode.initializer); |
801 } else { | 806 } else { |
802 var type = rules.elementType(element); | 807 var type = rules.elementType(element); |
803 value = new JS.LiteralNull(); | 808 value = new JS.LiteralNull(); |
804 if (rules.maybeNonNullableType(type)) { | 809 if (rules.maybeNonNullableType(type)) { |
805 value = js.call('dart.as(#, #)', [value, _emitTypeName(type)]); | 810 value = js.call('dart.as(#, #)', [value, _emitTypeName(type)]); |
806 } | 811 } |
807 } | 812 } |
808 fields[element] = value; | 813 fields[element] = value; |
809 }); | 814 }); |
810 | 815 |
811 var body = <JS.Statement>[]; | 816 var body = <JS.Statement>[]; |
812 fields.forEach((FieldElement e, JS.Expression initialValue) { | 817 fields.forEach((FieldElement e, JS.Expression initialValue) { |
813 var access = _emitMemberName(e.name, type: e.enclosingElement.type); | 818 var access = _emitMemberName(e.name, type: e.enclosingElement.type); |
814 body.add(js.statement('this.# = #;', [access, initialValue])); | 819 body.add(js.statement('this.# = #;', [access, initialValue])); |
815 }); | 820 }); |
| 821 |
| 822 if (isConst) _loader.finishTopLevel(cls.element); |
816 return _statement(body); | 823 return _statement(body); |
817 } | 824 } |
818 | 825 |
819 FormalParameterList _parametersOf(node) { | 826 FormalParameterList _parametersOf(node) { |
820 // TODO(jmesserly): clean this up. If we can model ES6 spread/rest args, we | 827 // TODO(jmesserly): clean this up. If we can model ES6 spread/rest args, we |
821 // could handle argument initializers more consistently in a separate | 828 // could handle argument initializers more consistently in a separate |
822 // lowering pass. | 829 // lowering pass. |
823 if (node is ConstructorDeclaration) return node.parameters; | 830 if (node is ConstructorDeclaration) return node.parameters; |
824 if (node is MethodDeclaration) return node.parameters; | 831 if (node is MethodDeclaration) return node.parameters; |
825 if (node is FunctionDeclaration) node = node.functionExpression; | 832 if (node is FunctionDeclaration) node = node.functionExpression; |
(...skipping 1057 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1883 !_isJSBuiltinType(type) && | 1890 !_isJSBuiltinType(type) && |
1884 _isNonNullableExpression(target)) { | 1891 _isNonNullableExpression(target)) { |
1885 return false; | 1892 return false; |
1886 } | 1893 } |
1887 return true; | 1894 return true; |
1888 } | 1895 } |
1889 | 1896 |
1890 /// Shared code for [PrefixedIdentifier] and [PropertyAccess]. | 1897 /// Shared code for [PrefixedIdentifier] and [PropertyAccess]. |
1891 JS.Expression _emitGet(Expression target, SimpleIdentifier memberId) { | 1898 JS.Expression _emitGet(Expression target, SimpleIdentifier memberId) { |
1892 var member = memberId.staticElement; | 1899 var member = memberId.staticElement; |
1893 bool isStatic = member is ExecutableElement && member.isStatic; | 1900 if (member is PropertyAccessorElement) member = member.variable; |
| 1901 bool isStatic = member is ClassMemberElement && member.isStatic; |
| 1902 if (isStatic) { |
| 1903 _loader.declareBeforeUse(member); |
| 1904 } |
1894 var name = _emitMemberName(memberId.name, | 1905 var name = _emitMemberName(memberId.name, |
1895 type: getStaticType(target), isStatic: isStatic); | 1906 type: getStaticType(target), isStatic: isStatic); |
1896 if (rules.isDynamicTarget(target)) { | 1907 if (rules.isDynamicTarget(target)) { |
1897 return js.call('dart.$DLOAD(#, #)', [_visit(target), name]); | 1908 return js.call('dart.$DLOAD(#, #)', [_visit(target), name]); |
1898 } | 1909 } |
1899 | 1910 |
1900 String code; | 1911 String code; |
1901 if (member != null && member is MethodElement) { | 1912 if (member != null && member is MethodElement) { |
1902 // Tear-off methods: explicitly bind it. | 1913 // Tear-off methods: explicitly bind it. |
1903 if (_requiresStaticDispatch(target, memberId.name)) { | 1914 if (_requiresStaticDispatch(target, memberId.name)) { |
(...skipping 571 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2475 | 2486 |
2476 /// A special kind of element created by the compiler, signifying a temporary | 2487 /// A special kind of element created by the compiler, signifying a temporary |
2477 /// variable. These objects use instance equality, and should be shared | 2488 /// variable. These objects use instance equality, and should be shared |
2478 /// everywhere in the tree where they are treated as the same variable. | 2489 /// everywhere in the tree where they are treated as the same variable. |
2479 class TemporaryVariableElement extends LocalVariableElementImpl { | 2490 class TemporaryVariableElement extends LocalVariableElementImpl { |
2480 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); | 2491 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); |
2481 | 2492 |
2482 int get hashCode => identityHashCode(this); | 2493 int get hashCode => identityHashCode(this); |
2483 bool operator ==(Object other) => identical(this, other); | 2494 bool operator ==(Object other) => identical(this, other); |
2484 } | 2495 } |
OLD | NEW |