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

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

Issue 1059583002: Extension method support to move us closer to a valid List implementation. (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: Created 5 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 library dev_compiler.src.codegen.js_codegen; 5 library dev_compiler.src.codegen.js_codegen;
6 6
7 import 'dart:collection' show HashSet, HashMap; 7 import 'dart:collection' show HashSet, HashMap;
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
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
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
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
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
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
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
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
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
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698