Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(966)

Side by Side Diff: lib/src/codegen/js_codegen.dart

Issue 1153003003: fixes #40, extension methods for primitive types (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
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
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
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698