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

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: done 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
« no previous file with comments | « lib/runtime/dart/isolate.js ('k') | test/codegen/expect/BenchmarkBase.js » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 }
OLDNEW
« no previous file with comments | « lib/runtime/dart/isolate.js ('k') | test/codegen/expect/BenchmarkBase.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698