| 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, SplayTreeSet; | 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; |
| (...skipping 523 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 534 | 534 |
| 535 /// Emit class members that need to come after the class declaration, such | 535 /// Emit class members that need to come after the class declaration, such |
| 536 /// as static fields. See [_emitClassMethods] for things that are emitted | 536 /// as static fields. See [_emitClassMethods] for things that are emitted |
| 537 /// inside the ES6 `class { ... }` node. | 537 /// inside the ES6 `class { ... }` node. |
| 538 JS.Statement _finishClassMembers(ClassElement classElem, | 538 JS.Statement _finishClassMembers(ClassElement classElem, |
| 539 JS.ClassExpression cls, List<ConstructorDeclaration> ctors, | 539 JS.ClassExpression cls, List<ConstructorDeclaration> ctors, |
| 540 List<FieldDeclaration> fields, List<MethodDeclaration> methods, | 540 List<FieldDeclaration> fields, List<MethodDeclaration> methods, |
| 541 String jsPeerName) { | 541 String jsPeerName) { |
| 542 var name = classElem.name; | 542 var name = classElem.name; |
| 543 var body = <JS.Statement>[]; | 543 var body = <JS.Statement>[]; |
| 544 |
| 545 if (jsPeerName != null) { |
| 546 var dartxNames = []; |
| 547 for (var m in methods) { |
| 548 if (!m.isAbstract && !m.isStatic && m.element.isPublic) { |
| 549 dartxNames.add(_elementMemberName(m.element, allowExtensions: false)); |
| 550 } |
| 551 } |
| 552 body.add(js.statement('dart.defineExtensionNames(#)', |
| 553 [new JS.ArrayInitializer(dartxNames, multiline: true)])); |
| 554 } |
| 555 |
| 544 body.add(new JS.ClassDeclaration(cls)); | 556 body.add(new JS.ClassDeclaration(cls)); |
| 545 | 557 |
| 546 // TODO(jmesserly): we should really just extend native Array. | 558 // TODO(jmesserly): we should really just extend native Array. |
| 547 if (jsPeerName != null && classElem.typeParameters.isNotEmpty) { | 559 if (jsPeerName != null && classElem.typeParameters.isNotEmpty) { |
| 548 body.add(js.statement('dart.setBaseClass(#, dart.global.#);', [ | 560 body.add(js.statement('dart.setBaseClass(#, dart.global.#);', [ |
| 549 classElem.name, | 561 classElem.name, |
| 550 _propertyName(jsPeerName) | 562 _propertyName(jsPeerName) |
| 551 ])); | 563 ])); |
| 552 } | 564 } |
| 553 | 565 |
| 554 // Interfaces | 566 // Interfaces |
| 555 if (classElem.interfaces.isNotEmpty) { | 567 if (classElem.interfaces.isNotEmpty) { |
| 556 body.add(js.statement('#[dart.implements] = () => #;', [ | 568 body.add(js.statement('#[dart.implements] = () => #;', [ |
| 557 name, | 569 name, |
| 558 new JS.ArrayInitializer( | 570 new JS.ArrayInitializer( |
| 559 classElem.interfaces.map(_emitTypeName).toList()) | 571 classElem.interfaces.map(_emitTypeName).toList()) |
| 560 ])); | 572 ])); |
| 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 } | |
| 576 } | 573 } |
| 577 | 574 |
| 578 // Named constructors | 575 // Named constructors |
| 579 for (ConstructorDeclaration member in ctors) { | 576 for (ConstructorDeclaration member in ctors) { |
| 580 if (member.name != null && member.factoryKeyword == null) { | 577 if (member.name != null && member.factoryKeyword == null) { |
| 581 body.add(js.statement('dart.defineNamedConstructor(#, #);', [ | 578 body.add(js.statement('dart.defineNamedConstructor(#, #);', [ |
| 582 name, | 579 name, |
| 583 _emitMemberName(member.name.name, isStatic: true) | 580 _emitMemberName(member.name.name, isStatic: true) |
| 584 ])); | 581 ])); |
| 585 } | 582 } |
| (...skipping 15 matching lines...) Expand all Loading... |
| 601 var tMethods = []; | 598 var tMethods = []; |
| 602 var sNames = []; | 599 var sNames = []; |
| 603 for (MethodDeclaration node in methods) { | 600 for (MethodDeclaration node in methods) { |
| 604 if (!(node.isSetter || node.isGetter || node.isAbstract)) { | 601 if (!(node.isSetter || node.isGetter || node.isAbstract)) { |
| 605 var name = node.name.name; | 602 var name = node.name.name; |
| 606 var element = node.element; | 603 var element = node.element; |
| 607 var inheritedElement = | 604 var inheritedElement = |
| 608 classElem.lookUpInheritedConcreteMethod(name, currentLibrary); | 605 classElem.lookUpInheritedConcreteMethod(name, currentLibrary); |
| 609 if (inheritedElement != null && | 606 if (inheritedElement != null && |
| 610 inheritedElement.type == element.type) continue; | 607 inheritedElement.type == element.type) continue; |
| 611 var memberName = _emitMemberDeclarationName(element); | 608 var memberName = _elementMemberName(element); |
| 612 var parts = | 609 var parts = |
| 613 _emitFunctionTypeParts(element.type, dynamicIsBottom: false); | 610 _emitFunctionTypeParts(element.type, dynamicIsBottom: false); |
| 614 var property = | 611 var property = |
| 615 new JS.Property(memberName, new JS.ArrayInitializer(parts)); | 612 new JS.Property(memberName, new JS.ArrayInitializer(parts)); |
| 616 if (node.isStatic) { | 613 if (node.isStatic) { |
| 617 tStatics.add(property); | 614 tStatics.add(property); |
| 618 sNames.add(memberName); | 615 sNames.add(memberName); |
| 619 } else { | 616 } else { |
| 620 tMethods.add(property); | 617 tMethods.add(property); |
| 621 } | 618 } |
| (...skipping 26 matching lines...) Expand all Loading... |
| 648 sigFields.add(build('statics', tStatics)); | 645 sigFields.add(build('statics', tStatics)); |
| 649 sigFields.add(aNames); | 646 sigFields.add(aNames); |
| 650 } | 647 } |
| 651 if (!sigFields.isEmpty) { | 648 if (!sigFields.isEmpty) { |
| 652 var sig = new JS.ObjectInitializer(sigFields); | 649 var sig = new JS.ObjectInitializer(sigFields); |
| 653 var classExpr = new JS.Identifier(name); | 650 var classExpr = new JS.Identifier(name); |
| 654 body.add(js.statement('dart.setSignature(#, #);', [classExpr, sig])); | 651 body.add(js.statement('dart.setSignature(#, #);', [classExpr, sig])); |
| 655 } | 652 } |
| 656 } | 653 } |
| 657 | 654 |
| 655 // If a concrete class implements one of our extensions, we might need to |
| 656 // add forwarders. |
| 657 var extensions = _extensionsToImplement(classElem); |
| 658 if (extensions.isNotEmpty) { |
| 659 var methodNames = []; |
| 660 for (var e in extensions) { |
| 661 methodNames.add(_elementMemberName(e)); |
| 662 } |
| 663 body.add(js.statement('dart.defineExtensionMembers(#, #);', [ |
| 664 name, |
| 665 new JS.ArrayInitializer(methodNames, multiline: methodNames.length > 4) |
| 666 ])); |
| 667 } |
| 668 |
| 658 return _statement(body); | 669 return _statement(body); |
| 659 } | 670 } |
| 660 | 671 |
| 661 List<ExecutableElement> _extensionsToImplement(ClassElement element) { | 672 List<ExecutableElement> _extensionsToImplement(ClassElement element) { |
| 662 var members = <ExecutableElement>[]; | 673 var members = <ExecutableElement>[]; |
| 663 if (_extensionTypes.contains(element)) return members; | 674 if (_extensionTypes.contains(element)) return members; |
| 664 | 675 |
| 665 // Collect all extension types we implement. | 676 // Collect all extension types we implement. |
| 666 var type = element.type; | 677 var type = element.type; |
| 667 var types = new Set<ClassElement>(); | 678 var types = new Set<ClassElement>(); |
| (...skipping 373 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1041 } | 1052 } |
| 1042 | 1053 |
| 1043 JS.Method _emitMethodDeclaration(DartType type, MethodDeclaration node) { | 1054 JS.Method _emitMethodDeclaration(DartType type, MethodDeclaration node) { |
| 1044 if (node.isAbstract || _externalOrNative(node)) { | 1055 if (node.isAbstract || _externalOrNative(node)) { |
| 1045 return null; | 1056 return null; |
| 1046 } | 1057 } |
| 1047 | 1058 |
| 1048 var params = _visit(node.parameters); | 1059 var params = _visit(node.parameters); |
| 1049 if (params == null) params = []; | 1060 if (params == null) params = []; |
| 1050 | 1061 |
| 1051 return new JS.Method(_emitMemberDeclarationName(node.element), | 1062 return new JS.Method( |
| 1052 new JS.Fun(params, _visit(node.body)), | 1063 _elementMemberName(node.element), new JS.Fun(params, _visit(node.body)), |
| 1053 isGetter: node.isGetter, | 1064 isGetter: node.isGetter, |
| 1054 isSetter: node.isSetter, | 1065 isSetter: node.isSetter, |
| 1055 isStatic: node.isStatic); | 1066 isStatic: node.isStatic); |
| 1056 } | 1067 } |
| 1057 | 1068 |
| 1058 @override | 1069 @override |
| 1059 JS.Statement visitFunctionDeclaration(FunctionDeclaration node) { | 1070 JS.Statement visitFunctionDeclaration(FunctionDeclaration node) { |
| 1060 assert(node.parent is CompilationUnit); | 1071 assert(node.parent is CompilationUnit); |
| 1061 | 1072 |
| 1062 if (_externalOrNative(node)) return null; | 1073 if (_externalOrNative(node)) return null; |
| (...skipping 1475 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2538 /// Visits a list of expressions, creating a comma expression if needed in JS. | 2549 /// Visits a list of expressions, creating a comma expression if needed in JS. |
| 2539 JS.Expression _visitListToBinary(List<Expression> nodes, String operator) { | 2550 JS.Expression _visitListToBinary(List<Expression> nodes, String operator) { |
| 2540 if (nodes == null || nodes.isEmpty) return null; | 2551 if (nodes == null || nodes.isEmpty) return null; |
| 2541 return new JS.Expression.binary(_visitList(nodes), operator); | 2552 return new JS.Expression.binary(_visitList(nodes), operator); |
| 2542 } | 2553 } |
| 2543 | 2554 |
| 2544 /// Like [_emitMemberName], but for declaration sites. | 2555 /// Like [_emitMemberName], but for declaration sites. |
| 2545 /// | 2556 /// |
| 2546 /// Unlike call sites, we always have an element available, so we can use it | 2557 /// Unlike call sites, we always have an element available, so we can use it |
| 2547 /// directly rather than computing the relevant options for [_emitMemberName]. | 2558 /// directly rather than computing the relevant options for [_emitMemberName]. |
| 2548 JS.Expression _emitMemberDeclarationName(ExecutableElement e) { | 2559 JS.Expression _elementMemberName(ExecutableElement e, |
| 2560 {bool allowExtensions: true}) { |
| 2549 String name; | 2561 String name; |
| 2550 if (e is PropertyAccessorElement) { | 2562 if (e is PropertyAccessorElement) { |
| 2551 name = e.variable.name; | 2563 name = e.variable.name; |
| 2552 } else { | 2564 } else { |
| 2553 name = e.name; | 2565 name = e.name; |
| 2554 } | 2566 } |
| 2555 return _emitMemberName(name, | 2567 return _emitMemberName(name, |
| 2556 type: (e.enclosingElement as ClassElement).type, | 2568 type: (e.enclosingElement as ClassElement).type, |
| 2557 unary: e.parameters.isEmpty, | 2569 unary: e.parameters.isEmpty, |
| 2558 isStatic: e.isStatic, | 2570 isStatic: e.isStatic, |
| 2559 declaration: true); | 2571 allowExtensions: allowExtensions); |
| 2560 } | 2572 } |
| 2561 | 2573 |
| 2562 /// This handles member renaming for private names and operators. | 2574 /// This handles member renaming for private names and operators. |
| 2563 /// | 2575 /// |
| 2564 /// Private names are generated using ES6 symbols: | 2576 /// Private names are generated using ES6 symbols: |
| 2565 /// | 2577 /// |
| 2566 /// // At the top of the module: | 2578 /// // At the top of the module: |
| 2567 /// let _x = Symbol('_x'); | 2579 /// let _x = Symbol('_x'); |
| 2568 /// let _y = Symbol('_y'); | 2580 /// let _y = Symbol('_y'); |
| 2569 /// ... | 2581 /// ... |
| (...skipping 24 matching lines...) Expand all Loading... |
| 2594 /// This follows the same pattern as EcmaScript 6 Map: | 2606 /// This follows the same pattern as EcmaScript 6 Map: |
| 2595 /// <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_
Objects/Map> | 2607 /// <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_
Objects/Map> |
| 2596 /// | 2608 /// |
| 2597 /// Unary minus looks like: `x['unary-']()`. Note that [unary] must be passed | 2609 /// Unary minus looks like: `x['unary-']()`. Note that [unary] must be passed |
| 2598 /// for this transformation to happen, otherwise binary minus is assumed. | 2610 /// for this transformation to happen, otherwise binary minus is assumed. |
| 2599 /// | 2611 /// |
| 2600 /// Equality is a bit special, it is generated via the Dart `equals` runtime | 2612 /// Equality is a bit special, it is generated via the Dart `equals` runtime |
| 2601 /// helper, that checks for null. The user defined method is called '=='. | 2613 /// helper, that checks for null. The user defined method is called '=='. |
| 2602 /// | 2614 /// |
| 2603 JS.Expression _emitMemberName(String name, {DartType type, bool unary: false, | 2615 JS.Expression _emitMemberName(String name, {DartType type, bool unary: false, |
| 2604 bool isStatic: false, bool declaration: false}) { | 2616 bool isStatic: false, bool allowExtensions: true}) { |
| 2605 | 2617 |
| 2606 // Static members skip the rename steps. | 2618 // Static members skip the rename steps. |
| 2607 if (isStatic) return _propertyName(name); | 2619 if (isStatic) return _propertyName(name); |
| 2608 | 2620 |
| 2609 if (name.startsWith('_')) { | 2621 if (name.startsWith('_')) { |
| 2610 return _privateNames.putIfAbsent( | 2622 return _privateNames.putIfAbsent( |
| 2611 name, () => _initSymbol(new JS.TemporaryId(name))); | 2623 name, () => _initSymbol(new JS.TemporaryId(name))); |
| 2612 } | 2624 } |
| 2613 | 2625 |
| 2614 if (name == '[]') { | 2626 if (name == '[]') { |
| 2615 name = 'get'; | 2627 name = 'get'; |
| 2616 } else if (name == '[]=') { | 2628 } else if (name == '[]=') { |
| 2617 name = 'set'; | 2629 name = 'set'; |
| 2618 } else if (name == '-' && unary) { | 2630 } else if (name == '-' && unary) { |
| 2619 name = 'unary-'; | 2631 name = 'unary-'; |
| 2620 } | 2632 } |
| 2621 | 2633 |
| 2622 // Dart "extension" methods. Used for JS Array, Boolean, Number, String. | 2634 // Dart "extension" methods. Used for JS Array, Boolean, Number, String. |
| 2623 if (!declaration && _extensionTypes.contains(type.element)) { | 2635 if (allowExtensions && _extensionTypes.contains(type.element)) { |
| 2624 // Special case `length`. We can call it directly. | 2636 // Special case `length`. We can call it directly. |
| 2625 if (name != 'length') return js.call('dartx.#', _propertyName(name)); | 2637 if (name != 'length') return js.call('dartx.#', _propertyName(name)); |
| 2626 } | 2638 } |
| 2627 | 2639 |
| 2628 return _propertyName(name); | 2640 return _propertyName(name); |
| 2629 } | 2641 } |
| 2630 | 2642 |
| 2631 bool _externalOrNative(node) => | 2643 bool _externalOrNative(node) => |
| 2632 node.externalKeyword != null || _functionBody(node) is NativeFunctionBody; | 2644 node.externalKeyword != null || _functionBody(node) is NativeFunctionBody; |
| 2633 | 2645 |
| (...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2720 | 2732 |
| 2721 /// A special kind of element created by the compiler, signifying a temporary | 2733 /// A special kind of element created by the compiler, signifying a temporary |
| 2722 /// variable. These objects use instance equality, and should be shared | 2734 /// variable. These objects use instance equality, and should be shared |
| 2723 /// everywhere in the tree where they are treated as the same variable. | 2735 /// everywhere in the tree where they are treated as the same variable. |
| 2724 class TemporaryVariableElement extends LocalVariableElementImpl { | 2736 class TemporaryVariableElement extends LocalVariableElementImpl { |
| 2725 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); | 2737 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); |
| 2726 | 2738 |
| 2727 int get hashCode => identityHashCode(this); | 2739 int get hashCode => identityHashCode(this); |
| 2728 bool operator ==(Object other) => identical(this, other); | 2740 bool operator ==(Object other) => identical(this, other); |
| 2729 } | 2741 } |
| OLD | NEW |