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

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

Issue 1099743002: fix fields that override getters/setters (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: merged 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_runtime.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;
(...skipping 11 matching lines...) Expand all
22 // TODO(jmesserly): import from its own package 22 // TODO(jmesserly): import from its own package
23 import 'package:dev_compiler/src/js/js_ast.dart' as JS; 23 import 'package:dev_compiler/src/js/js_ast.dart' as JS;
24 import 'package:dev_compiler/src/js/js_ast.dart' show js; 24 import 'package:dev_compiler/src/js/js_ast.dart' show js;
25 25
26 import 'package:dev_compiler/src/checker/rules.dart'; 26 import 'package:dev_compiler/src/checker/rules.dart';
27 import 'package:dev_compiler/src/info.dart'; 27 import 'package:dev_compiler/src/info.dart';
28 import 'package:dev_compiler/src/options.dart'; 28 import 'package:dev_compiler/src/options.dart';
29 import 'package:dev_compiler/src/utils.dart'; 29 import 'package:dev_compiler/src/utils.dart';
30 30
31 import 'code_generator.dart'; 31 import 'code_generator.dart';
32 import 'js_field_storage.dart';
32 import 'js_names.dart' show JSTemporary, invalidJSStaticMethodName; 33 import 'js_names.dart' show JSTemporary, invalidJSStaticMethodName;
33 import 'js_metalet.dart'; 34 import 'js_metalet.dart';
34 import 'js_printer.dart' show writeJsLibrary; 35 import 'js_printer.dart' show writeJsLibrary;
35 import 'side_effect_analysis.dart'; 36 import 'side_effect_analysis.dart';
36 37
37 // Various dynamic helpers we call. 38 // Various dynamic helpers we call.
38 // If renaming these, make sure to check other places like the 39 // If renaming these, make sure to check other places like the
39 // dart_runtime.js file and comments. 40 // dart_runtime.js file and comments.
40 // TODO(jmesserly): ideally we'd have a "dynamic call" dart library we can 41 // TODO(jmesserly): ideally we'd have a "dynamic call" dart library we can
41 // import and generate calls to, rather than dart_runtime.js 42 // import and generate calls to, rather than dart_runtime.js
42 const DPUT = 'dput'; 43 const DPUT = 'dput';
43 const DLOAD = 'dload'; 44 const DLOAD = 'dload';
44 const DINDEX = 'dindex'; 45 const DINDEX = 'dindex';
45 const DSETINDEX = 'dsetindex'; 46 const DSETINDEX = 'dsetindex';
46 const DCALL = 'dcall'; 47 const DCALL = 'dcall';
47 const DSEND = 'dsend'; 48 const DSEND = 'dsend';
48 49
49 class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { 50 class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
51 final CompilerOptions options;
52 final TypeRules rules;
50 final LibraryInfo libraryInfo; 53 final LibraryInfo libraryInfo;
51 final TypeRules rules;
52 54
53 /// The global extension method table. 55 /// The global extension method table.
54 final HashMap<String, List<InterfaceType>> _extensionMethods; 56 final HashMap<String, List<InterfaceType>> _extensionMethods;
55 57
56 final CompilerOptions options; 58 /// Information that is precomputed for this library, indicates which fields
59 /// need storage slots.
60 final HashSet<FieldElement> _fieldsNeedingStorage;
61
57 /// The variable for the target of the current `..` cascade expression. 62 /// The variable for the target of the current `..` cascade expression.
58 SimpleIdentifier _cascadeTarget; 63 SimpleIdentifier _cascadeTarget;
64
59 /// The variable for the current catch clause 65 /// The variable for the current catch clause
60 SimpleIdentifier _catchParameter; 66 SimpleIdentifier _catchParameter;
61 67
62 ClassDeclaration currentClass; 68 ClassDeclaration currentClass;
63 ConstantEvaluator _constEvaluator; 69 ConstantEvaluator _constEvaluator;
64 70
65 final _exports = new Set<String>(); 71 final _exports = new Set<String>();
66 final _lazyFields = <VariableDeclaration>[]; 72 final _lazyFields = <VariableDeclaration>[];
67 final _properties = <FunctionDeclaration>[]; 73 final _properties = <FunctionDeclaration>[];
68 final _privateNames = new HashMap<String, JSTemporary>(); 74 final _privateNames = new HashMap<String, JSTemporary>();
69 final _pendingPrivateNames = <JSTemporary>[];
70 final _extensionMethodNames = new HashSet<String>(); 75 final _extensionMethodNames = new HashSet<String>();
71 final _pendingExtensionMethodNames = <String>[]; 76 final _pendingSymbols = <JS.Identifier>[];
72 final _temps = new HashMap<Element, JSTemporary>(); 77 final _temps = new HashMap<Element, JSTemporary>();
73 78
74 /// The name for the library's exports inside itself. 79 /// The name for the library's exports inside itself.
75 /// This much be a constant because we interpolate it into template strings, 80 /// This much be a constant because we interpolate it into template strings,
76 /// and otherwise it would break caching for them. 81 /// and otherwise it would break caching for them.
77 /// `exports` was chosen as the most similar to ES module patterns. 82 /// `exports` was chosen as the most similar to ES module patterns.
78 final JSTemporary _exportsVar = new JSTemporary('exports'); 83 final JSTemporary _exportsVar = new JSTemporary('exports');
79 final JSTemporary _namedArgTemp = new JSTemporary('opts'); 84 final JSTemporary _namedArgTemp = new JSTemporary('opts');
80 85
81 /// Classes we have not emitted yet. Values can be [ClassDeclaration] or 86 /// Classes we have not emitted yet. Values can be [ClassDeclaration] or
82 /// [ClassTypeAlias]. 87 /// [ClassTypeAlias].
83 final _pendingClasses = new HashMap<Element, CompilationUnitMember>(); 88 final _pendingClasses = new HashMap<Element, CompilationUnitMember>();
84 89
85 /// Memoized results of [_lazyClass]. 90 /// Memoized results of [_lazyClass].
86 final _lazyClassMemo = new HashMap<Element, bool>(); 91 final _lazyClassMemo = new HashMap<Element, bool>();
87 92
88 /// Memoized results of [_inLibraryCycle]. 93 /// Memoized results of [_inLibraryCycle].
89 final _libraryCycleMemo = new HashMap<LibraryElement, bool>(); 94 final _libraryCycleMemo = new HashMap<LibraryElement, bool>();
90 95
91 JSCodegenVisitor( 96 JSCodegenVisitor(this.options, this.rules, this.libraryInfo,
92 this.libraryInfo, this.rules, this.options, this._extensionMethods); 97 this._extensionMethods, this._fieldsNeedingStorage);
93 98
94 LibraryElement get currentLibrary => libraryInfo.library; 99 LibraryElement get currentLibrary => libraryInfo.library;
95 TypeProvider get types => rules.provider; 100 TypeProvider get types => rules.provider;
96 101
97 JS.Program emitLibrary(LibraryUnit library) { 102 JS.Program emitLibrary(LibraryUnit library) {
98 String jsDefaultValue = null; 103 String jsDefaultValue = null;
99 104
100 // Modify the AST to make coercions explicit. 105 // Modify the AST to make coercions explicit.
101 new CoercionReifier(library, rules, options).reify(); 106 new CoercionReifier(library, rules, options).reify();
102 107
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
144 js.statement("(function(#) { 'use strict'; #; })(# || (# = #));", [ 149 js.statement("(function(#) { 'use strict'; #; })(# || (# = #));", [
145 _exportsVar, 150 _exportsVar,
146 body, 151 body,
147 name, 152 name,
148 name, 153 name,
149 defaultValue 154 defaultValue
150 ]) 155 ])
151 ]); 156 ]);
152 } 157 }
153 158
154 JS.Statement _initPrivateSymbol(JSTemporary tmp) => 159 JS.Statement _initSymbol(JS.Identifier id) =>
155 js.statement('let # = $_SYMBOL(#);', [tmp, js.string(tmp.name, "'")]); 160 js.statement('let # = $_SYMBOL(#);', [id, js.string(id.name, "'")]);
156
157 JS.Statement _initExtensionMethodSymbol(String name) => js.statement(
158 'let # = $_SYMBOL(#);', [new JS.Identifier(name), js.string(name, "'")]);
159 161
160 // TODO(jmesserly): this is a temporary workaround for `Symbol` in core, 162 // TODO(jmesserly): this is a temporary workaround for `Symbol` in core,
161 // until we have better name tracking. 163 // until we have better name tracking.
162 String get _SYMBOL { 164 String get _SYMBOL {
163 var name = currentLibrary.name; 165 var name = currentLibrary.name;
164 if (name == 'dart.core' || name == 'dart._internal') return 'dart.JsSymbol'; 166 if (name == 'dart.core' || name == 'dart._internal') return 'dart.JsSymbol';
165 return 'Symbol'; 167 return 'Symbol';
166 } 168 }
167 169
168 @override 170 @override
169 JS.Statement visitCompilationUnit(CompilationUnit node) { 171 JS.Statement visitCompilationUnit(CompilationUnit node) {
170 var source = node.element.source; 172 var source = node.element.source;
171 173
172 _constEvaluator = new ConstantEvaluator(source, types); 174 _constEvaluator = new ConstantEvaluator(source, types);
173 175
174 // TODO(jmesserly): scriptTag, directives. 176 // TODO(jmesserly): scriptTag, directives.
175 var body = <JS.Statement>[]; 177 var body = <JS.Statement>[];
176 for (var child in node.declarations) { 178 for (var child in node.declarations) {
177 // Attempt to group adjacent fields/properties. 179 // Attempt to group adjacent fields/properties.
178 if (child is! TopLevelVariableDeclaration) _flushLazyFields(body); 180 if (child is! TopLevelVariableDeclaration) _flushLazyFields(body);
179 if (child is! FunctionDeclaration) _flushLibraryProperties(body); 181 if (child is! FunctionDeclaration) _flushLibraryProperties(body);
180 182
181 var code = _visit(child); 183 var code = _visit(child);
182
183 if (code != null) { 184 if (code != null) {
184 if (_pendingPrivateNames.isNotEmpty) { 185 if (_pendingSymbols.isNotEmpty) {
185 body.addAll(_pendingPrivateNames.map(_initPrivateSymbol)); 186 body.addAll(_pendingSymbols.map(_initSymbol));
186 _pendingPrivateNames.clear(); 187 _pendingSymbols.clear();
187 }
188 if (_pendingExtensionMethodNames.isNotEmpty) {
189 body.addAll(
190 _pendingExtensionMethodNames.map(_initExtensionMethodSymbol));
191 _pendingExtensionMethodNames.clear();
192 } 188 }
193 body.add(code); 189 body.add(code);
194 } 190 }
195 } 191 }
196 192
197 // Flush any unwritten fields/properties. 193 // Flush any unwritten fields/properties.
198 _flushLazyFields(body); 194 _flushLazyFields(body);
199 _flushLibraryProperties(body); 195 _flushLibraryProperties(body);
200 196
201 assert(_pendingPrivateNames.isEmpty); 197 assert(_pendingSymbols.isEmpty);
202 return _statement(body); 198 return _statement(body);
203 } 199 }
204 200
205 bool isPublic(String name) => !name.startsWith('_'); 201 bool isPublic(String name) => !name.startsWith('_');
206 202
207 /// Conversions that we don't handle end up here. 203 /// Conversions that we don't handle end up here.
208 @override 204 @override
209 visitConversion(Conversion node) { 205 visitConversion(Conversion node) {
210 throw 'Unlowered conversion ${node.runtimeType}: $node'; 206 throw 'Unlowered conversion ${node.runtimeType}: $node';
211 } 207 }
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after
309 // generate it correctly when we refer to it. 305 // generate it correctly when we refer to it.
310 if (isPublic(dartClassName)) _addExport(dartClassName); 306 if (isPublic(dartClassName)) _addExport(dartClassName);
311 return js.statement('let # = #;', [dartClassName, jsTypeName]); 307 return js.statement('let # = #;', [dartClassName, jsTypeName]);
312 } 308 }
313 return null; 309 return null;
314 } 310 }
315 311
316 @override 312 @override
317 JS.Statement visitClassDeclaration(ClassDeclaration node) { 313 JS.Statement visitClassDeclaration(ClassDeclaration node) {
318 // If we've already emitted this class, skip it. 314 // If we've already emitted this class, skip it.
319 var type = node.element.type; 315 var classElem = node.element;
320 if (_pendingClasses.remove(node.element) == null) return null; 316 var type = classElem.type;
317 if (_pendingClasses.remove(classElem) == null) return null;
321 318
322 var jsName = getAnnotationValue(node, _isJsNameAnnotation); 319 var jsName = getAnnotationValue(node, _isJsNameAnnotation);
323 if (jsName != null) return _emitJsType(node.name.name, jsName); 320 if (jsName != null) return _emitJsType(node.name.name, jsName);
324 321
325 currentClass = node; 322 currentClass = node;
326 323
327 var ctors = <ConstructorDeclaration>[]; 324 var ctors = <ConstructorDeclaration>[];
328 var fields = <FieldDeclaration>[]; 325 var fields = <FieldDeclaration>[];
329 var staticFields = <FieldDeclaration>[]; 326 var staticFields = <FieldDeclaration>[];
330 for (var member in node.members) { 327 for (var member in node.members) {
331 if (member is ConstructorDeclaration) { 328 if (member is ConstructorDeclaration) {
332 ctors.add(member); 329 ctors.add(member);
333 } else if (member is FieldDeclaration) { 330 } else if (member is FieldDeclaration) {
334 (member.isStatic ? staticFields : fields).add(member); 331 (member.isStatic ? staticFields : fields).add(member);
335 } 332 }
336 } 333 }
337 334
338 var classExpr = new JS.ClassExpression(new JS.Identifier(type.name), 335 var classExpr = new JS.ClassExpression(new JS.Identifier(type.name),
339 _classHeritage(node), _emitClassMethods(node, ctors, fields)); 336 _classHeritage(node), _emitClassMethods(node, ctors, fields));
340 337
341 var body = 338 var body =
342 _finishClassMembers(node.element, classExpr, ctors, staticFields); 339 _finishClassMembers(classElem, classExpr, ctors, fields, staticFields);
343 currentClass = null; 340 currentClass = null;
344 341
345 return _finishClassDef(type, body); 342 return _finishClassDef(type, body);
346 } 343 }
347 344
348 @override 345 @override
349 JS.Statement visitEnumDeclaration(EnumDeclaration node) => 346 JS.Statement visitEnumDeclaration(EnumDeclaration node) =>
350 _unimplementedCall("Unimplemented enum: $node").toStatement(); 347 _unimplementedCall("Unimplemented enum: $node").toStatement();
351 348
352 /// Given a class element and body, complete the class declaration. 349 /// Given a class element and body, complete the class declaration.
(...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after
543 return heritage; 540 return heritage;
544 } 541 }
545 542
546 List<JS.Method> _emitClassMethods(ClassDeclaration node, 543 List<JS.Method> _emitClassMethods(ClassDeclaration node,
547 List<ConstructorDeclaration> ctors, List<FieldDeclaration> fields) { 544 List<ConstructorDeclaration> ctors, List<FieldDeclaration> fields) {
548 var element = node.element; 545 var element = node.element;
549 var type = element.type; 546 var type = element.type;
550 var isObject = type.isObject; 547 var isObject = type.isObject;
551 var name = node.name.name; 548 var name = node.name.name;
552 549
553 var jsMethods = <JS.Method>[];
554 // Iff no constructor is specified for a class C, it implicitly has a 550 // Iff no constructor is specified for a class C, it implicitly has a
555 // default constructor `C() : super() {}`, unless C is class Object. 551 // default constructor `C() : super() {}`, unless C is class Object.
552 var jsMethods = <JS.Method>[];
556 if (ctors.isEmpty && !isObject) { 553 if (ctors.isEmpty && !isObject) {
557 jsMethods.add(_emitImplicitConstructor(node, name, fields)); 554 jsMethods.add(_emitImplicitConstructor(node, name, fields));
558 } 555 }
556
559 for (var member in node.members) { 557 for (var member in node.members) {
560 if (member is ConstructorDeclaration) { 558 if (member is ConstructorDeclaration) {
561 jsMethods.add(_emitConstructor(member, name, fields, isObject)); 559 jsMethods.add(_emitConstructor(member, name, fields, isObject));
562 } else if (member is MethodDeclaration) { 560 } else if (member is MethodDeclaration) {
563 jsMethods.add(_emitMethodDeclaration(type, member)); 561 jsMethods.add(_emitMethodDeclaration(type, member));
564 } 562 }
565 } 563 }
566 564
567 // Support for adapting dart:core Iterator/Iterable to ES6 versions. 565 // Support for adapting dart:core Iterator/Iterable to ES6 versions.
568 // This lets them use for-of loops transparently. 566 // This lets them use for-of loops transparently.
569 // https://github.com/lukehoban/es6features#iterators--forof 567 // https://github.com/lukehoban/es6features#iterators--forof
570 if (element.library.isDartCore && element.name == 'Iterable') { 568 if (element.library.isDartCore && element.name == 'Iterable') {
571 JS.Fun body = js.call('''function() { 569 JS.Fun body = js.call('''function() {
572 var iterator = this.iterator; 570 var iterator = this.iterator;
573 return { 571 return {
574 next() { 572 next() {
575 var done = iterator.moveNext(); 573 var done = iterator.moveNext();
576 return { done: done, current: done ? void 0 : iterator.current }; 574 return { done: done, current: done ? void 0 : iterator.current };
577 } 575 }
578 }; 576 };
579 }'''); 577 }''');
580 jsMethods.add(new JS.Method(js.call('$_SYMBOL.iterator'), body)); 578 jsMethods.add(new JS.Method(js.call('$_SYMBOL.iterator'), body));
581 } 579 }
582 return jsMethods.where((m) => m != null).toList(growable: false); 580 return jsMethods.where((m) => m != null).toList(growable: false);
583 } 581 }
584 582
585 /// Emit class members that need to come after the class declaration, such 583 /// Emit class members that need to come after the class declaration, such
586 /// as static fields. See [_emitClassMethods] for things that are emitted 584 /// as static fields. See [_emitClassMethods] for things that are emitted
587 /// insite the ES6 `class { ... }` node. 585 /// inside the ES6 `class { ... }` node.
588 JS.Statement _finishClassMembers(ClassElement classElem, 586 JS.Statement _finishClassMembers(ClassElement classElem,
589 JS.ClassExpression cls, List<ConstructorDeclaration> ctors, 587 JS.ClassExpression cls, List<ConstructorDeclaration> ctors,
590 List<FieldDeclaration> staticFields) { 588 List<FieldDeclaration> fields, List<FieldDeclaration> staticFields) {
591 var name = classElem.name; 589 var name = classElem.name;
592
593 var body = <JS.Statement>[]; 590 var body = <JS.Statement>[];
594 body.add(new JS.ClassDeclaration(cls)); 591 body.add(new JS.ClassDeclaration(cls));
595 592
596 // Interfaces 593 // Interfaces
597 if (classElem.interfaces.isNotEmpty) { 594 if (classElem.interfaces.isNotEmpty) {
598 body.add(js.statement('#[dart.implements] = () => #;', [ 595 body.add(js.statement('#[dart.implements] = () => #;', [
599 name, 596 name,
600 new JS.ArrayInitializer( 597 new JS.ArrayInitializer(
601 classElem.interfaces.map(_emitTypeName).toList()) 598 classElem.interfaces.map(_emitTypeName).toList())
602 ])); 599 ]));
603 } 600 }
604 601
605 // Named constructors 602 // Named constructors
606 for (ConstructorDeclaration member in ctors) { 603 for (ConstructorDeclaration member in ctors) {
607 if (member.name != null) { 604 if (member.name != null) {
608 body.add(js.statement('dart.defineNamedConstructor(#, #);', [ 605 body.add(js.statement('dart.defineNamedConstructor(#, #);', [
609 name, 606 name,
610 _emitMemberName(member.name.name, isStatic: true) 607 _emitMemberName(member.name.name, isStatic: true)
611 ])); 608 ]));
612 } 609 }
613 } 610 }
614 611
612 // Instance fields, if they override getter/setter pairs
613 for (FieldDeclaration member in fields) {
614 for (VariableDeclaration fieldDecl in member.fields.variables) {
615 var field = fieldDecl.element;
616 if (_fieldsNeedingStorage.contains(field)) {
617 body.add(_overrideField(field));
618 }
619 }
620 }
621
615 // Static fields 622 // Static fields
616 var lazyStatics = <VariableDeclaration>[]; 623 var lazyStatics = <VariableDeclaration>[];
617 for (FieldDeclaration member in staticFields) { 624 for (FieldDeclaration member in staticFields) {
618 for (VariableDeclaration field in member.fields.variables) { 625 for (VariableDeclaration field in member.fields.variables) {
619 var fieldName = field.name.name; 626 var fieldName = field.name.name;
620 if (field.isConst || _isFieldInitConstant(field)) { 627 if (field.isConst || _isFieldInitConstant(field)) {
621 var init = _visit(field.initializer); 628 var init = _visit(field.initializer);
622 if (init == null) init = new JS.LiteralNull(); 629 if (init == null) init = new JS.LiteralNull();
623 body.add(js.statement('#.# = #;', [name, fieldName, init])); 630 body.add(js.statement('#.# = #;', [name, fieldName, init]));
624 } else { 631 } else {
625 lazyStatics.add(field); 632 lazyStatics.add(field);
626 } 633 }
627 } 634 }
628 } 635 }
629 var lazy = _emitLazyFields(new JS.Identifier(name), lazyStatics); 636 var lazy = _emitLazyFields(new JS.Identifier(name), lazyStatics);
630 if (lazy != null) body.add(lazy); 637 if (lazy != null) body.add(lazy);
631 638
632 return _statement(body); 639 return _statement(body);
633 } 640 }
634 641
642 JS.Statement _overrideField(FieldElement e) {
643 var cls = e.enclosingElement;
644 return js.statement('dart.virtualField(#, #)', [
645 cls.name,
646 _emitMemberName(e.name, type: cls.type)
647 ]);
648 }
649
635 /// Generates the implicit default constructor for class C of the form 650 /// Generates the implicit default constructor for class C of the form
636 /// `C() : super() {}`. 651 /// `C() : super() {}`.
637 JS.Method _emitImplicitConstructor( 652 JS.Method _emitImplicitConstructor(
638 ClassDeclaration node, String name, List<FieldDeclaration> fields) { 653 ClassDeclaration node, String name, List<FieldDeclaration> fields) {
639 // If we don't have a method body, skip this. 654 // If we don't have a method body, skip this.
640 if (fields.isEmpty) return null; 655 if (fields.isEmpty) return null;
641 656
642 dynamic body = _initializeFields(fields); 657 dynamic body = _initializeFields(fields);
643 var superCall = _superConstructorCall(node); 658 var superCall = _superConstructorCall(node);
644 if (superCall != null) body = [[body, superCall]]; 659 if (superCall != null) body = [[body, superCall]];
(...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after
771 var args = node != null ? _visit(node.argumentList) : []; 786 var args = node != null ? _visit(node.argumentList) : [];
772 return js.statement('super.#(#);', [name, args])..sourceInformation = node; 787 return js.statement('super.#(#);', [name, args])..sourceInformation = node;
773 } 788 }
774 789
775 /// Initialize fields. They follow the sequence: 790 /// Initialize fields. They follow the sequence:
776 /// 791 ///
777 /// 1. field declaration initializer if non-const, 792 /// 1. field declaration initializer if non-const,
778 /// 2. field initializing parameters, 793 /// 2. field initializing parameters,
779 /// 3. constructor field initializers, 794 /// 3. constructor field initializers,
780 /// 4. initialize fields not covered in 1-3 795 /// 4. initialize fields not covered in 1-3
781 JS.Statement _initializeFields(List<FieldDeclaration> fields, 796 JS.Statement _initializeFields(List<FieldDeclaration> fieldDecls,
782 [FormalParameterList parameters, 797 [FormalParameterList parameters,
783 NodeList<ConstructorInitializer> initializers]) { 798 NodeList<ConstructorInitializer> initializers]) {
784 var body = <JS.Statement>[];
785 799
786 // Run field initializers if they can have side-effects. 800 // Run field initializers if they can have side-effects.
801 var fields = new Map<FieldElement, JS.Expression>();
787 var unsetFields = new Map<FieldElement, VariableDeclaration>(); 802 var unsetFields = new Map<FieldElement, VariableDeclaration>();
788 for (var declaration in fields) { 803 for (var declaration in fieldDecls) {
789 for (var field in declaration.fields.variables) { 804 for (var fieldNode in declaration.fields.variables) {
790 if (_isFieldInitConstant(field)) { 805 var element = fieldNode.element;
791 unsetFields[field.element] = field; 806 if (_isFieldInitConstant(fieldNode)) {
807 unsetFields[element] = fieldNode;
792 } else { 808 } else {
793 body.add(js.statement( 809 fields[element] = _visitInitializer(fieldNode);
794 '# = #;', [_visit(field.name), _visitInitializer(field)]));
795 } 810 }
796 } 811 }
797 } 812 }
798 813
799 // Initialize fields from `this.fieldName` parameters. 814 // Initialize fields from `this.fieldName` parameters.
800 if (parameters != null) { 815 if (parameters != null) {
801 for (var p in parameters.parameters) { 816 for (var p in parameters.parameters) {
802 if (p is DefaultFormalParameter) p = p.parameter; 817 var element = p.element;
803 if (p is FieldFormalParameter) { 818 if (element is FieldFormalParameterElement) {
804 var field = (p.element as FieldFormalParameterElement).field; 819 fields[element.field] = _visit(p);
805 // Use the getter to initialize the field. This is a bit strange, but
806 // final fields don't have a setter element that we could use instead.
807
808 var memberName =
809 _emitMemberName(field.name, type: field.enclosingElement.type);
810 body.add(js.statement('this.# = #;', [memberName, _visit(p)]));
811 unsetFields.remove(field);
812 } 820 }
813 } 821 }
814 } 822 }
815 823
816 // Run constructor field initializers such as `: foo = bar.baz` 824 // Run constructor field initializers such as `: foo = bar.baz`
817 if (initializers != null) { 825 if (initializers != null) {
818 for (var init in initializers) { 826 for (var init in initializers) {
819 if (init is ConstructorFieldInitializer) { 827 if (init is ConstructorFieldInitializer) {
820 body.add(js.statement( 828 fields[init.fieldName.staticElement] = _visit(init.expression);
821 '# = #;', [_visit(init.fieldName), _visit(init.expression)]));
822 unsetFields.remove(init.fieldName.staticElement);
823 } 829 }
824 } 830 }
825 } 831 }
826 832
833 for (var f in fields.keys) unsetFields.remove(f);
834
827 // Initialize all remaining fields 835 // Initialize all remaining fields
828 unsetFields.forEach((field, fieldNode) { 836 unsetFields.forEach((element, fieldNode) {
829 JS.Expression value; 837 JS.Expression value;
830 if (fieldNode.initializer != null) { 838 if (fieldNode.initializer != null) {
831 value = _visit(fieldNode.initializer); 839 value = _visit(fieldNode.initializer);
832 } else { 840 } else {
833 var type = rules.elementType(field); 841 var type = rules.elementType(element);
834 value = new JS.LiteralNull(); 842 value = new JS.LiteralNull();
835 if (rules.maybeNonNullableType(type)) { 843 if (rules.maybeNonNullableType(type)) {
836 value = js.call('dart.as(#, #)', [value, _emitTypeName(type)]); 844 value = js.call('dart.as(#, #)', [value, _emitTypeName(type)]);
837 } 845 }
838 } 846 }
839 var memberName = 847 fields[element] = value;
840 _emitMemberName(field.name, type: field.enclosingElement.type);
841 body.add(js.statement('this.# = #;', [memberName, value]));
842 }); 848 });
843 849
850 var body = <JS.Statement>[];
851 fields.forEach((FieldElement e, JS.Expression initialValue) {
852 var access = _emitMemberName(e.name, type: e.enclosingElement.type);
853 body.add(js.statement('this.# = #;', [access, initialValue]));
854 });
844 return _statement(body); 855 return _statement(body);
845 } 856 }
846 857
847 FormalParameterList _parametersOf(node) { 858 FormalParameterList _parametersOf(node) {
848 // Note: ConstructorDeclaration is intentionally skipped here so we can 859 // Note: ConstructorDeclaration is intentionally skipped here so we can
849 // emit the argument initializers in a different place. 860 // emit the argument initializers in a different place.
850 // TODO(jmesserly): clean this up. If we can model ES6 spread/rest args, we 861 // TODO(jmesserly): clean this up. If we can model ES6 spread/rest args, we
851 // could handle argument initializers more consistently in a separate 862 // could handle argument initializers more consistently in a separate
852 // lowering pass. 863 // lowering pass.
853 if (node is MethodDeclaration) return node.parameters; 864 if (node is MethodDeclaration) return node.parameters;
(...skipping 1344 matching lines...) Expand 10 before | Expand all | Expand 10 after
2198 /// for this transformation to happen, otherwise binary minus is assumed. 2209 /// for this transformation to happen, otherwise binary minus is assumed.
2199 /// 2210 ///
2200 /// Equality is a bit special, it is generated via the Dart `equals` runtime 2211 /// Equality is a bit special, it is generated via the Dart `equals` runtime
2201 /// helper, that checks for null. The user defined method is called '=='. 2212 /// helper, that checks for null. The user defined method is called '=='.
2202 /// 2213 ///
2203 JS.Expression _emitMemberName(String name, 2214 JS.Expression _emitMemberName(String name,
2204 {DartType type, bool unary: false, bool isStatic: false}) { 2215 {DartType type, bool unary: false, bool isStatic: false}) {
2205 if (name.startsWith('_')) { 2216 if (name.startsWith('_')) {
2206 return _privateNames.putIfAbsent(name, () { 2217 return _privateNames.putIfAbsent(name, () {
2207 var t = new JSTemporary(name); 2218 var t = new JSTemporary(name);
2208 _pendingPrivateNames.add(t); 2219 _pendingSymbols.add(t);
2209 return t; 2220 return t;
2210 }); 2221 });
2211 } 2222 }
2223
2212 // Check for extension method: 2224 // Check for extension method:
2213 var extLibrary = _findExtensionLibrary(name, type); 2225 var extLibrary = _findExtensionLibrary(name, type);
2214 2226
2215 if (name == '[]') { 2227 if (name == '[]') {
2216 name = 'get'; 2228 name = 'get';
2217 } else if (name == '[]=') { 2229 } else if (name == '[]=') {
2218 name = 'set'; 2230 name = 'set';
2219 } else if (name == '-' && unary) { 2231 } else if (name == '-' && unary) {
2220 name = 'unary-'; 2232 name = 'unary-';
2221 } 2233 }
2222 2234
2223 if (isStatic && invalidJSStaticMethodName(name)) { 2235 if (isStatic && invalidJSStaticMethodName(name)) {
2224 // Choose an string name. Use an invalid identifier so it won't conflict 2236 // Choose an string name. Use an invalid identifier so it won't conflict
2225 // with any valid member names. 2237 // with any valid member names.
2226 // TODO(jmesserly): this works around the problem, but I'm pretty sure we 2238 // TODO(jmesserly): this works around the problem, but I'm pretty sure we
2227 // don't need it, as static methods seemed to work. The only concrete 2239 // don't need it, as static methods seemed to work. The only concrete
2228 // issue we saw was in the defineNamedConstructor helper function. 2240 // issue we saw was in the defineNamedConstructor helper function.
2229 name = '$name*'; 2241 name = '$name*';
2230 } 2242 }
2231 2243
2232 if (extLibrary != null) { 2244 if (extLibrary != null) {
2233 return js.call('#.#', [ 2245 return _extensionMethodName(name, extLibrary);
2234 _libraryName(extLibrary),
2235 _propertyName(_addExtensionMethodName(name, extLibrary))
2236 ]);
2237 } 2246 }
2238 2247
2239 return _propertyName(name); 2248 return _propertyName(name);
2240 } 2249 }
2241 2250
2242 LibraryElement _findExtensionLibrary(String name, DartType type) { 2251 LibraryElement _findExtensionLibrary(String name, DartType type) {
2243 if (type is! InterfaceType) return null; 2252 if (type is! InterfaceType) return null;
2244 2253
2245 var extLibrary = null; 2254 var extLibrary = null;
2246 var extensionTypes = _extensionMethods[name]; 2255 var extensionTypes = _extensionMethods[name];
2247 if (extensionTypes != null) { 2256 if (extensionTypes != null) {
2248 // Normalize the type to ignore generics. 2257 // Normalize the type to ignore generics.
2249 type = fillDynamicTypeArgs(type, types); 2258 type = fillDynamicTypeArgs(type, types);
2250 for (var t in extensionTypes) { 2259 for (var t in extensionTypes) {
2251 if (rules.isSubTypeOf(type, t)) { 2260 if (rules.isSubTypeOf(type, t)) {
2252 assert(extLibrary == null || extLibrary == t.element.library); 2261 assert(extLibrary == null || extLibrary == t.element.library);
2253 extLibrary = t.element.library; 2262 extLibrary = t.element.library;
2254 } 2263 }
2255 } 2264 }
2256 } 2265 }
2257 return extLibrary; 2266 return extLibrary;
2258 } 2267 }
2259 2268
2260 String _addExtensionMethodName(String name, LibraryElement extLibrary) { 2269 JS.Expression _extensionMethodName(String name, LibraryElement library) {
2261 var extensionMethodName = '\$$name'; 2270 var extName = '\$$name';
2262 if (extLibrary == currentLibrary) { 2271 if (library == currentLibrary) {
2263 // TODO(jacobr): need to do a better job ensuring that extension method 2272 // TODO(jacobr): need to do a better job ensuring that extension method
2264 // name symbols do not conflict with other symbols before we can let 2273 // name symbols do not conflict with other symbols before we can let
2265 // user defined libraries define extension methods. 2274 // user defined libraries define extension methods.
2266 if (_extensionMethodNames.add(extensionMethodName)) { 2275 if (_extensionMethodNames.add(extName)) {
2267 _pendingExtensionMethodNames.add(extensionMethodName); 2276 _pendingSymbols.add(new JS.Identifier(extName));
2268 _addExport(extensionMethodName); 2277 _addExport(extName);
2269 } 2278 }
2270 } 2279 }
2271 return extensionMethodName; 2280 return js.call('#.#', [_libraryName(library), _propertyName(extName)]);
2272 } 2281 }
2273 2282
2274 bool _externalOrNative(node) => 2283 bool _externalOrNative(node) =>
2275 node.externalKeyword != null || _functionBody(node) is NativeFunctionBody; 2284 node.externalKeyword != null || _functionBody(node) is NativeFunctionBody;
2276 2285
2277 FunctionBody _functionBody(node) => 2286 FunctionBody _functionBody(node) =>
2278 node is FunctionDeclaration ? node.functionExpression.body : node.body; 2287 node is FunctionDeclaration ? node.functionExpression.body : node.body;
2279 2288
2280 /// Choose a canonical name from the library element. 2289 /// Choose a canonical name from the library element.
2281 /// This never uses the library's name (the identifier in the `library` 2290 /// This never uses the library's name (the identifier in the `library`
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
2315 ..addAll(e.accessors.map((m) => m.name)); 2324 ..addAll(e.accessors.map((m) => m.name));
2316 for (var name in names) { 2325 for (var name in names) {
2317 _extensionMethods.putIfAbsent(name, () => []).add(type); 2326 _extensionMethods.putIfAbsent(name, () => []).add(type);
2318 } 2327 }
2319 } 2328 }
2320 } 2329 }
2321 2330
2322 TypeProvider get types => rules.provider; 2331 TypeProvider get types => rules.provider;
2323 2332
2324 String generateLibrary(LibraryUnit unit, LibraryInfo info) { 2333 String generateLibrary(LibraryUnit unit, LibraryInfo info) {
2325 var codegen = new JSCodegenVisitor(info, rules, options, _extensionMethods); 2334 var fields = findFieldsNeedingStorage(unit);
2335 var codegen =
2336 new JSCodegenVisitor(options, rules, info, _extensionMethods, fields);
2326 var module = codegen.emitLibrary(unit); 2337 var module = codegen.emitLibrary(unit);
2327 var dir = path.join(outDir, jsOutputPath(info, root)); 2338 var dir = path.join(outDir, jsOutputPath(info, root));
2328 return writeJsLibrary(module, dir, emitSourceMaps: options.emitSourceMaps); 2339 return writeJsLibrary(module, dir, emitSourceMaps: options.emitSourceMaps);
2329 } 2340 }
2330 } 2341 }
2331 2342
2332 /// Choose a canonical name from the library element. 2343 /// Choose a canonical name from the library element.
2333 /// This never uses the library's name (the identifier in the `library` 2344 /// This never uses the library's name (the identifier in the `library`
2334 /// declaration) as it doesn't have any meaningful rules enforced. 2345 /// declaration) as it doesn't have any meaningful rules enforced.
2335 String jsLibraryName(LibraryElement library) => canonicalLibraryName(library); 2346 String jsLibraryName(LibraryElement library) => canonicalLibraryName(library);
(...skipping 22 matching lines...) Expand all
2358 return filepath; 2369 return filepath;
2359 } 2370 }
2360 2371
2361 // TODO(jmesserly): validate the library. See issue #135. 2372 // TODO(jmesserly): validate the library. See issue #135.
2362 bool _isJsNameAnnotation(DartObjectImpl value) => value.type.name == 'JsName'; 2373 bool _isJsNameAnnotation(DartObjectImpl value) => value.type.name == 'JsName';
2363 2374
2364 // TODO(jacobr): we would like to do something like the following 2375 // TODO(jacobr): we would like to do something like the following
2365 // but we don't have summary support yet. 2376 // but we don't have summary support yet.
2366 // bool _supportJsExtensionMethod(AnnotatedNode node) => 2377 // bool _supportJsExtensionMethod(AnnotatedNode node) =>
2367 // _getAnnotation(node, "SupportJsExtensionMethod") != null; 2378 // _getAnnotation(node, "SupportJsExtensionMethod") != null;
OLDNEW
« no previous file with comments | « lib/runtime/dart_runtime.js ('k') | lib/src/codegen/js_field_storage.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698