| 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 js_codegen; | 5 library js_codegen; |
| 6 | 6 |
| 7 import 'dart:collection' show HashSet, HashMap, SplayTreeSet; | 7 import 'dart:collection' show HashSet, HashMap, SplayTreeSet; |
| 8 | 8 |
| 9 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; | 9 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; |
| 10 import 'package:analyzer/dart/ast/token.dart'; | 10 import 'package:analyzer/dart/ast/token.dart'; |
| (...skipping 626 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 637 if (type.mixins.isNotEmpty) { | 637 if (type.mixins.isNotEmpty) { |
| 638 var mixins = type.mixins.map(_emitTypeName).toList(); | 638 var mixins = type.mixins.map(_emitTypeName).toList(); |
| 639 mixins.insert(0, heritage); | 639 mixins.insert(0, heritage); |
| 640 heritage = js.call('dart.mixin(#)', [mixins]); | 640 heritage = js.call('dart.mixin(#)', [mixins]); |
| 641 } | 641 } |
| 642 | 642 |
| 643 _loader.finishTopLevel(element); | 643 _loader.finishTopLevel(element); |
| 644 return heritage; | 644 return heritage; |
| 645 } | 645 } |
| 646 | 646 |
| 647 /// Provide Dart getters and setters that forward to the underlying native |
| 648 /// field. Note that the Dart names are always symbolized to avoid |
| 649 /// conflicts. They will be installed as extension methods on the underlying |
| 650 /// native type. |
| 651 List<JS.Method> _emitNativeFieldAccessors(FieldDeclaration node) { |
| 652 // TODO(vsm): Can this by meta-programmed? |
| 653 // E.g., dart.nativeField(symbol, jsName) |
| 654 // Alternatively, perhaps it could be meta-programmed directly in |
| 655 // dart.registerExtensions? |
| 656 var jsMethods = <JS.Method>[]; |
| 657 if (!node.isStatic) { |
| 658 for (var decl in node.fields.variables) { |
| 659 var field = decl.element; |
| 660 var name = decl.name.name; |
| 661 var annotation = findAnnotation(field, isJsName); |
| 662 if (annotation != null) { |
| 663 name = getConstantField(annotation, 'name', types.stringType) |
| 664 ?.toStringValue(); |
| 665 } |
| 666 // Generate getter |
| 667 var fn = new JS.Fun([], js.statement('{ return this.#; }', [name])); |
| 668 var method = |
| 669 new JS.Method(_elementMemberName(field.getter), fn, isGetter: true); |
| 670 jsMethods.add(method); |
| 671 |
| 672 // Generate setter |
| 673 if (!decl.isFinal) { |
| 674 var value = new JS.TemporaryId('value'); |
| 675 fn = new JS.Fun( |
| 676 [value], js.statement('{ this.# = #; }', [name, value])); |
| 677 method = new JS.Method(_elementMemberName(field.setter), fn, |
| 678 isSetter: true); |
| 679 jsMethods.add(method); |
| 680 } |
| 681 } |
| 682 } |
| 683 return jsMethods; |
| 684 } |
| 685 |
| 647 List<JS.Method> _emitClassMethods(ClassDeclaration node, | 686 List<JS.Method> _emitClassMethods(ClassDeclaration node, |
| 648 List<ConstructorDeclaration> ctors, List<FieldDeclaration> fields) { | 687 List<ConstructorDeclaration> ctors, List<FieldDeclaration> fields) { |
| 649 var element = node.element; | 688 var element = node.element; |
| 650 var type = element.type; | 689 var type = element.type; |
| 651 var isObject = type.isObject; | 690 var isObject = type.isObject; |
| 652 | 691 |
| 653 // Iff no constructor is specified for a class C, it implicitly has a | 692 // Iff no constructor is specified for a class C, it implicitly has a |
| 654 // default constructor `C() : super() {}`, unless C is class Object. | 693 // default constructor `C() : super() {}`, unless C is class Object. |
| 655 var jsMethods = <JS.Method>[]; | 694 var jsMethods = <JS.Method>[]; |
| 656 if (ctors.isEmpty && !isObject) { | 695 if (ctors.isEmpty && !isObject) { |
| 657 jsMethods.add(_emitImplicitConstructor(node, fields)); | 696 jsMethods.add(_emitImplicitConstructor(node, fields)); |
| 658 } | 697 } |
| 659 | 698 |
| 660 bool hasJsPeer = findAnnotation(element, isJsPeerInterface) != null; | 699 bool hasJsPeer = findAnnotation(element, isJsPeerInterface) != null; |
| 661 | 700 |
| 662 bool hasIterator = false; | 701 bool hasIterator = false; |
| 663 for (var m in node.members) { | 702 for (var m in node.members) { |
| 664 if (m is ConstructorDeclaration) { | 703 if (m is ConstructorDeclaration) { |
| 665 jsMethods.add(_emitConstructor(m, type, fields, isObject)); | 704 jsMethods.add(_emitConstructor(m, type, fields, isObject)); |
| 666 } else if (m is MethodDeclaration) { | 705 } else if (m is MethodDeclaration) { |
| 667 jsMethods.add(_emitMethodDeclaration(type, m)); | 706 jsMethods.add(_emitMethodDeclaration(type, m)); |
| 668 | 707 |
| 669 if (!hasJsPeer && m.isGetter && m.name.name == 'iterator') { | 708 if (!hasJsPeer && m.isGetter && m.name.name == 'iterator') { |
| 670 hasIterator = true; | 709 hasIterator = true; |
| 671 jsMethods.add(_emitIterable(type)); | 710 jsMethods.add(_emitIterable(type)); |
| 672 } | 711 } |
| 712 } else if (m is FieldDeclaration && _extensionTypes.contains(element)) { |
| 713 jsMethods.addAll(_emitNativeFieldAccessors(m)); |
| 673 } | 714 } |
| 674 } | 715 } |
| 675 | 716 |
| 676 // If the type doesn't have an `iterator`, but claims to implement Iterable, | 717 // If the type doesn't have an `iterator`, but claims to implement Iterable, |
| 677 // we inject the adaptor method here, as it's less code size to put the | 718 // we inject the adaptor method here, as it's less code size to put the |
| 678 // helper on a parent class. This pattern is common in the core libraries | 719 // helper on a parent class. This pattern is common in the core libraries |
| 679 // (e.g. IterableMixin<E> and IterableBase<E>). | 720 // (e.g. IterableMixin<E> and IterableBase<E>). |
| 680 // | 721 // |
| 681 // (We could do this same optimization for any interface with an `iterator` | 722 // (We could do this same optimization for any interface with an `iterator` |
| 682 // method, but that's more expensive to check for, so it doesn't seem worth | 723 // method, but that's more expensive to check for, so it doesn't seem worth |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 763 var name = classElem.name; | 804 var name = classElem.name; |
| 764 var body = <JS.Statement>[]; | 805 var body = <JS.Statement>[]; |
| 765 | 806 |
| 766 if (_extensionTypes.contains(classElem)) { | 807 if (_extensionTypes.contains(classElem)) { |
| 767 var dartxNames = <JS.Expression>[]; | 808 var dartxNames = <JS.Expression>[]; |
| 768 for (var m in methods) { | 809 for (var m in methods) { |
| 769 if (!m.isAbstract && !m.isStatic && m.element.isPublic) { | 810 if (!m.isAbstract && !m.isStatic && m.element.isPublic) { |
| 770 dartxNames.add(_elementMemberName(m.element, allowExtensions: false)); | 811 dartxNames.add(_elementMemberName(m.element, allowExtensions: false)); |
| 771 } | 812 } |
| 772 } | 813 } |
| 814 for (var f in fields) { |
| 815 if (!f.isStatic) { |
| 816 for (var d in f.fields.variables) { |
| 817 if (d.element.isPublic) { |
| 818 dartxNames.add( |
| 819 _elementMemberName(d.element.getter, allowExtensions: false)); |
| 820 } |
| 821 } |
| 822 } |
| 823 } |
| 773 if (dartxNames.isNotEmpty) { | 824 if (dartxNames.isNotEmpty) { |
| 774 body.add(js.statement('dart.defineExtensionNames(#)', | 825 body.add(js.statement('dart.defineExtensionNames(#)', |
| 775 [new JS.ArrayInitializer(dartxNames, multiline: true)])); | 826 [new JS.ArrayInitializer(dartxNames, multiline: true)])); |
| 776 } | 827 } |
| 777 } | 828 } |
| 778 | 829 |
| 779 body.add(_emitClassHeritageWorkaround(cls)); | 830 body.add(_emitClassHeritageWorkaround(cls)); |
| 780 | 831 |
| 781 // TODO(jmesserly): we should really just extend native Array. | 832 // TODO(jmesserly): we should really just extend native Array. |
| 782 if (jsPeerName != null && classElem.typeParameters.isNotEmpty) { | 833 if (jsPeerName != null && classElem.typeParameters.isNotEmpty) { |
| (...skipping 2203 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2986 return AstBuilder.methodInvoke( | 3037 return AstBuilder.methodInvoke( |
| 2987 newTarget, invoke.methodName, invoke.argumentList.arguments); | 3038 newTarget, invoke.methodName, invoke.argumentList.arguments); |
| 2988 } | 3039 } |
| 2989 } | 3040 } |
| 2990 | 3041 |
| 2991 bool _requiresStaticDispatch(Expression target, String memberName) { | 3042 bool _requiresStaticDispatch(Expression target, String memberName) { |
| 2992 var type = getStaticType(target); | 3043 var type = getStaticType(target); |
| 2993 if (!_isObjectProperty(memberName)) { | 3044 if (!_isObjectProperty(memberName)) { |
| 2994 return false; | 3045 return false; |
| 2995 } | 3046 } |
| 2996 if (!type.isObject && | 3047 |
| 2997 !_isJSBuiltinType(type) && | 3048 // If the target could be `null`, we need static dispatch. |
| 2998 !_extensionTypes.contains(type.element) && | 3049 // If the target may be an extension type, we also use static dispatch |
| 2999 !_isNullable(target)) { | 3050 // as we don't symbolize object properties like hashCode. |
| 3000 return false; | 3051 return _isNullable(target) || |
| 3001 } | 3052 (_extensionTypes.contains(type.element) && target is! SuperExpression); |
| 3002 return true; | |
| 3003 } | 3053 } |
| 3004 | 3054 |
| 3005 /// Shared code for [PrefixedIdentifier] and [PropertyAccess]. | 3055 /// Shared code for [PrefixedIdentifier] and [PropertyAccess]. |
| 3006 JS.Expression _emitGet(Expression target, SimpleIdentifier memberId) { | 3056 JS.Expression _emitGet(Expression target, SimpleIdentifier memberId) { |
| 3007 var member = memberId.staticElement; | 3057 var member = memberId.staticElement; |
| 3008 if (member is PropertyAccessorElement) { | 3058 if (member is PropertyAccessorElement) { |
| 3009 member = (member as PropertyAccessorElement).variable; | 3059 member = (member as PropertyAccessorElement).variable; |
| 3010 } | 3060 } |
| 3011 bool isStatic = member is ClassMemberElement && member.isStatic; | 3061 bool isStatic = member is ClassMemberElement && member.isStatic; |
| 3012 var name = _emitMemberName(memberId.name, | 3062 var name = _emitMemberName(memberId.name, |
| (...skipping 566 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3579 // conflict. We could use practically any character for this. | 3629 // conflict. We could use practically any character for this. |
| 3580 name = '+$name'; | 3630 name = '+$name'; |
| 3581 } | 3631 } |
| 3582 | 3632 |
| 3583 // Dart "extension" methods. Used for JS Array, Boolean, Number, String. | 3633 // Dart "extension" methods. Used for JS Array, Boolean, Number, String. |
| 3584 var baseType = type; | 3634 var baseType = type; |
| 3585 while (baseType is TypeParameterType) { | 3635 while (baseType is TypeParameterType) { |
| 3586 baseType = baseType.element.bound; | 3636 baseType = baseType.element.bound; |
| 3587 } | 3637 } |
| 3588 if (allowExtensions && | 3638 if (allowExtensions && |
| 3639 baseType != null && |
| 3589 _extensionTypes.contains(baseType.element) && | 3640 _extensionTypes.contains(baseType.element) && |
| 3590 !_isObjectProperty(name)) { | 3641 !_isObjectProperty(name)) { |
| 3591 return js.call('dartx.#', _propertyName(name)); | 3642 return js.call('dartx.#', _propertyName(name)); |
| 3592 } | 3643 } |
| 3593 | 3644 |
| 3594 return _propertyName(name); | 3645 return _propertyName(name); |
| 3595 } | 3646 } |
| 3596 | 3647 |
| 3597 bool _externalOrNative(node) => | 3648 bool _externalOrNative(node) => |
| 3598 node.externalKeyword != null || _functionBody(node) is NativeFunctionBody; | 3649 node.externalKeyword != null || _functionBody(node) is NativeFunctionBody; |
| (...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3725 | 3776 |
| 3726 JSGenerator(AbstractCompiler compiler) | 3777 JSGenerator(AbstractCompiler compiler) |
| 3727 : _types = compiler.context.typeProvider, | 3778 : _types = compiler.context.typeProvider, |
| 3728 super(compiler) { | 3779 super(compiler) { |
| 3729 // TODO(vsm): Eventually, we want to make this extensible - i.e., find | 3780 // TODO(vsm): Eventually, we want to make this extensible - i.e., find |
| 3730 // annotations in user code as well. It would need to be summarized in | 3781 // annotations in user code as well. It would need to be summarized in |
| 3731 // the element model - not searched this way on every compile. | 3782 // the element model - not searched this way on every compile. |
| 3732 var finder = new _ExtensionFinder(context, _extensionTypes, _types); | 3783 var finder = new _ExtensionFinder(context, _extensionTypes, _types); |
| 3733 finder._addExtensionTypes('dart:_interceptors'); | 3784 finder._addExtensionTypes('dart:_interceptors'); |
| 3734 finder._addExtensionTypes('dart:_native_typed_data'); | 3785 finder._addExtensionTypes('dart:_native_typed_data'); |
| 3786 finder._addExtensionTypes('dart:html'); |
| 3787 finder._addExtensionTypes('dart:indexed_db'); |
| 3788 finder._addExtensionTypes('dart:svg'); |
| 3789 finder._addExtensionTypes('dart:web_audio'); |
| 3790 finder._addExtensionTypes('dart:web_gl'); |
| 3791 finder._addExtensionTypes('dart:web_sql'); |
| 3735 | 3792 |
| 3736 // TODO(vsm): If we're analyzing against the main SDK, those | 3793 // TODO(vsm): If we're analyzing against the main SDK, those |
| 3737 // types are not explicitly annotated. | 3794 // types are not explicitly annotated. |
| 3738 finder._addExtensionType(_types.intType); | 3795 finder._addExtensionType(_types.intType); |
| 3739 finder._addExtensionType(_types.doubleType); | 3796 finder._addExtensionType(_types.doubleType); |
| 3740 finder._addExtensionType(_types.boolType); | 3797 finder._addExtensionType(_types.boolType); |
| 3741 finder._addExtensionType(_types.stringType); | 3798 finder._addExtensionType(_types.stringType); |
| 3742 } | 3799 } |
| 3743 | 3800 |
| 3744 String generateLibrary(LibraryUnit unit) { | 3801 String generateLibrary(LibraryUnit unit) { |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3779 | 3836 |
| 3780 /// A special kind of element created by the compiler, signifying a temporary | 3837 /// A special kind of element created by the compiler, signifying a temporary |
| 3781 /// variable. These objects use instance equality, and should be shared | 3838 /// variable. These objects use instance equality, and should be shared |
| 3782 /// everywhere in the tree where they are treated as the same variable. | 3839 /// everywhere in the tree where they are treated as the same variable. |
| 3783 class TemporaryVariableElement extends LocalVariableElementImpl { | 3840 class TemporaryVariableElement extends LocalVariableElementImpl { |
| 3784 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); | 3841 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); |
| 3785 | 3842 |
| 3786 int get hashCode => identityHashCode(this); | 3843 int get hashCode => identityHashCode(this); |
| 3787 bool operator ==(Object other) => identical(this, other); | 3844 bool operator ==(Object other) => identical(this, other); |
| 3788 } | 3845 } |
| OLD | NEW |