| 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, SplayTreeSet; |
| 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; |
| 11 import 'package:analyzer/src/generated/constant.dart'; | 11 import 'package:analyzer/src/generated/constant.dart'; |
| 12 import 'package:analyzer/src/generated/element.dart'; | 12 import 'package:analyzer/src/generated/element.dart'; |
| 13 import 'package:analyzer/src/generated/resolver.dart' show TypeProvider; | 13 import 'package:analyzer/src/generated/resolver.dart' show TypeProvider; |
| 14 import 'package:analyzer/src/generated/scanner.dart' | 14 import 'package:analyzer/src/generated/scanner.dart' |
| 15 show StringToken, Token, TokenType; | 15 show StringToken, Token, TokenType; |
| 16 import 'package:path/path.dart' as path; | 16 import 'package:path/path.dart' as path; |
| 17 | 17 |
| (...skipping 30 matching lines...) Expand all Loading... |
| 48 const DSETINDEX = 'dsetindex'; | 48 const DSETINDEX = 'dsetindex'; |
| 49 const DCALL = 'dcall'; | 49 const DCALL = 'dcall'; |
| 50 const DSEND = 'dsend'; | 50 const DSEND = 'dsend'; |
| 51 | 51 |
| 52 class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { | 52 class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
| 53 final AbstractCompiler compiler; | 53 final AbstractCompiler compiler; |
| 54 final CompilerOptions options; | 54 final CompilerOptions options; |
| 55 final TypeRules rules; | 55 final TypeRules rules; |
| 56 final LibraryInfo libraryInfo; | 56 final LibraryInfo libraryInfo; |
| 57 | 57 |
| 58 /// The global extension method table. | 58 /// The global extension type table. |
| 59 final HashMap<String, List<InterfaceType>> _extensionMethods; | 59 final HashSet<ClassElement> _extensionTypes; |
| 60 | 60 |
| 61 /// Information that is precomputed for this library, indicates which fields | 61 /// Information that is precomputed for this library, indicates which fields |
| 62 /// need storage slots. | 62 /// need storage slots. |
| 63 final HashSet<FieldElement> _fieldsNeedingStorage; | 63 final HashSet<FieldElement> _fieldsNeedingStorage; |
| 64 | 64 |
| 65 /// The variable for the target of the current `..` cascade expression. | 65 /// The variable for the target of the current `..` cascade expression. |
| 66 SimpleIdentifier _cascadeTarget; | 66 SimpleIdentifier _cascadeTarget; |
| 67 | 67 |
| 68 /// The variable for the current catch clause | 68 /// The variable for the current catch clause |
| 69 SimpleIdentifier _catchParameter; | 69 SimpleIdentifier _catchParameter; |
| 70 | 70 |
| 71 /// Imported libraries, and the temporaries used to refer to them. | 71 /// Imported libraries, and the temporaries used to refer to them. |
| 72 final _imports = new Map<LibraryElement, JS.TemporaryId>(); | 72 final _imports = new Map<LibraryElement, JS.TemporaryId>(); |
| 73 final _exports = new Set<String>(); | 73 final _exports = new Set<String>(); |
| 74 final _lazyFields = <VariableDeclaration>[]; | 74 final _lazyFields = <VariableDeclaration>[]; |
| 75 final _properties = <FunctionDeclaration>[]; | 75 final _properties = <FunctionDeclaration>[]; |
| 76 final _privateNames = new HashMap<String, JS.TemporaryId>(); | 76 final _privateNames = new HashMap<String, JS.TemporaryId>(); |
| 77 final _extensionMethodNames = new HashSet<String>(); | |
| 78 final _moduleItems = <JS.Statement>[]; | 77 final _moduleItems = <JS.Statement>[]; |
| 79 final _temps = new HashMap<Element, JS.TemporaryId>(); | 78 final _temps = new HashMap<Element, JS.TemporaryId>(); |
| 80 final _qualifiedIds = new HashMap<Element, JS.MaybeQualifiedId>(); | 79 final _qualifiedIds = new HashMap<Element, JS.MaybeQualifiedId>(); |
| 81 final _qualifiedGenericIds = new HashMap<Element, JS.MaybeQualifiedId>(); | 80 final _qualifiedGenericIds = new HashMap<Element, JS.MaybeQualifiedId>(); |
| 82 | 81 |
| 83 /// The name for the library's exports inside itself. | 82 /// The name for the library's exports inside itself. |
| 84 /// `exports` was chosen as the most similar to ES module patterns. | 83 /// `exports` was chosen as the most similar to ES module patterns. |
| 85 final _exportsVar = new JS.TemporaryId('exports'); | 84 final _exportsVar = new JS.TemporaryId('exports'); |
| 86 final _namedArgTemp = new JS.TemporaryId('opts'); | 85 final _namedArgTemp = new JS.TemporaryId('opts'); |
| 87 | 86 |
| 88 ConstFieldVisitor _constField; | 87 ConstFieldVisitor _constField; |
| 89 | 88 |
| 90 ModuleItemLoadOrder _loader; | 89 ModuleItemLoadOrder _loader; |
| 91 | 90 |
| 92 JSCodegenVisitor(AbstractCompiler compiler, this.libraryInfo, | 91 JSCodegenVisitor(AbstractCompiler compiler, this.libraryInfo, |
| 93 this._extensionMethods, this._fieldsNeedingStorage) | 92 this._extensionTypes, this._fieldsNeedingStorage) |
| 94 : compiler = compiler, | 93 : compiler = compiler, |
| 95 options = compiler.options, | 94 options = compiler.options, |
| 96 rules = compiler.rules { | 95 rules = compiler.rules { |
| 97 _loader = new ModuleItemLoadOrder(_emitModuleItem); | 96 _loader = new ModuleItemLoadOrder(_emitModuleItem); |
| 98 } | 97 } |
| 99 | 98 |
| 100 LibraryElement get currentLibrary => libraryInfo.library; | 99 LibraryElement get currentLibrary => libraryInfo.library; |
| 101 TypeProvider get types => rules.provider; | 100 TypeProvider get types => rules.provider; |
| 102 | 101 |
| 103 JS.Program emitLibrary(LibraryUnit library) { | 102 JS.Program emitLibrary(LibraryUnit library) { |
| (...skipping 448 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 552 ])); | 551 ])); |
| 553 } | 552 } |
| 554 | 553 |
| 555 // Interfaces | 554 // Interfaces |
| 556 if (classElem.interfaces.isNotEmpty) { | 555 if (classElem.interfaces.isNotEmpty) { |
| 557 body.add(js.statement('#[dart.implements] = () => #;', [ | 556 body.add(js.statement('#[dart.implements] = () => #;', [ |
| 558 name, | 557 name, |
| 559 new JS.ArrayInitializer( | 558 new JS.ArrayInitializer( |
| 560 classElem.interfaces.map(_emitTypeName).toList()) | 559 classElem.interfaces.map(_emitTypeName).toList()) |
| 561 ])); | 560 ])); |
| 561 |
| 562 // If a concrete class implements one of our extensions, we might need to |
| 563 // add forwarders. |
| 564 var extensions = _extensionsToImplement(classElem); |
| 565 if (extensions.isNotEmpty) { |
| 566 var methodNames = []; |
| 567 for (var e in extensions) { |
| 568 methodNames.add(_emitMemberDeclarationName(e)); |
| 569 } |
| 570 body.add(js.statement('dart.defineExtensionMembers(#, #);', [ |
| 571 name, |
| 572 new JS.ArrayInitializer(methodNames, |
| 573 multiline: methodNames.length > 4) |
| 574 ])); |
| 575 } |
| 562 } | 576 } |
| 563 | 577 |
| 564 // Named constructors | 578 // Named constructors |
| 565 for (ConstructorDeclaration member in ctors) { | 579 for (ConstructorDeclaration member in ctors) { |
| 566 if (member.name != null && member.factoryKeyword == null) { | 580 if (member.name != null && member.factoryKeyword == null) { |
| 567 body.add(js.statement('dart.defineNamedConstructor(#, #);', [ | 581 body.add(js.statement('dart.defineNamedConstructor(#, #);', [ |
| 568 name, | 582 name, |
| 569 _emitMemberName(member.name.name, isStatic: true) | 583 _emitMemberName(member.name.name, isStatic: true) |
| 570 ])); | 584 ])); |
| 571 } | 585 } |
| 572 } | 586 } |
| 573 | 587 |
| 574 // Instance fields, if they override getter/setter pairs | 588 // Instance fields, if they override getter/setter pairs |
| 575 for (FieldDeclaration member in fields) { | 589 for (FieldDeclaration member in fields) { |
| 576 for (VariableDeclaration fieldDecl in member.fields.variables) { | 590 for (VariableDeclaration fieldDecl in member.fields.variables) { |
| 577 var field = fieldDecl.element; | 591 var field = fieldDecl.element; |
| 578 if (_fieldsNeedingStorage.contains(field)) { | 592 if (_fieldsNeedingStorage.contains(field)) { |
| 579 body.add(_overrideField(field)); | 593 body.add(_overrideField(field)); |
| 580 } | 594 } |
| 581 } | 595 } |
| 582 } | 596 } |
| 583 | 597 |
| 584 // Emit the signature on the class recording the runtime type information | 598 // Emit the signature on the class recording the runtime type information |
| 585 { | 599 { |
| 586 var tStatics = []; | 600 var tStatics = []; |
| 587 var tMethods = []; | 601 var tMethods = []; |
| 588 var sNames = []; | 602 var sNames = []; |
| 589 var cType = classElem.type; | |
| 590 for (MethodDeclaration node in methods) { | 603 for (MethodDeclaration node in methods) { |
| 591 if (!(node.isSetter || node.isGetter || node.isAbstract)) { | 604 if (!(node.isSetter || node.isGetter || node.isAbstract)) { |
| 592 var name = node.name.name; | 605 var name = node.name.name; |
| 593 var element = node.element; | 606 var element = node.element; |
| 594 var inheritedElement = | 607 var inheritedElement = |
| 595 classElem.lookUpInheritedConcreteMethod(name, currentLibrary); | 608 classElem.lookUpInheritedConcreteMethod(name, currentLibrary); |
| 596 if (inheritedElement != null && | 609 if (inheritedElement != null && |
| 597 inheritedElement.type == element.type) continue; | 610 inheritedElement.type == element.type) continue; |
| 598 var unary = node.parameters.parameters.isEmpty; | 611 var memberName = _emitMemberDeclarationName(element); |
| 599 var memberName = _emitMemberName(name, | |
| 600 type: cType, unary: unary, isStatic: node.isStatic); | |
| 601 var parts = | 612 var parts = |
| 602 _emitFunctionTypeParts(element.type, dynamicIsBottom: false); | 613 _emitFunctionTypeParts(element.type, dynamicIsBottom: false); |
| 603 var property = | 614 var property = |
| 604 new JS.Property(memberName, new JS.ArrayInitializer(parts)); | 615 new JS.Property(memberName, new JS.ArrayInitializer(parts)); |
| 605 if (node.isStatic) { | 616 if (node.isStatic) { |
| 606 tStatics.add(property); | 617 tStatics.add(property); |
| 607 sNames.add(memberName); | 618 sNames.add(memberName); |
| 608 } else tMethods.add(property); | 619 } else { |
| 620 tMethods.add(property); |
| 621 } |
| 609 } | 622 } |
| 610 } | 623 } |
| 611 var tCtors = []; | 624 var tCtors = []; |
| 612 for (ConstructorDeclaration node in ctors) { | 625 for (ConstructorDeclaration node in ctors) { |
| 613 var memberName = _constructorName(node.element); | 626 var memberName = _constructorName(node.element); |
| 614 var element = node.element; | 627 var element = node.element; |
| 615 var parts = | 628 var parts = |
| 616 _emitFunctionTypeParts(element.type, dynamicIsBottom: false); | 629 _emitFunctionTypeParts(element.type, dynamicIsBottom: false); |
| 617 var property = | 630 var property = |
| 618 new JS.Property(memberName, new JS.ArrayInitializer(parts)); | 631 new JS.Property(memberName, new JS.ArrayInitializer(parts)); |
| 619 tCtors.add(property); | 632 tCtors.add(property); |
| 620 } | 633 } |
| 621 build(name, elements) { | 634 build(name, elements) { |
| 622 var o = | 635 var o = |
| 623 new JS.ObjectInitializer(elements, vertical: elements.length > 1); | 636 new JS.ObjectInitializer(elements, multiline: elements.length > 1); |
| 624 var e = js.call('() => #', o); | 637 var e = js.call('() => #', o); |
| 625 var p = new JS.Property(_propertyName(name), e); | 638 var p = new JS.Property(_propertyName(name), e); |
| 626 return p; | 639 return p; |
| 627 } | 640 } |
| 628 var sigFields = []; | 641 var sigFields = []; |
| 629 if (!tCtors.isEmpty) sigFields.add(build('constructors', tCtors)); | 642 if (!tCtors.isEmpty) sigFields.add(build('constructors', tCtors)); |
| 630 if (!tMethods.isEmpty) sigFields.add(build('methods', tMethods)); | 643 if (!tMethods.isEmpty) sigFields.add(build('methods', tMethods)); |
| 631 if (!tStatics.isEmpty) { | 644 if (!tStatics.isEmpty) { |
| 632 assert(!sNames.isEmpty); | 645 assert(!sNames.isEmpty); |
| 633 var aNames = new JS.Property( | 646 var aNames = new JS.Property( |
| 634 _propertyName('names'), new JS.ArrayInitializer(sNames)); | 647 _propertyName('names'), new JS.ArrayInitializer(sNames)); |
| 635 sigFields.add(build('statics', tStatics)); | 648 sigFields.add(build('statics', tStatics)); |
| 636 sigFields.add(aNames); | 649 sigFields.add(aNames); |
| 637 } | 650 } |
| 638 if (!sigFields.isEmpty) { | 651 if (!sigFields.isEmpty) { |
| 639 var sig = new JS.ObjectInitializer(sigFields); | 652 var sig = new JS.ObjectInitializer(sigFields); |
| 640 var classExpr = new JS.Identifier(name); | 653 var classExpr = new JS.Identifier(name); |
| 641 body.add(js.statement('dart.setSignature(#, #);', [classExpr, sig])); | 654 body.add(js.statement('dart.setSignature(#, #);', [classExpr, sig])); |
| 642 } | 655 } |
| 643 } | 656 } |
| 644 | 657 |
| 645 return _statement(body); | 658 return _statement(body); |
| 646 } | 659 } |
| 647 | 660 |
| 661 List<ExecutableElement> _extensionsToImplement(ClassElement element) { |
| 662 var members = <ExecutableElement>[]; |
| 663 if (_extensionTypes.contains(element)) return members; |
| 664 |
| 665 // Collect all extension types we implement. |
| 666 var type = element.type; |
| 667 var types = new Set<ClassElement>(); |
| 668 _collectExtensions(type, types); |
| 669 if (types.isEmpty) return members; |
| 670 |
| 671 // Collect all possible extension method names. |
| 672 var extensionMembers = new HashSet<String>(); |
| 673 for (var t in types) { |
| 674 for (var m in [t.methods, t.accessors].expand((e) => e)) { |
| 675 if (!m.isStatic) extensionMembers.add(m.name); |
| 676 } |
| 677 } |
| 678 |
| 679 // Collect all of extension methods this type implements. |
| 680 for (var m in [type.methods, type.accessors].expand((e) => e)) { |
| 681 if (!m.isStatic && !m.isAbstract && extensionMembers.contains(m.name)) { |
| 682 members.add(m); |
| 683 } |
| 684 } |
| 685 return members; |
| 686 } |
| 687 |
| 688 /// Collections the type and all supertypes, including interfaces, but |
| 689 /// excluding [Object]. |
| 690 void _collectExtensions(InterfaceType type, Set<ClassElement> types) { |
| 691 if (type.isObject) return; |
| 692 var element = type.element; |
| 693 if (_extensionTypes.contains(element)) types.add(element); |
| 694 for (var m in type.mixins.reversed) { |
| 695 _collectExtensions(m, types); |
| 696 } |
| 697 for (var i in type.interfaces) { |
| 698 _collectExtensions(i, types); |
| 699 } |
| 700 _collectExtensions(type.superclass, types); |
| 701 } |
| 702 |
| 648 JS.Statement _overrideField(FieldElement e) { | 703 JS.Statement _overrideField(FieldElement e) { |
| 649 var cls = e.enclosingElement; | 704 var cls = e.enclosingElement; |
| 650 return js.statement('dart.virtualField(#, #)', [ | 705 return js.statement('dart.virtualField(#, #)', [ |
| 651 cls.name, | 706 cls.name, |
| 652 _emitMemberName(e.name, type: cls.type) | 707 _emitMemberName(e.name, type: cls.type) |
| 653 ]); | 708 ]); |
| 654 } | 709 } |
| 655 | 710 |
| 656 /// Generates the implicit default constructor for class C of the form | 711 /// Generates the implicit default constructor for class C of the form |
| 657 /// `C() : super() {}`. | 712 /// `C() : super() {}`. |
| (...skipping 328 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 986 } | 1041 } |
| 987 | 1042 |
| 988 JS.Method _emitMethodDeclaration(DartType type, MethodDeclaration node) { | 1043 JS.Method _emitMethodDeclaration(DartType type, MethodDeclaration node) { |
| 989 if (node.isAbstract || _externalOrNative(node)) { | 1044 if (node.isAbstract || _externalOrNative(node)) { |
| 990 return null; | 1045 return null; |
| 991 } | 1046 } |
| 992 | 1047 |
| 993 var params = _visit(node.parameters); | 1048 var params = _visit(node.parameters); |
| 994 if (params == null) params = []; | 1049 if (params == null) params = []; |
| 995 | 1050 |
| 996 var memberName = _emitMemberName(node.name.name, | 1051 return new JS.Method(_emitMemberDeclarationName(node.element), |
| 997 type: type, unary: params.isEmpty, isStatic: node.isStatic); | 1052 new JS.Fun(params, _visit(node.body)), |
| 998 return new JS.Method(memberName, new JS.Fun(params, _visit(node.body)), | |
| 999 isGetter: node.isGetter, | 1053 isGetter: node.isGetter, |
| 1000 isSetter: node.isSetter, | 1054 isSetter: node.isSetter, |
| 1001 isStatic: node.isStatic); | 1055 isStatic: node.isStatic); |
| 1002 } | 1056 } |
| 1003 | 1057 |
| 1004 @override | 1058 @override |
| 1005 JS.Statement visitFunctionDeclaration(FunctionDeclaration node) { | 1059 JS.Statement visitFunctionDeclaration(FunctionDeclaration node) { |
| 1006 assert(node.parent is CompilationUnit); | 1060 assert(node.parent is CompilationUnit); |
| 1007 | 1061 |
| 1008 if (_externalOrNative(node)) return null; | 1062 if (_externalOrNative(node)) return null; |
| (...skipping 651 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1660 body.add(js.statement('dart.copyProperties(#, { # });', [ | 1714 body.add(js.statement('dart.copyProperties(#, { # });', [ |
| 1661 _exportsVar, | 1715 _exportsVar, |
| 1662 _properties.map(_emitTopLevelProperty) | 1716 _properties.map(_emitTopLevelProperty) |
| 1663 ])); | 1717 ])); |
| 1664 _properties.clear(); | 1718 _properties.clear(); |
| 1665 } | 1719 } |
| 1666 | 1720 |
| 1667 @override | 1721 @override |
| 1668 visitConstructorName(ConstructorName node) { | 1722 visitConstructorName(ConstructorName node) { |
| 1669 var typeName = _visit(node.type); | 1723 var typeName = _visit(node.type); |
| 1670 if (node.name != null || node.staticElement.isFactory) { | 1724 var element = node.staticElement; |
| 1671 var namedCtor = _constructorName(node.staticElement); | 1725 if (node.name != null || element.isFactory) { |
| 1726 var namedCtor = _constructorName(element); |
| 1672 return new JS.PropertyAccess(typeName, namedCtor); | 1727 return new JS.PropertyAccess(typeName, namedCtor); |
| 1673 } | 1728 } |
| 1674 return typeName; | 1729 return typeName; |
| 1675 } | 1730 } |
| 1676 | 1731 |
| 1677 @override | 1732 @override |
| 1678 visitInstanceCreationExpression(InstanceCreationExpression node) { | 1733 visitInstanceCreationExpression(InstanceCreationExpression node) { |
| 1679 emitNew() { | 1734 emitNew() { |
| 1680 var ctor = _visit(node.constructorName); | 1735 JS.Expression ctor; |
| 1736 bool isFactory = false; |
| 1737 var element = node.staticElement; |
| 1738 if (element == null) { |
| 1739 // TODO(jmesserly): this only happens if we had a static error. |
| 1740 // Should we generate a throw instead? |
| 1741 ctor = _visit(node.constructorName.type); |
| 1742 var ctorName = node.constructorName.name; |
| 1743 if (ctorName != null) { |
| 1744 ctor = new JS.PropertyAccess(ctor, _propertyName(ctorName.name)); |
| 1745 } |
| 1746 } else { |
| 1747 ctor = _visit(node.constructorName); |
| 1748 isFactory = element.isFactory; |
| 1749 } |
| 1681 var args = _visit(node.argumentList); | 1750 var args = _visit(node.argumentList); |
| 1682 var isFactory = node.staticElement.isFactory; | |
| 1683 return isFactory ? new JS.Call(ctor, args) : new JS.New(ctor, args); | 1751 return isFactory ? new JS.Call(ctor, args) : new JS.New(ctor, args); |
| 1684 } | 1752 } |
| 1685 if (node.isConst) return _emitConst(node, emitNew); | 1753 if (node.isConst) return _emitConst(node, emitNew); |
| 1686 return emitNew(); | 1754 return emitNew(); |
| 1687 } | 1755 } |
| 1688 | 1756 |
| 1689 /// True if this type is built-in to JS, and we use the values unwrapped. | 1757 /// True if this type is built-in to JS, and we use the values unwrapped. |
| 1690 /// For these types we generate a calling convention via static | 1758 /// For these types we generate a calling convention via static |
| 1691 /// "extension methods". This allows types to be extended without adding | 1759 /// "extension methods". This allows types to be extended without adding |
| 1692 /// extensions directly on the prototype. | 1760 /// extensions directly on the prototype. |
| (...skipping 642 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2335 return new JS.New(_emitTypeName(types.symbolType), [name]); | 2403 return new JS.New(_emitTypeName(types.symbolType), [name]); |
| 2336 } | 2404 } |
| 2337 return _emitConst(node, emitSymbol); | 2405 return _emitConst(node, emitSymbol); |
| 2338 } | 2406 } |
| 2339 | 2407 |
| 2340 @override | 2408 @override |
| 2341 visitListLiteral(ListLiteral node) { | 2409 visitListLiteral(ListLiteral node) { |
| 2342 emitList() { | 2410 emitList() { |
| 2343 JS.Expression list = new JS.ArrayInitializer(_visitList(node.elements)); | 2411 JS.Expression list = new JS.ArrayInitializer(_visitList(node.elements)); |
| 2344 ParameterizedType type = node.staticType; | 2412 ParameterizedType type = node.staticType; |
| 2345 if (type.typeArguments.any((a) => a != types.dynamicType)) { | 2413 var elementType = type.typeArguments.single; |
| 2346 list = js.call('dart.setType(#, #)', [list, _emitTypeName(type)]); | 2414 if (elementType != types.dynamicType) { |
| 2415 list = js.call('dart.list(#, #)', [list, _emitTypeName(elementType)]); |
| 2347 } | 2416 } |
| 2348 return list; | 2417 return list; |
| 2349 } | 2418 } |
| 2350 if (node.constKeyword != null) return _emitConst(node, emitList); | 2419 if (node.constKeyword != null) return _emitConst(node, emitList); |
| 2351 return emitList(); | 2420 return emitList(); |
| 2352 } | 2421 } |
| 2353 | 2422 |
| 2354 @override | 2423 @override |
| 2355 visitMapLiteral(MapLiteral node) { | 2424 visitMapLiteral(MapLiteral node) { |
| 2356 // TODO(jmesserly): we can likely make these faster. | 2425 // TODO(jmesserly): we can likely make these faster. |
| (...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2444 for (var node in nodes) result.add(_visit(node)); | 2513 for (var node in nodes) result.add(_visit(node)); |
| 2445 return result; | 2514 return result; |
| 2446 } | 2515 } |
| 2447 | 2516 |
| 2448 /// Visits a list of expressions, creating a comma expression if needed in JS. | 2517 /// Visits a list of expressions, creating a comma expression if needed in JS. |
| 2449 JS.Expression _visitListToBinary(List<Expression> nodes, String operator) { | 2518 JS.Expression _visitListToBinary(List<Expression> nodes, String operator) { |
| 2450 if (nodes == null || nodes.isEmpty) return null; | 2519 if (nodes == null || nodes.isEmpty) return null; |
| 2451 return new JS.Expression.binary(_visitList(nodes), operator); | 2520 return new JS.Expression.binary(_visitList(nodes), operator); |
| 2452 } | 2521 } |
| 2453 | 2522 |
| 2523 /// Like [_emitMemberName], but for declaration sites. |
| 2524 /// |
| 2525 /// Unlike call sites, we always have an element available, so we can use it |
| 2526 /// directly rather than computing the relevant options for [_emitMemberName]. |
| 2527 JS.Expression _emitMemberDeclarationName(ExecutableElement e) { |
| 2528 String name; |
| 2529 if (e is PropertyAccessorElement) { |
| 2530 name = e.variable.name; |
| 2531 } else { |
| 2532 name = e.name; |
| 2533 } |
| 2534 return _emitMemberName(name, |
| 2535 type: (e.enclosingElement as ClassElement).type, |
| 2536 unary: e.parameters.isEmpty, |
| 2537 isStatic: e.isStatic, |
| 2538 declaration: true); |
| 2539 } |
| 2540 |
| 2454 /// This handles member renaming for private names and operators. | 2541 /// This handles member renaming for private names and operators. |
| 2455 /// | 2542 /// |
| 2456 /// Private names are generated using ES6 symbols: | 2543 /// Private names are generated using ES6 symbols: |
| 2457 /// | 2544 /// |
| 2458 /// // At the top of the module: | 2545 /// // At the top of the module: |
| 2459 /// let _x = Symbol('_x'); | 2546 /// let _x = Symbol('_x'); |
| 2460 /// let _y = Symbol('_y'); | 2547 /// let _y = Symbol('_y'); |
| 2461 /// ... | 2548 /// ... |
| 2462 /// | 2549 /// |
| 2463 /// class Point { | 2550 /// class Point { |
| (...skipping 21 matching lines...) Expand all Loading... |
| 2485 /// | 2572 /// |
| 2486 /// This follows the same pattern as EcmaScript 6 Map: | 2573 /// This follows the same pattern as EcmaScript 6 Map: |
| 2487 /// <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_
Objects/Map> | 2574 /// <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_
Objects/Map> |
| 2488 /// | 2575 /// |
| 2489 /// Unary minus looks like: `x['unary-']()`. Note that [unary] must be passed | 2576 /// Unary minus looks like: `x['unary-']()`. Note that [unary] must be passed |
| 2490 /// for this transformation to happen, otherwise binary minus is assumed. | 2577 /// for this transformation to happen, otherwise binary minus is assumed. |
| 2491 /// | 2578 /// |
| 2492 /// Equality is a bit special, it is generated via the Dart `equals` runtime | 2579 /// Equality is a bit special, it is generated via the Dart `equals` runtime |
| 2493 /// helper, that checks for null. The user defined method is called '=='. | 2580 /// helper, that checks for null. The user defined method is called '=='. |
| 2494 /// | 2581 /// |
| 2495 JS.Expression _emitMemberName(String name, | 2582 JS.Expression _emitMemberName(String name, {DartType type, bool unary: false, |
| 2496 {DartType type, bool unary: false, bool isStatic: false}) { | 2583 bool isStatic: false, bool declaration: false}) { |
| 2497 | 2584 |
| 2498 // Static members skip the rename steps. | 2585 // Static members skip the rename steps. |
| 2499 if (isStatic) return _propertyName(name); | 2586 if (isStatic) return _propertyName(name); |
| 2500 | 2587 |
| 2501 if (name.startsWith('_')) { | 2588 if (name.startsWith('_')) { |
| 2502 return _privateNames.putIfAbsent( | 2589 return _privateNames.putIfAbsent( |
| 2503 name, () => _initSymbol(new JS.TemporaryId(name))); | 2590 name, () => _initSymbol(new JS.TemporaryId(name))); |
| 2504 } | 2591 } |
| 2505 | 2592 |
| 2506 // Check for extension method: | |
| 2507 var extLibrary = _findExtensionLibrary(name, type); | |
| 2508 | |
| 2509 if (name == '[]') { | 2593 if (name == '[]') { |
| 2510 name = 'get'; | 2594 name = 'get'; |
| 2511 } else if (name == '[]=') { | 2595 } else if (name == '[]=') { |
| 2512 name = 'set'; | 2596 name = 'set'; |
| 2513 } else if (name == '-' && unary) { | 2597 } else if (name == '-' && unary) { |
| 2514 name = 'unary-'; | 2598 name = 'unary-'; |
| 2515 } | 2599 } |
| 2516 | 2600 |
| 2517 if (extLibrary != null) { | 2601 // Dart "extension" methods. Used for JS Array, Boolean, Number, String. |
| 2518 return _extensionMethodName(name, extLibrary); | 2602 if (!declaration && _extensionTypes.contains(type.element)) { |
| 2603 // Special case `length`. We can call it directly. |
| 2604 if (name != 'length') return js.call('dartx.#', _propertyName(name)); |
| 2519 } | 2605 } |
| 2520 | 2606 |
| 2521 return _propertyName(name); | 2607 return _propertyName(name); |
| 2522 } | 2608 } |
| 2523 | 2609 |
| 2524 LibraryElement _findExtensionLibrary(String name, DartType type) { | |
| 2525 if (type is! InterfaceType) return null; | |
| 2526 | |
| 2527 var extLibrary = null; | |
| 2528 var extensionTypes = _extensionMethods[name]; | |
| 2529 if (extensionTypes != null) { | |
| 2530 // Normalize the type to ignore generics. | |
| 2531 type = fillDynamicTypeArgs(type, types); | |
| 2532 for (var t in extensionTypes) { | |
| 2533 if (rules.isSubTypeOf(type, t)) { | |
| 2534 assert(extLibrary == null || extLibrary == t.element.library); | |
| 2535 extLibrary = t.element.library; | |
| 2536 } | |
| 2537 } | |
| 2538 } | |
| 2539 return extLibrary; | |
| 2540 } | |
| 2541 | |
| 2542 JS.Expression _extensionMethodName(String name, LibraryElement library) { | |
| 2543 var extName = '\$$name'; | |
| 2544 if (library == currentLibrary) { | |
| 2545 // TODO(jacobr): need to do a better job ensuring that extension method | |
| 2546 // name symbols do not conflict with other symbols before we can let | |
| 2547 // user defined libraries define extension methods. | |
| 2548 if (_extensionMethodNames.add(extName)) { | |
| 2549 _initSymbol(new JS.Identifier(extName)); | |
| 2550 _addExport(extName); | |
| 2551 } | |
| 2552 return new JS.Identifier(extName); | |
| 2553 } | |
| 2554 return js.call('#.#', [_libraryName(library), _propertyName(extName)]); | |
| 2555 } | |
| 2556 | |
| 2557 bool _externalOrNative(node) => | 2610 bool _externalOrNative(node) => |
| 2558 node.externalKeyword != null || _functionBody(node) is NativeFunctionBody; | 2611 node.externalKeyword != null || _functionBody(node) is NativeFunctionBody; |
| 2559 | 2612 |
| 2560 FunctionBody _functionBody(node) => | 2613 FunctionBody _functionBody(node) => |
| 2561 node is FunctionDeclaration ? node.functionExpression.body : node.body; | 2614 node is FunctionDeclaration ? node.functionExpression.body : node.body; |
| 2562 | 2615 |
| 2563 /// Choose a canonical name from the library element. | 2616 /// Choose a canonical name from the library element. |
| 2564 /// This never uses the library's name (the identifier in the `library` | 2617 /// This never uses the library's name (the identifier in the `library` |
| 2565 /// declaration) as it doesn't have any meaningful rules enforced. | 2618 /// declaration) as it doesn't have any meaningful rules enforced. |
| 2566 JS.Identifier _libraryName(LibraryElement library) { | 2619 JS.Identifier _libraryName(LibraryElement library) { |
| 2567 if (library == currentLibrary) return _exportsVar; | 2620 if (library == currentLibrary) return _exportsVar; |
| 2568 return _imports.putIfAbsent( | 2621 return _imports.putIfAbsent( |
| 2569 library, () => new JS.TemporaryId(jsLibraryName(library))); | 2622 library, () => new JS.TemporaryId(jsLibraryName(library))); |
| 2570 } | 2623 } |
| 2571 | 2624 |
| 2572 DartType getStaticType(Expression e) => rules.getStaticType(e); | 2625 DartType getStaticType(Expression e) => rules.getStaticType(e); |
| 2573 } | 2626 } |
| 2574 | 2627 |
| 2575 class JSGenerator extends CodeGenerator { | 2628 class JSGenerator extends CodeGenerator { |
| 2576 /// For fast lookup of extension methods, we first check the name, then do a | 2629 final _extensionTypes = new HashSet<ClassElement>(); |
| 2577 /// (possibly expensive) subtype test to see if it matches one of the types | |
| 2578 /// that declares that method. | |
| 2579 final _extensionMethods = new HashMap<String, List<InterfaceType>>(); | |
| 2580 | 2630 |
| 2581 JSGenerator(AbstractCompiler context) : super(context) { | 2631 JSGenerator(AbstractCompiler compiler) : super(compiler) { |
| 2582 | |
| 2583 // TODO(jacobr): determine the the set of types with extension methods from | 2632 // TODO(jacobr): determine the the set of types with extension methods from |
| 2584 // the annotations rather than hard coding the list once the analyzer | 2633 // the annotations rather than hard coding the list once the analyzer |
| 2585 // supports summaries. | 2634 // supports summaries. |
| 2586 var extensionTypes = [types.listType, types.iterableType]; | 2635 var context = compiler.context; |
| 2587 for (var type in extensionTypes) { | 2636 var src = context.sourceFactory.forUri('dart:_interceptors'); |
| 2588 type = fillDynamicTypeArgs(type, rules.provider); | 2637 var interceptors = context.computeLibraryElement(src); |
| 2589 var e = type.element; | 2638 for (var t in ['JSArray', 'JSString', 'JSInt', 'JSDouble', 'JSBool']) { |
| 2590 var names = new HashSet<String>() | 2639 _addExtensionType(interceptors.getType(t).type); |
| 2591 ..addAll(e.methods.map((m) => m.name)) | |
| 2592 ..addAll(e.accessors.map((m) => m.name)); | |
| 2593 for (var name in names) { | |
| 2594 _extensionMethods.putIfAbsent(name, () => []).add(type); | |
| 2595 } | |
| 2596 } | 2640 } |
| 2597 } | 2641 } |
| 2598 | 2642 |
| 2599 TypeProvider get types => rules.provider; | 2643 void _addExtensionType(InterfaceType t) { |
| 2644 if (t.isObject || !_extensionTypes.add(t.element)) return; |
| 2645 t = fillDynamicTypeArgs(t, rules.provider); |
| 2646 t.interfaces.forEach(_addExtensionType); |
| 2647 t.mixins.forEach(_addExtensionType); |
| 2648 _addExtensionType(t.superclass); |
| 2649 } |
| 2600 | 2650 |
| 2601 String generateLibrary(LibraryUnit unit, LibraryInfo info) { | 2651 String generateLibrary(LibraryUnit unit, LibraryInfo info) { |
| 2602 var fields = findFieldsNeedingStorage(unit); | 2652 var fields = findFieldsNeedingStorage(unit); |
| 2603 var codegen = | 2653 var codegen = new JSCodegenVisitor(compiler, info, _extensionTypes, fields); |
| 2604 new JSCodegenVisitor(compiler, info, _extensionMethods, fields); | |
| 2605 var module = codegen.emitLibrary(unit); | 2654 var module = codegen.emitLibrary(unit); |
| 2606 var dir = path.join(outDir, jsOutputPath(info, root)); | 2655 var dir = path.join(outDir, jsOutputPath(info, root)); |
| 2607 return writeJsLibrary(module, dir, emitSourceMaps: options.emitSourceMaps); | 2656 return writeJsLibrary(module, dir, emitSourceMaps: options.emitSourceMaps); |
| 2608 } | 2657 } |
| 2609 } | 2658 } |
| 2610 | 2659 |
| 2611 /// Choose a canonical name from the library element. | 2660 /// Choose a canonical name from the library element. |
| 2612 /// This never uses the library's name (the identifier in the `library` | 2661 /// This never uses the library's name (the identifier in the `library` |
| 2613 /// declaration) as it doesn't have any meaningful rules enforced. | 2662 /// declaration) as it doesn't have any meaningful rules enforced. |
| 2614 String jsLibraryName(LibraryElement library) => canonicalLibraryName(library); | 2663 String jsLibraryName(LibraryElement library) => canonicalLibraryName(library); |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2650 | 2699 |
| 2651 /// A special kind of element created by the compiler, signifying a temporary | 2700 /// A special kind of element created by the compiler, signifying a temporary |
| 2652 /// variable. These objects use instance equality, and should be shared | 2701 /// variable. These objects use instance equality, and should be shared |
| 2653 /// everywhere in the tree where they are treated as the same variable. | 2702 /// everywhere in the tree where they are treated as the same variable. |
| 2654 class TemporaryVariableElement extends LocalVariableElementImpl { | 2703 class TemporaryVariableElement extends LocalVariableElementImpl { |
| 2655 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); | 2704 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); |
| 2656 | 2705 |
| 2657 int get hashCode => identityHashCode(this); | 2706 int get hashCode => identityHashCode(this); |
| 2658 bool operator ==(Object other) => identical(this, other); | 2707 bool operator ==(Object other) => identical(this, other); |
| 2659 } | 2708 } |
| OLD | NEW |