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 |