Chromium Code Reviews| 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 | |
| 50 | |
| 45 class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { | 51 class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
| 46 final LibraryInfo libraryInfo; | 52 final LibraryInfo libraryInfo; |
| 47 final TypeRules rules; | 53 final TypeRules rules; |
| 48 | 54 |
| 49 // TODO(jmesserly): this is needed because RestrictedTypeRules can send | 55 // TODO(jmesserly): this is needed because RestrictedTypeRules can send |
| 50 // messages to CheckerReporter, for things like missing types. | 56 // messages to CheckerReporter, for things like missing types. |
| 51 // We should probably refactor so this can't happen, as codegen would be too | 57 // We should probably refactor so this can't happen, as codegen would be too |
| 52 // late to be issuing these messages. | 58 // late to be issuing these messages. |
| 53 final CheckerReporter _checkerReporter; | 59 final CheckerReporter _checkerReporter; |
| 54 | 60 |
| 55 /// The variable for the target of the current `..` cascade expression. | 61 /// The variable for the target of the current `..` cascade expression. |
| 56 SimpleIdentifier _cascadeTarget; | 62 SimpleIdentifier _cascadeTarget; |
| 57 /// The variable for the current catch clause | 63 /// The variable for the current catch clause |
| 58 SimpleIdentifier _catchParameter; | 64 SimpleIdentifier _catchParameter; |
| 59 | 65 |
| 60 ClassDeclaration currentClass; | 66 ClassDeclaration currentClass; |
| 61 ConstantEvaluator _constEvaluator; | 67 ConstantEvaluator _constEvaluator; |
| 62 | 68 |
| 63 final _exports = <String>[]; | 69 final _exports = <String>[]; |
| 64 final _lazyFields = <VariableDeclaration>[]; | 70 final _lazyFields = <VariableDeclaration>[]; |
| 65 final _properties = <FunctionDeclaration>[]; | 71 final _properties = <FunctionDeclaration>[]; |
| 66 final _privateNames = new HashSet<String>(); | 72 final _privateNames = new HashSet<String>(); |
| 67 final _pendingPrivateNames = <String>[]; | 73 final _pendingPrivateNames = <String>[]; |
| 74 final _extensionMethodNames = new HashSet<String>(); | |
| 75 final _pendingExtensionMethodNames = <String>[]; | |
| 76 | |
| 77 // TODO(jacobr): determine the the set of types with extension methods from | |
| 78 // the annotations rather than hard coding the list once the analyzer | |
| 79 // supports summaries. | |
| 80 List<InterfaceType> get _JsExtensionMethodTypes => | |
|
Jennifer Messerly
2015/04/03 16:25:28
nit: should be lower case _jsExtension...
Jacob
2015/04/03 20:25:58
Done.
| |
| 81 <InterfaceType>[rules.provider.listType, rules.provider.iterableType]; | |
| 68 | 82 |
| 69 /// Classes we have not emitted yet. Values can be [ClassDeclaration] or | 83 /// Classes we have not emitted yet. Values can be [ClassDeclaration] or |
| 70 /// [ClassTypeAlias]. | 84 /// [ClassTypeAlias]. |
| 71 final _pendingClasses = new HashMap<ClassElement, CompilationUnitMember>(); | 85 final _pendingClasses = new HashMap<ClassElement, CompilationUnitMember>(); |
| 72 | 86 |
| 73 /// Memoized results of [_lazyClass]. | 87 /// Memoized results of [_lazyClass]. |
| 74 final _lazyClassMemo = new HashMap<ClassElement, bool>(); | 88 final _lazyClassMemo = new HashMap<ClassElement, bool>(); |
| 75 | 89 |
| 76 /// Memoized results of [_inLibraryCycle]. | 90 /// Memoized results of [_inLibraryCycle]. |
| 77 final _libraryCycleMemo = new HashMap<LibraryElement, bool>(); | 91 final _libraryCycleMemo = new HashMap<LibraryElement, bool>(); |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 136 name, | 150 name, |
| 137 name, | 151 name, |
| 138 defaultValue | 152 defaultValue |
| 139 ]) | 153 ]) |
| 140 ]); | 154 ]); |
| 141 } | 155 } |
| 142 | 156 |
| 143 JS.Statement _initPrivateSymbol(String name) => js.statement( | 157 JS.Statement _initPrivateSymbol(String name) => js.statement( |
| 144 'let # = $_SYMBOL(#);', [new JSTemporary(name), js.string(name, "'")]); | 158 'let # = $_SYMBOL(#);', [new JSTemporary(name), js.string(name, "'")]); |
| 145 | 159 |
| 160 JS.Statement _initExtensionMethodSymbol(String name) => js.statement( | |
| 161 'let # = $_SYMBOL(#);', [new JS.Identifier(name, allowRename: false), js.s tring(name, "'")]); | |
|
Jennifer Messerly
2015/04/03 16:25:28
hmm, making this allowRename: false is a bit probl
Jacob
2015/04/03 20:25:58
Talked offline. Added checks when we try to add th
| |
| 162 | |
| 146 // TODO(jmesserly): this is a temporary workaround for `Symbol` in core, | 163 // TODO(jmesserly): this is a temporary workaround for `Symbol` in core, |
| 147 // until we have better name tracking. | 164 // until we have better name tracking. |
| 148 String get _SYMBOL { | 165 String get _SYMBOL { |
| 149 var name = currentLibrary.name; | 166 var name = currentLibrary.name; |
| 150 if (name == 'dart.core' || name == 'dart._internal') return 'dart.JsSymbol'; | 167 if (name == 'dart.core' || name == 'dart._internal') return 'dart.JsSymbol'; |
| 151 return 'Symbol'; | 168 return 'Symbol'; |
| 152 } | 169 } |
| 153 | 170 |
| 154 @override | 171 @override |
| 155 JS.Statement visitCompilationUnit(CompilationUnit node) { | 172 JS.Statement visitCompilationUnit(CompilationUnit node) { |
| 156 var source = node.element.source; | 173 var source = node.element.source; |
| 157 | 174 |
| 158 _constEvaluator = new ConstantEvaluator(source, rules.provider); | 175 _constEvaluator = new ConstantEvaluator(source, rules.provider); |
| 159 _checkerReporter.enterSource(source); | 176 _checkerReporter.enterSource(source); |
| 160 | 177 |
| 161 // TODO(jmesserly): scriptTag, directives. | 178 // TODO(jmesserly): scriptTag, directives. |
| 162 var body = <JS.Statement>[]; | 179 var body = <JS.Statement>[]; |
| 163 for (var child in node.declarations) { | 180 for (var child in node.declarations) { |
| 164 // Attempt to group adjacent fields/properties. | 181 // Attempt to group adjacent fields/properties. |
| 165 if (child is! TopLevelVariableDeclaration) _flushLazyFields(body); | 182 if (child is! TopLevelVariableDeclaration) _flushLazyFields(body); |
| 166 if (child is! FunctionDeclaration) _flushLibraryProperties(body); | 183 if (child is! FunctionDeclaration) _flushLibraryProperties(body); |
| 167 | 184 |
| 168 var code = _visit(child); | 185 var code = _visit(child); |
| 169 | 186 |
| 170 if (code != null) { | 187 if (code != null) { |
| 171 if (_pendingPrivateNames.isNotEmpty) { | 188 if (_pendingPrivateNames.isNotEmpty) { |
| 172 body.addAll(_pendingPrivateNames.map(_initPrivateSymbol)); | 189 body.addAll(_pendingPrivateNames.map(_initPrivateSymbol)); |
| 173 _pendingPrivateNames.clear(); | 190 _pendingPrivateNames.clear(); |
| 174 } | 191 } |
| 192 if (_pendingExtensionMethodNames.isNotEmpty) { | |
| 193 body.addAll(_pendingExtensionMethodNames.map(_initExtensionMethodSymbo l)); | |
|
Jennifer Messerly
2015/04/03 16:25:28
run formatter?
pub run dart_style:format -w lib/s
Jacob
2015/04/03 20:25:58
Done.
| |
| 194 _pendingExtensionMethodNames.clear(); | |
| 195 } | |
| 175 body.add(code); | 196 body.add(code); |
| 176 } | 197 } |
| 177 } | 198 } |
| 178 | 199 |
| 179 // Flush any unwritten fields/properties. | 200 // Flush any unwritten fields/properties. |
| 180 _flushLazyFields(body); | 201 _flushLazyFields(body); |
| 181 _flushLibraryProperties(body); | 202 _flushLibraryProperties(body); |
| 182 | 203 |
| 183 _checkerReporter.leaveSource(); | 204 _checkerReporter.leaveSource(); |
| 184 | 205 |
| (...skipping 312 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 497 heritage = _emitTypeName(rules.provider.objectType); | 518 heritage = _emitTypeName(rules.provider.objectType); |
| 498 } | 519 } |
| 499 if (node.withClause != null) { | 520 if (node.withClause != null) { |
| 500 var mixins = _visitList(node.withClause.mixinTypes); | 521 var mixins = _visitList(node.withClause.mixinTypes); |
| 501 mixins.insert(0, heritage); | 522 mixins.insert(0, heritage); |
| 502 heritage = js.call('dart.mixin(#)', [mixins]); | 523 heritage = js.call('dart.mixin(#)', [mixins]); |
| 503 } | 524 } |
| 504 return heritage; | 525 return heritage; |
| 505 } | 526 } |
| 506 | 527 |
| 528 // TODO(jacobr): why doesn't the generic i1.isSubTypeOf(i2) work? | |
|
Jennifer Messerly
2015/04/03 16:25:28
did you try rules.isSubTypeOf(i1, i2) ? that will
Jacob
2015/04/03 20:25:59
That doesn't work. The output changes dramatically
Jennifer Messerly
2015/04/03 21:08:05
Doh! Well it'd be good to figure out. Something se
vsm
2015/04/03 21:15:35
It'd be good to use the rules.isSubTypeOf. I susp
| |
| 529 bool _isInterfaceSubTypeOf(InterfaceType i1, InterfaceType i2) { | |
| 530 if (i1 == i2) return true; | |
| 531 | |
| 532 if (i1.element == i2.element) { | |
| 533 return true; | |
| 534 } | |
| 535 | |
| 536 if (i2.isDartCoreFunction) { | |
| 537 if (i1.element.getMethod("call") != null) return true; | |
| 538 } | |
| 539 | |
| 540 if (i1 == rules.provider.objectType) return false; | |
| 541 | |
| 542 if (_isInterfaceSubTypeOf(i1.superclass, i2)) return true; | |
| 543 | |
| 544 for (final parent in i1.interfaces) { | |
| 545 if (_isInterfaceSubTypeOf(parent, i2)) return true; | |
| 546 } | |
| 547 | |
| 548 for (final parent in i1.mixins) { | |
| 549 if (_isInterfaceSubTypeOf(parent, i2)) return true; | |
| 550 } | |
| 551 | |
| 552 return false; | |
| 553 } | |
| 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 List<InterfaceType> getMatchingExtensionMethodTypes(InterfaceType type) { | |
|
Jennifer Messerly
2015/04/03 16:25:28
is this method just:
_JsExtensionMethodTypes.w
Jacob
2015/04/03 20:25:58
done
| |
| 558 var extensionTypes = <InterfaceType>[]; | |
| 559 for (var extensionType in _JsExtensionMethodTypes) { | |
| 560 if (_isInterfaceSubTypeOf(type, extensionType)) { | |
| 561 extensionTypes.add(extensionType); | |
| 562 } | |
| 563 } | |
| 564 return extensionTypes; | |
| 565 } | |
| 566 | |
| 567 LibraryElement lookupExtensionLibrary(Iterable<InterfaceType> extensionTypes, String name, {bool isGetter: false, bool isSetter: false}) { | |
|
Jennifer Messerly
2015/04/03 16:25:28
i'd probably use "get" instead of "lookup" for con
Jacob
2015/04/03 20:25:58
Done.
| |
| 568 var extensionLibrary; | |
| 569 assert (!isGetter || !isSetter); | |
| 570 for (var extensionType in extensionTypes) { | |
|
Jennifer Messerly
2015/04/03 16:25:28
I'd probably just name this "type" and "extensionL
Jacob
2015/04/03 20:25:58
Done.
| |
| 571 var match; | |
| 572 if (isGetter) { | |
| 573 match = extensionType.getGetter(name); | |
|
Jennifer Messerly
2015/04/03 16:25:28
I was looking at the package:analyzer implementati
Jacob
2015/04/03 20:25:58
Done.
| |
| 574 } else if (isSetter) { | |
| 575 match = extensionType.getSetter(name); | |
| 576 } else { | |
| 577 match = extensionType.getMethod(name); | |
| 578 } | |
| 579 // It is possible that the same method could need to be an extension | |
| 580 // method for classes defined in multiple libraries. Instead of handling | |
| 581 // that case we currently assert. The correct behavior is unclear so we | |
| 582 // might need to prevent this with a compile time error. | |
| 583 if (match != null) { | |
| 584 assert(extensionLibrary == null || extensionLibrary == extensionType.ele ment.library); | |
| 585 extensionLibrary = extensionType.element.library; | |
| 586 } | |
| 587 } | |
| 588 return extensionLibrary; | |
| 589 } | |
| 590 | |
| 591 JS.Expression nameIfExtension(DartType targetType, String name, {bool isGetter : false, bool isSetter: false}) { | |
|
Jennifer Messerly
2015/04/03 16:25:28
everywhere we call this we do rules.getStaticType(
Jacob
2015/04/03 20:25:59
Done.
| |
| 592 if (targetType is! InterfaceType) return null; | |
| 593 var extensionLibrary = lookupExtensionLibrary(getMatchingExtensionMethodType s(targetType), name, isGetter: isGetter, isSetter: isSetter); | |
| 594 if (extensionLibrary == null) return null; | |
|
Jennifer Messerly
2015/04/03 16:25:28
from the places we call this, we end up with code
Jacob
2015/04/03 20:25:59
There are a lot of places we call emitMemberName (
| |
| 595 return js.call('#.#', [_libraryName(extensionLibrary), _extensionMethodName( name)]); | |
| 596 } | |
| 597 | |
| 509 List<JS.Method> _emitClassMethods(ClassDeclaration node, | 598 List<JS.Method> _emitClassMethods(ClassDeclaration node, |
| 510 List<ConstructorDeclaration> ctors, List<FieldDeclaration> fields) { | 599 List<ConstructorDeclaration> ctors, List<FieldDeclaration> fields) { |
| 511 var element = node.element; | 600 var element = node.element; |
| 512 var isObject = element.type.isObject; | 601 var isObject = element.type.isObject; |
| 513 var name = node.name.name; | 602 var name = node.name.name; |
| 514 | 603 |
| 515 var jsMethods = <JS.Method>[]; | 604 var jsMethods = <JS.Method>[]; |
| 516 // Iff no constructor is specified for a class C, it implicitly has a | 605 // Iff no constructor is specified for a class C, it implicitly has a |
| 517 // default constructor `C() : super() {}`, unless C is class Object. | 606 // default constructor `C() : super() {}`, unless C is class Object. |
| 518 if (ctors.isEmpty && !isObject) { | 607 if (ctors.isEmpty && !isObject) { |
| 519 jsMethods.add(_emitImplicitConstructor(node, name, fields)); | 608 jsMethods.add(_emitImplicitConstructor(node, name, fields)); |
| 520 } | 609 } |
| 521 | 610 var extensionTypes = getMatchingExtensionMethodTypes(element.type); |
| 522 for (var member in node.members) { | 611 for (var member in node.members) { |
| 523 if (member is ConstructorDeclaration) { | 612 if (member is ConstructorDeclaration) { |
| 524 jsMethods.add(_emitConstructor(member, name, fields, isObject)); | 613 jsMethods.add(_emitConstructor(member, name, fields, isObject)); |
| 525 } else if (member is MethodDeclaration) { | 614 } else if (member is MethodDeclaration) { |
| 526 jsMethods.add(_visit(member)); | 615 jsMethods.add(_visitMethodDeclaration(member, extensionTypes)); |
|
Jennifer Messerly
2015/04/03 16:25:28
one thing to watch out for: _visit will associate
Jacob
2015/04/03 20:25:59
good to know
| |
| 527 } | 616 } |
| 528 } | 617 } |
| 529 | 618 |
| 530 // Support for adapting dart:core Iterator/Iterable to ES6 versions. | 619 // Support for adapting dart:core Iterator/Iterable to ES6 versions. |
| 531 // This lets them use for-of loops transparently. | 620 // This lets them use for-of loops transparently. |
| 532 // https://github.com/lukehoban/es6features#iterators--forof | 621 // https://github.com/lukehoban/es6features#iterators--forof |
| 533 if (element.library.isDartCore && element.name == 'Iterable') { | 622 if (element.library.isDartCore && element.name == 'Iterable') { |
| 534 JS.Fun body = js.call('''function() { | 623 JS.Fun body = js.call('''function() { |
| 535 var iterator = this.iterator; | 624 var iterator = this.iterator; |
| 536 return { | 625 return { |
| (...skipping 314 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 851 } | 940 } |
| 852 | 941 |
| 853 JS.Expression _defaultParamValue(FormalParameter param) { | 942 JS.Expression _defaultParamValue(FormalParameter param) { |
| 854 if (param is DefaultFormalParameter && param.defaultValue != null) { | 943 if (param is DefaultFormalParameter && param.defaultValue != null) { |
| 855 return _visit(param.defaultValue); | 944 return _visit(param.defaultValue); |
| 856 } else { | 945 } else { |
| 857 return new JS.LiteralNull(); | 946 return new JS.LiteralNull(); |
| 858 } | 947 } |
| 859 } | 948 } |
| 860 | 949 |
| 861 @override | 950 JS.Method _visitMethodDeclaration(MethodDeclaration node, List<InterfaceType> extensionTypes) { |
|
Jennifer Messerly
2015/04/03 16:25:28
_emitMethodDeclaration? At some point we sort of s
Jacob
2015/04/03 20:25:58
_might _as _well _remove _the _underscores as a _f
Jennifer Messerly
2015/04/03 21:08:05
haha :)
| |
| 862 JS.Method visitMethodDeclaration(MethodDeclaration node) { | |
| 863 if (node.isAbstract || _externalOrNative(node)) { | 951 if (node.isAbstract || _externalOrNative(node)) { |
| 864 return null; | 952 return null; |
| 865 } | 953 } |
| 866 | 954 |
| 867 var params = _visit(node.parameters); | 955 var params = _visit(node.parameters); |
| 868 if (params == null) params = []; | 956 if (params == null) params = []; |
| 869 | 957 |
| 870 return new JS.Method(_jsMemberName(node.name.name, isStatic: node.isStatic), | 958 var extensionLibrary; |
| 959 var memberName; | |
| 960 if (node.isStatic == false && | |
|
Jennifer Messerly
2015/04/03 16:25:28
!node.isStatic
Jacob
2015/04/03 20:25:58
Done.
| |
| 961 (extensionLibrary = lookupExtensionLibrary(extensionTypes, node.name.name, i sGetter: node.isGetter, isSetter: node.isSetter)) != null) { | |
|
Jennifer Messerly
2015/04/03 16:25:28
style wise, I try to avoid assignment expression i
Jacob
2015/04/03 20:25:59
Done.
| |
| 962 var extensionMethodName = _extensionMethodNameRaw(node.name.name); | |
| 963 if (extensionLibrary == libraryInfo.library.library) { | |
| 964 // TODO(jacobr): need to do a better job ensuring that extension method | |
| 965 // name symbols do not conflict with other symbols before we can let | |
| 966 // user defined libraries define extension methods. | |
| 967 if (_extensionMethodNames.add(extensionMethodName)) { | |
| 968 _pendingExtensionMethodNames.add(extensionMethodName); | |
| 969 _exports.add(extensionMethodName); | |
| 970 } | |
| 971 } | |
| 972 memberName= js.call('#.#', [_libraryName(extensionLibrary), js.string(exte nsionMethodName, "'")]); | |
| 973 } else { | |
| 974 memberName = _jsMemberName(node.name.name, isStatic: node.isStatic); | |
| 975 } | |
| 976 return new JS.Method(memberName, | |
| 871 new JS.Fun(params, _visit(node.body)), | 977 new JS.Fun(params, _visit(node.body)), |
| 872 isGetter: node.isGetter, | 978 isGetter: node.isGetter, |
| 873 isSetter: node.isSetter, | 979 isSetter: node.isSetter, |
| 874 isStatic: node.isStatic); | 980 isStatic: node.isStatic); |
| 875 } | 981 } |
| 876 | 982 |
| 877 @override | 983 @override |
| 878 JS.Statement visitFunctionDeclaration(FunctionDeclaration node) { | 984 JS.Statement visitFunctionDeclaration(FunctionDeclaration node) { |
| 879 assert(node.parent is CompilationUnit); | 985 assert(node.parent is CompilationUnit); |
| 880 | 986 |
| (...skipping 144 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1025 } | 1131 } |
| 1026 | 1132 |
| 1027 bool _needQualifiedName(Element element) { | 1133 bool _needQualifiedName(Element element) { |
| 1028 var lib = element.library; | 1134 var lib = element.library; |
| 1029 | 1135 |
| 1030 return lib != null && | 1136 return lib != null && |
| 1031 (lib != currentLibrary || | 1137 (lib != currentLibrary || |
| 1032 element is ClassElement && _lazyClass(element)); | 1138 element is ClassElement && _lazyClass(element)); |
| 1033 } | 1139 } |
| 1034 | 1140 |
| 1035 JS.Node _emitDPutIfDynamic( | 1141 JS.Node _emitDSetIfDynamic( |
| 1036 Expression target, SimpleIdentifier id, Expression rhs) { | 1142 Expression target, SimpleIdentifier id, Expression rhs) { |
| 1037 if (rules.isDynamicTarget(target)) { | 1143 if (rules.isDynamicTarget(target)) { |
| 1038 return js.call('dart.dput(#, #, #)', [ | 1144 return js.call('dart.dput(#, #, #)', [ |
| 1039 _visit(target), | 1145 _visit(target), |
| 1040 js.string(id.name, "'"), | 1146 js.string(id.name, "'"), |
| 1041 _visit(rhs) | 1147 _visit(rhs) |
| 1042 ]); | 1148 ]); |
| 1043 } else { | 1149 } else { |
| 1044 return null; | 1150 return null; |
| 1045 } | 1151 } |
| 1046 } | 1152 } |
| 1047 | 1153 |
| 1048 @override | 1154 @override |
| 1049 JS.Node visitAssignmentExpression(AssignmentExpression node) { | 1155 JS.Node visitAssignmentExpression(AssignmentExpression node) { |
| 1050 var lhs = node.leftHandSide; | 1156 var lhs = node.leftHandSide; |
| 1051 var rhs = node.rightHandSide; | 1157 var rhs = node.rightHandSide; |
| 1052 return _emitAssignment(lhs, rhs, node.parent); | 1158 return _emitSet(lhs, rhs, node.parent); |
| 1053 } | 1159 } |
| 1054 | 1160 |
| 1055 JS.Node _emitAssignment(Expression lhs, Expression rhs, [AstNode parent]) { | 1161 JS.Node _emitSet(Expression lhs, Expression rhs, [AstNode parent]) { |
| 1056 if (lhs is IndexExpression) { | 1162 if (lhs is IndexExpression) { |
| 1057 String code; | 1163 String code; |
| 1058 var target = _getTarget(lhs); | 1164 var target = _getTarget(lhs); |
| 1059 if (rules.isDynamicTarget(target)) { | 1165 if (rules.isDynamicTarget(target)) { |
| 1060 code = 'dart.dsetindex(#, #, #)'; | 1166 code = 'dart.dsetindex(#, #, #)'; |
| 1061 } else { | 1167 return js.call(code, [_visit(target), _visit(lhs.index), _visit(rhs)]); |
| 1062 code = '#.set(#, #)'; | |
| 1063 } | 1168 } |
| 1064 return js.call(code, [_visit(target), _visit(lhs.index), _visit(rhs)]); | 1169 var methodName = nameIfExtension(rules.getStaticType(target), "[]="); |
| 1170 return js.call('#.#(#, #)', [_visit(target), methodName != null ? methodNa me: js.string('set'), _visit(lhs.index), _visit(rhs)]); | |
| 1065 } | 1171 } |
| 1066 | 1172 |
| 1067 if (lhs is PropertyAccess) { | 1173 if (lhs is PropertyAccess) { |
| 1068 var result = _emitDPutIfDynamic(_getTarget(lhs), lhs.propertyName, rhs); | 1174 var result = _emitDSetIfDynamic(_getTarget(lhs), lhs.propertyName, rhs); |
|
Jennifer Messerly
2015/04/03 16:25:28
ideas: _tryEmitDynamicSet? _maybeEmitDynamicSet? _
Jacob
2015/04/03 20:25:58
Done.
| |
| 1069 if (result != null) return result; | 1175 if (result != null) return result; |
| 1070 } else if (lhs is PrefixedIdentifier) { | 1176 } else if (lhs is PrefixedIdentifier) { |
| 1071 // TODO(vsm): Is this the right code if the prefix is a library? | 1177 // TODO(vsm): Is this the right code if the prefix is a library? |
| 1072 var result = _emitDPutIfDynamic(lhs.prefix, lhs.identifier, rhs); | 1178 var result = _emitDSetIfDynamic(lhs.prefix, lhs.identifier, rhs); |
| 1073 if (result != null) return result; | 1179 if (result != null) return result; |
| 1074 } | 1180 } |
| 1075 | 1181 |
| 1076 if (parent is ExpressionStatement && | 1182 if (parent is ExpressionStatement && |
| 1077 rhs is CascadeExpression && | 1183 rhs is CascadeExpression && |
| 1078 _isStateless(lhs, rhs)) { | 1184 _isStateless(lhs, rhs)) { |
| 1079 // Special case: cascade assignment to a variable in a statement. | 1185 // Special case: cascade assignment to a variable in a statement. |
| 1080 // We can reuse the variable to desugar it: | 1186 // We can reuse the variable to desugar it: |
| 1081 // result = []..length = length; | 1187 // result = []..length = length; |
| 1082 // becomes: | 1188 // becomes: |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1137 } else { | 1243 } else { |
| 1138 return js.call('dart.dinvokef(#, #)', [_visit(node.methodName), args]); | 1244 return js.call('dart.dinvokef(#, #)', [_visit(node.methodName), args]); |
| 1139 } | 1245 } |
| 1140 } | 1246 } |
| 1141 | 1247 |
| 1142 // TODO(jmesserly): if this resolves to a getter returning a function with | 1248 // TODO(jmesserly): if this resolves to a getter returning a function with |
| 1143 // a call method, we don't generate the `.call` correctly. | 1249 // a call method, we don't generate the `.call` correctly. |
| 1144 | 1250 |
| 1145 var targetJs; | 1251 var targetJs; |
| 1146 if (target != null) { | 1252 if (target != null) { |
| 1147 targetJs = js.call('#.#', [_visit(target), node.methodName.name]); | 1253 var methodNameJs = nameIfExtension(rules.getStaticType(target), node.metho dName.name); |
| 1254 targetJs = js.call('#.#', [_visit(target), methodNameJs != null ? methodNa meJs : node.methodName.name]); | |
| 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 394 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1552 JS.Expression _emitPostfixIncrement(Expression expr, Token op) { | 1659 JS.Expression _emitPostfixIncrement(Expression expr, Token op) { |
| 1553 var type = rules.getStaticType(expr); | 1660 var type = rules.getStaticType(expr); |
| 1554 assert(type != null); | 1661 assert(type != null); |
| 1555 var tmp = _createTemporary('x', type); | 1662 var tmp = _createTemporary('x', type); |
| 1556 | 1663 |
| 1557 // Increment and write | 1664 // Increment and write |
| 1558 var one = AstBuilder.integerLiteral(1); | 1665 var one = AstBuilder.integerLiteral(1); |
| 1559 one.staticType = rules.provider.intType; | 1666 one.staticType = rules.provider.intType; |
| 1560 var increment = AstBuilder.binaryExpression(tmp, op.lexeme[0], one); | 1667 var increment = AstBuilder.binaryExpression(tmp, op.lexeme[0], one); |
| 1561 increment.staticType = type; | 1668 increment.staticType = type; |
| 1562 var write = _emitAssignment(expr, increment); | 1669 var write = _emitSet(expr, increment); |
| 1563 | 1670 |
| 1564 var bindThis = _maybeBindThis(expr); | 1671 var bindThis = _maybeBindThis(expr); |
| 1565 return js.call("((#) => (#, #))$bindThis(#)", [ | 1672 return js.call("((#) => (#, #))$bindThis(#)", [ |
| 1566 _visit(tmp), | 1673 _visit(tmp), |
| 1567 write, | 1674 write, |
| 1568 _visit(tmp), | 1675 _visit(tmp), |
| 1569 _visit(expr) | 1676 _visit(expr) |
| 1570 ]); | 1677 ]); |
| 1571 } | 1678 } |
| 1572 | 1679 |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 1588 } | 1695 } |
| 1589 | 1696 |
| 1590 assert(op.lexeme == '++' || op.lexeme == '--'); | 1697 assert(op.lexeme == '++' || op.lexeme == '--'); |
| 1591 return _emitPostfixIncrement(expr, op); | 1698 return _emitPostfixIncrement(expr, op); |
| 1592 } | 1699 } |
| 1593 | 1700 |
| 1594 JS.Expression _emitPrefixIncrement(Token op, Expression expr) { | 1701 JS.Expression _emitPrefixIncrement(Token op, Expression expr) { |
| 1595 var one = AstBuilder.integerLiteral(1); | 1702 var one = AstBuilder.integerLiteral(1); |
| 1596 one.staticType = rules.provider.intType; | 1703 one.staticType = rules.provider.intType; |
| 1597 var increment = AstBuilder.binaryExpression(expr, op.lexeme[0], one); | 1704 var increment = AstBuilder.binaryExpression(expr, op.lexeme[0], one); |
| 1598 return _emitAssignment(expr, increment); | 1705 return _emitSet(expr, increment); |
| 1599 } | 1706 } |
| 1600 | 1707 |
| 1601 @override | 1708 @override |
| 1602 JS.Expression visitPrefixExpression(PrefixExpression node) { | 1709 JS.Expression visitPrefixExpression(PrefixExpression node) { |
| 1603 return _emitPrefixExpression(node.operator, node.operand); | 1710 return _emitPrefixExpression(node.operator, node.operand); |
| 1604 } | 1711 } |
| 1605 | 1712 |
| 1606 JS.Expression _emitPrefixExpression(Token op, Expression expr) { | 1713 JS.Expression _emitPrefixExpression(Token op, Expression expr) { |
| 1607 var dispatchType = rules.getStaticType(expr); | 1714 var dispatchType = rules.getStaticType(expr); |
| 1608 if (unaryOperationIsPrimitive(dispatchType)) { | 1715 if (unaryOperationIsPrimitive(dispatchType)) { |
| (...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1716 JS.This visitThisExpression(ThisExpression node) => new JS.This(); | 1823 JS.This visitThisExpression(ThisExpression node) => new JS.This(); |
| 1717 | 1824 |
| 1718 @override | 1825 @override |
| 1719 JS.Super visitSuperExpression(SuperExpression node) => new JS.Super(); | 1826 JS.Super visitSuperExpression(SuperExpression node) => new JS.Super(); |
| 1720 | 1827 |
| 1721 @override | 1828 @override |
| 1722 visitPrefixedIdentifier(PrefixedIdentifier node) { | 1829 visitPrefixedIdentifier(PrefixedIdentifier node) { |
| 1723 if (node.prefix.staticElement is PrefixElement) { | 1830 if (node.prefix.staticElement is PrefixElement) { |
| 1724 return _visit(node.identifier); | 1831 return _visit(node.identifier); |
| 1725 } else { | 1832 } else { |
| 1726 return _visitGet(node.prefix, node.identifier); | 1833 return _emitGet(node.prefix, node.identifier); |
| 1727 } | 1834 } |
| 1728 } | 1835 } |
| 1729 | 1836 |
| 1730 @override | 1837 @override |
| 1731 visitPropertyAccess(PropertyAccess node) => | 1838 visitPropertyAccess(PropertyAccess node) => |
| 1732 _visitGet(_getTarget(node), node.propertyName); | 1839 _emitGet(_getTarget(node), node.propertyName); |
| 1733 | 1840 |
| 1734 /// Shared code for [PrefixedIdentifier] and [PropertyAccess]. | 1841 /// Shared code for [PrefixedIdentifier] and [PropertyAccess]. |
| 1735 _visitGet(Expression target, SimpleIdentifier name) { | 1842 _emitGet(Expression target, SimpleIdentifier name) { |
| 1736 if (rules.isDynamicTarget(target)) { | 1843 if (rules.isDynamicTarget(target)) { |
| 1737 return js.call( | 1844 return js.call( |
| 1738 'dart.dload(#, #)', [_visit(target), js.string(name.name, "'")]); | 1845 'dart.dload(#, #)', [_visit(target), js.string(name.name, "'")]); |
| 1739 } else { | 1846 } else { |
| 1740 var e = name.staticElement; | 1847 var e = name.staticElement; |
| 1741 return js.call('#.#', [ | 1848 bool isStatic = e is ExecutableElement && e.isStatic; |
| 1849 var memberName; | |
| 1850 if (!isStatic) { | |
| 1851 memberName = nameIfExtension(rules.getStaticType(target), name.name, isG etter: true); | |
| 1852 } | |
| 1853 | |
| 1854 var ret = js.call('#.#', [ | |
| 1742 _visit(target), | 1855 _visit(target), |
| 1743 _jsMemberName(name.name, isStatic: e is ExecutableElement && e.isStatic) | 1856 memberName != null ? memberName : _jsMemberName(name.name, isStatic: isS tatic) |
| 1744 ]); | 1857 ]); |
| 1858 return ret; | |
| 1745 } | 1859 } |
| 1746 } | 1860 } |
| 1747 | 1861 |
| 1748 @override | 1862 @override |
| 1749 visitIndexExpression(IndexExpression node) { | 1863 visitIndexExpression(IndexExpression node) { |
| 1750 var target = _getTarget(node); | 1864 var target = _getTarget(node); |
| 1751 var code; | |
| 1752 if (rules.isDynamicTarget(target)) { | 1865 if (rules.isDynamicTarget(target)) { |
| 1753 code = 'dart.dindex(#, #)'; | 1866 return js.call('dart.dindex(#, #)', [_visit(target), _visit(node.index)]); |
| 1754 } else { | |
| 1755 code = '#.get(#)'; | |
| 1756 } | 1867 } |
| 1757 return js.call(code, [_visit(target), _visit(node.index)]); | 1868 |
| 1869 var targetJs = nameIfExtension(rules.getStaticType(target), '[]'); | |
| 1870 return js.call('#.#(#)', [_visit(target), targetJs != null ? targetJs : js.s tring('get'), _visit(node.index)]); | |
| 1758 } | 1871 } |
| 1759 | 1872 |
| 1760 /// Gets the target of a [PropertyAccess] or [IndexExpression]. | 1873 /// Gets the target of a [PropertyAccess] or [IndexExpression]. |
| 1761 /// Those two nodes are special because they're both allowed on left side of | 1874 /// Those two nodes are special because they're both allowed on left side of |
| 1762 /// an assignment expression and cascades. | 1875 /// an assignment expression and cascades. |
| 1763 Expression _getTarget(node) { | 1876 Expression _getTarget(node) { |
| 1764 assert(node is IndexExpression || node is PropertyAccess); | 1877 assert(node is IndexExpression || node is PropertyAccess); |
| 1765 return node.isCascaded ? _cascadeTarget : node.target; | 1878 return node.isCascaded ? _cascadeTarget : node.target; |
| 1766 } | 1879 } |
| 1767 | 1880 |
| (...skipping 371 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2139 /// | 2252 /// |
| 2140 /// Equality is a bit special, it is generated via the Dart `equals` runtime | 2253 /// 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 '=='. | 2254 /// helper, that checks for null. The user defined method is called '=='. |
| 2142 /// | 2255 /// |
| 2143 JS.Expression _jsMemberName(String name, | 2256 JS.Expression _jsMemberName(String name, |
| 2144 {bool unary: false, bool isStatic: false}) { | 2257 {bool unary: false, bool isStatic: false}) { |
| 2145 if (name.startsWith('_')) { | 2258 if (name.startsWith('_')) { |
| 2146 if (_privateNames.add(name)) _pendingPrivateNames.add(name); | 2259 if (_privateNames.add(name)) _pendingPrivateNames.add(name); |
| 2147 return new JSTemporary(name); | 2260 return new JSTemporary(name); |
| 2148 } | 2261 } |
| 2149 if (name == '[]') { | 2262 return _propertyName(_transformMemberNameHelper(name, unary: unary, isStatic : isStatic)); |
| 2150 name = 'get'; | 2263 } |
| 2151 } else if (name == '[]=') { | 2264 |
| 2152 name = 'set'; | 2265 String _transformMemberNameHelper(String name, |
|
Jennifer Messerly
2015/04/03 16:25:28
maybe call this one _jsMemberName, and the other o
Jacob
2015/04/03 20:25:58
that is better. done
| |
| 2153 } else if (unary && name == '-') { | 2266 {bool unary: false, bool isStatic: false}) { |
| 2154 name = 'unary-'; | 2267 if (name == '[]') return 'get'; |
| 2155 } else if (isStatic && invalidJSStaticMethodName(name)) { | 2268 if (name == '[]=') return 'set'; |
| 2269 if (unary && name == '-') return 'unary-'; | |
| 2270 if (isStatic && invalidJSStaticMethodName(name)) { | |
| 2156 // Choose an string name. Use an invalid identifier so it won't conflict | 2271 // Choose an string name. Use an invalid identifier so it won't conflict |
| 2157 // with any valid member names. | 2272 // with any valid member names. |
| 2158 // TODO(jmesserly): this works around the problem, but I'm pretty sure we | 2273 // 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 | 2274 // don't need it, as static methods seemed to work. The only concrete |
| 2160 // issue we saw was in the defineNamedConstructor helper function. | 2275 // issue we saw was in the defineNamedConstructor helper function. |
| 2161 name = '$name*'; | 2276 return '$name*'; |
| 2162 } | 2277 } |
| 2163 return _propertyName(name); | 2278 return name; |
| 2164 } | 2279 } |
| 2165 | 2280 |
| 2281 | |
| 2282 // TODO(jacobr): we need to avoid possible collisions between extension | |
| 2283 // methods names and regular names. | |
|
Jennifer Messerly
2015/04/03 16:28:27
I think if you change _initExtensionMethodSymbol t
Jacob
2015/04/03 20:25:58
Done.
| |
| 2284 JS.LiteralString _extensionMethodName(String name) => | |
| 2285 js.string(_extensionMethodNameRaw(name), "'"); | |
| 2286 | |
| 2287 String _extensionMethodNameRaw(String name) => '\$${_transformMemberNameHelper (name)}'; | |
| 2288 | |
| 2166 bool _externalOrNative(node) => | 2289 bool _externalOrNative(node) => |
| 2167 node.externalKeyword != null || _functionBody(node) is NativeFunctionBody; | 2290 node.externalKeyword != null || _functionBody(node) is NativeFunctionBody; |
| 2168 | 2291 |
| 2169 FunctionBody _functionBody(node) => | 2292 FunctionBody _functionBody(node) => |
| 2170 node is FunctionDeclaration ? node.functionExpression.body : node.body; | 2293 node is FunctionDeclaration ? node.functionExpression.body : node.body; |
| 2171 | 2294 |
| 2172 /// Choose a canonical name from the library element. | 2295 /// Choose a canonical name from the library element. |
| 2173 /// This never uses the library's name (the identifier in the `library` | 2296 /// This never uses the library's name (the identifier in the `library` |
| 2174 /// declaration) as it doesn't have any meaningful rules enforced. | 2297 /// declaration) as it doesn't have any meaningful rules enforced. |
| 2175 JS.Identifier _libraryName(LibraryElement library) { | 2298 JS.Identifier _libraryName(LibraryElement library) { |
| (...skipping 208 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2384 | 2507 |
| 2385 // TODO(jmesserly): in many cases marking the end will be unncessary. | 2508 // TODO(jmesserly): in many cases marking the end will be unncessary. |
| 2386 printer.mark(_location(node.end)); | 2509 printer.mark(_location(node.end)); |
| 2387 } | 2510 } |
| 2388 | 2511 |
| 2389 String _getIdentifier(AstNode node) { | 2512 String _getIdentifier(AstNode node) { |
| 2390 if (node is SimpleIdentifier) return node.name; | 2513 if (node is SimpleIdentifier) return node.name; |
| 2391 return null; | 2514 return null; |
| 2392 } | 2515 } |
| 2393 } | 2516 } |
| OLD | NEW |