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

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

Issue 1090313002: fixes #52, fields shadowing getters/setters or other fields (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
« no previous file with comments | « lib/runtime/dart/math.js ('k') | lib/src/codegen/js_field_storage.dart » ('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 8
9 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; 9 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator;
10 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator; 10 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator;
11 import 'package:analyzer/src/generated/constant.dart'; 11 import 'package:analyzer/src/generated/constant.dart';
12 import 'package:analyzer/src/generated/element.dart'; 12 import 'package:analyzer/src/generated/element.dart';
13 import 'package:analyzer/src/generated/resolver.dart' show TypeProvider; 13 import 'package:analyzer/src/generated/resolver.dart' show TypeProvider;
14 import 'package:analyzer/src/generated/scanner.dart' 14 import 'package:analyzer/src/generated/scanner.dart'
15 show StringToken, Token, TokenType; 15 show StringToken, Token, TokenType;
16 import 'package:path/path.dart' as path; 16 import 'package:path/path.dart' as path;
17 17
18 import 'package:dev_compiler/src/codegen/ast_builder.dart' show AstBuilder; 18 import 'package:dev_compiler/src/codegen/ast_builder.dart' show AstBuilder;
19 19
20 // TODO(jmesserly): import from its own package 20 // TODO(jmesserly): import from its own package
21 import 'package:dev_compiler/src/js/js_ast.dart' as JS; 21 import 'package:dev_compiler/src/js/js_ast.dart' as JS;
22 import 'package:dev_compiler/src/js/js_ast.dart' show js; 22 import 'package:dev_compiler/src/js/js_ast.dart' show js;
23 23
24 import 'package:dev_compiler/src/checker/rules.dart'; 24 import 'package:dev_compiler/src/checker/rules.dart';
25 import 'package:dev_compiler/src/info.dart'; 25 import 'package:dev_compiler/src/info.dart';
26 import 'package:dev_compiler/src/options.dart'; 26 import 'package:dev_compiler/src/options.dart';
27 import 'package:dev_compiler/src/utils.dart'; 27 import 'package:dev_compiler/src/utils.dart';
28 28
29 import 'code_generator.dart'; 29 import 'code_generator.dart';
30 import 'js_field_storage.dart' show findFieldsNeedingStorage;
30 import 'js_names.dart' show JSTemporary, invalidJSStaticMethodName; 31 import 'js_names.dart' show JSTemporary, invalidJSStaticMethodName;
31 import 'js_metalet.dart'; 32 import 'js_metalet.dart';
32 import 'js_printer.dart' show writeJsLibrary; 33 import 'js_printer.dart' show writeJsLibrary;
33 import 'side_effect_analysis.dart'; 34 import 'side_effect_analysis.dart';
34 35
35 // Various dynamic helpers we call. 36 // Various dynamic helpers we call.
36 // If renaming these, make sure to check other places like the 37 // If renaming these, make sure to check other places like the
37 // dart_runtime.js file and comments. 38 // dart_runtime.js file and comments.
38 // TODO(jmesserly): ideally we'd have a "dynamic call" dart library we can 39 // TODO(jmesserly): ideally we'd have a "dynamic call" dart library we can
39 // import and generate calls to, rather than dart_runtime.js 40 // import and generate calls to, rather than dart_runtime.js
40 const DPUT = 'dput'; 41 const DPUT = 'dput';
41 const DLOAD = 'dload'; 42 const DLOAD = 'dload';
42 const DINDEX = 'dindex'; 43 const DINDEX = 'dindex';
43 const DSETINDEX = 'dsetindex'; 44 const DSETINDEX = 'dsetindex';
44 const DCALL = 'dcall'; 45 const DCALL = 'dcall';
45 const DSEND = 'dsend'; 46 const DSEND = 'dsend';
46 47
47 class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { 48 class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
48 final LibraryInfo libraryInfo; 49 final LibraryInfo libraryInfo;
49 final TypeRules rules; 50 final TypeRules rules;
50 51
51 /// The global extension method table. 52 /// The global extension method table.
52 final HashMap<String, List<InterfaceType>> _extensionMethods; 53 final HashMap<String, List<InterfaceType>> _extensionMethods;
53 54
55 /// Information that is precomputed for this library, indicates which fields
56 /// need storage slots. See [_fieldStorage] for more explanation.
57 final HashSet<FieldElement> _fieldNeedsStorage;
58
54 /// The variable for the target of the current `..` cascade expression. 59 /// The variable for the target of the current `..` cascade expression.
55 SimpleIdentifier _cascadeTarget; 60 SimpleIdentifier _cascadeTarget;
61
56 /// The variable for the current catch clause 62 /// The variable for the current catch clause
57 SimpleIdentifier _catchParameter; 63 SimpleIdentifier _catchParameter;
58 64
59 ClassDeclaration currentClass; 65 ClassDeclaration currentClass;
60 ConstantEvaluator _constEvaluator; 66 ConstantEvaluator _constEvaluator;
61 67
62 final _exports = new Set<String>(); 68 final _exports = new Set<String>();
63 final _lazyFields = <VariableDeclaration>[]; 69 final _lazyFields = <VariableDeclaration>[];
64 final _properties = <FunctionDeclaration>[]; 70 final _properties = <FunctionDeclaration>[];
65 final _privateNames = new HashMap<String, JSTemporary>(); 71 final _privateNames = new HashMap<String, JSTemporary>();
66 final _pendingPrivateNames = <JSTemporary>[];
67 final _extensionMethodNames = new HashSet<String>(); 72 final _extensionMethodNames = new HashSet<String>();
68 final _pendingExtensionMethodNames = <String>[]; 73 final _pendingSymbols = <JS.Identifier>[];
69 final _temps = new HashMap<Element, JSTemporary>(); 74 final _temps = new HashMap<Element, JSTemporary>();
70 75
71 /// The name for the library's exports inside itself. 76 /// The name for the library's exports inside itself.
72 /// This much be a constant because we interpolate it into template strings, 77 /// This much be a constant because we interpolate it into template strings,
73 /// and otherwise it would break caching for them. 78 /// and otherwise it would break caching for them.
74 /// `exports` was chosen as the most similar to ES module patterns. 79 /// `exports` was chosen as the most similar to ES module patterns.
75 final JSTemporary _exportsVar = new JSTemporary('exports'); 80 final JSTemporary _exportsVar = new JSTemporary('exports');
76 final JSTemporary _namedArgTemp = new JSTemporary('opts'); 81 final JSTemporary _namedArgTemp = new JSTemporary('opts');
77 82
78 /// Classes we have not emitted yet. Values can be [ClassDeclaration] or 83 /// Classes we have not emitted yet. Values can be [ClassDeclaration] or
79 /// [ClassTypeAlias]. 84 /// [ClassTypeAlias].
80 final _pendingClasses = new HashMap<Element, CompilationUnitMember>(); 85 final _pendingClasses = new HashMap<Element, CompilationUnitMember>();
81 86
82 /// Memoized results of [_lazyClass]. 87 /// Memoized results of [_lazyClass].
83 final _lazyClassMemo = new HashMap<Element, bool>(); 88 final _lazyClassMemo = new HashMap<Element, bool>();
84 89
85 /// Memoized results of [_inLibraryCycle]. 90 /// Memoized results of [_inLibraryCycle].
86 final _libraryCycleMemo = new HashMap<LibraryElement, bool>(); 91 final _libraryCycleMemo = new HashMap<LibraryElement, bool>();
87 92
88 JSCodegenVisitor(this.libraryInfo, this.rules, this._extensionMethods); 93 JSCodegenVisitor(this.libraryInfo, this.rules, this._extensionMethods,
94 this._fieldNeedsStorage);
89 95
90 LibraryElement get currentLibrary => libraryInfo.library; 96 LibraryElement get currentLibrary => libraryInfo.library;
91 TypeProvider get types => rules.provider; 97 TypeProvider get types => rules.provider;
92 98
93 JS.Program emitLibrary(LibraryUnit library) { 99 JS.Program emitLibrary(LibraryUnit library) {
94 String jsDefaultValue = null; 100 String jsDefaultValue = null;
95 var unit = library.library; 101 var unit = library.library;
96 if (unit.directives.isNotEmpty) { 102 if (unit.directives.isNotEmpty) {
97 var libraryDir = unit.directives.first; 103 var libraryDir = unit.directives.first;
98 if (libraryDir is LibraryDirective) { 104 if (libraryDir is LibraryDirective) {
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
136 js.statement("(function(#) { 'use strict'; #; })(# || (# = #));", [ 142 js.statement("(function(#) { 'use strict'; #; })(# || (# = #));", [
137 _exportsVar, 143 _exportsVar,
138 body, 144 body,
139 name, 145 name,
140 name, 146 name,
141 defaultValue 147 defaultValue
142 ]) 148 ])
143 ]); 149 ]);
144 } 150 }
145 151
146 JS.Statement _initPrivateSymbol(JSTemporary tmp) => 152 JS.Statement _initSymbol(JS.Identifier id) =>
147 js.statement('let # = $_SYMBOL(#);', [tmp, js.string(tmp.name, "'")]); 153 js.statement('let # = $_SYMBOL(#);', [id, js.string(id.name, "'")]);
148
149 JS.Statement _initExtensionMethodSymbol(String name) => js.statement(
150 'let # = $_SYMBOL(#);', [new JS.Identifier(name), js.string(name, "'")]);
151 154
152 // TODO(jmesserly): this is a temporary workaround for `Symbol` in core, 155 // TODO(jmesserly): this is a temporary workaround for `Symbol` in core,
153 // until we have better name tracking. 156 // until we have better name tracking.
154 String get _SYMBOL { 157 String get _SYMBOL {
155 var name = currentLibrary.name; 158 var name = currentLibrary.name;
156 if (name == 'dart.core' || name == 'dart._internal') return 'dart.JsSymbol'; 159 if (name == 'dart.core' || name == 'dart._internal') return 'dart.JsSymbol';
157 return 'Symbol'; 160 return 'Symbol';
158 } 161 }
159 162
160 @override 163 @override
161 JS.Statement visitCompilationUnit(CompilationUnit node) { 164 JS.Statement visitCompilationUnit(CompilationUnit node) {
162 var source = node.element.source; 165 var source = node.element.source;
163 166
164 _constEvaluator = new ConstantEvaluator(source, types); 167 _constEvaluator = new ConstantEvaluator(source, types);
165 168
166 // TODO(jmesserly): scriptTag, directives. 169 // TODO(jmesserly): scriptTag, directives.
167 var body = <JS.Statement>[]; 170 var body = <JS.Statement>[];
168 for (var child in node.declarations) { 171 for (var child in node.declarations) {
169 // Attempt to group adjacent fields/properties. 172 // Attempt to group adjacent fields/properties.
170 if (child is! TopLevelVariableDeclaration) _flushLazyFields(body); 173 if (child is! TopLevelVariableDeclaration) _flushLazyFields(body);
171 if (child is! FunctionDeclaration) _flushLibraryProperties(body); 174 if (child is! FunctionDeclaration) _flushLibraryProperties(body);
172 175
173 var code = _visit(child); 176 var code = _visit(child);
174
175 if (code != null) { 177 if (code != null) {
176 if (_pendingPrivateNames.isNotEmpty) { 178 if (_pendingSymbols.isNotEmpty) {
177 body.addAll(_pendingPrivateNames.map(_initPrivateSymbol)); 179 body.addAll(_pendingSymbols.map(_initSymbol));
178 _pendingPrivateNames.clear(); 180 _pendingSymbols.clear();
179 }
180 if (_pendingExtensionMethodNames.isNotEmpty) {
181 body.addAll(
182 _pendingExtensionMethodNames.map(_initExtensionMethodSymbol));
183 _pendingExtensionMethodNames.clear();
184 } 181 }
185 body.add(code); 182 body.add(code);
186 } 183 }
187 } 184 }
188 185
189 // Flush any unwritten fields/properties. 186 // Flush any unwritten fields/properties.
190 _flushLazyFields(body); 187 _flushLazyFields(body);
191 _flushLibraryProperties(body); 188 _flushLibraryProperties(body);
192 189
193 assert(_pendingPrivateNames.isEmpty); 190 assert(_pendingSymbols.isEmpty);
194 return _statement(body); 191 return _statement(body);
195 } 192 }
196 193
197 bool isPublic(String name) => !name.startsWith('_'); 194 bool isPublic(String name) => !name.startsWith('_');
198 195
199 /// Conversions that we don't handle end up here. 196 /// Conversions that we don't handle end up here.
200 @override 197 @override
201 visitConversion(Conversion node) { 198 visitConversion(Conversion node) {
202 var from = node.baseType; 199 var from = node.baseType;
203 var to = node.convertedType; 200 var to = node.convertedType;
(...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after
310 // generate it correctly when we refer to it. 307 // generate it correctly when we refer to it.
311 if (isPublic(dartClassName)) _addExport(dartClassName); 308 if (isPublic(dartClassName)) _addExport(dartClassName);
312 return js.statement('let # = #;', [dartClassName, jsTypeName]); 309 return js.statement('let # = #;', [dartClassName, jsTypeName]);
313 } 310 }
314 return null; 311 return null;
315 } 312 }
316 313
317 @override 314 @override
318 JS.Statement visitClassDeclaration(ClassDeclaration node) { 315 JS.Statement visitClassDeclaration(ClassDeclaration node) {
319 // If we've already emitted this class, skip it. 316 // If we've already emitted this class, skip it.
320 var type = node.element.type; 317 var classElem = node.element;
321 if (_pendingClasses.remove(node.element) == null) return null; 318 var type = classElem.type;
319 if (_pendingClasses.remove(classElem) == null) return null;
322 320
323 var jsName = getAnnotationValue(node, _isJsNameAnnotation); 321 var jsName = getAnnotationValue(node, _isJsNameAnnotation);
324 if (jsName != null) return _emitJsType(node.name.name, jsName); 322 if (jsName != null) return _emitJsType(node.name.name, jsName);
325 323
326 currentClass = node; 324 currentClass = node;
327 325
328 var ctors = <ConstructorDeclaration>[]; 326 var ctors = <ConstructorDeclaration>[];
329 var fields = <FieldDeclaration>[]; 327 var fields = <FieldDeclaration>[];
330 var staticFields = <FieldDeclaration>[]; 328 var staticFields = <FieldDeclaration>[];
331 for (var member in node.members) { 329 for (var member in node.members) {
332 if (member is ConstructorDeclaration) { 330 if (member is ConstructorDeclaration) {
333 ctors.add(member); 331 ctors.add(member);
334 } else if (member is FieldDeclaration) { 332 } else if (member is FieldDeclaration) {
335 (member.isStatic ? staticFields : fields).add(member); 333 (member.isStatic ? staticFields : fields).add(member);
336 } 334 }
337 } 335 }
338 336
339 var classExpr = new JS.ClassExpression(new JS.Identifier(type.name), 337 var classExpr = new JS.ClassExpression(new JS.Identifier(type.name),
340 _classHeritage(node), _emitClassMethods(node, ctors, fields)); 338 _classHeritage(node), _emitClassMethods(node, ctors, fields));
341 339
342 var body = 340 var body = _finishClassMembers(classElem, classExpr, ctors, staticFields);
343 _finishClassMembers(node.element, classExpr, ctors, staticFields);
344 currentClass = null; 341 currentClass = null;
345 342
346 return _finishClassDef(type, body); 343 return _finishClassDef(type, body);
347 } 344 }
348 345
349 @override 346 @override
350 JS.Statement visitEnumDeclaration(EnumDeclaration node) => 347 JS.Statement visitEnumDeclaration(EnumDeclaration node) =>
351 _unimplementedCall("Unimplemented enum: $node").toStatement(); 348 _unimplementedCall("Unimplemented enum: $node").toStatement();
352 349
353 /// Given a class element and body, complete the class declaration. 350 /// Given a class element and body, complete the class declaration.
(...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after
544 return heritage; 541 return heritage;
545 } 542 }
546 543
547 List<JS.Method> _emitClassMethods(ClassDeclaration node, 544 List<JS.Method> _emitClassMethods(ClassDeclaration node,
548 List<ConstructorDeclaration> ctors, List<FieldDeclaration> fields) { 545 List<ConstructorDeclaration> ctors, List<FieldDeclaration> fields) {
549 var element = node.element; 546 var element = node.element;
550 var type = element.type; 547 var type = element.type;
551 var isObject = type.isObject; 548 var isObject = type.isObject;
552 var name = node.name.name; 549 var name = node.name.name;
553 550
554 var jsMethods = <JS.Method>[];
555 // Iff no constructor is specified for a class C, it implicitly has a 551 // Iff no constructor is specified for a class C, it implicitly has a
556 // default constructor `C() : super() {}`, unless C is class Object. 552 // default constructor `C() : super() {}`, unless C is class Object.
553 var jsMethods = <JS.Method>[];
557 if (ctors.isEmpty && !isObject) { 554 if (ctors.isEmpty && !isObject) {
558 jsMethods.add(_emitImplicitConstructor(node, name, fields)); 555 jsMethods.add(_emitImplicitConstructor(node, name, fields));
559 } 556 }
557
560 for (var member in node.members) { 558 for (var member in node.members) {
561 if (member is ConstructorDeclaration) { 559 if (member is ConstructorDeclaration) {
562 jsMethods.add(_emitConstructor(member, name, fields, isObject)); 560 jsMethods.add(_emitConstructor(member, name, fields, isObject));
563 } else if (member is MethodDeclaration) { 561 } else if (member is MethodDeclaration) {
564 jsMethods.add(_emitMethodDeclaration(type, member)); 562 jsMethods.add(_emitMethodDeclaration(type, member));
563 } else if (member is FieldDeclaration) {
564 _emitFieldDeclaration(jsMethods, member);
565 } 565 }
566 } 566 }
567 567
568 // Support for adapting dart:core Iterator/Iterable to ES6 versions. 568 // Support for adapting dart:core Iterator/Iterable to ES6 versions.
569 // This lets them use for-of loops transparently. 569 // This lets them use for-of loops transparently.
570 // https://github.com/lukehoban/es6features#iterators--forof 570 // https://github.com/lukehoban/es6features#iterators--forof
571 if (element.library.isDartCore && element.name == 'Iterable') { 571 if (element.library.isDartCore && element.name == 'Iterable') {
572 JS.Fun body = js.call('''function() { 572 JS.Fun body = js.call('''function() {
573 var iterator = this.iterator; 573 var iterator = this.iterator;
574 return { 574 return {
575 next() { 575 next() {
576 var done = iterator.moveNext(); 576 var done = iterator.moveNext();
577 return { done: done, current: done ? void 0 : iterator.current }; 577 return { done: done, current: done ? void 0 : iterator.current };
578 } 578 }
579 }; 579 };
580 }'''); 580 }''');
581 jsMethods.add(new JS.Method(js.call('$_SYMBOL.iterator'), body)); 581 jsMethods.add(new JS.Method(js.call('$_SYMBOL.iterator'), body));
582 } 582 }
583 return jsMethods.where((m) => m != null).toList(growable: false); 583 return jsMethods.where((m) => m != null).toList(growable: false);
584 } 584 }
585 585
586 /// Emit class members that need to come after the class declaration, such 586 /// Emit class members that need to come after the class declaration, such
587 /// as static fields. See [_emitClassMethods] for things that are emitted 587 /// as static fields. See [_emitClassMethods] for things that are emitted
588 /// insite the ES6 `class { ... }` node. 588 /// inside the ES6 `class { ... }` node.
589 JS.Statement _finishClassMembers(ClassElement classElem, 589 JS.Statement _finishClassMembers(ClassElement classElem,
590 JS.ClassExpression cls, List<ConstructorDeclaration> ctors, 590 JS.ClassExpression cls, List<ConstructorDeclaration> ctors,
591 List<FieldDeclaration> staticFields) { 591 List<FieldDeclaration> staticFields) {
592 var name = classElem.name; 592 var name = classElem.name;
593
594 var body = <JS.Statement>[]; 593 var body = <JS.Statement>[];
595 body.add(new JS.ClassDeclaration(cls)); 594 body.add(new JS.ClassDeclaration(cls));
596 595
597 // Interfaces 596 // Interfaces
598 if (classElem.interfaces.isNotEmpty) { 597 if (classElem.interfaces.isNotEmpty) {
599 body.add(js.statement('#[dart.implements] = () => #;', [ 598 body.add(js.statement('#[dart.implements] = () => #;', [
600 name, 599 name,
601 new JS.ArrayInitializer( 600 new JS.ArrayInitializer(
602 classElem.interfaces.map(_emitTypeName).toList()) 601 classElem.interfaces.map(_emitTypeName).toList())
603 ])); 602 ]));
(...skipping 162 matching lines...) Expand 10 before | Expand all | Expand 10 after
766 return null; 765 return null;
767 } 766 }
768 767
769 var supertypeName = element.supertype.name; 768 var supertypeName = element.supertype.name;
770 var name = _constructorName(supertypeName, superCtorName); 769 var name = _constructorName(supertypeName, superCtorName);
771 770
772 var args = node != null ? _visit(node.argumentList) : []; 771 var args = node != null ? _visit(node.argumentList) : [];
773 return js.statement('super.#(#);', [name, args])..sourceInformation = node; 772 return js.statement('super.#(#);', [name, args])..sourceInformation = node;
774 } 773 }
775 774
775 /// Emits the [field]'s induced getter/setter into [methods].
776 void _emitFieldDeclaration(List<JS.Method> methods, FieldDeclaration fields) {
777 if (fields.isStatic) return;
778
779 for (var field in fields.fields.variables) {
780 // For fields that use storage slots, we need to emit a getter/setter
781 // pair, otherwise we don't.
782 var storage = _fieldStorage(field.element);
783 if (storage == null) continue;
784
785 var e = field.element;
786 var memberName = _emitMemberName(e.name, type: e.enclosingElement.type);
787 methods.add(new JS.Method(
788 memberName, js.call('function() { return this[#]; }', storage),
789 isGetter: true));
790 if (!field.isFinal) {
791 methods.add(new JS.Method(memberName,
792 js.call('function(value) { this[#] = value; }', storage),
793 isSetter: true));
794 }
795 }
796 }
797
798 /// We use a storage slot for fields that override or can be overridden by
799 /// getter/setter pairs. If the field element [e] needs storage, this will
800 /// return a temporary that contains the JS `Symbol` with storage. Otherwise
801 /// it will return null to indicate that no storage is needed.
802 JSTemporary _fieldStorage(FieldElement e) {
803 if (!_fieldNeedsStorage.contains(e)) return null;
804
805 // TODO(jmesserly): if we know two types can't be (or aren't, in the case of
806 // private classes) mixed together, we could reuse field storage. This would
807 // result in less `name$` `name$0` temps being generated. We'd likely need
808 // to push this into the [findFieldsNeedingStorage] analysis method.
809 return _temps.putIfAbsent(e, () {
810 // TODO(jmesserly): maybe we could have two candidate names, and one of
811 // them is ClassName_fieldName? That might be more readable in case of
812 // conflict.
813 var t = new JSTemporary(e.name);
814 _pendingSymbols.add(t);
815 return t;
816 });
817 }
818
776 /// Initialize fields. They follow the sequence: 819 /// Initialize fields. They follow the sequence:
777 /// 820 ///
778 /// 1. field declaration initializer if non-const, 821 /// 1. field declaration initializer if non-const,
779 /// 2. field initializing parameters, 822 /// 2. field initializing parameters,
780 /// 3. constructor field initializers, 823 /// 3. constructor field initializers,
781 /// 4. initialize fields not covered in 1-3 824 /// 4. initialize fields not covered in 1-3
782 JS.Statement _initializeFields(List<FieldDeclaration> fields, 825 JS.Statement _initializeFields(List<FieldDeclaration> fieldDecls,
783 [FormalParameterList parameters, 826 [FormalParameterList parameters,
784 NodeList<ConstructorInitializer> initializers]) { 827 NodeList<ConstructorInitializer> initializers]) {
785 var body = <JS.Statement>[];
786 828
787 // Run field initializers if they can have side-effects. 829 // Run field initializers if they can have side-effects.
830 var fields = new Map<FieldElement, JS.Expression>();
788 var unsetFields = new Map<FieldElement, VariableDeclaration>(); 831 var unsetFields = new Map<FieldElement, VariableDeclaration>();
789 for (var declaration in fields) { 832 for (var declaration in fieldDecls) {
790 for (var field in declaration.fields.variables) { 833 for (var fieldNode in declaration.fields.variables) {
791 if (_isFieldInitConstant(field)) { 834 var element = fieldNode.element;
792 unsetFields[field.element] = field; 835 if (_isFieldInitConstant(fieldNode)) {
836 unsetFields[element] = fieldNode;
793 } else { 837 } else {
794 body.add(js.statement( 838 fields[element] = _visitInitializer(fieldNode);
795 '# = #;', [_visit(field.name), _visitInitializer(field)]));
796 } 839 }
797 } 840 }
798 } 841 }
799 842
800 // Initialize fields from `this.fieldName` parameters. 843 // Initialize fields from `this.fieldName` parameters.
801 if (parameters != null) { 844 if (parameters != null) {
802 for (var p in parameters.parameters) { 845 for (var p in parameters.parameters) {
803 if (p is DefaultFormalParameter) p = p.parameter; 846 var element = p.element;
804 if (p is FieldFormalParameter) { 847 if (element is FieldFormalParameterElement) {
805 var field = (p.element as FieldFormalParameterElement).field; 848 fields[element.field] = _visit(p);
806 // Use the getter to initialize the field. This is a bit strange, but
807 // final fields don't have a setter element that we could use instead.
808
809 var memberName =
810 _emitMemberName(field.name, type: field.enclosingElement.type);
811 body.add(js.statement('this.# = #;', [memberName, _visit(p)]));
812 unsetFields.remove(field);
813 } 849 }
814 } 850 }
815 } 851 }
816 852
817 // Run constructor field initializers such as `: foo = bar.baz` 853 // Run constructor field initializers such as `: foo = bar.baz`
818 if (initializers != null) { 854 if (initializers != null) {
819 for (var init in initializers) { 855 for (var init in initializers) {
820 if (init is ConstructorFieldInitializer) { 856 if (init is ConstructorFieldInitializer) {
821 body.add(js.statement( 857 fields[init.fieldName.staticElement] = _visit(init.expression);
822 '# = #;', [_visit(init.fieldName), _visit(init.expression)]));
823 unsetFields.remove(init.fieldName.staticElement);
824 } 858 }
825 } 859 }
826 } 860 }
827 861
862 for (var f in fields.keys) unsetFields.remove(f);
863
828 // Initialize all remaining fields 864 // Initialize all remaining fields
829 unsetFields.forEach((field, fieldNode) { 865 unsetFields.forEach((element, fieldNode) {
830 JS.Expression value; 866 JS.Expression value;
831 if (fieldNode.initializer != null) { 867 if (fieldNode.initializer != null) {
832 value = _visit(fieldNode.initializer); 868 value = _visit(fieldNode.initializer);
833 } else { 869 } else {
834 var type = rules.elementType(field); 870 var type = rules.elementType(element);
835 value = new JS.LiteralNull(); 871 value = new JS.LiteralNull();
836 if (rules.maybeNonNullableType(type)) { 872 if (rules.maybeNonNullableType(type)) {
837 value = js.call('dart.as(#, #)', [value, _emitTypeName(type)]); 873 value = js.call('dart.as(#, #)', [value, _emitTypeName(type)]);
838 } 874 }
839 } 875 }
840 var memberName = 876 fields[element] = value;
841 _emitMemberName(field.name, type: field.enclosingElement.type);
842 body.add(js.statement('this.# = #;', [memberName, value]));
843 }); 877 });
844 878
879 var body = <JS.Statement>[];
880 fields.forEach((FieldElement e, JS.Expression initialValue) {
881 var access = _fieldStorage(e);
882 if (access == null) {
883 access = _emitMemberName(e.name, type: e.enclosingElement.type);
884 }
885 body.add(js.statement('this.# = #;', [access, initialValue]));
886 });
845 return _statement(body); 887 return _statement(body);
846 } 888 }
847 889
848 FormalParameterList _parametersOf(node) { 890 FormalParameterList _parametersOf(node) {
849 // Note: ConstructorDeclaration is intentionally skipped here so we can 891 // Note: ConstructorDeclaration is intentionally skipped here so we can
850 // emit the argument initializers in a different place. 892 // emit the argument initializers in a different place.
851 // TODO(jmesserly): clean this up. If we can model ES6 spread/rest args, we 893 // TODO(jmesserly): clean this up. If we can model ES6 spread/rest args, we
852 // could handle argument initializers more consistently in a separate 894 // could handle argument initializers more consistently in a separate
853 // lowering pass. 895 // lowering pass.
854 if (node is MethodDeclaration) return node.parameters; 896 if (node is MethodDeclaration) return node.parameters;
(...skipping 1344 matching lines...) Expand 10 before | Expand all | Expand 10 after
2199 /// for this transformation to happen, otherwise binary minus is assumed. 2241 /// for this transformation to happen, otherwise binary minus is assumed.
2200 /// 2242 ///
2201 /// Equality is a bit special, it is generated via the Dart `equals` runtime 2243 /// Equality is a bit special, it is generated via the Dart `equals` runtime
2202 /// helper, that checks for null. The user defined method is called '=='. 2244 /// helper, that checks for null. The user defined method is called '=='.
2203 /// 2245 ///
2204 JS.Expression _emitMemberName(String name, 2246 JS.Expression _emitMemberName(String name,
2205 {DartType type, bool unary: false, bool isStatic: false}) { 2247 {DartType type, bool unary: false, bool isStatic: false}) {
2206 if (name.startsWith('_')) { 2248 if (name.startsWith('_')) {
2207 return _privateNames.putIfAbsent(name, () { 2249 return _privateNames.putIfAbsent(name, () {
2208 var t = new JSTemporary(name); 2250 var t = new JSTemporary(name);
2209 _pendingPrivateNames.add(t); 2251 _pendingSymbols.add(t);
2210 return t; 2252 return t;
2211 }); 2253 });
2212 } 2254 }
2255
2213 // Check for extension method: 2256 // Check for extension method:
2214 var extLibrary = _findExtensionLibrary(name, type); 2257 var extLibrary = _findExtensionLibrary(name, type);
2215 2258
2216 if (name == '[]') { 2259 if (name == '[]') {
2217 name = 'get'; 2260 name = 'get';
2218 } else if (name == '[]=') { 2261 } else if (name == '[]=') {
2219 name = 'set'; 2262 name = 'set';
2220 } else if (name == '-' && unary) { 2263 } else if (name == '-' && unary) {
2221 name = 'unary-'; 2264 name = 'unary-';
2222 } 2265 }
2223 2266
2224 if (isStatic && invalidJSStaticMethodName(name)) { 2267 if (isStatic && invalidJSStaticMethodName(name)) {
2225 // Choose an string name. Use an invalid identifier so it won't conflict 2268 // Choose an string name. Use an invalid identifier so it won't conflict
2226 // with any valid member names. 2269 // with any valid member names.
2227 // TODO(jmesserly): this works around the problem, but I'm pretty sure we 2270 // TODO(jmesserly): this works around the problem, but I'm pretty sure we
2228 // don't need it, as static methods seemed to work. The only concrete 2271 // don't need it, as static methods seemed to work. The only concrete
2229 // issue we saw was in the defineNamedConstructor helper function. 2272 // issue we saw was in the defineNamedConstructor helper function.
2230 name = '$name*'; 2273 name = '$name*';
2231 } 2274 }
2232 2275
2233 if (extLibrary != null) { 2276 if (extLibrary != null) {
2234 return js.call('#.#', [ 2277 return _extensionMethodName(name, extLibrary);
2235 _libraryName(extLibrary),
2236 _propertyName(_addExtensionMethodName(name, extLibrary))
2237 ]);
2238 } 2278 }
2239 2279
2240 return _propertyName(name); 2280 return _propertyName(name);
2241 } 2281 }
2242 2282
2243 LibraryElement _findExtensionLibrary(String name, DartType type) { 2283 LibraryElement _findExtensionLibrary(String name, DartType type) {
2244 if (type is! InterfaceType) return null; 2284 if (type is! InterfaceType) return null;
2245 2285
2246 var extLibrary = null; 2286 var extLibrary = null;
2247 var extensionTypes = _extensionMethods[name]; 2287 var extensionTypes = _extensionMethods[name];
2248 if (extensionTypes != null) { 2288 if (extensionTypes != null) {
2249 // Normalize the type to ignore generics. 2289 // Normalize the type to ignore generics.
2250 type = fillDynamicTypeArgs(type, types); 2290 type = fillDynamicTypeArgs(type, types);
2251 for (var t in extensionTypes) { 2291 for (var t in extensionTypes) {
2252 if (rules.isSubTypeOf(type, t)) { 2292 if (rules.isSubTypeOf(type, t)) {
2253 assert(extLibrary == null || extLibrary == t.element.library); 2293 assert(extLibrary == null || extLibrary == t.element.library);
2254 extLibrary = t.element.library; 2294 extLibrary = t.element.library;
2255 } 2295 }
2256 } 2296 }
2257 } 2297 }
2258 return extLibrary; 2298 return extLibrary;
2259 } 2299 }
2260 2300
2261 String _addExtensionMethodName(String name, LibraryElement extLibrary) { 2301 JS.Expression _extensionMethodName(String name, LibraryElement library) {
2262 var extensionMethodName = '\$$name'; 2302 var extName = '\$$name';
2263 if (extLibrary == currentLibrary) { 2303 if (library == currentLibrary) {
2264 // TODO(jacobr): need to do a better job ensuring that extension method 2304 // TODO(jacobr): need to do a better job ensuring that extension method
2265 // name symbols do not conflict with other symbols before we can let 2305 // name symbols do not conflict with other symbols before we can let
2266 // user defined libraries define extension methods. 2306 // user defined libraries define extension methods.
2267 if (_extensionMethodNames.add(extensionMethodName)) { 2307 if (_extensionMethodNames.add(extName)) {
2268 _pendingExtensionMethodNames.add(extensionMethodName); 2308 _pendingSymbols.add(new JS.Identifier(extName));
2269 _addExport(extensionMethodName); 2309 _addExport(extName);
2270 } 2310 }
2271 } 2311 }
2272 return extensionMethodName; 2312 return js.call('#.#', [_libraryName(library), _propertyName(extName)]);
2273 } 2313 }
2274 2314
2275 bool _externalOrNative(node) => 2315 bool _externalOrNative(node) =>
2276 node.externalKeyword != null || _functionBody(node) is NativeFunctionBody; 2316 node.externalKeyword != null || _functionBody(node) is NativeFunctionBody;
2277 2317
2278 FunctionBody _functionBody(node) => 2318 FunctionBody _functionBody(node) =>
2279 node is FunctionDeclaration ? node.functionExpression.body : node.body; 2319 node is FunctionDeclaration ? node.functionExpression.body : node.body;
2280 2320
2281 /// Choose a canonical name from the library element. 2321 /// Choose a canonical name from the library element.
2282 /// This never uses the library's name (the identifier in the `library` 2322 /// This never uses the library's name (the identifier in the `library`
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
2316 ..addAll(e.accessors.map((m) => m.name)); 2356 ..addAll(e.accessors.map((m) => m.name));
2317 for (var name in names) { 2357 for (var name in names) {
2318 _extensionMethods.putIfAbsent(name, () => []).add(type); 2358 _extensionMethods.putIfAbsent(name, () => []).add(type);
2319 } 2359 }
2320 } 2360 }
2321 } 2361 }
2322 2362
2323 TypeProvider get types => rules.provider; 2363 TypeProvider get types => rules.provider;
2324 2364
2325 String generateLibrary(LibraryUnit unit, LibraryInfo info) { 2365 String generateLibrary(LibraryUnit unit, LibraryInfo info) {
2326 var codegen = new JSCodegenVisitor(info, rules, _extensionMethods); 2366 var fields = findFieldsNeedingStorage(unit);
2367 var codegen = new JSCodegenVisitor(info, rules, _extensionMethods, fields);
2327 var module = codegen.emitLibrary(unit); 2368 var module = codegen.emitLibrary(unit);
2328 var dir = path.join(outDir, jsOutputPath(info, root)); 2369 var dir = path.join(outDir, jsOutputPath(info, root));
2329 return writeJsLibrary(module, dir, emitSourceMaps: options.emitSourceMaps); 2370 return writeJsLibrary(module, dir, emitSourceMaps: options.emitSourceMaps);
2330 } 2371 }
2331 } 2372 }
2332 2373
2333 /// Choose a canonical name from the library element. 2374 /// Choose a canonical name from the library element.
2334 /// This never uses the library's name (the identifier in the `library` 2375 /// This never uses the library's name (the identifier in the `library`
2335 /// declaration) as it doesn't have any meaningful rules enforced. 2376 /// declaration) as it doesn't have any meaningful rules enforced.
2336 String jsLibraryName(LibraryElement library) => canonicalLibraryName(library); 2377 String jsLibraryName(LibraryElement library) => canonicalLibraryName(library);
(...skipping 22 matching lines...) Expand all
2359 return filepath; 2400 return filepath;
2360 } 2401 }
2361 2402
2362 // TODO(jmesserly): validate the library. See issue #135. 2403 // TODO(jmesserly): validate the library. See issue #135.
2363 bool _isJsNameAnnotation(DartObjectImpl value) => value.type.name == 'JsName'; 2404 bool _isJsNameAnnotation(DartObjectImpl value) => value.type.name == 'JsName';
2364 2405
2365 // TODO(jacobr): we would like to do something like the following 2406 // TODO(jacobr): we would like to do something like the following
2366 // but we don't have summary support yet. 2407 // but we don't have summary support yet.
2367 // bool _supportJsExtensionMethod(AnnotatedNode node) => 2408 // bool _supportJsExtensionMethod(AnnotatedNode node) =>
2368 // _getAnnotation(node, "SupportJsExtensionMethod") != null; 2409 // _getAnnotation(node, "SupportJsExtensionMethod") != null;
OLDNEW
« no previous file with comments | « lib/runtime/dart/math.js ('k') | lib/src/codegen/js_field_storage.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698