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

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

Issue 1093143004: 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_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;
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';
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.
57 final HashSet<FieldElement> _fieldsNeedingStorage;
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._fieldsNeedingStorage);
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 13 matching lines...) Expand all
112 if (decl is ClassDeclaration || 118 if (decl is ClassDeclaration ||
113 decl is ClassTypeAlias || 119 decl is ClassTypeAlias ||
114 decl is FunctionTypeAlias) { 120 decl is FunctionTypeAlias) {
115 _pendingClasses[decl.element] = decl; 121 _pendingClasses[decl.element] = decl;
116 } 122 }
117 } 123 }
118 } 124 }
119 125
120 for (var unit in library.partsThenLibrary) body.add(_visit(unit)); 126 for (var unit in library.partsThenLibrary) body.add(_visit(unit));
121 127
128 // Ensure field slots for any fields we override from other libraries.
129 _fieldsNeedingStorage.forEach(_overrideField);
130
122 assert(_pendingClasses.isEmpty); 131 assert(_pendingClasses.isEmpty);
123 132
124 if (_exports.isNotEmpty) body.add(js.comment('Exports:')); 133 if (_exports.isNotEmpty) body.add(js.comment('Exports:'));
125 134
126 // TODO(jmesserly): make these immutable in JS? 135 // TODO(jmesserly): make these immutable in JS?
127 for (var name in _exports) { 136 for (var name in _exports) {
128 body.add(js.statement('#.# = #;', [_exportsVar, name, name])); 137 body.add(js.statement('#.# = #;', [_exportsVar, name, name]));
129 } 138 }
130 139
131 var name = jsLibraryName(libraryInfo.library); 140 var name = jsLibraryName(libraryInfo.library);
132 141
133 var defaultValue = js.call(jsDefaultValue); 142 var defaultValue = js.call(jsDefaultValue);
134 return new JS.Program([ 143 return new JS.Program([
135 js.statement('var #;', name), 144 js.statement('var #;', name),
136 js.statement("(function(#) { 'use strict'; #; })(# || (# = #));", [ 145 js.statement("(function(#) { 'use strict'; #; })(# || (# = #));", [
137 _exportsVar, 146 _exportsVar,
138 body, 147 body,
139 name, 148 name,
140 name, 149 name,
141 defaultValue 150 defaultValue
142 ]) 151 ])
143 ]); 152 ]);
144 } 153 }
145 154
146 JS.Statement _initPrivateSymbol(JSTemporary tmp) => 155 JS.Statement _initSymbol(JS.Identifier id) =>
147 js.statement('let # = $_SYMBOL(#);', [tmp, js.string(tmp.name, "'")]); 156 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 157
152 // TODO(jmesserly): this is a temporary workaround for `Symbol` in core, 158 // TODO(jmesserly): this is a temporary workaround for `Symbol` in core,
153 // until we have better name tracking. 159 // until we have better name tracking.
154 String get _SYMBOL { 160 String get _SYMBOL {
155 var name = currentLibrary.name; 161 var name = currentLibrary.name;
156 if (name == 'dart.core' || name == 'dart._internal') return 'dart.JsSymbol'; 162 if (name == 'dart.core' || name == 'dart._internal') return 'dart.JsSymbol';
157 return 'Symbol'; 163 return 'Symbol';
158 } 164 }
159 165
160 @override 166 @override
161 JS.Statement visitCompilationUnit(CompilationUnit node) { 167 JS.Statement visitCompilationUnit(CompilationUnit node) {
162 var source = node.element.source; 168 var source = node.element.source;
163 169
164 _constEvaluator = new ConstantEvaluator(source, types); 170 _constEvaluator = new ConstantEvaluator(source, types);
165 171
166 // TODO(jmesserly): scriptTag, directives. 172 // TODO(jmesserly): scriptTag, directives.
167 var body = <JS.Statement>[]; 173 var body = <JS.Statement>[];
168 for (var child in node.declarations) { 174 for (var child in node.declarations) {
169 // Attempt to group adjacent fields/properties. 175 // Attempt to group adjacent fields/properties.
170 if (child is! TopLevelVariableDeclaration) _flushLazyFields(body); 176 if (child is! TopLevelVariableDeclaration) _flushLazyFields(body);
171 if (child is! FunctionDeclaration) _flushLibraryProperties(body); 177 if (child is! FunctionDeclaration) _flushLibraryProperties(body);
172 178
173 var code = _visit(child); 179 var code = _visit(child);
174
175 if (code != null) { 180 if (code != null) {
176 if (_pendingPrivateNames.isNotEmpty) { 181 if (_pendingSymbols.isNotEmpty) {
177 body.addAll(_pendingPrivateNames.map(_initPrivateSymbol)); 182 body.addAll(_pendingSymbols.map(_initSymbol));
178 _pendingPrivateNames.clear(); 183 _pendingSymbols.clear();
179 }
180 if (_pendingExtensionMethodNames.isNotEmpty) {
181 body.addAll(
182 _pendingExtensionMethodNames.map(_initExtensionMethodSymbol));
183 _pendingExtensionMethodNames.clear();
184 } 184 }
185 body.add(code); 185 body.add(code);
186 } 186 }
187 } 187 }
188 188
189 // Flush any unwritten fields/properties. 189 // Flush any unwritten fields/properties.
190 _flushLazyFields(body); 190 _flushLazyFields(body);
191 _flushLibraryProperties(body); 191 _flushLibraryProperties(body);
192 192
193 assert(_pendingPrivateNames.isEmpty); 193 assert(_pendingSymbols.isEmpty);
194 return _statement(body); 194 return _statement(body);
195 } 195 }
196 196
197 bool isPublic(String name) => !name.startsWith('_'); 197 bool isPublic(String name) => !name.startsWith('_');
198 198
199 /// Conversions that we don't handle end up here. 199 /// Conversions that we don't handle end up here.
200 @override 200 @override
201 visitConversion(Conversion node) { 201 visitConversion(Conversion node) {
202 var from = node.baseType; 202 var from = node.baseType;
203 var to = node.convertedType; 203 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. 310 // generate it correctly when we refer to it.
311 if (isPublic(dartClassName)) _addExport(dartClassName); 311 if (isPublic(dartClassName)) _addExport(dartClassName);
312 return js.statement('let # = #;', [dartClassName, jsTypeName]); 312 return js.statement('let # = #;', [dartClassName, jsTypeName]);
313 } 313 }
314 return null; 314 return null;
315 } 315 }
316 316
317 @override 317 @override
318 JS.Statement visitClassDeclaration(ClassDeclaration node) { 318 JS.Statement visitClassDeclaration(ClassDeclaration node) {
319 // If we've already emitted this class, skip it. 319 // If we've already emitted this class, skip it.
320 var type = node.element.type; 320 var classElem = node.element;
321 if (_pendingClasses.remove(node.element) == null) return null; 321 var type = classElem.type;
322 if (_pendingClasses.remove(classElem) == null) return null;
322 323
323 var jsName = getAnnotationValue(node, _isJsNameAnnotation); 324 var jsName = getAnnotationValue(node, _isJsNameAnnotation);
324 if (jsName != null) return _emitJsType(node.name.name, jsName); 325 if (jsName != null) return _emitJsType(node.name.name, jsName);
325 326
326 currentClass = node; 327 currentClass = node;
327 328
328 var ctors = <ConstructorDeclaration>[]; 329 var ctors = <ConstructorDeclaration>[];
329 var fields = <FieldDeclaration>[]; 330 var fields = <FieldDeclaration>[];
330 var staticFields = <FieldDeclaration>[]; 331 var staticFields = <FieldDeclaration>[];
331 for (var member in node.members) { 332 for (var member in node.members) {
332 if (member is ConstructorDeclaration) { 333 if (member is ConstructorDeclaration) {
333 ctors.add(member); 334 ctors.add(member);
334 } else if (member is FieldDeclaration) { 335 } else if (member is FieldDeclaration) {
335 (member.isStatic ? staticFields : fields).add(member); 336 (member.isStatic ? staticFields : fields).add(member);
336 } 337 }
337 } 338 }
338 339
339 var classExpr = new JS.ClassExpression(new JS.Identifier(type.name), 340 var classExpr = new JS.ClassExpression(new JS.Identifier(type.name),
340 _classHeritage(node), _emitClassMethods(node, ctors, fields)); 341 _classHeritage(node), _emitClassMethods(node, ctors, fields));
341 342
342 var body = 343 var body =
343 _finishClassMembers(node.element, classExpr, ctors, staticFields); 344 _finishClassMembers(classElem, classExpr, ctors, fields, staticFields);
344 currentClass = null; 345 currentClass = null;
345 346
346 return _finishClassDef(type, body); 347 return _finishClassDef(type, body);
347 } 348 }
348 349
349 @override 350 @override
350 JS.Statement visitEnumDeclaration(EnumDeclaration node) => 351 JS.Statement visitEnumDeclaration(EnumDeclaration node) =>
351 _unimplementedCall("Unimplemented enum: $node").toStatement(); 352 _unimplementedCall("Unimplemented enum: $node").toStatement();
352 353
353 /// Given a class element and body, complete the class declaration. 354 /// 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; 545 return heritage;
545 } 546 }
546 547
547 List<JS.Method> _emitClassMethods(ClassDeclaration node, 548 List<JS.Method> _emitClassMethods(ClassDeclaration node,
548 List<ConstructorDeclaration> ctors, List<FieldDeclaration> fields) { 549 List<ConstructorDeclaration> ctors, List<FieldDeclaration> fields) {
549 var element = node.element; 550 var element = node.element;
550 var type = element.type; 551 var type = element.type;
551 var isObject = type.isObject; 552 var isObject = type.isObject;
552 var name = node.name.name; 553 var name = node.name.name;
553 554
554 var jsMethods = <JS.Method>[];
555 // Iff no constructor is specified for a class C, it implicitly has a 555 // Iff no constructor is specified for a class C, it implicitly has a
556 // default constructor `C() : super() {}`, unless C is class Object. 556 // default constructor `C() : super() {}`, unless C is class Object.
557 var jsMethods = <JS.Method>[];
557 if (ctors.isEmpty && !isObject) { 558 if (ctors.isEmpty && !isObject) {
558 jsMethods.add(_emitImplicitConstructor(node, name, fields)); 559 jsMethods.add(_emitImplicitConstructor(node, name, fields));
559 } 560 }
561
560 for (var member in node.members) { 562 for (var member in node.members) {
561 if (member is ConstructorDeclaration) { 563 if (member is ConstructorDeclaration) {
562 jsMethods.add(_emitConstructor(member, name, fields, isObject)); 564 jsMethods.add(_emitConstructor(member, name, fields, isObject));
563 } else if (member is MethodDeclaration) { 565 } else if (member is MethodDeclaration) {
564 jsMethods.add(_emitMethodDeclaration(type, member)); 566 jsMethods.add(_emitMethodDeclaration(type, member));
565 } 567 }
566 } 568 }
567 569
568 // Support for adapting dart:core Iterator/Iterable to ES6 versions. 570 // Support for adapting dart:core Iterator/Iterable to ES6 versions.
569 // This lets them use for-of loops transparently. 571 // This lets them use for-of loops transparently.
570 // https://github.com/lukehoban/es6features#iterators--forof 572 // https://github.com/lukehoban/es6features#iterators--forof
571 if (element.library.isDartCore && element.name == 'Iterable') { 573 if (element.library.isDartCore && element.name == 'Iterable') {
572 JS.Fun body = js.call('''function() { 574 JS.Fun body = js.call('''function() {
573 var iterator = this.iterator; 575 var iterator = this.iterator;
574 return { 576 return {
575 next() { 577 next() {
576 var done = iterator.moveNext(); 578 var done = iterator.moveNext();
577 return { done: done, current: done ? void 0 : iterator.current }; 579 return { done: done, current: done ? void 0 : iterator.current };
578 } 580 }
579 }; 581 };
580 }'''); 582 }''');
581 jsMethods.add(new JS.Method(js.call('$_SYMBOL.iterator'), body)); 583 jsMethods.add(new JS.Method(js.call('$_SYMBOL.iterator'), body));
582 } 584 }
583 return jsMethods.where((m) => m != null).toList(growable: false); 585 return jsMethods.where((m) => m != null).toList(growable: false);
584 } 586 }
585 587
586 /// Emit class members that need to come after the class declaration, such 588 /// Emit class members that need to come after the class declaration, such
587 /// as static fields. See [_emitClassMethods] for things that are emitted 589 /// as static fields. See [_emitClassMethods] for things that are emitted
588 /// insite the ES6 `class { ... }` node. 590 /// inside the ES6 `class { ... }` node.
589 JS.Statement _finishClassMembers(ClassElement classElem, 591 JS.Statement _finishClassMembers(ClassElement classElem,
590 JS.ClassExpression cls, List<ConstructorDeclaration> ctors, 592 JS.ClassExpression cls, List<ConstructorDeclaration> ctors,
591 List<FieldDeclaration> staticFields) { 593 List<FieldDeclaration> fields, List<FieldDeclaration> staticFields) {
592 var name = classElem.name; 594 var name = classElem.name;
593
594 var body = <JS.Statement>[]; 595 var body = <JS.Statement>[];
595 body.add(new JS.ClassDeclaration(cls)); 596 body.add(new JS.ClassDeclaration(cls));
596 597
597 // Interfaces 598 // Interfaces
598 if (classElem.interfaces.isNotEmpty) { 599 if (classElem.interfaces.isNotEmpty) {
599 body.add(js.statement('#[dart.implements] = () => #;', [ 600 body.add(js.statement('#[dart.implements] = () => #;', [
600 name, 601 name,
601 new JS.ArrayInitializer( 602 new JS.ArrayInitializer(
602 classElem.interfaces.map(_emitTypeName).toList()) 603 classElem.interfaces.map(_emitTypeName).toList())
603 ])); 604 ]));
604 } 605 }
605 606
606 // Named constructors 607 // Named constructors
607 for (ConstructorDeclaration member in ctors) { 608 for (ConstructorDeclaration member in ctors) {
608 if (member.name != null) { 609 if (member.name != null) {
609 body.add(js.statement('dart.defineNamedConstructor(#, #);', [ 610 body.add(js.statement('dart.defineNamedConstructor(#, #);', [
610 name, 611 name,
611 _emitMemberName(member.name.name, isStatic: true) 612 _emitMemberName(member.name.name, isStatic: true)
612 ])); 613 ]));
613 } 614 }
614 } 615 }
615 616
617 // Instance fields, if they override getter/setter pairs
618 for (FieldDeclaration member in fields) {
619 for (VariableDeclaration fieldDecl in member.fields.variables) {
620 var field = fieldDecl.element;
621 if (_fieldsNeedingStorage.remove(field)) {
622 body.add(_overrideField(field));
623 }
624 }
625 }
626
616 // Static fields 627 // Static fields
617 var lazyStatics = <VariableDeclaration>[]; 628 var lazyStatics = <VariableDeclaration>[];
618 for (FieldDeclaration member in staticFields) { 629 for (FieldDeclaration member in staticFields) {
619 for (VariableDeclaration field in member.fields.variables) { 630 for (VariableDeclaration field in member.fields.variables) {
620 var fieldName = field.name.name; 631 var fieldName = field.name.name;
621 if (field.isConst || _isFieldInitConstant(field)) { 632 if (field.isConst || _isFieldInitConstant(field)) {
622 var init = _visit(field.initializer); 633 var init = _visit(field.initializer);
623 if (init == null) init = new JS.LiteralNull(); 634 if (init == null) init = new JS.LiteralNull();
624 body.add(js.statement('#.# = #;', [name, fieldName, init])); 635 body.add(js.statement('#.# = #;', [name, fieldName, init]));
625 } else { 636 } else {
626 lazyStatics.add(field); 637 lazyStatics.add(field);
627 } 638 }
628 } 639 }
629 } 640 }
630 var lazy = _emitLazyFields(new JS.Identifier(name), lazyStatics); 641 var lazy = _emitLazyFields(new JS.Identifier(name), lazyStatics);
631 if (lazy != null) body.add(lazy); 642 if (lazy != null) body.add(lazy);
632 643
633 return _statement(body); 644 return _statement(body);
634 } 645 }
635 646
647 JS.Statement _overrideField(FieldElement e) {
648 var cls = e.enclosingElement;
649 return js.statement('dart.virtualField(#, #)', [
650 cls.name,
651 _emitMemberName(e.name, type: cls.type)
652 ]);
653 }
654
636 /// Generates the implicit default constructor for class C of the form 655 /// Generates the implicit default constructor for class C of the form
637 /// `C() : super() {}`. 656 /// `C() : super() {}`.
638 JS.Method _emitImplicitConstructor( 657 JS.Method _emitImplicitConstructor(
639 ClassDeclaration node, String name, List<FieldDeclaration> fields) { 658 ClassDeclaration node, String name, List<FieldDeclaration> fields) {
640 // If we don't have a method body, skip this. 659 // If we don't have a method body, skip this.
641 if (fields.isEmpty) return null; 660 if (fields.isEmpty) return null;
642 661
643 dynamic body = _initializeFields(fields); 662 dynamic body = _initializeFields(node, fields);
644 var superCall = _superConstructorCall(node); 663 var superCall = _superConstructorCall(node);
645 if (superCall != null) body = [[body, superCall]]; 664 if (superCall != null) body = [[body, superCall]];
646 return new JS.Method( 665 return new JS.Method(
647 _propertyName(name), js.call('function() { #; }', body)); 666 _propertyName(name), js.call('function() { #; }', body));
648 } 667 }
649 668
650 JS.Method _emitConstructor(ConstructorDeclaration node, String className, 669 JS.Method _emitConstructor(ConstructorDeclaration node, String className,
651 List<FieldDeclaration> fields, bool isObject) { 670 List<FieldDeclaration> fields, bool isObject) {
652 if (_externalOrNative(node)) return null; 671 if (_externalOrNative(node)) return null;
653 672
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after
723 body.add(_visit(redirectCall)); 742 body.add(_visit(redirectCall));
724 return new JS.Block(body); 743 return new JS.Block(body);
725 } 744 }
726 745
727 // Initializers only run for non-factory constructors. 746 // Initializers only run for non-factory constructors.
728 if (node.factoryKeyword == null) { 747 if (node.factoryKeyword == null) {
729 // Generate field initializers. 748 // Generate field initializers.
730 // These are expanded into each non-redirecting constructor. 749 // These are expanded into each non-redirecting constructor.
731 // In the future we may want to create an initializer function if we have 750 // In the future we may want to create an initializer function if we have
732 // multiple constructors, but it needs to be balanced against readability. 751 // multiple constructors, but it needs to be balanced against readability.
733 body.add(_initializeFields(fields, node.parameters, node.initializers)); 752 body.add(_initializeFields(
753 node.parent, fields, node.parameters, node.initializers));
734 754
735 var superCall = node.initializers.firstWhere( 755 var superCall = node.initializers.firstWhere(
736 (i) => i is SuperConstructorInvocation, orElse: () => null); 756 (i) => i is SuperConstructorInvocation, orElse: () => null);
737 757
738 // If no superinitializer is provided, an implicit superinitializer of the 758 // If no superinitializer is provided, an implicit superinitializer of the
739 // form `super()` is added at the end of the initializer list, unless the 759 // form `super()` is added at the end of the initializer list, unless the
740 // enclosing class is class Object. 760 // enclosing class is class Object.
741 var jsSuper = _superConstructorCall(node.parent, superCall); 761 var jsSuper = _superConstructorCall(node.parent, superCall);
742 if (jsSuper != null) body.add(jsSuper); 762 if (jsSuper != null) body.add(jsSuper);
743 } 763 }
(...skipping 28 matching lines...) Expand all
772 var args = node != null ? _visit(node.argumentList) : []; 792 var args = node != null ? _visit(node.argumentList) : [];
773 return js.statement('super.#(#);', [name, args])..sourceInformation = node; 793 return js.statement('super.#(#);', [name, args])..sourceInformation = node;
774 } 794 }
775 795
776 /// Initialize fields. They follow the sequence: 796 /// Initialize fields. They follow the sequence:
777 /// 797 ///
778 /// 1. field declaration initializer if non-const, 798 /// 1. field declaration initializer if non-const,
779 /// 2. field initializing parameters, 799 /// 2. field initializing parameters,
780 /// 3. constructor field initializers, 800 /// 3. constructor field initializers,
781 /// 4. initialize fields not covered in 1-3 801 /// 4. initialize fields not covered in 1-3
782 JS.Statement _initializeFields(List<FieldDeclaration> fields, 802 JS.Statement _initializeFields(
803 ClassDeclaration classDecl, List<FieldDeclaration> fieldDecls,
783 [FormalParameterList parameters, 804 [FormalParameterList parameters,
784 NodeList<ConstructorInitializer> initializers]) { 805 NodeList<ConstructorInitializer> initializers]) {
785 var body = <JS.Statement>[];
786 806
787 // Run field initializers if they can have side-effects. 807 // Run field initializers if they can have side-effects.
808 var fields = new Map<FieldElement, JS.Expression>();
788 var unsetFields = new Map<FieldElement, VariableDeclaration>(); 809 var unsetFields = new Map<FieldElement, VariableDeclaration>();
789 for (var declaration in fields) { 810 for (var declaration in fieldDecls) {
790 for (var field in declaration.fields.variables) { 811 for (var fieldNode in declaration.fields.variables) {
791 if (_isFieldInitConstant(field)) { 812 var element = fieldNode.element;
792 unsetFields[field.element] = field; 813 if (_isFieldInitConstant(fieldNode)) {
814 unsetFields[element] = fieldNode;
793 } else { 815 } else {
794 body.add(js.statement( 816 fields[element] = _visitInitializer(fieldNode);
795 '# = #;', [_visit(field.name), _visitInitializer(field)]));
796 } 817 }
797 } 818 }
798 } 819 }
799 820
800 // Initialize fields from `this.fieldName` parameters. 821 // Initialize fields from `this.fieldName` parameters.
801 if (parameters != null) { 822 if (parameters != null) {
802 for (var p in parameters.parameters) { 823 for (var p in parameters.parameters) {
803 if (p is DefaultFormalParameter) p = p.parameter; 824 var element = p.element;
804 if (p is FieldFormalParameter) { 825 if (element is FieldFormalParameterElement) {
805 var field = (p.element as FieldFormalParameterElement).field; 826 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 } 827 }
814 } 828 }
815 } 829 }
816 830
817 // Run constructor field initializers such as `: foo = bar.baz` 831 // Run constructor field initializers such as `: foo = bar.baz`
818 if (initializers != null) { 832 if (initializers != null) {
819 for (var init in initializers) { 833 for (var init in initializers) {
820 if (init is ConstructorFieldInitializer) { 834 if (init is ConstructorFieldInitializer) {
821 body.add(js.statement( 835 fields[init.fieldName.staticElement] = _visit(init.expression);
822 '# = #;', [_visit(init.fieldName), _visit(init.expression)]));
823 unsetFields.remove(init.fieldName.staticElement);
824 } 836 }
825 } 837 }
826 } 838 }
827 839
840 for (var f in fields.keys) unsetFields.remove(f);
841
828 // Initialize all remaining fields 842 // Initialize all remaining fields
829 unsetFields.forEach((field, fieldNode) { 843 unsetFields.forEach((element, fieldNode) {
830 JS.Expression value; 844 JS.Expression value;
831 if (fieldNode.initializer != null) { 845 if (fieldNode.initializer != null) {
832 value = _visit(fieldNode.initializer); 846 value = _visit(fieldNode.initializer);
833 } else { 847 } else {
834 var type = rules.elementType(field); 848 var type = rules.elementType(element);
835 value = new JS.LiteralNull(); 849 value = new JS.LiteralNull();
836 if (rules.maybeNonNullableType(type)) { 850 if (rules.maybeNonNullableType(type)) {
837 value = js.call('dart.as(#, #)', [value, _emitTypeName(type)]); 851 value = js.call('dart.as(#, #)', [value, _emitTypeName(type)]);
838 } 852 }
839 } 853 }
840 var memberName = 854 fields[element] = value;
841 _emitMemberName(field.name, type: field.enclosingElement.type);
842 body.add(js.statement('this.# = #;', [memberName, value]));
843 }); 855 });
844 856
857 var classElem = classDecl.element;
858 bool classCanBeMixin = !classElem.type.isObject &&
859 classElem.supertype.isObject &&
860 classElem.mixins.isEmpty;
861
862 var body = <JS.Statement>[];
863 fields.forEach((FieldElement e, JS.Expression initialValue) {
864 var access = _emitMemberName(e.name, type: e.enclosingElement.type);
865
866 // Fields on private types can't be extended outside this library.
867 // Private fields on public types also can't be extended, as long as the
868 // type can't be mixed in.
869 bool isSealed = classElem.isPrivate || e.isPrivate && !classCanBeMixin;
870
871 JS.Statement init;
872 if (isSealed && !_fieldsNeedingStorage.contains(e)) {
873 // Concrete field: doesn't override anything else, and is sealed so it
874 // isn't and can't be overridden by anything else.
875 init = js.statement('this.# = #;', [access, initialValue]);
876 } else {
877 init = js.statement('dart.initField(#, this, #, #);', [
878 classDecl.name.name,
879 access,
880 initialValue
881 ]);
882 }
883 body.add(init);
884 });
845 return _statement(body); 885 return _statement(body);
846 } 886 }
847 887
848 FormalParameterList _parametersOf(node) { 888 FormalParameterList _parametersOf(node) {
849 // Note: ConstructorDeclaration is intentionally skipped here so we can 889 // Note: ConstructorDeclaration is intentionally skipped here so we can
850 // emit the argument initializers in a different place. 890 // emit the argument initializers in a different place.
851 // TODO(jmesserly): clean this up. If we can model ES6 spread/rest args, we 891 // TODO(jmesserly): clean this up. If we can model ES6 spread/rest args, we
852 // could handle argument initializers more consistently in a separate 892 // could handle argument initializers more consistently in a separate
853 // lowering pass. 893 // lowering pass.
854 if (node is MethodDeclaration) return node.parameters; 894 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. 2239 /// for this transformation to happen, otherwise binary minus is assumed.
2200 /// 2240 ///
2201 /// Equality is a bit special, it is generated via the Dart `equals` runtime 2241 /// 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 '=='. 2242 /// helper, that checks for null. The user defined method is called '=='.
2203 /// 2243 ///
2204 JS.Expression _emitMemberName(String name, 2244 JS.Expression _emitMemberName(String name,
2205 {DartType type, bool unary: false, bool isStatic: false}) { 2245 {DartType type, bool unary: false, bool isStatic: false}) {
2206 if (name.startsWith('_')) { 2246 if (name.startsWith('_')) {
2207 return _privateNames.putIfAbsent(name, () { 2247 return _privateNames.putIfAbsent(name, () {
2208 var t = new JSTemporary(name); 2248 var t = new JSTemporary(name);
2209 _pendingPrivateNames.add(t); 2249 _pendingSymbols.add(t);
2210 return t; 2250 return t;
2211 }); 2251 });
2212 } 2252 }
2253
2213 // Check for extension method: 2254 // Check for extension method:
2214 var extLibrary = _findExtensionLibrary(name, type); 2255 var extLibrary = _findExtensionLibrary(name, type);
2215 2256
2216 if (name == '[]') { 2257 if (name == '[]') {
2217 name = 'get'; 2258 name = 'get';
2218 } else if (name == '[]=') { 2259 } else if (name == '[]=') {
2219 name = 'set'; 2260 name = 'set';
2220 } else if (name == '-' && unary) { 2261 } else if (name == '-' && unary) {
2221 name = 'unary-'; 2262 name = 'unary-';
2222 } 2263 }
2223 2264
2224 if (isStatic && invalidJSStaticMethodName(name)) { 2265 if (isStatic && invalidJSStaticMethodName(name)) {
2225 // Choose an string name. Use an invalid identifier so it won't conflict 2266 // Choose an string name. Use an invalid identifier so it won't conflict
2226 // with any valid member names. 2267 // with any valid member names.
2227 // TODO(jmesserly): this works around the problem, but I'm pretty sure we 2268 // 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 2269 // don't need it, as static methods seemed to work. The only concrete
2229 // issue we saw was in the defineNamedConstructor helper function. 2270 // issue we saw was in the defineNamedConstructor helper function.
2230 name = '$name*'; 2271 name = '$name*';
2231 } 2272 }
2232 2273
2233 if (extLibrary != null) { 2274 if (extLibrary != null) {
2234 return js.call('#.#', [ 2275 return _extensionMethodName(name, extLibrary);
2235 _libraryName(extLibrary),
2236 _propertyName(_addExtensionMethodName(name, extLibrary))
2237 ]);
2238 } 2276 }
2239 2277
2240 return _propertyName(name); 2278 return _propertyName(name);
2241 } 2279 }
2242 2280
2243 LibraryElement _findExtensionLibrary(String name, DartType type) { 2281 LibraryElement _findExtensionLibrary(String name, DartType type) {
2244 if (type is! InterfaceType) return null; 2282 if (type is! InterfaceType) return null;
2245 2283
2246 var extLibrary = null; 2284 var extLibrary = null;
2247 var extensionTypes = _extensionMethods[name]; 2285 var extensionTypes = _extensionMethods[name];
2248 if (extensionTypes != null) { 2286 if (extensionTypes != null) {
2249 // Normalize the type to ignore generics. 2287 // Normalize the type to ignore generics.
2250 type = fillDynamicTypeArgs(type, types); 2288 type = fillDynamicTypeArgs(type, types);
2251 for (var t in extensionTypes) { 2289 for (var t in extensionTypes) {
2252 if (rules.isSubTypeOf(type, t)) { 2290 if (rules.isSubTypeOf(type, t)) {
2253 assert(extLibrary == null || extLibrary == t.element.library); 2291 assert(extLibrary == null || extLibrary == t.element.library);
2254 extLibrary = t.element.library; 2292 extLibrary = t.element.library;
2255 } 2293 }
2256 } 2294 }
2257 } 2295 }
2258 return extLibrary; 2296 return extLibrary;
2259 } 2297 }
2260 2298
2261 String _addExtensionMethodName(String name, LibraryElement extLibrary) { 2299 JS.Expression _extensionMethodName(String name, LibraryElement library) {
2262 var extensionMethodName = '\$$name'; 2300 var extName = '\$$name';
2263 if (extLibrary == currentLibrary) { 2301 if (library == currentLibrary) {
2264 // TODO(jacobr): need to do a better job ensuring that extension method 2302 // 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 2303 // name symbols do not conflict with other symbols before we can let
2266 // user defined libraries define extension methods. 2304 // user defined libraries define extension methods.
2267 if (_extensionMethodNames.add(extensionMethodName)) { 2305 if (_extensionMethodNames.add(extName)) {
2268 _pendingExtensionMethodNames.add(extensionMethodName); 2306 _pendingSymbols.add(new JS.Identifier(extName));
2269 _addExport(extensionMethodName); 2307 _addExport(extName);
2270 } 2308 }
2271 } 2309 }
2272 return extensionMethodName; 2310 return js.call('#.#', [_libraryName(library), _propertyName(extName)]);
2273 } 2311 }
2274 2312
2275 bool _externalOrNative(node) => 2313 bool _externalOrNative(node) =>
2276 node.externalKeyword != null || _functionBody(node) is NativeFunctionBody; 2314 node.externalKeyword != null || _functionBody(node) is NativeFunctionBody;
2277 2315
2278 FunctionBody _functionBody(node) => 2316 FunctionBody _functionBody(node) =>
2279 node is FunctionDeclaration ? node.functionExpression.body : node.body; 2317 node is FunctionDeclaration ? node.functionExpression.body : node.body;
2280 2318
2281 /// Choose a canonical name from the library element. 2319 /// Choose a canonical name from the library element.
2282 /// This never uses the library's name (the identifier in the `library` 2320 /// 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)); 2354 ..addAll(e.accessors.map((m) => m.name));
2317 for (var name in names) { 2355 for (var name in names) {
2318 _extensionMethods.putIfAbsent(name, () => []).add(type); 2356 _extensionMethods.putIfAbsent(name, () => []).add(type);
2319 } 2357 }
2320 } 2358 }
2321 } 2359 }
2322 2360
2323 TypeProvider get types => rules.provider; 2361 TypeProvider get types => rules.provider;
2324 2362
2325 String generateLibrary(LibraryUnit unit, LibraryInfo info) { 2363 String generateLibrary(LibraryUnit unit, LibraryInfo info) {
2326 var codegen = new JSCodegenVisitor(info, rules, _extensionMethods); 2364 var fields = findFieldOverrides(unit);
2365 var codegen = new JSCodegenVisitor(info, rules, _extensionMethods, fields);
2327 var module = codegen.emitLibrary(unit); 2366 var module = codegen.emitLibrary(unit);
2328 var dir = path.join(outDir, jsOutputPath(info, root)); 2367 var dir = path.join(outDir, jsOutputPath(info, root));
2329 return writeJsLibrary(module, dir, emitSourceMaps: options.emitSourceMaps); 2368 return writeJsLibrary(module, dir, emitSourceMaps: options.emitSourceMaps);
2330 } 2369 }
2331 } 2370 }
2332 2371
2333 /// Choose a canonical name from the library element. 2372 /// Choose a canonical name from the library element.
2334 /// This never uses the library's name (the identifier in the `library` 2373 /// This never uses the library's name (the identifier in the `library`
2335 /// declaration) as it doesn't have any meaningful rules enforced. 2374 /// declaration) as it doesn't have any meaningful rules enforced.
2336 String jsLibraryName(LibraryElement library) => canonicalLibraryName(library); 2375 String jsLibraryName(LibraryElement library) => canonicalLibraryName(library);
(...skipping 22 matching lines...) Expand all
2359 return filepath; 2398 return filepath;
2360 } 2399 }
2361 2400
2362 // TODO(jmesserly): validate the library. See issue #135. 2401 // TODO(jmesserly): validate the library. See issue #135.
2363 bool _isJsNameAnnotation(DartObjectImpl value) => value.type.name == 'JsName'; 2402 bool _isJsNameAnnotation(DartObjectImpl value) => value.type.name == 'JsName';
2364 2403
2365 // TODO(jacobr): we would like to do something like the following 2404 // TODO(jacobr): we would like to do something like the following
2366 // but we don't have summary support yet. 2405 // but we don't have summary support yet.
2367 // bool _supportJsExtensionMethod(AnnotatedNode node) => 2406 // bool _supportJsExtensionMethod(AnnotatedNode node) =>
2368 // _getAnnotation(node, "SupportJsExtensionMethod") != null; 2407 // _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