| 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 37 matching lines...) Expand 10 before | Expand all | Expand 10 after 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; |
| (...skipping 13 matching lines...) Expand all Loading... |
| 83 /// The name for the library's exports inside itself. | 83 /// The name for the library's exports inside itself. |
| 84 /// `exports` was chosen as the most similar to ES module patterns. | 84 /// `exports` was chosen as the most similar to ES module patterns. |
| 85 final _exportsVar = new JS.TemporaryId('exports'); | 85 final _exportsVar = new JS.TemporaryId('exports'); |
| 86 final _namedArgTemp = new JS.TemporaryId('opts'); | 86 final _namedArgTemp = new JS.TemporaryId('opts'); |
| 87 | 87 |
| 88 ConstFieldVisitor _constField; | 88 ConstFieldVisitor _constField; |
| 89 | 89 |
| 90 ModuleItemLoadOrder _loader; | 90 ModuleItemLoadOrder _loader; |
| 91 | 91 |
| 92 JSCodegenVisitor(AbstractCompiler compiler, this.libraryInfo, | 92 JSCodegenVisitor(AbstractCompiler compiler, this.libraryInfo, |
| 93 this._extensionMethods, this._fieldsNeedingStorage) | 93 this._extensionTypes, this._fieldsNeedingStorage) |
| 94 : compiler = compiler, | 94 : compiler = compiler, |
| 95 options = compiler.options, | 95 options = compiler.options, |
| 96 rules = compiler.rules { | 96 rules = compiler.rules { |
| 97 _loader = new ModuleItemLoadOrder(_emitModuleItem); | 97 _loader = new ModuleItemLoadOrder(_emitModuleItem); |
| 98 } | 98 } |
| 99 | 99 |
| 100 LibraryElement get currentLibrary => libraryInfo.library; | 100 LibraryElement get currentLibrary => libraryInfo.library; |
| 101 TypeProvider get types => rules.provider; | 101 TypeProvider get types => rules.provider; |
| 102 | 102 |
| 103 JS.Program emitLibrary(LibraryUnit library) { | 103 JS.Program emitLibrary(LibraryUnit library) { |
| (...skipping 448 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 552 ])); | 552 ])); |
| 553 } | 553 } |
| 554 | 554 |
| 555 // Interfaces | 555 // Interfaces |
| 556 if (classElem.interfaces.isNotEmpty) { | 556 if (classElem.interfaces.isNotEmpty) { |
| 557 body.add(js.statement('#[dart.implements] = () => #;', [ | 557 body.add(js.statement('#[dart.implements] = () => #;', [ |
| 558 name, | 558 name, |
| 559 new JS.ArrayInitializer( | 559 new JS.ArrayInitializer( |
| 560 classElem.interfaces.map(_emitTypeName).toList()) | 560 classElem.interfaces.map(_emitTypeName).toList()) |
| 561 ])); | 561 ])); |
| 562 |
| 563 // If a concrete class implements one of our extensions, we might need to |
| 564 // add forwarders. |
| 565 var extensions = _extensionsToImplement(classElem); |
| 566 if (extensions.isNotEmpty) { |
| 567 // Get the dynamic substitution of the extension interface, as we only |
| 568 // care about what methods it has, not their implementation. |
| 569 var jsTypes = extensions |
| 570 .map((e) => _emitTypeName(fillDynamicTypeArgs(e.type, types))); |
| 571 body.add(js.statement('dart.implementExtension(#, () => #);', [ |
| 572 name, |
| 573 new JS.ArrayInitializer(jsTypes.toList(growable: false)) |
| 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 } |
| (...skipping 18 matching lines...) Expand all Loading... |
| 590 for (MethodDeclaration node in methods) { | 604 for (MethodDeclaration node in methods) { |
| 591 if (!(node.isSetter || node.isGetter || node.isAbstract)) { | 605 if (!(node.isSetter || node.isGetter || node.isAbstract)) { |
| 592 var name = node.name.name; | 606 var name = node.name.name; |
| 593 var element = node.element; | 607 var element = node.element; |
| 594 var inheritedElement = | 608 var inheritedElement = |
| 595 classElem.lookUpInheritedConcreteMethod(name, currentLibrary); | 609 classElem.lookUpInheritedConcreteMethod(name, currentLibrary); |
| 596 if (inheritedElement != null && | 610 if (inheritedElement != null && |
| 597 inheritedElement.type == element.type) continue; | 611 inheritedElement.type == element.type) continue; |
| 598 var unary = node.parameters.parameters.isEmpty; | 612 var unary = node.parameters.parameters.isEmpty; |
| 599 var memberName = _emitMemberName(name, | 613 var memberName = _emitMemberName(name, |
| 600 type: cType, unary: unary, isStatic: node.isStatic); | 614 type: cType, |
| 615 unary: unary, |
| 616 isStatic: node.isStatic, |
| 617 declaration: true); |
| 601 var parts = | 618 var parts = |
| 602 _emitFunctionTypeParts(element.type, dynamicIsBottom: false); | 619 _emitFunctionTypeParts(element.type, dynamicIsBottom: false); |
| 603 var property = | 620 var property = |
| 604 new JS.Property(memberName, new JS.ArrayInitializer(parts)); | 621 new JS.Property(memberName, new JS.ArrayInitializer(parts)); |
| 605 if (node.isStatic) { | 622 if (node.isStatic) { |
| 606 tStatics.add(property); | 623 tStatics.add(property); |
| 607 sNames.add(memberName); | 624 sNames.add(memberName); |
| 608 } else tMethods.add(property); | 625 } else tMethods.add(property); |
| 609 } | 626 } |
| 610 } | 627 } |
| (...skipping 27 matching lines...) Expand all Loading... |
| 638 if (!sigFields.isEmpty) { | 655 if (!sigFields.isEmpty) { |
| 639 var sig = new JS.ObjectInitializer(sigFields); | 656 var sig = new JS.ObjectInitializer(sigFields); |
| 640 var classExpr = new JS.Identifier(name); | 657 var classExpr = new JS.Identifier(name); |
| 641 body.add(js.statement('dart.setSignature(#, #);', [classExpr, sig])); | 658 body.add(js.statement('dart.setSignature(#, #);', [classExpr, sig])); |
| 642 } | 659 } |
| 643 } | 660 } |
| 644 | 661 |
| 645 return _statement(body); | 662 return _statement(body); |
| 646 } | 663 } |
| 647 | 664 |
| 665 Set<ClassElement> _extensionsToImplement(ClassElement element) { |
| 666 var extensions = new Set<ClassElement>(); |
| 667 if (_extensionTypes.contains(element)) return extensions; |
| 668 |
| 669 // Collect all extension types we implement. |
| 670 _collectExtensions(element.type, extensions); |
| 671 if (extensions.isEmpty) return extensions; |
| 672 |
| 673 // Filter out extensions implemented by our superclass or mixins. |
| 674 var s = element.supertype.element; |
| 675 if (!s.isAbstract) extensions.removeAll(_extensionsToImplement(s)); |
| 676 for (var mixin in element.mixins) { |
| 677 var m = mixin.element; |
| 678 if (!m.isAbstract) extensions.removeAll(_extensionsToImplement(m)); |
| 679 } |
| 680 return extensions; |
| 681 } |
| 682 |
| 683 /// Collections the type and all supertypes, including interfaces, but |
| 684 /// excluding [Object]. |
| 685 void _collectExtensions(InterfaceType type, Set<ClassElement> types) { |
| 686 if (type.isObject) return; |
| 687 var element = type.element; |
| 688 if (_extensionTypes.contains(element)) types.add(element); |
| 689 for (var m in type.mixins.reversed) { |
| 690 _collectExtensions(m, types); |
| 691 } |
| 692 for (var i in type.interfaces) { |
| 693 _collectExtensions(i, types); |
| 694 } |
| 695 _collectExtensions(type.superclass, types); |
| 696 } |
| 697 |
| 648 JS.Statement _overrideField(FieldElement e) { | 698 JS.Statement _overrideField(FieldElement e) { |
| 649 var cls = e.enclosingElement; | 699 var cls = e.enclosingElement; |
| 650 return js.statement('dart.virtualField(#, #)', [ | 700 return js.statement('dart.virtualField(#, #)', [ |
| 651 cls.name, | 701 cls.name, |
| 652 _emitMemberName(e.name, type: cls.type) | 702 _emitMemberName(e.name, type: cls.type) |
| 653 ]); | 703 ]); |
| 654 } | 704 } |
| 655 | 705 |
| 656 /// Generates the implicit default constructor for class C of the form | 706 /// Generates the implicit default constructor for class C of the form |
| 657 /// `C() : super() {}`. | 707 /// `C() : super() {}`. |
| (...skipping 329 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 987 | 1037 |
| 988 JS.Method _emitMethodDeclaration(DartType type, MethodDeclaration node) { | 1038 JS.Method _emitMethodDeclaration(DartType type, MethodDeclaration node) { |
| 989 if (node.isAbstract || _externalOrNative(node)) { | 1039 if (node.isAbstract || _externalOrNative(node)) { |
| 990 return null; | 1040 return null; |
| 991 } | 1041 } |
| 992 | 1042 |
| 993 var params = _visit(node.parameters); | 1043 var params = _visit(node.parameters); |
| 994 if (params == null) params = []; | 1044 if (params == null) params = []; |
| 995 | 1045 |
| 996 var memberName = _emitMemberName(node.name.name, | 1046 var memberName = _emitMemberName(node.name.name, |
| 997 type: type, unary: params.isEmpty, isStatic: node.isStatic); | 1047 type: type, |
| 1048 unary: params.isEmpty, |
| 1049 isStatic: node.isStatic, |
| 1050 declaration: true); |
| 998 return new JS.Method(memberName, new JS.Fun(params, _visit(node.body)), | 1051 return new JS.Method(memberName, new JS.Fun(params, _visit(node.body)), |
| 999 isGetter: node.isGetter, | 1052 isGetter: node.isGetter, |
| 1000 isSetter: node.isSetter, | 1053 isSetter: node.isSetter, |
| 1001 isStatic: node.isStatic); | 1054 isStatic: node.isStatic); |
| 1002 } | 1055 } |
| 1003 | 1056 |
| 1004 @override | 1057 @override |
| 1005 JS.Statement visitFunctionDeclaration(FunctionDeclaration node) { | 1058 JS.Statement visitFunctionDeclaration(FunctionDeclaration node) { |
| 1006 assert(node.parent is CompilationUnit); | 1059 assert(node.parent is CompilationUnit); |
| 1007 | 1060 |
| (...skipping 651 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1659 body.add(js.statement('dart.copyProperties(#, { # });', [ | 1712 body.add(js.statement('dart.copyProperties(#, { # });', [ |
| 1660 _exportsVar, | 1713 _exportsVar, |
| 1661 _properties.map(_emitTopLevelProperty) | 1714 _properties.map(_emitTopLevelProperty) |
| 1662 ])); | 1715 ])); |
| 1663 _properties.clear(); | 1716 _properties.clear(); |
| 1664 } | 1717 } |
| 1665 | 1718 |
| 1666 @override | 1719 @override |
| 1667 visitConstructorName(ConstructorName node) { | 1720 visitConstructorName(ConstructorName node) { |
| 1668 var typeName = _visit(node.type); | 1721 var typeName = _visit(node.type); |
| 1669 if (node.name != null || node.staticElement.isFactory) { | 1722 var element = node.staticElement; |
| 1670 var namedCtor = _constructorName(node.staticElement); | 1723 if (node.name != null || element.isFactory) { |
| 1724 var namedCtor = _constructorName(element); |
| 1671 return new JS.PropertyAccess(typeName, namedCtor); | 1725 return new JS.PropertyAccess(typeName, namedCtor); |
| 1672 } | 1726 } |
| 1673 return typeName; | 1727 return typeName; |
| 1674 } | 1728 } |
| 1675 | 1729 |
| 1676 @override | 1730 @override |
| 1677 visitInstanceCreationExpression(InstanceCreationExpression node) { | 1731 visitInstanceCreationExpression(InstanceCreationExpression node) { |
| 1678 emitNew() { | 1732 emitNew() { |
| 1679 var ctor = _visit(node.constructorName); | 1733 JS.Expression ctor; |
| 1734 bool isFactory = false; |
| 1735 var element = node.staticElement; |
| 1736 if (element == null) { |
| 1737 // TODO(jmesserly): this only happens if we had a static error. |
| 1738 // Should we generate a throw instead? |
| 1739 ctor = _visit(node.constructorName.type); |
| 1740 var ctorName = node.constructorName.name; |
| 1741 if (ctorName != null) { |
| 1742 ctor = new JS.PropertyAccess(ctor, _propertyName(ctorName.name)); |
| 1743 } |
| 1744 } else { |
| 1745 ctor = _visit(node.constructorName); |
| 1746 isFactory = element.isFactory; |
| 1747 } |
| 1680 var args = _visit(node.argumentList); | 1748 var args = _visit(node.argumentList); |
| 1681 var isFactory = node.staticElement.isFactory; | |
| 1682 return isFactory ? new JS.Call(ctor, args) : new JS.New(ctor, args); | 1749 return isFactory ? new JS.Call(ctor, args) : new JS.New(ctor, args); |
| 1683 } | 1750 } |
| 1684 if (node.isConst) return _emitConst(node, emitNew); | 1751 if (node.isConst) return _emitConst(node, emitNew); |
| 1685 return emitNew(); | 1752 return emitNew(); |
| 1686 } | 1753 } |
| 1687 | 1754 |
| 1688 /// True if this type is built-in to JS, and we use the values unwrapped. | 1755 /// True if this type is built-in to JS, and we use the values unwrapped. |
| 1689 /// For these types we generate a calling convention via static | 1756 /// For these types we generate a calling convention via static |
| 1690 /// "extension methods". This allows types to be extended without adding | 1757 /// "extension methods". This allows types to be extended without adding |
| 1691 /// extensions directly on the prototype. | 1758 /// extensions directly on the prototype. |
| (...skipping 639 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2331 return new JS.New(_emitTypeName(types.symbolType), [name]); | 2398 return new JS.New(_emitTypeName(types.symbolType), [name]); |
| 2332 } | 2399 } |
| 2333 return _emitConst(node, emitSymbol); | 2400 return _emitConst(node, emitSymbol); |
| 2334 } | 2401 } |
| 2335 | 2402 |
| 2336 @override | 2403 @override |
| 2337 visitListLiteral(ListLiteral node) { | 2404 visitListLiteral(ListLiteral node) { |
| 2338 emitList() { | 2405 emitList() { |
| 2339 JS.Expression list = new JS.ArrayInitializer(_visitList(node.elements)); | 2406 JS.Expression list = new JS.ArrayInitializer(_visitList(node.elements)); |
| 2340 ParameterizedType type = node.staticType; | 2407 ParameterizedType type = node.staticType; |
| 2341 if (type.typeArguments.any((a) => a != types.dynamicType)) { | 2408 var elementType = type.typeArguments.single; |
| 2342 list = js.call('dart.setType(#, #)', [list, _emitTypeName(type)]); | 2409 if (elementType != types.dynamicType) { |
| 2410 list = js.call('dart.list(#, #)', [list, _emitTypeName(elementType)]); |
| 2343 } | 2411 } |
| 2344 return list; | 2412 return list; |
| 2345 } | 2413 } |
| 2346 if (node.constKeyword != null) return _emitConst(node, emitList); | 2414 if (node.constKeyword != null) return _emitConst(node, emitList); |
| 2347 return emitList(); | 2415 return emitList(); |
| 2348 } | 2416 } |
| 2349 | 2417 |
| 2350 @override | 2418 @override |
| 2351 visitMapLiteral(MapLiteral node) { | 2419 visitMapLiteral(MapLiteral node) { |
| 2352 // TODO(jmesserly): we can likely make these faster. | 2420 // TODO(jmesserly): we can likely make these faster. |
| (...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2481 /// | 2549 /// |
| 2482 /// This follows the same pattern as EcmaScript 6 Map: | 2550 /// This follows the same pattern as EcmaScript 6 Map: |
| 2483 /// <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_
Objects/Map> | 2551 /// <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_
Objects/Map> |
| 2484 /// | 2552 /// |
| 2485 /// Unary minus looks like: `x['unary-']()`. Note that [unary] must be passed | 2553 /// Unary minus looks like: `x['unary-']()`. Note that [unary] must be passed |
| 2486 /// for this transformation to happen, otherwise binary minus is assumed. | 2554 /// for this transformation to happen, otherwise binary minus is assumed. |
| 2487 /// | 2555 /// |
| 2488 /// Equality is a bit special, it is generated via the Dart `equals` runtime | 2556 /// Equality is a bit special, it is generated via the Dart `equals` runtime |
| 2489 /// helper, that checks for null. The user defined method is called '=='. | 2557 /// helper, that checks for null. The user defined method is called '=='. |
| 2490 /// | 2558 /// |
| 2491 JS.Expression _emitMemberName(String name, | 2559 JS.Expression _emitMemberName(String name, {DartType type, bool unary: false, |
| 2492 {DartType type, bool unary: false, bool isStatic: false}) { | 2560 bool isStatic: false, bool declaration: false}) { |
| 2493 | 2561 |
| 2494 // Static members skip the rename steps. | 2562 // Static members skip the rename steps. |
| 2495 if (isStatic) return _propertyName(name); | 2563 if (isStatic) return _propertyName(name); |
| 2496 | 2564 |
| 2497 if (name.startsWith('_')) { | 2565 if (name.startsWith('_')) { |
| 2498 return _privateNames.putIfAbsent( | 2566 return _privateNames.putIfAbsent( |
| 2499 name, () => _initSymbol(new JS.TemporaryId(name))); | 2567 name, () => _initSymbol(new JS.TemporaryId(name))); |
| 2500 } | 2568 } |
| 2501 | 2569 |
| 2502 // Check for extension method: | |
| 2503 var extLibrary = _findExtensionLibrary(name, type); | |
| 2504 | |
| 2505 if (name == '[]') { | 2570 if (name == '[]') { |
| 2506 name = 'get'; | 2571 name = 'get'; |
| 2507 } else if (name == '[]=') { | 2572 } else if (name == '[]=') { |
| 2508 name = 'set'; | 2573 name = 'set'; |
| 2509 } else if (name == '-' && unary) { | 2574 } else if (name == '-' && unary) { |
| 2510 name = 'unary-'; | 2575 name = 'unary-'; |
| 2511 } | 2576 } |
| 2512 | 2577 |
| 2513 if (extLibrary != null) { | 2578 // Dart "extension" methods. Used for JS Array, Boolean, Number, String. |
| 2514 return _extensionMethodName(name, extLibrary); | 2579 if (!declaration && _extensionTypes.contains(type.element)) { |
| 2580 // Special case `length`. We can call it directly. |
| 2581 if (name != 'length') return js.call('dartx.#', _propertyName(name)); |
| 2515 } | 2582 } |
| 2516 | 2583 |
| 2517 return _propertyName(name); | 2584 return _propertyName(name); |
| 2518 } | 2585 } |
| 2519 | 2586 |
| 2520 LibraryElement _findExtensionLibrary(String name, DartType type) { | |
| 2521 if (type is! InterfaceType) return null; | |
| 2522 | |
| 2523 var extLibrary = null; | |
| 2524 var extensionTypes = _extensionMethods[name]; | |
| 2525 if (extensionTypes != null) { | |
| 2526 // Normalize the type to ignore generics. | |
| 2527 type = fillDynamicTypeArgs(type, types); | |
| 2528 for (var t in extensionTypes) { | |
| 2529 if (rules.isSubTypeOf(type, t)) { | |
| 2530 assert(extLibrary == null || extLibrary == t.element.library); | |
| 2531 extLibrary = t.element.library; | |
| 2532 } | |
| 2533 } | |
| 2534 } | |
| 2535 return extLibrary; | |
| 2536 } | |
| 2537 | |
| 2538 JS.Expression _extensionMethodName(String name, LibraryElement library) { | |
| 2539 var extName = '\$$name'; | |
| 2540 if (library == currentLibrary) { | |
| 2541 // TODO(jacobr): need to do a better job ensuring that extension method | |
| 2542 // name symbols do not conflict with other symbols before we can let | |
| 2543 // user defined libraries define extension methods. | |
| 2544 if (_extensionMethodNames.add(extName)) { | |
| 2545 _initSymbol(new JS.Identifier(extName)); | |
| 2546 _addExport(extName); | |
| 2547 } | |
| 2548 return new JS.Identifier(extName); | |
| 2549 } | |
| 2550 return js.call('#.#', [_libraryName(library), _propertyName(extName)]); | |
| 2551 } | |
| 2552 | |
| 2553 bool _externalOrNative(node) => | 2587 bool _externalOrNative(node) => |
| 2554 node.externalKeyword != null || _functionBody(node) is NativeFunctionBody; | 2588 node.externalKeyword != null || _functionBody(node) is NativeFunctionBody; |
| 2555 | 2589 |
| 2556 FunctionBody _functionBody(node) => | 2590 FunctionBody _functionBody(node) => |
| 2557 node is FunctionDeclaration ? node.functionExpression.body : node.body; | 2591 node is FunctionDeclaration ? node.functionExpression.body : node.body; |
| 2558 | 2592 |
| 2559 /// Choose a canonical name from the library element. | 2593 /// Choose a canonical name from the library element. |
| 2560 /// This never uses the library's name (the identifier in the `library` | 2594 /// This never uses the library's name (the identifier in the `library` |
| 2561 /// declaration) as it doesn't have any meaningful rules enforced. | 2595 /// declaration) as it doesn't have any meaningful rules enforced. |
| 2562 JS.Identifier _libraryName(LibraryElement library) { | 2596 JS.Identifier _libraryName(LibraryElement library) { |
| 2563 if (library == currentLibrary) return _exportsVar; | 2597 if (library == currentLibrary) return _exportsVar; |
| 2564 return _imports.putIfAbsent( | 2598 return _imports.putIfAbsent( |
| 2565 library, () => new JS.TemporaryId(jsLibraryName(library))); | 2599 library, () => new JS.TemporaryId(jsLibraryName(library))); |
| 2566 } | 2600 } |
| 2567 | 2601 |
| 2568 DartType getStaticType(Expression e) => rules.getStaticType(e); | 2602 DartType getStaticType(Expression e) => rules.getStaticType(e); |
| 2569 } | 2603 } |
| 2570 | 2604 |
| 2571 class JSGenerator extends CodeGenerator { | 2605 class JSGenerator extends CodeGenerator { |
| 2572 /// For fast lookup of extension methods, we first check the name, then do a | 2606 final _extensionTypes = new HashSet<ClassElement>(); |
| 2573 /// (possibly expensive) subtype test to see if it matches one of the types | |
| 2574 /// that declares that method. | |
| 2575 final _extensionMethods = new HashMap<String, List<InterfaceType>>(); | |
| 2576 | 2607 |
| 2577 JSGenerator(AbstractCompiler context) : super(context) { | 2608 JSGenerator(AbstractCompiler compiler) : super(compiler) { |
| 2578 | |
| 2579 // TODO(jacobr): determine the the set of types with extension methods from | 2609 // TODO(jacobr): determine the the set of types with extension methods from |
| 2580 // the annotations rather than hard coding the list once the analyzer | 2610 // the annotations rather than hard coding the list once the analyzer |
| 2581 // supports summaries. | 2611 // supports summaries. |
| 2582 var extensionTypes = [types.listType, types.iterableType]; | 2612 var context = compiler.context; |
| 2583 for (var type in extensionTypes) { | 2613 var src = context.sourceFactory.forUri('dart:_interceptors'); |
| 2584 type = fillDynamicTypeArgs(type, rules.provider); | 2614 var interceptors = context.computeLibraryElement(src); |
| 2585 var e = type.element; | 2615 for (var t in ['JSArray', 'JSString', 'JSInt', 'JSDouble', 'JSBool']) { |
| 2586 var names = new HashSet<String>() | 2616 _addExtensionType(interceptors.getType(t).type); |
| 2587 ..addAll(e.methods.map((m) => m.name)) | |
| 2588 ..addAll(e.accessors.map((m) => m.name)); | |
| 2589 for (var name in names) { | |
| 2590 _extensionMethods.putIfAbsent(name, () => []).add(type); | |
| 2591 } | |
| 2592 } | 2617 } |
| 2593 } | 2618 } |
| 2594 | 2619 |
| 2595 TypeProvider get types => rules.provider; | 2620 void _addExtensionType(InterfaceType t) { |
| 2621 if (t.isObject || !_extensionTypes.add(t.element)) return; |
| 2622 t = fillDynamicTypeArgs(t, rules.provider); |
| 2623 t.interfaces.forEach(_addExtensionType); |
| 2624 t.mixins.forEach(_addExtensionType); |
| 2625 _addExtensionType(t.superclass); |
| 2626 } |
| 2596 | 2627 |
| 2597 String generateLibrary(LibraryUnit unit, LibraryInfo info) { | 2628 String generateLibrary(LibraryUnit unit, LibraryInfo info) { |
| 2598 var fields = findFieldsNeedingStorage(unit); | 2629 var fields = findFieldsNeedingStorage(unit); |
| 2599 var codegen = | 2630 var codegen = new JSCodegenVisitor(compiler, info, _extensionTypes, fields); |
| 2600 new JSCodegenVisitor(compiler, info, _extensionMethods, fields); | |
| 2601 var module = codegen.emitLibrary(unit); | 2631 var module = codegen.emitLibrary(unit); |
| 2602 var dir = path.join(outDir, jsOutputPath(info, root)); | 2632 var dir = path.join(outDir, jsOutputPath(info, root)); |
| 2603 return writeJsLibrary(module, dir, emitSourceMaps: options.emitSourceMaps); | 2633 return writeJsLibrary(module, dir, emitSourceMaps: options.emitSourceMaps); |
| 2604 } | 2634 } |
| 2605 } | 2635 } |
| 2606 | 2636 |
| 2607 /// Choose a canonical name from the library element. | 2637 /// Choose a canonical name from the library element. |
| 2608 /// This never uses the library's name (the identifier in the `library` | 2638 /// This never uses the library's name (the identifier in the `library` |
| 2609 /// declaration) as it doesn't have any meaningful rules enforced. | 2639 /// declaration) as it doesn't have any meaningful rules enforced. |
| 2610 String jsLibraryName(LibraryElement library) => canonicalLibraryName(library); | 2640 String jsLibraryName(LibraryElement library) => canonicalLibraryName(library); |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2646 | 2676 |
| 2647 /// A special kind of element created by the compiler, signifying a temporary | 2677 /// A special kind of element created by the compiler, signifying a temporary |
| 2648 /// variable. These objects use instance equality, and should be shared | 2678 /// variable. These objects use instance equality, and should be shared |
| 2649 /// everywhere in the tree where they are treated as the same variable. | 2679 /// everywhere in the tree where they are treated as the same variable. |
| 2650 class TemporaryVariableElement extends LocalVariableElementImpl { | 2680 class TemporaryVariableElement extends LocalVariableElementImpl { |
| 2651 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); | 2681 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); |
| 2652 | 2682 |
| 2653 int get hashCode => identityHashCode(this); | 2683 int get hashCode => identityHashCode(this); |
| 2654 bool operator ==(Object other) => identical(this, other); | 2684 bool operator ==(Object other) => identical(this, other); |
| 2655 } | 2685 } |
| OLD | NEW |