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 |