| 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 import 'dart:io' show Directory, File; | 8 import 'dart:io' show Directory, File; |
| 9 | 9 |
| 10 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; | 10 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; |
| (...skipping 24 matching lines...) Expand all Loading... |
| 35 | 35 |
| 36 bool _isAnnotationType(Annotation m, String name) => m.name.name == name; | 36 bool _isAnnotationType(Annotation m, String name) => m.name.name == name; |
| 37 | 37 |
| 38 Annotation _getAnnotation(AnnotatedNode node, String name) => node.metadata | 38 Annotation _getAnnotation(AnnotatedNode node, String name) => node.metadata |
| 39 .firstWhere((annotation) => _isAnnotationType(annotation, name), | 39 .firstWhere((annotation) => _isAnnotationType(annotation, name), |
| 40 orElse: () => null); | 40 orElse: () => null); |
| 41 | 41 |
| 42 Annotation _getJsNameAnnotation(AnnotatedNode node) => | 42 Annotation _getJsNameAnnotation(AnnotatedNode node) => |
| 43 _getAnnotation(node, "JsName"); | 43 _getAnnotation(node, "JsName"); |
| 44 | 44 |
| 45 // TODO(jacobr): we would like to do something like the following |
| 46 // but we don't have summary support yet. |
| 47 // bool _supportJsExtensionMethod(AnnotatedNode node) => |
| 48 // _getAnnotation(node, "SupportJsExtensionMethod") != null; |
| 49 |
| 45 class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { | 50 class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
| 46 final LibraryInfo libraryInfo; | 51 final LibraryInfo libraryInfo; |
| 47 final TypeRules rules; | 52 final TypeRules rules; |
| 48 | 53 |
| 49 // TODO(jmesserly): this is needed because RestrictedTypeRules can send | 54 // TODO(jmesserly): this is needed because RestrictedTypeRules can send |
| 50 // messages to CheckerReporter, for things like missing types. | 55 // messages to CheckerReporter, for things like missing types. |
| 51 // We should probably refactor so this can't happen, as codegen would be too | 56 // We should probably refactor so this can't happen, as codegen would be too |
| 52 // late to be issuing these messages. | 57 // late to be issuing these messages. |
| 53 final CheckerReporter _checkerReporter; | 58 final CheckerReporter _checkerReporter; |
| 54 | 59 |
| 55 /// The variable for the target of the current `..` cascade expression. | 60 /// The variable for the target of the current `..` cascade expression. |
| 56 SimpleIdentifier _cascadeTarget; | 61 SimpleIdentifier _cascadeTarget; |
| 57 /// The variable for the current catch clause | 62 /// The variable for the current catch clause |
| 58 SimpleIdentifier _catchParameter; | 63 SimpleIdentifier _catchParameter; |
| 59 | 64 |
| 60 ClassDeclaration currentClass; | 65 ClassDeclaration currentClass; |
| 61 ConstantEvaluator _constEvaluator; | 66 ConstantEvaluator _constEvaluator; |
| 62 | 67 |
| 63 final _exports = <String>[]; | 68 final _exports = new Set<String>(); |
| 64 final _lazyFields = <VariableDeclaration>[]; | 69 final _lazyFields = <VariableDeclaration>[]; |
| 65 final _properties = <FunctionDeclaration>[]; | 70 final _properties = <FunctionDeclaration>[]; |
| 66 final _privateNames = new HashSet<String>(); | 71 final _privateNames = new HashSet<String>(); |
| 67 final _pendingPrivateNames = <String>[]; | 72 final _pendingPrivateNames = <String>[]; |
| 73 final _extensionMethodNames = new HashSet<String>(); |
| 74 final _pendingExtensionMethodNames = <String>[]; |
| 75 |
| 76 InterfaceType _fillDynamicTypeArgs(InterfaceType t) { |
| 77 var d = rules.provider.dynamicType; |
| 78 return t.substitute4(new List.filled(t.typeArguments.length, d)); |
| 79 } |
| 80 // TODO(jacobr): determine the the set of types with extension methods from |
| 81 // the annotations rather than hard coding the list once the analyzer |
| 82 // supports summaries. |
| 83 List<InterfaceType> _jsExtensionMethodTypes; |
| 84 List<InterfaceType> get jsExtensionMethodTypes { |
| 85 if (_jsExtensionMethodTypes != null) return _jsExtensionMethodTypes; |
| 86 _jsExtensionMethodTypes = <InterfaceType>[ |
| 87 rules.provider.listType, |
| 88 rules.provider.iterableType |
| 89 ].map(_fillDynamicTypeArgs).toList(); |
| 90 return _jsExtensionMethodTypes; |
| 91 } |
| 92 |
| 93 Map<ClassElement, Set<String>> _extensionMethods; |
| 94 |
| 95 Map<ClassElement, Set<String>> get extensionMethods { |
| 96 if (_extensionMethods != null) return _extensionMethods; |
| 97 _extensionMethods = new HashMap<ClassElement, HashSet<String>>(); |
| 98 |
| 99 for (var type in jsExtensionMethodTypes) { |
| 100 var names = new HashSet<String>(); |
| 101 var e = type.element; |
| 102 names.addAll(e.methods.map((m) => m.name)); |
| 103 names.addAll(e.accessors.map((m) => m.name)); |
| 104 _extensionMethods[e] = names; |
| 105 } |
| 106 return _extensionMethods; |
| 107 } |
| 68 | 108 |
| 69 /// Classes we have not emitted yet. Values can be [ClassDeclaration] or | 109 /// Classes we have not emitted yet. Values can be [ClassDeclaration] or |
| 70 /// [ClassTypeAlias]. | 110 /// [ClassTypeAlias]. |
| 71 final _pendingClasses = new HashMap<ClassElement, CompilationUnitMember>(); | 111 final _pendingClasses = new HashMap<ClassElement, CompilationUnitMember>(); |
| 72 | 112 |
| 73 /// Memoized results of [_lazyClass]. | 113 /// Memoized results of [_lazyClass]. |
| 74 final _lazyClassMemo = new HashMap<ClassElement, bool>(); | 114 final _lazyClassMemo = new HashMap<ClassElement, bool>(); |
| 75 | 115 |
| 76 /// Memoized results of [_inLibraryCycle]. | 116 /// Memoized results of [_inLibraryCycle]. |
| 77 final _libraryCycleMemo = new HashMap<LibraryElement, bool>(); | 117 final _libraryCycleMemo = new HashMap<LibraryElement, bool>(); |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 136 name, | 176 name, |
| 137 name, | 177 name, |
| 138 defaultValue | 178 defaultValue |
| 139 ]) | 179 ]) |
| 140 ]); | 180 ]); |
| 141 } | 181 } |
| 142 | 182 |
| 143 JS.Statement _initPrivateSymbol(String name) => js.statement( | 183 JS.Statement _initPrivateSymbol(String name) => js.statement( |
| 144 'let # = $_SYMBOL(#);', [new JSTemporary(name), js.string(name, "'")]); | 184 'let # = $_SYMBOL(#);', [new JSTemporary(name), js.string(name, "'")]); |
| 145 | 185 |
| 186 JS.Statement _initExtensionMethodSymbol(String name) => js.statement( |
| 187 'let # = $_SYMBOL(#);', [new JS.Identifier(name), js.string(name, "'")]); |
| 188 |
| 146 // TODO(jmesserly): this is a temporary workaround for `Symbol` in core, | 189 // TODO(jmesserly): this is a temporary workaround for `Symbol` in core, |
| 147 // until we have better name tracking. | 190 // until we have better name tracking. |
| 148 String get _SYMBOL { | 191 String get _SYMBOL { |
| 149 var name = currentLibrary.name; | 192 var name = currentLibrary.name; |
| 150 if (name == 'dart.core' || name == 'dart._internal') return 'dart.JsSymbol'; | 193 if (name == 'dart.core' || name == 'dart._internal') return 'dart.JsSymbol'; |
| 151 return 'Symbol'; | 194 return 'Symbol'; |
| 152 } | 195 } |
| 153 | 196 |
| 154 @override | 197 @override |
| 155 JS.Statement visitCompilationUnit(CompilationUnit node) { | 198 JS.Statement visitCompilationUnit(CompilationUnit node) { |
| 156 var source = node.element.source; | 199 var source = node.element.source; |
| 157 | 200 |
| 158 _constEvaluator = new ConstantEvaluator(source, rules.provider); | 201 _constEvaluator = new ConstantEvaluator(source, rules.provider); |
| 159 _checkerReporter.enterSource(source); | 202 _checkerReporter.enterSource(source); |
| 160 | 203 |
| 161 // TODO(jmesserly): scriptTag, directives. | 204 // TODO(jmesserly): scriptTag, directives. |
| 162 var body = <JS.Statement>[]; | 205 var body = <JS.Statement>[]; |
| 163 for (var child in node.declarations) { | 206 for (var child in node.declarations) { |
| 164 // Attempt to group adjacent fields/properties. | 207 // Attempt to group adjacent fields/properties. |
| 165 if (child is! TopLevelVariableDeclaration) _flushLazyFields(body); | 208 if (child is! TopLevelVariableDeclaration) _flushLazyFields(body); |
| 166 if (child is! FunctionDeclaration) _flushLibraryProperties(body); | 209 if (child is! FunctionDeclaration) _flushLibraryProperties(body); |
| 167 | 210 |
| 168 var code = _visit(child); | 211 var code = _visit(child); |
| 169 | 212 |
| 170 if (code != null) { | 213 if (code != null) { |
| 171 if (_pendingPrivateNames.isNotEmpty) { | 214 if (_pendingPrivateNames.isNotEmpty) { |
| 172 body.addAll(_pendingPrivateNames.map(_initPrivateSymbol)); | 215 body.addAll(_pendingPrivateNames.map(_initPrivateSymbol)); |
| 173 _pendingPrivateNames.clear(); | 216 _pendingPrivateNames.clear(); |
| 174 } | 217 } |
| 218 if (_pendingExtensionMethodNames.isNotEmpty) { |
| 219 body.addAll( |
| 220 _pendingExtensionMethodNames.map(_initExtensionMethodSymbol)); |
| 221 _pendingExtensionMethodNames.clear(); |
| 222 } |
| 175 body.add(code); | 223 body.add(code); |
| 176 } | 224 } |
| 177 } | 225 } |
| 178 | 226 |
| 179 // Flush any unwritten fields/properties. | 227 // Flush any unwritten fields/properties. |
| 180 _flushLazyFields(body); | 228 _flushLazyFields(body); |
| 181 _flushLibraryProperties(body); | 229 _flushLibraryProperties(body); |
| 182 | 230 |
| 183 _checkerReporter.leaveSource(); | 231 _checkerReporter.leaveSource(); |
| 184 | 232 |
| (...skipping 168 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 353 | 401 |
| 354 return js.statement( | 402 return js.statement( |
| 355 'dart.defineLazyClass(#, { get #() { #; return #; } });', [ | 403 'dart.defineLazyClass(#, { get #() { #; return #; } });', [ |
| 356 _exportsVar, | 404 _exportsVar, |
| 357 _propertyName(name), | 405 _propertyName(name), |
| 358 body, | 406 body, |
| 359 name | 407 name |
| 360 ]); | 408 ]); |
| 361 } | 409 } |
| 362 | 410 |
| 363 if (isPublic(name)) _exports.add(name); | 411 if (isPublic(name)) _addExport(name); |
| 364 | 412 |
| 365 if (genericDef != null) { | 413 if (genericDef != null) { |
| 366 body = js.statement('{ #; let # = #; }', [genericDef, name, genericInst]); | 414 body = js.statement('{ #; let # = #; }', [genericDef, name, genericInst]); |
| 367 if (isPublic(name)) _exports.add(genericName); | 415 if (isPublic(name)) _addExport(genericName); |
| 368 } | 416 } |
| 369 | 417 |
| 370 if (classElem.type.isObject) return body; | 418 if (classElem.type.isObject) return body; |
| 371 | 419 |
| 372 // If we're not lazy, we still need to ensure our dependencies are | 420 // If we're not lazy, we still need to ensure our dependencies are |
| 373 // generated first. | 421 // generated first. |
| 374 var classDefs = <JS.Statement>[]; | 422 var classDefs = <JS.Statement>[]; |
| 375 _emitClassIfNeeded(classDefs, classElem.supertype.element); | 423 _emitClassIfNeeded(classDefs, classElem.supertype.element); |
| 376 for (var m in classElem.mixins) { | 424 for (var m in classElem.mixins) { |
| 377 _emitClassIfNeeded(classDefs, m.element); | 425 _emitClassIfNeeded(classDefs, m.element); |
| (...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 499 if (node.withClause != null) { | 547 if (node.withClause != null) { |
| 500 var mixins = _visitList(node.withClause.mixinTypes); | 548 var mixins = _visitList(node.withClause.mixinTypes); |
| 501 mixins.insert(0, heritage); | 549 mixins.insert(0, heritage); |
| 502 heritage = js.call('dart.mixin(#)', [mixins]); | 550 heritage = js.call('dart.mixin(#)', [mixins]); |
| 503 } | 551 } |
| 504 return heritage; | 552 return heritage; |
| 505 } | 553 } |
| 506 | 554 |
| 507 /// Emit class members that can be generated as methods. | 555 /// Emit class members that can be generated as methods. |
| 508 /// Anything not handled here will be addressed in [_finishClassMembers]. | 556 /// Anything not handled here will be addressed in [_finishClassMembers]. |
| 557 Iterable<InterfaceType> getMatchingExtensionMethodTypes(InterfaceType type) => |
| 558 jsExtensionMethodTypes.where((t) => rules.isSubTypeOf(type, t)); |
| 559 |
| 560 LibraryElement getExtensionLibrary( |
| 561 Iterable<InterfaceType> extensionTypes, String name) { |
| 562 var library = null; |
| 563 for (var type in extensionTypes) { |
| 564 var element = type.element; |
| 565 if (extensionMethods[element].contains(name)) { |
| 566 assert(library == null || library == element.library); |
| 567 library = element.library; |
| 568 } |
| 569 } |
| 570 return library; |
| 571 } |
| 572 |
| 573 JS.Expression nameIfExtension(Expression target, String name) { |
| 574 var targetType = rules.getStaticType(target); |
| 575 if (targetType is! InterfaceType) return null; |
| 576 var extensionLibrary = |
| 577 getExtensionLibrary(getMatchingExtensionMethodTypes(targetType), name); |
| 578 if (extensionLibrary == null) return null; |
| 579 return js.call('#.#', [ |
| 580 _libraryName(extensionLibrary), |
| 581 _emitExtensionMethodName(name) |
| 582 ]); |
| 583 } |
| 584 |
| 509 List<JS.Method> _emitClassMethods(ClassDeclaration node, | 585 List<JS.Method> _emitClassMethods(ClassDeclaration node, |
| 510 List<ConstructorDeclaration> ctors, List<FieldDeclaration> fields) { | 586 List<ConstructorDeclaration> ctors, List<FieldDeclaration> fields) { |
| 511 var element = node.element; | 587 var element = node.element; |
| 512 var isObject = element.type.isObject; | 588 var isObject = element.type.isObject; |
| 513 var name = node.name.name; | 589 var name = node.name.name; |
| 514 | 590 |
| 515 var jsMethods = <JS.Method>[]; | 591 var jsMethods = <JS.Method>[]; |
| 516 // Iff no constructor is specified for a class C, it implicitly has a | 592 // Iff no constructor is specified for a class C, it implicitly has a |
| 517 // default constructor `C() : super() {}`, unless C is class Object. | 593 // default constructor `C() : super() {}`, unless C is class Object. |
| 518 if (ctors.isEmpty && !isObject) { | 594 if (ctors.isEmpty && !isObject) { |
| 519 jsMethods.add(_emitImplicitConstructor(node, name, fields)); | 595 jsMethods.add(_emitImplicitConstructor(node, name, fields)); |
| 520 } | 596 } |
| 521 | 597 var extensionTypes = getMatchingExtensionMethodTypes(element.type); |
| 522 for (var member in node.members) { | 598 for (var member in node.members) { |
| 523 if (member is ConstructorDeclaration) { | 599 if (member is ConstructorDeclaration) { |
| 524 jsMethods.add(_emitConstructor(member, name, fields, isObject)); | 600 jsMethods.add(_emitConstructor(member, name, fields, isObject)); |
| 525 } else if (member is MethodDeclaration) { | 601 } else if (member is MethodDeclaration) { |
| 526 jsMethods.add(_visit(member)); | 602 jsMethods.add(_emitMethodDeclaration(member, extensionTypes)); |
| 527 } | 603 } |
| 528 } | 604 } |
| 529 | 605 |
| 530 // Support for adapting dart:core Iterator/Iterable to ES6 versions. | 606 // Support for adapting dart:core Iterator/Iterable to ES6 versions. |
| 531 // This lets them use for-of loops transparently. | 607 // This lets them use for-of loops transparently. |
| 532 // https://github.com/lukehoban/es6features#iterators--forof | 608 // https://github.com/lukehoban/es6features#iterators--forof |
| 533 if (element.library.isDartCore && element.name == 'Iterable') { | 609 if (element.library.isDartCore && element.name == 'Iterable') { |
| 534 JS.Fun body = js.call('''function() { | 610 JS.Fun body = js.call('''function() { |
| 535 var iterator = this.iterator; | 611 var iterator = this.iterator; |
| 536 return { | 612 return { |
| (...skipping 26 matching lines...) Expand all Loading... |
| 563 new JS.ArrayInitializer( | 639 new JS.ArrayInitializer( |
| 564 classElem.interfaces.map(_emitTypeName).toList()) | 640 classElem.interfaces.map(_emitTypeName).toList()) |
| 565 ])); | 641 ])); |
| 566 } | 642 } |
| 567 | 643 |
| 568 // Named constructors | 644 // Named constructors |
| 569 for (ConstructorDeclaration member in ctors) { | 645 for (ConstructorDeclaration member in ctors) { |
| 570 if (member.name != null) { | 646 if (member.name != null) { |
| 571 body.add(js.statement('dart.defineNamedConstructor(#, #);', [ | 647 body.add(js.statement('dart.defineNamedConstructor(#, #);', [ |
| 572 name, | 648 name, |
| 573 _jsMemberName(member.name.name, isStatic: true) | 649 _emitMemberName(member.name.name, isStatic: true) |
| 574 ])); | 650 ])); |
| 575 } | 651 } |
| 576 } | 652 } |
| 577 | 653 |
| 578 // Static fields | 654 // Static fields |
| 579 var lazyStatics = <VariableDeclaration>[]; | 655 var lazyStatics = <VariableDeclaration>[]; |
| 580 for (FieldDeclaration member in staticFields) { | 656 for (FieldDeclaration member in staticFields) { |
| 581 for (VariableDeclaration field in member.fields.variables) { | 657 for (VariableDeclaration field in member.fields.variables) { |
| 582 var fieldName = field.name.name; | 658 var fieldName = field.name.name; |
| 583 if (field.isConst || _isFieldInitConstant(field)) { | 659 if (field.isConst || _isFieldInitConstant(field)) { |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 648 | 724 |
| 649 // We generate constructors as initializer methods in the class; | 725 // We generate constructors as initializer methods in the class; |
| 650 // this allows use of `super` for instance methods/properties. | 726 // this allows use of `super` for instance methods/properties. |
| 651 // It also avoids V8 restrictions on `super` in default constructors. | 727 // It also avoids V8 restrictions on `super` in default constructors. |
| 652 return new JS.Method(name, new JS.Fun(_visit(node.parameters), body)) | 728 return new JS.Method(name, new JS.Fun(_visit(node.parameters), body)) |
| 653 ..sourceInformation = node; | 729 ..sourceInformation = node; |
| 654 } | 730 } |
| 655 | 731 |
| 656 JS.Expression _constructorName(String className, SimpleIdentifier name) { | 732 JS.Expression _constructorName(String className, SimpleIdentifier name) { |
| 657 if (name == null) return js.string(className, "'"); | 733 if (name == null) return js.string(className, "'"); |
| 658 return _jsMemberName(name.name, isStatic: true); | 734 return _emitMemberName(name.name, isStatic: true); |
| 659 } | 735 } |
| 660 | 736 |
| 661 JS.Block _emitConstructorBody( | 737 JS.Block _emitConstructorBody( |
| 662 ConstructorDeclaration node, List<FieldDeclaration> fields) { | 738 ConstructorDeclaration node, List<FieldDeclaration> fields) { |
| 663 // Wacky factory redirecting constructors: factory Foo.q(x, y) = Bar.baz; | 739 // Wacky factory redirecting constructors: factory Foo.q(x, y) = Bar.baz; |
| 664 if (node.redirectedConstructor != null) { | 740 if (node.redirectedConstructor != null) { |
| 665 return js.statement('{ return new #(#); }', [ | 741 return js.statement('{ return new #(#); }', [ |
| 666 _visit(node.redirectedConstructor), | 742 _visit(node.redirectedConstructor), |
| 667 _visit(node.parameters) | 743 _visit(node.parameters) |
| 668 ]); | 744 ]); |
| (...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 759 } | 835 } |
| 760 } | 836 } |
| 761 | 837 |
| 762 // Initialize fields from `this.fieldName` parameters. | 838 // Initialize fields from `this.fieldName` parameters. |
| 763 if (parameters != null) { | 839 if (parameters != null) { |
| 764 for (var p in parameters.parameters) { | 840 for (var p in parameters.parameters) { |
| 765 if (p is DefaultFormalParameter) p = p.parameter; | 841 if (p is DefaultFormalParameter) p = p.parameter; |
| 766 if (p is FieldFormalParameter) { | 842 if (p is FieldFormalParameter) { |
| 767 var name = p.identifier.name; | 843 var name = p.identifier.name; |
| 768 body.add( | 844 body.add( |
| 769 js.statement('this.# = #;', [_jsMemberName(name), _visit(p)])); | 845 js.statement('this.# = #;', [_emitMemberName(name), _visit(p)])); |
| 770 unsetFields.remove(name); | 846 unsetFields.remove(name); |
| 771 } | 847 } |
| 772 } | 848 } |
| 773 } | 849 } |
| 774 | 850 |
| 775 // Run constructor field initializers such as `: foo = bar.baz` | 851 // Run constructor field initializers such as `: foo = bar.baz` |
| 776 if (initializers != null) { | 852 if (initializers != null) { |
| 777 for (var init in initializers) { | 853 for (var init in initializers) { |
| 778 if (init is ConstructorFieldInitializer) { | 854 if (init is ConstructorFieldInitializer) { |
| 779 body.add(js.statement( | 855 body.add(js.statement( |
| 780 '# = #;', [_visit(init.fieldName), _visit(init.expression)])); | 856 '# = #;', [_visit(init.fieldName), _visit(init.expression)])); |
| 781 unsetFields.remove(init.fieldName.name); | 857 unsetFields.remove(init.fieldName.name); |
| 782 } | 858 } |
| 783 } | 859 } |
| 784 } | 860 } |
| 785 | 861 |
| 786 // Initialize all remaining fields | 862 // Initialize all remaining fields |
| 787 unsetFields.forEach((name, field) { | 863 unsetFields.forEach((name, field) { |
| 788 JS.Expression value; | 864 JS.Expression value; |
| 789 if (field.initializer != null) { | 865 if (field.initializer != null) { |
| 790 value = _visit(field.initializer); | 866 value = _visit(field.initializer); |
| 791 } else { | 867 } else { |
| 792 var type = rules.elementType(field.element); | 868 var type = rules.elementType(field.element); |
| 793 if (rules.maybeNonNullableType(type)) { | 869 if (rules.maybeNonNullableType(type)) { |
| 794 value = js.call('dart.as(null, #)', _emitTypeName(type)); | 870 value = js.call('dart.as(null, #)', _emitTypeName(type)); |
| 795 } else { | 871 } else { |
| 796 value = new JS.LiteralNull(); | 872 value = new JS.LiteralNull(); |
| 797 } | 873 } |
| 798 } | 874 } |
| 799 body.add(js.statement('this.# = #;', [_jsMemberName(name), value])); | 875 body.add(js.statement('this.# = #;', [_emitMemberName(name), value])); |
| 800 }); | 876 }); |
| 801 | 877 |
| 802 return _statement(body); | 878 return _statement(body); |
| 803 } | 879 } |
| 804 | 880 |
| 805 FormalParameterList _parametersOf(node) { | 881 FormalParameterList _parametersOf(node) { |
| 806 // Note: ConstructorDeclaration is intentionally skipped here so we can | 882 // Note: ConstructorDeclaration is intentionally skipped here so we can |
| 807 // emit the argument initializers in a different place. | 883 // emit the argument initializers in a different place. |
| 808 // TODO(jmesserly): clean this up. If we can model ES6 spread/rest args, we | 884 // TODO(jmesserly): clean this up. If we can model ES6 spread/rest args, we |
| 809 // could handle argument initializers more consistently in a separate | 885 // could handle argument initializers more consistently in a separate |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 851 } | 927 } |
| 852 | 928 |
| 853 JS.Expression _defaultParamValue(FormalParameter param) { | 929 JS.Expression _defaultParamValue(FormalParameter param) { |
| 854 if (param is DefaultFormalParameter && param.defaultValue != null) { | 930 if (param is DefaultFormalParameter && param.defaultValue != null) { |
| 855 return _visit(param.defaultValue); | 931 return _visit(param.defaultValue); |
| 856 } else { | 932 } else { |
| 857 return new JS.LiteralNull(); | 933 return new JS.LiteralNull(); |
| 858 } | 934 } |
| 859 } | 935 } |
| 860 | 936 |
| 861 @override | 937 JS.Method _emitMethodDeclaration( |
| 862 JS.Method visitMethodDeclaration(MethodDeclaration node) { | 938 MethodDeclaration node, Iterable<InterfaceType> extensionTypes) { |
| 863 if (node.isAbstract || _externalOrNative(node)) { | 939 if (node.isAbstract || _externalOrNative(node)) { |
| 864 return null; | 940 return null; |
| 865 } | 941 } |
| 866 | 942 |
| 867 var params = _visit(node.parameters); | 943 var params = _visit(node.parameters); |
| 868 if (params == null) params = []; | 944 if (params == null) params = []; |
| 869 | 945 |
| 870 return new JS.Method(_jsMemberName(node.name.name, isStatic: node.isStatic), | 946 var memberName; |
| 871 new JS.Fun(params, _visit(node.body)), | 947 var extensionLibrary; |
| 948 |
| 949 if (!node.isStatic) { |
| 950 extensionLibrary = getExtensionLibrary(extensionTypes, node.name.name); |
| 951 } |
| 952 |
| 953 if (extensionLibrary != null) { |
| 954 var extensionMethodName = _extensionMethodName(node.name.name); |
| 955 if (extensionLibrary == libraryInfo.library.library) { |
| 956 // TODO(jacobr): need to do a better job ensuring that extension method |
| 957 // name symbols do not conflict with other symbols before we can let |
| 958 // user defined libraries define extension methods. |
| 959 if (_extensionMethodNames.add(extensionMethodName)) { |
| 960 _pendingExtensionMethodNames.add(extensionMethodName); |
| 961 _addExport(extensionMethodName); |
| 962 } |
| 963 } |
| 964 memberName = js.call('#.#', [ |
| 965 _libraryName(extensionLibrary), |
| 966 js.string(extensionMethodName, "'") |
| 967 ]); |
| 968 } else { |
| 969 memberName = _emitMemberName(node.name.name, isStatic: node.isStatic); |
| 970 } |
| 971 return new JS.Method(memberName, new JS.Fun(params, _visit(node.body)), |
| 872 isGetter: node.isGetter, | 972 isGetter: node.isGetter, |
| 873 isSetter: node.isSetter, | 973 isSetter: node.isSetter, |
| 874 isStatic: node.isStatic); | 974 isStatic: node.isStatic); |
| 875 } | 975 } |
| 876 | 976 |
| 877 @override | 977 @override |
| 878 JS.Statement visitFunctionDeclaration(FunctionDeclaration node) { | 978 JS.Statement visitFunctionDeclaration(FunctionDeclaration node) { |
| 879 assert(node.parent is CompilationUnit); | 979 assert(node.parent is CompilationUnit); |
| 880 | 980 |
| 881 if (_externalOrNative(node)) return null; | 981 if (_externalOrNative(node)) return null; |
| 882 | 982 |
| 883 if (node.isGetter || node.isSetter) { | 983 if (node.isGetter || node.isSetter) { |
| 884 // Add these later so we can use getter/setter syntax. | 984 // Add these later so we can use getter/setter syntax. |
| 885 _properties.add(node); | 985 _properties.add(node); |
| 886 return null; | 986 return null; |
| 887 } | 987 } |
| 888 | 988 |
| 889 var body = <JS.Statement>[]; | 989 var body = <JS.Statement>[]; |
| 890 _flushLibraryProperties(body); | 990 _flushLibraryProperties(body); |
| 891 | 991 |
| 892 var name = node.name.name; | 992 var name = node.name.name; |
| 893 body.add(js.comment('Function $name: ${node.element.type}')); | 993 body.add(js.comment('Function $name: ${node.element.type}')); |
| 894 | 994 |
| 895 body.add(new JS.FunctionDeclaration( | 995 body.add(new JS.FunctionDeclaration( |
| 896 new JS.Identifier(name), _visit(node.functionExpression))); | 996 new JS.Identifier(name), _visit(node.functionExpression))); |
| 897 | 997 |
| 898 if (isPublic(name)) _exports.add(name); | 998 if (isPublic(name)) _addExport(name); |
| 899 return _statement(body); | 999 return _statement(body); |
| 900 } | 1000 } |
| 901 | 1001 |
| 902 JS.Method _emitTopLevelProperty(FunctionDeclaration node) { | 1002 JS.Method _emitTopLevelProperty(FunctionDeclaration node) { |
| 903 var name = node.name.name; | 1003 var name = node.name.name; |
| 904 return new JS.Method(_propertyName(name), _visit(node.functionExpression), | 1004 return new JS.Method(_propertyName(name), _visit(node.functionExpression), |
| 905 isGetter: node.isGetter, isSetter: node.isSetter); | 1005 isGetter: node.isGetter, isSetter: node.isSetter); |
| 906 } | 1006 } |
| 907 | 1007 |
| 908 @override | 1008 @override |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 959 | 1059 |
| 960 // library member | 1060 // library member |
| 961 if (e.enclosingElement is CompilationUnitElement && | 1061 if (e.enclosingElement is CompilationUnitElement && |
| 962 (e.library != libraryInfo.library || | 1062 (e.library != libraryInfo.library || |
| 963 variable is TopLevelVariableElement && !variable.isConst)) { | 1063 variable is TopLevelVariableElement && !variable.isConst)) { |
| 964 return js.call('#.#', [_libraryName(e.library), name]); | 1064 return js.call('#.#', [_libraryName(e.library), name]); |
| 965 } | 1065 } |
| 966 | 1066 |
| 967 // instance member | 1067 // instance member |
| 968 if (currentClass != null && _needsImplicitThis(e)) { | 1068 if (currentClass != null && _needsImplicitThis(e)) { |
| 969 return js.call('this.#', _jsMemberName(name)); | 1069 return js.call('this.#', _emitMemberName(name)); |
| 970 } | 1070 } |
| 971 | 1071 |
| 972 // static member | 1072 // static member |
| 973 if (e is ExecutableElement && | 1073 if (e is ExecutableElement && |
| 974 e.isStatic && | 1074 e.isStatic && |
| 975 variable.enclosingElement is ClassElement) { | 1075 variable.enclosingElement is ClassElement) { |
| 976 var className = (variable.enclosingElement as ClassElement).name; | 1076 var className = (variable.enclosingElement as ClassElement).name; |
| 977 return js.call('#.#', [className, _jsMemberName(name, isStatic: true)]); | 1077 return js.call('#.#', [className, _emitMemberName(name, isStatic: true)]); |
| 978 } | 1078 } |
| 979 | 1079 |
| 980 // initializing formal parameter, e.g. `Point(this.x)` | 1080 // initializing formal parameter, e.g. `Point(this.x)` |
| 981 if (e is ParameterElement && e.isInitializingFormal && e.isPrivate) { | 1081 if (e is ParameterElement && e.isInitializingFormal && e.isPrivate) { |
| 982 /// Rename private names so they don't shadow the private field symbol. | 1082 /// Rename private names so they don't shadow the private field symbol. |
| 983 /// The renamer would handle this, but it would prefer to rename the | 1083 /// The renamer would handle this, but it would prefer to rename the |
| 984 /// temporary used for the private symbol. Instead rename the parameter. | 1084 /// temporary used for the private symbol. Instead rename the parameter. |
| 985 return new JSTemporary('${name.substring(1)}'); | 1085 return new JSTemporary('${name.substring(1)}'); |
| 986 } | 1086 } |
| 987 | 1087 |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1025 } | 1125 } |
| 1026 | 1126 |
| 1027 bool _needQualifiedName(Element element) { | 1127 bool _needQualifiedName(Element element) { |
| 1028 var lib = element.library; | 1128 var lib = element.library; |
| 1029 | 1129 |
| 1030 return lib != null && | 1130 return lib != null && |
| 1031 (lib != currentLibrary || | 1131 (lib != currentLibrary || |
| 1032 element is ClassElement && _lazyClass(element)); | 1132 element is ClassElement && _lazyClass(element)); |
| 1033 } | 1133 } |
| 1034 | 1134 |
| 1035 JS.Node _emitDPutIfDynamic( | 1135 JS.Node _emitDSetIfDynamic( |
| 1036 Expression target, SimpleIdentifier id, Expression rhs) { | 1136 Expression target, SimpleIdentifier id, Expression rhs) { |
| 1037 if (rules.isDynamicTarget(target)) { | 1137 if (rules.isDynamicTarget(target)) { |
| 1038 return js.call('dart.dput(#, #, #)', [ | 1138 return js.call('dart.dput(#, #, #)', [ |
| 1039 _visit(target), | 1139 _visit(target), |
| 1040 js.string(id.name, "'"), | 1140 js.string(id.name, "'"), |
| 1041 _visit(rhs) | 1141 _visit(rhs) |
| 1042 ]); | 1142 ]); |
| 1043 } else { | 1143 } else { |
| 1044 return null; | 1144 return null; |
| 1045 } | 1145 } |
| 1046 } | 1146 } |
| 1047 | 1147 |
| 1048 @override | 1148 @override |
| 1049 JS.Node visitAssignmentExpression(AssignmentExpression node) { | 1149 JS.Node visitAssignmentExpression(AssignmentExpression node) { |
| 1050 var lhs = node.leftHandSide; | 1150 var lhs = node.leftHandSide; |
| 1051 var rhs = node.rightHandSide; | 1151 var rhs = node.rightHandSide; |
| 1052 return _emitAssignment(lhs, rhs, node.parent); | 1152 return _emitSet(lhs, rhs, node.parent); |
| 1053 } | 1153 } |
| 1054 | 1154 |
| 1055 JS.Node _emitAssignment(Expression lhs, Expression rhs, [AstNode parent]) { | 1155 JS.Node _emitSet(Expression lhs, Expression rhs, [AstNode parent]) { |
| 1056 if (lhs is IndexExpression) { | 1156 if (lhs is IndexExpression) { |
| 1057 String code; | 1157 String code; |
| 1058 var target = _getTarget(lhs); | 1158 var target = _getTarget(lhs); |
| 1059 if (rules.isDynamicTarget(target)) { | 1159 if (rules.isDynamicTarget(target)) { |
| 1060 code = 'dart.dsetindex(#, #, #)'; | 1160 code = 'dart.dsetindex(#, #, #)'; |
| 1061 } else { | 1161 return js.call(code, [_visit(target), _visit(lhs.index), _visit(rhs)]); |
| 1062 code = '#.set(#, #)'; | |
| 1063 } | 1162 } |
| 1064 return js.call(code, [_visit(target), _visit(lhs.index), _visit(rhs)]); | 1163 return js.call('#.#(#, #)', [ |
| 1164 _visit(target), |
| 1165 _emitMemberName('[]=', target: target), |
| 1166 _visit(lhs.index), |
| 1167 _visit(rhs) |
| 1168 ]); |
| 1065 } | 1169 } |
| 1066 | 1170 |
| 1067 if (lhs is PropertyAccess) { | 1171 if (lhs is PropertyAccess) { |
| 1068 var result = _emitDPutIfDynamic(_getTarget(lhs), lhs.propertyName, rhs); | 1172 var result = _emitDSetIfDynamic(_getTarget(lhs), lhs.propertyName, rhs); |
| 1069 if (result != null) return result; | 1173 if (result != null) return result; |
| 1070 } else if (lhs is PrefixedIdentifier) { | 1174 } else if (lhs is PrefixedIdentifier) { |
| 1071 // TODO(vsm): Is this the right code if the prefix is a library? | 1175 // TODO(vsm): Is this the right code if the prefix is a library? |
| 1072 var result = _emitDPutIfDynamic(lhs.prefix, lhs.identifier, rhs); | 1176 var result = _emitDSetIfDynamic(lhs.prefix, lhs.identifier, rhs); |
| 1073 if (result != null) return result; | 1177 if (result != null) return result; |
| 1074 } | 1178 } |
| 1075 | 1179 |
| 1076 if (parent is ExpressionStatement && | 1180 if (parent is ExpressionStatement && |
| 1077 rhs is CascadeExpression && | 1181 rhs is CascadeExpression && |
| 1078 _isStateless(lhs, rhs)) { | 1182 _isStateless(lhs, rhs)) { |
| 1079 // Special case: cascade assignment to a variable in a statement. | 1183 // Special case: cascade assignment to a variable in a statement. |
| 1080 // We can reuse the variable to desugar it: | 1184 // We can reuse the variable to desugar it: |
| 1081 // result = []..length = length; | 1185 // result = []..length = length; |
| 1082 // becomes: | 1186 // becomes: |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1137 } else { | 1241 } else { |
| 1138 return js.call('dart.dinvokef(#, #)', [_visit(node.methodName), args]); | 1242 return js.call('dart.dinvokef(#, #)', [_visit(node.methodName), args]); |
| 1139 } | 1243 } |
| 1140 } | 1244 } |
| 1141 | 1245 |
| 1142 // TODO(jmesserly): if this resolves to a getter returning a function with | 1246 // TODO(jmesserly): if this resolves to a getter returning a function with |
| 1143 // a call method, we don't generate the `.call` correctly. | 1247 // a call method, we don't generate the `.call` correctly. |
| 1144 | 1248 |
| 1145 var targetJs; | 1249 var targetJs; |
| 1146 if (target != null) { | 1250 if (target != null) { |
| 1147 targetJs = js.call('#.#', [_visit(target), node.methodName.name]); | 1251 targetJs = js.call('#.#', [ |
| 1252 _visit(target), |
| 1253 _emitMemberName(node.methodName.name, target: target) |
| 1254 ]); |
| 1148 } else { | 1255 } else { |
| 1149 targetJs = _visit(node.methodName); | 1256 targetJs = _visit(node.methodName); |
| 1150 } | 1257 } |
| 1151 | 1258 |
| 1152 return js.call('#(#)', [targetJs, _visit(node.argumentList)]); | 1259 return js.call('#(#)', [targetJs, _visit(node.argumentList)]); |
| 1153 } | 1260 } |
| 1154 | 1261 |
| 1155 /// Emits code for the `JS(...)` builtin. | 1262 /// Emits code for the `JS(...)` builtin. |
| 1156 _emitForeignJS(MethodInvocation node) { | 1263 _emitForeignJS(MethodInvocation node) { |
| 1157 var e = node.methodName.staticElement; | 1264 var e = node.methodName.staticElement; |
| (...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1248 visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { | 1355 visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { |
| 1249 var body = <JS.Statement>[]; | 1356 var body = <JS.Statement>[]; |
| 1250 | 1357 |
| 1251 for (var field in node.variables.variables) { | 1358 for (var field in node.variables.variables) { |
| 1252 if (field.isConst) { | 1359 if (field.isConst) { |
| 1253 // constant fields don't change, so we can generate them as `let` | 1360 // constant fields don't change, so we can generate them as `let` |
| 1254 // but add them to the module's exports | 1361 // but add them to the module's exports |
| 1255 var name = field.name.name; | 1362 var name = field.name.name; |
| 1256 body.add(js.statement( | 1363 body.add(js.statement( |
| 1257 'let # = #;', [new JS.Identifier(name), _visitInitializer(field)])); | 1364 'let # = #;', [new JS.Identifier(name), _visitInitializer(field)])); |
| 1258 if (isPublic(name)) _exports.add(name); | 1365 if (isPublic(name)) _addExport(name); |
| 1259 } else if (_isFieldInitConstant(field)) { | 1366 } else if (_isFieldInitConstant(field)) { |
| 1260 body.add(js.statement( | 1367 body.add(js.statement( |
| 1261 '# = #;', [_visit(field.name), _visitInitializer(field)])); | 1368 '# = #;', [_visit(field.name), _visitInitializer(field)])); |
| 1262 } else { | 1369 } else { |
| 1263 _lazyFields.add(field); | 1370 _lazyFields.add(field); |
| 1264 } | 1371 } |
| 1265 } | 1372 } |
| 1266 | 1373 |
| 1267 return _statement(body); | 1374 return _statement(body); |
| 1268 } | 1375 } |
| 1269 | 1376 |
| 1377 _addExport(String name) { |
| 1378 if (!_exports.add(name)) throw 'Duplicate top level name found: $name'; |
| 1379 } |
| 1380 |
| 1270 @override | 1381 @override |
| 1271 visitVariableDeclarationList(VariableDeclarationList node) { | 1382 visitVariableDeclarationList(VariableDeclarationList node) { |
| 1272 var last = node.variables.last; | 1383 var last = node.variables.last; |
| 1273 var lastInitializer = last.initializer; | 1384 var lastInitializer = last.initializer; |
| 1274 | 1385 |
| 1275 List<JS.VariableInitialization> variables; | 1386 List<JS.VariableInitialization> variables; |
| 1276 if (lastInitializer is CascadeExpression && | 1387 if (lastInitializer is CascadeExpression && |
| 1277 node.parent is VariableDeclarationStatement) { | 1388 node.parent is VariableDeclarationStatement) { |
| 1278 // Special case: cascade as variable initializer | 1389 // Special case: cascade as variable initializer |
| 1279 // | 1390 // |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1354 @override | 1465 @override |
| 1355 JS.Statement visitVariableDeclarationStatement( | 1466 JS.Statement visitVariableDeclarationStatement( |
| 1356 VariableDeclarationStatement node) => | 1467 VariableDeclarationStatement node) => |
| 1357 _expressionStatement(_visit(node.variables)); | 1468 _expressionStatement(_visit(node.variables)); |
| 1358 | 1469 |
| 1359 @override | 1470 @override |
| 1360 visitConstructorName(ConstructorName node) { | 1471 visitConstructorName(ConstructorName node) { |
| 1361 var typeName = _visit(node.type); | 1472 var typeName = _visit(node.type); |
| 1362 if (node.name != null) { | 1473 if (node.name != null) { |
| 1363 return js.call( | 1474 return js.call( |
| 1364 '#.#', [typeName, _jsMemberName(node.name.name, isStatic: true)]); | 1475 '#.#', [typeName, _emitMemberName(node.name.name, isStatic: true)]); |
| 1365 } | 1476 } |
| 1366 return typeName; | 1477 return typeName; |
| 1367 } | 1478 } |
| 1368 | 1479 |
| 1369 @override | 1480 @override |
| 1370 visitInstanceCreationExpression(InstanceCreationExpression node) { | 1481 visitInstanceCreationExpression(InstanceCreationExpression node) { |
| 1371 return js.call( | 1482 return js.call( |
| 1372 'new #(#)', [_visit(node.constructorName), _visit(node.argumentList)]); | 1483 'new #(#)', [_visit(node.constructorName), _visit(node.argumentList)]); |
| 1373 } | 1484 } |
| 1374 | 1485 |
| (...skipping 177 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1552 JS.Expression _emitPostfixIncrement(Expression expr, Token op) { | 1663 JS.Expression _emitPostfixIncrement(Expression expr, Token op) { |
| 1553 var type = rules.getStaticType(expr); | 1664 var type = rules.getStaticType(expr); |
| 1554 assert(type != null); | 1665 assert(type != null); |
| 1555 var tmp = _createTemporary('x', type); | 1666 var tmp = _createTemporary('x', type); |
| 1556 | 1667 |
| 1557 // Increment and write | 1668 // Increment and write |
| 1558 var one = AstBuilder.integerLiteral(1); | 1669 var one = AstBuilder.integerLiteral(1); |
| 1559 one.staticType = rules.provider.intType; | 1670 one.staticType = rules.provider.intType; |
| 1560 var increment = AstBuilder.binaryExpression(tmp, op.lexeme[0], one); | 1671 var increment = AstBuilder.binaryExpression(tmp, op.lexeme[0], one); |
| 1561 increment.staticType = type; | 1672 increment.staticType = type; |
| 1562 var write = _emitAssignment(expr, increment); | 1673 var write = _emitSet(expr, increment); |
| 1563 | 1674 |
| 1564 var bindThis = _maybeBindThis(expr); | 1675 var bindThis = _maybeBindThis(expr); |
| 1565 return js.call("((#) => (#, #))$bindThis(#)", [ | 1676 return js.call("((#) => (#, #))$bindThis(#)", [ |
| 1566 _visit(tmp), | 1677 _visit(tmp), |
| 1567 write, | 1678 write, |
| 1568 _visit(tmp), | 1679 _visit(tmp), |
| 1569 _visit(expr) | 1680 _visit(expr) |
| 1570 ]); | 1681 ]); |
| 1571 } | 1682 } |
| 1572 | 1683 |
| (...skipping 15 matching lines...) Expand all Loading... |
| 1588 } | 1699 } |
| 1589 | 1700 |
| 1590 assert(op.lexeme == '++' || op.lexeme == '--'); | 1701 assert(op.lexeme == '++' || op.lexeme == '--'); |
| 1591 return _emitPostfixIncrement(expr, op); | 1702 return _emitPostfixIncrement(expr, op); |
| 1592 } | 1703 } |
| 1593 | 1704 |
| 1594 JS.Expression _emitPrefixIncrement(Token op, Expression expr) { | 1705 JS.Expression _emitPrefixIncrement(Token op, Expression expr) { |
| 1595 var one = AstBuilder.integerLiteral(1); | 1706 var one = AstBuilder.integerLiteral(1); |
| 1596 one.staticType = rules.provider.intType; | 1707 one.staticType = rules.provider.intType; |
| 1597 var increment = AstBuilder.binaryExpression(expr, op.lexeme[0], one); | 1708 var increment = AstBuilder.binaryExpression(expr, op.lexeme[0], one); |
| 1598 return _emitAssignment(expr, increment); | 1709 return _emitSet(expr, increment); |
| 1599 } | 1710 } |
| 1600 | 1711 |
| 1601 @override | 1712 @override |
| 1602 JS.Expression visitPrefixExpression(PrefixExpression node) { | 1713 JS.Expression visitPrefixExpression(PrefixExpression node) { |
| 1603 return _emitPrefixExpression(node.operator, node.operand); | 1714 return _emitPrefixExpression(node.operator, node.operand); |
| 1604 } | 1715 } |
| 1605 | 1716 |
| 1606 JS.Expression _emitPrefixExpression(Token op, Expression expr) { | 1717 JS.Expression _emitPrefixExpression(Token op, Expression expr) { |
| 1607 var dispatchType = rules.getStaticType(expr); | 1718 var dispatchType = rules.getStaticType(expr); |
| 1608 if (unaryOperationIsPrimitive(dispatchType)) { | 1719 if (unaryOperationIsPrimitive(dispatchType)) { |
| 1609 if (_isNonNullableExpression(expr)) { | 1720 if (_isNonNullableExpression(expr)) { |
| 1610 return js.call('$op#', _visit(expr)); | 1721 return js.call('$op#', _visit(expr)); |
| 1611 } else if (op.lexeme == '++' || op.lexeme == '--') { | 1722 } else if (op.lexeme == '++' || op.lexeme == '--') { |
| 1612 // We need a null check, so the increment must be expanded out. | 1723 // We need a null check, so the increment must be expanded out. |
| 1613 var mathop = op.lexeme[0]; | 1724 var mathop = op.lexeme[0]; |
| 1614 return js.call('# = # $mathop 1', [_visit(expr), notNull(expr)]); | 1725 return js.call('# = # $mathop 1', [_visit(expr), notNull(expr)]); |
| 1615 } else { | 1726 } else { |
| 1616 return js.call('$op#', notNull(expr)); | 1727 return js.call('$op#', notNull(expr)); |
| 1617 } | 1728 } |
| 1618 } else { | 1729 } else { |
| 1619 // Increment or decrement requires expansion. | 1730 // Increment or decrement requires expansion. |
| 1620 if (op.lexeme == '++' || op.lexeme == '--') { | 1731 if (op.lexeme == '++' || op.lexeme == '--') { |
| 1621 return _emitPrefixIncrement(op, expr); | 1732 return _emitPrefixIncrement(op, expr); |
| 1622 } | 1733 } |
| 1623 } | 1734 } |
| 1624 | 1735 |
| 1625 // Call the operator | 1736 // Call the operator |
| 1626 var opString = _jsMemberName(op.lexeme, unary: true); | 1737 var opString = _emitMemberName(op.lexeme, unary: true); |
| 1627 if (rules.isDynamicTarget(expr)) { | 1738 if (rules.isDynamicTarget(expr)) { |
| 1628 // dynamic dispatch | 1739 // dynamic dispatch |
| 1629 return js.call('dart.dunary(#, #)', [opString, _visit(expr)]); | 1740 return js.call('dart.dunary(#, #)', [opString, _visit(expr)]); |
| 1630 } else if (_isJSBuiltinType(dispatchType)) { | 1741 } else if (_isJSBuiltinType(dispatchType)) { |
| 1631 return js.call( | 1742 return js.call( |
| 1632 '#.#(#)', [_emitTypeName(dispatchType), opString, _visit(expr)]); | 1743 '#.#(#)', [_emitTypeName(dispatchType), opString, _visit(expr)]); |
| 1633 } else { | 1744 } else { |
| 1634 // Generic static-dispatch, user-defined operator code path. | 1745 // Generic static-dispatch, user-defined operator code path. |
| 1635 return js.call('#.#()', [_visit(expr), opString]); | 1746 return js.call('#.#()', [_visit(expr), opString]); |
| 1636 } | 1747 } |
| (...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1716 JS.This visitThisExpression(ThisExpression node) => new JS.This(); | 1827 JS.This visitThisExpression(ThisExpression node) => new JS.This(); |
| 1717 | 1828 |
| 1718 @override | 1829 @override |
| 1719 JS.Super visitSuperExpression(SuperExpression node) => new JS.Super(); | 1830 JS.Super visitSuperExpression(SuperExpression node) => new JS.Super(); |
| 1720 | 1831 |
| 1721 @override | 1832 @override |
| 1722 visitPrefixedIdentifier(PrefixedIdentifier node) { | 1833 visitPrefixedIdentifier(PrefixedIdentifier node) { |
| 1723 if (node.prefix.staticElement is PrefixElement) { | 1834 if (node.prefix.staticElement is PrefixElement) { |
| 1724 return _visit(node.identifier); | 1835 return _visit(node.identifier); |
| 1725 } else { | 1836 } else { |
| 1726 return _visitGet(node.prefix, node.identifier); | 1837 return _emitGet(node.prefix, node.identifier); |
| 1727 } | 1838 } |
| 1728 } | 1839 } |
| 1729 | 1840 |
| 1730 @override | 1841 @override |
| 1731 visitPropertyAccess(PropertyAccess node) => | 1842 visitPropertyAccess(PropertyAccess node) => |
| 1732 _visitGet(_getTarget(node), node.propertyName); | 1843 _emitGet(_getTarget(node), node.propertyName); |
| 1733 | 1844 |
| 1734 /// Shared code for [PrefixedIdentifier] and [PropertyAccess]. | 1845 /// Shared code for [PrefixedIdentifier] and [PropertyAccess]. |
| 1735 _visitGet(Expression target, SimpleIdentifier name) { | 1846 _emitGet(Expression target, SimpleIdentifier name) { |
| 1736 if (rules.isDynamicTarget(target)) { | 1847 if (rules.isDynamicTarget(target)) { |
| 1737 return js.call( | 1848 return js.call( |
| 1738 'dart.dload(#, #)', [_visit(target), js.string(name.name, "'")]); | 1849 'dart.dload(#, #)', [_visit(target), js.string(name.name, "'")]); |
| 1739 } else { | 1850 } else { |
| 1740 var e = name.staticElement; | 1851 var e = name.staticElement; |
| 1741 return js.call('#.#', [ | 1852 var ret = js.call('#.#', [ |
| 1742 _visit(target), | 1853 _visit(target), |
| 1743 _jsMemberName(name.name, isStatic: e is ExecutableElement && e.isStatic) | 1854 _emitMemberName(name.name, |
| 1855 isStatic: e is ExecutableElement && e.isStatic, target: target) |
| 1744 ]); | 1856 ]); |
| 1857 return ret; |
| 1745 } | 1858 } |
| 1746 } | 1859 } |
| 1747 | 1860 |
| 1748 @override | 1861 @override |
| 1749 visitIndexExpression(IndexExpression node) { | 1862 visitIndexExpression(IndexExpression node) { |
| 1750 var target = _getTarget(node); | 1863 var target = _getTarget(node); |
| 1751 var code; | |
| 1752 if (rules.isDynamicTarget(target)) { | 1864 if (rules.isDynamicTarget(target)) { |
| 1753 code = 'dart.dindex(#, #)'; | 1865 return js.call('dart.dindex(#, #)', [_visit(target), _visit(node.index)]); |
| 1754 } else { | |
| 1755 code = '#.get(#)'; | |
| 1756 } | 1866 } |
| 1757 return js.call(code, [_visit(target), _visit(node.index)]); | 1867 |
| 1868 return js.call('#.#(#)', [ |
| 1869 _visit(target), |
| 1870 _emitMemberName('[]', target: target), |
| 1871 _visit(node.index) |
| 1872 ]); |
| 1758 } | 1873 } |
| 1759 | 1874 |
| 1760 /// Gets the target of a [PropertyAccess] or [IndexExpression]. | 1875 /// Gets the target of a [PropertyAccess] or [IndexExpression]. |
| 1761 /// Those two nodes are special because they're both allowed on left side of | 1876 /// Those two nodes are special because they're both allowed on left side of |
| 1762 /// an assignment expression and cascades. | 1877 /// an assignment expression and cascades. |
| 1763 Expression _getTarget(node) { | 1878 Expression _getTarget(node) { |
| 1764 assert(node is IndexExpression || node is PropertyAccess); | 1879 assert(node is IndexExpression || node is PropertyAccess); |
| 1765 return node.isCascaded ? _cascadeTarget : node.target; | 1880 return node.isCascaded ? _cascadeTarget : node.target; |
| 1766 } | 1881 } |
| 1767 | 1882 |
| (...skipping 365 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2133 /// | 2248 /// |
| 2134 /// This follows the same pattern as EcmaScript 6 Map: | 2249 /// This follows the same pattern as EcmaScript 6 Map: |
| 2135 /// <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_
Objects/Map> | 2250 /// <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_
Objects/Map> |
| 2136 /// | 2251 /// |
| 2137 /// Unary minus looks like: `x['unary-']()`. Note that [unary] must be passed | 2252 /// Unary minus looks like: `x['unary-']()`. Note that [unary] must be passed |
| 2138 /// for this transformation to happen, otherwise binary minus is assumed. | 2253 /// for this transformation to happen, otherwise binary minus is assumed. |
| 2139 /// | 2254 /// |
| 2140 /// Equality is a bit special, it is generated via the Dart `equals` runtime | 2255 /// Equality is a bit special, it is generated via the Dart `equals` runtime |
| 2141 /// helper, that checks for null. The user defined method is called '=='. | 2256 /// helper, that checks for null. The user defined method is called '=='. |
| 2142 /// | 2257 /// |
| 2143 JS.Expression _jsMemberName(String name, | 2258 JS.Expression _emitMemberName(String name, |
| 2144 {bool unary: false, bool isStatic: false}) { | 2259 {bool unary: false, bool isStatic: false, Expression target}) { |
| 2260 if (isStatic == false && target != null) { |
| 2261 var ret = nameIfExtension(target, name); |
| 2262 if (ret != null) return ret; |
| 2263 } |
| 2145 if (name.startsWith('_')) { | 2264 if (name.startsWith('_')) { |
| 2146 if (_privateNames.add(name)) _pendingPrivateNames.add(name); | 2265 if (_privateNames.add(name)) _pendingPrivateNames.add(name); |
| 2147 return new JSTemporary(name); | 2266 return new JSTemporary(name); |
| 2148 } | 2267 } |
| 2149 if (name == '[]') { | 2268 return _propertyName(_jsMemberName(name, unary: unary, isStatic: isStatic)); |
| 2150 name = 'get'; | 2269 } |
| 2151 } else if (name == '[]=') { | 2270 |
| 2152 name = 'set'; | 2271 String _jsMemberName(String name, {bool unary: false, bool isStatic: false}) { |
| 2153 } else if (unary && name == '-') { | 2272 if (name == '[]') return 'get'; |
| 2154 name = 'unary-'; | 2273 if (name == '[]=') return 'set'; |
| 2155 } else if (isStatic && invalidJSStaticMethodName(name)) { | 2274 if (unary && name == '-') return 'unary-'; |
| 2275 if (isStatic && invalidJSStaticMethodName(name)) { |
| 2156 // Choose an string name. Use an invalid identifier so it won't conflict | 2276 // Choose an string name. Use an invalid identifier so it won't conflict |
| 2157 // with any valid member names. | 2277 // with any valid member names. |
| 2158 // TODO(jmesserly): this works around the problem, but I'm pretty sure we | 2278 // TODO(jmesserly): this works around the problem, but I'm pretty sure we |
| 2159 // don't need it, as static methods seemed to work. The only concrete | 2279 // don't need it, as static methods seemed to work. The only concrete |
| 2160 // issue we saw was in the defineNamedConstructor helper function. | 2280 // issue we saw was in the defineNamedConstructor helper function. |
| 2161 name = '$name*'; | 2281 return '$name*'; |
| 2162 } | 2282 } |
| 2163 return _propertyName(name); | 2283 return name; |
| 2164 } | 2284 } |
| 2165 | 2285 |
| 2286 JS.LiteralString _emitExtensionMethodName(String name) => |
| 2287 js.string(_extensionMethodName(name), "'"); |
| 2288 |
| 2289 String _extensionMethodName(String name) => '\$${_jsMemberName(name)}'; |
| 2290 |
| 2166 bool _externalOrNative(node) => | 2291 bool _externalOrNative(node) => |
| 2167 node.externalKeyword != null || _functionBody(node) is NativeFunctionBody; | 2292 node.externalKeyword != null || _functionBody(node) is NativeFunctionBody; |
| 2168 | 2293 |
| 2169 FunctionBody _functionBody(node) => | 2294 FunctionBody _functionBody(node) => |
| 2170 node is FunctionDeclaration ? node.functionExpression.body : node.body; | 2295 node is FunctionDeclaration ? node.functionExpression.body : node.body; |
| 2171 | 2296 |
| 2172 /// Choose a canonical name from the library element. | 2297 /// Choose a canonical name from the library element. |
| 2173 /// This never uses the library's name (the identifier in the `library` | 2298 /// This never uses the library's name (the identifier in the `library` |
| 2174 /// declaration) as it doesn't have any meaningful rules enforced. | 2299 /// declaration) as it doesn't have any meaningful rules enforced. |
| 2175 JS.Identifier _libraryName(LibraryElement library) { | 2300 JS.Identifier _libraryName(LibraryElement library) { |
| (...skipping 208 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2384 | 2509 |
| 2385 // TODO(jmesserly): in many cases marking the end will be unncessary. | 2510 // TODO(jmesserly): in many cases marking the end will be unncessary. |
| 2386 printer.mark(_location(node.end)); | 2511 printer.mark(_location(node.end)); |
| 2387 } | 2512 } |
| 2388 | 2513 |
| 2389 String _getIdentifier(AstNode node) { | 2514 String _getIdentifier(AstNode node) { |
| 2390 if (node is SimpleIdentifier) return node.name; | 2515 if (node is SimpleIdentifier) return node.name; |
| 2391 return null; | 2516 return null; |
| 2392 } | 2517 } |
| 2393 } | 2518 } |
| OLD | NEW |