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

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

Issue 1636233002: fixes #427, static fields emitted outside the scope of their class (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: Created 4 years, 10 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/typed_data.js ('k') | lib/src/codegen/js_module_item_order.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, SplayTreeSet; 7 import 'dart:collection' show HashSet, HashMap, SplayTreeSet;
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 62 matching lines...) Expand 10 before | Expand all | Expand 10 after
73 73
74 /// The variable for the current catch clause 74 /// The variable for the current catch clause
75 SimpleIdentifier _catchParameter; 75 SimpleIdentifier _catchParameter;
76 76
77 /// In an async* function, this represents the stream controller parameter. 77 /// In an async* function, this represents the stream controller parameter.
78 JS.TemporaryId _asyncStarController; 78 JS.TemporaryId _asyncStarController;
79 79
80 /// Imported libraries, and the temporaries used to refer to them. 80 /// Imported libraries, and the temporaries used to refer to them.
81 final _imports = new Map<LibraryElement, JS.TemporaryId>(); 81 final _imports = new Map<LibraryElement, JS.TemporaryId>();
82 final _exports = new Set<String>(); 82 final _exports = new Set<String>();
83 final _lazyFields = <VariableDeclaration>[];
84 final _properties = <FunctionDeclaration>[]; 83 final _properties = <FunctionDeclaration>[];
85 final _privateNames = new HashMap<String, JS.TemporaryId>(); 84 final _privateNames = new HashMap<String, JS.TemporaryId>();
86 final _moduleItems = <JS.Statement>[]; 85 final _moduleItems = <JS.Statement>[];
87 final _temps = new HashMap<Element, JS.TemporaryId>(); 86 final _temps = new HashMap<Element, JS.TemporaryId>();
88 final _qualifiedIds = new List<Tuple2<Element, JS.MaybeQualifiedId>>(); 87 final _qualifiedIds = new List<Tuple2<Element, JS.MaybeQualifiedId>>();
89 88
90 /// The name for the library's exports inside itself. 89 /// The name for the library's exports inside itself.
91 /// `exports` was chosen as the most similar to ES module patterns. 90 /// `exports` was chosen as the most similar to ES module patterns.
92 final _dartxVar = new JS.Identifier('dartx'); 91 final _dartxVar = new JS.Identifier('dartx');
93 final _exportsVar = new JS.TemporaryId('exports'); 92 final _exportsVar = new JS.TemporaryId('exports');
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
147 146
148 for (var unit in library.partsThenLibrary) { 147 for (var unit in library.partsThenLibrary) {
149 _constField = new ConstFieldVisitor(types, unit); 148 _constField = new ConstFieldVisitor(types, unit);
150 149
151 for (var decl in unit.declarations) { 150 for (var decl in unit.declarations) {
152 if (decl is TopLevelVariableDeclaration) { 151 if (decl is TopLevelVariableDeclaration) {
153 visitTopLevelVariableDeclaration(decl); 152 visitTopLevelVariableDeclaration(decl);
154 } else { 153 } else {
155 _loader.loadDeclaration(decl, decl.element); 154 _loader.loadDeclaration(decl, decl.element);
156 } 155 }
157 if (decl is ClassDeclaration) {
158 // Static fields can be emitted into the top-level code, so they need
159 // to potentially be ordered independently of the class.
160 for (var member in decl.members) {
161 if (member is FieldDeclaration) {
162 visitFieldDeclaration(member);
163 }
164 }
165 }
166 } 156 }
167 } 157 }
168 158
169 // Flush any unwritten fields/properties. 159 // Flush any unwritten fields/properties.
170 _flushLazyFields(_moduleItems);
171 _flushLibraryProperties(_moduleItems); 160 _flushLibraryProperties(_moduleItems);
172 161
173 // Mark all qualified names as qualified or not, depending on if they need 162 // Mark all qualified names as qualified or not, depending on if they need
174 // to be loaded lazily or not. 163 // to be loaded lazily or not.
175 for (var elementIdPairs in _qualifiedIds) { 164 for (var elementIdPairs in _qualifiedIds) {
176 var element = elementIdPairs.e0; 165 var element = elementIdPairs.e0;
177 var id = elementIdPairs.e1; 166 var id = elementIdPairs.e1;
178 id.setQualified(!_loader.isLoaded(element)); 167 id.setQualified(!_loader.isLoaded(element));
179 } 168 }
180 169
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
233 // TODO(jmesserly): scriptTag support. 222 // TODO(jmesserly): scriptTag support.
234 // Enable this if we know we're targetting command line environment? 223 // Enable this if we know we're targetting command line environment?
235 // It doesn't work in browser. 224 // It doesn't work in browser.
236 // var jsBin = compiler.options.runnerOptions.v8Binary; 225 // var jsBin = compiler.options.runnerOptions.v8Binary;
237 // String scriptTag = null; 226 // String scriptTag = null;
238 // if (library.library.scriptTag != null) scriptTag = '/usr/bin/env $jsBin'; 227 // if (library.library.scriptTag != null) scriptTag = '/usr/bin/env $jsBin';
239 return new JS.Program(<JS.Statement>[moduleDef]); 228 return new JS.Program(<JS.Statement>[moduleDef]);
240 } 229 }
241 230
242 void _emitModuleItem(AstNode node) { 231 void _emitModuleItem(AstNode node) {
243 // Attempt to group adjacent fields/properties. 232 // Attempt to group adjacent properties.
244 if (node is! VariableDeclaration) _flushLazyFields(_moduleItems);
245 if (node is! FunctionDeclaration) _flushLibraryProperties(_moduleItems); 233 if (node is! FunctionDeclaration) _flushLibraryProperties(_moduleItems);
246 234
247 var code = _visit(node); 235 var code = _visit(node);
248 if (code != null) _moduleItems.add(code); 236 if (code != null) _moduleItems.add(code);
249 } 237 }
250 238
251 @override 239 @override
252 void visitLibraryDirective(LibraryDirective node) { 240 void visitLibraryDirective(LibraryDirective node) {
253 assert(_jsModuleValue == null); 241 assert(_jsModuleValue == null);
254 242
(...skipping 172 matching lines...) Expand 10 before | Expand all | Expand 10 after
427 @override 415 @override
428 JS.Statement visitClassDeclaration(ClassDeclaration node) { 416 JS.Statement visitClassDeclaration(ClassDeclaration node) {
429 var classElem = node.element; 417 var classElem = node.element;
430 var type = classElem.type; 418 var type = classElem.type;
431 var jsName = findAnnotation(classElem, isJSAnnotation); 419 var jsName = findAnnotation(classElem, isJSAnnotation);
432 420
433 if (jsName != null) return _emitJsType(node.name.name, jsName); 421 if (jsName != null) return _emitJsType(node.name.name, jsName);
434 422
435 var ctors = <ConstructorDeclaration>[]; 423 var ctors = <ConstructorDeclaration>[];
436 var fields = <FieldDeclaration>[]; 424 var fields = <FieldDeclaration>[];
425 var staticFields = <FieldDeclaration>[];
437 var methods = <MethodDeclaration>[]; 426 var methods = <MethodDeclaration>[];
438 for (var member in node.members) { 427 for (var member in node.members) {
439 if (member is ConstructorDeclaration) { 428 if (member is ConstructorDeclaration) {
440 ctors.add(member); 429 ctors.add(member);
441 } else if (member is FieldDeclaration && !member.isStatic) { 430 } else if (member is FieldDeclaration) {
442 fields.add(member); 431 (member.isStatic ? staticFields : fields).add(member);
443 } else if (member is MethodDeclaration) { 432 } else if (member is MethodDeclaration) {
444 methods.add(member); 433 methods.add(member);
445 } 434 }
446 } 435 }
447 436
448 var classExpr = new JS.ClassExpression(new JS.Identifier(type.name), 437 var classExpr = new JS.ClassExpression(new JS.Identifier(type.name),
449 _classHeritage(classElem), _emitClassMethods(node, ctors, fields)); 438 _classHeritage(classElem), _emitClassMethods(node, ctors, fields));
450 439
451 String jsPeerName; 440 String jsPeerName;
452 var jsPeer = findAnnotation(classElem, isJsPeerInterface); 441 var jsPeer = findAnnotation(classElem, isJsPeerInterface);
453 if (jsPeer != null) { 442 if (jsPeer != null) {
454 jsPeerName = 443 jsPeerName =
455 getConstantField(jsPeer, 'name', types.stringType)?.toStringValue(); 444 getConstantField(jsPeer, 'name', types.stringType)?.toStringValue();
456 } 445 }
457 446
458 var body = _finishClassMembers(classElem, classExpr, ctors, fields, methods, 447 var body = _finishClassMembers(classElem, classExpr, ctors, fields,
459 node.metadata, jsPeerName); 448 staticFields, methods, node.metadata, jsPeerName);
460 449
461 var result = _finishClassDef(type, body); 450 var result = _finishClassDef(type, body);
462 451
463 if (jsPeerName != null) { 452 if (jsPeerName != null) {
464 // This class isn't allowed to be lazy, because we need to set up 453 // This class isn't allowed to be lazy, because we need to set up
465 // the native JS type eagerly at this point. 454 // the native JS type eagerly at this point.
466 // If we wanted to support laziness, we could defer the hookup until 455 // If we wanted to support laziness, we could defer the hookup until
467 // the end of the Dart library cycle load. 456 // the end of the Dart library cycle load.
468 assert(_loader.isLoaded(classElem)); 457 assert(_loader.isLoaded(classElem));
469 458
(...skipping 228 matching lines...) Expand 10 before | Expand all | Expand 10 after
698 } 687 }
699 688
700 /// Emit class members that need to come after the class declaration, such 689 /// Emit class members that need to come after the class declaration, such
701 /// as static fields. See [_emitClassMethods] for things that are emitted 690 /// as static fields. See [_emitClassMethods] for things that are emitted
702 /// inside the ES6 `class { ... }` node. 691 /// inside the ES6 `class { ... }` node.
703 JS.Statement _finishClassMembers( 692 JS.Statement _finishClassMembers(
704 ClassElement classElem, 693 ClassElement classElem,
705 JS.ClassExpression cls, 694 JS.ClassExpression cls,
706 List<ConstructorDeclaration> ctors, 695 List<ConstructorDeclaration> ctors,
707 List<FieldDeclaration> fields, 696 List<FieldDeclaration> fields,
697 List<FieldDeclaration> staticFields,
708 List<MethodDeclaration> methods, 698 List<MethodDeclaration> methods,
709 List<Annotation> metadata, 699 List<Annotation> metadata,
710 String jsPeerName) { 700 String jsPeerName) {
711 var name = classElem.name; 701 var name = classElem.name;
712 var body = <JS.Statement>[]; 702 var body = <JS.Statement>[];
713 703
714 if (_extensionTypes.contains(classElem)) { 704 if (_extensionTypes.contains(classElem)) {
715 var dartxNames = <JS.Expression>[]; 705 var dartxNames = <JS.Expression>[];
716 for (var m in methods) { 706 for (var m in methods) {
717 if (!m.isAbstract && !m.isStatic && m.element.isPublic) { 707 if (!m.isAbstract && !m.isStatic && m.element.isPublic) {
(...skipping 30 matching lines...) Expand all
748 } 738 }
749 739
750 // Named constructors 740 // Named constructors
751 for (ConstructorDeclaration member in ctors) { 741 for (ConstructorDeclaration member in ctors) {
752 if (member.name != null && member.factoryKeyword == null) { 742 if (member.name != null && member.factoryKeyword == null) {
753 body.add(js.statement('dart.defineNamedConstructor(#, #);', 743 body.add(js.statement('dart.defineNamedConstructor(#, #);',
754 [name, _emitMemberName(member.name.name, isStatic: true)])); 744 [name, _emitMemberName(member.name.name, isStatic: true)]));
755 } 745 }
756 } 746 }
757 747
758 // Instance fields, if they override getter/setter pairs 748 // Emits instance fields, if they are virtual
749 // (in other words, they override a getter/setter pair).
759 for (FieldDeclaration member in fields) { 750 for (FieldDeclaration member in fields) {
760 for (VariableDeclaration fieldDecl in member.fields.variables) { 751 for (VariableDeclaration field in member.fields.variables) {
761 var field = fieldDecl.element as FieldElement; 752 if (_fieldsNeedingStorage.contains(field.element)) {
762 if (_fieldsNeedingStorage.contains(field)) { 753 body.add(_overrideField(field.element));
763 body.add(_overrideField(field));
764 } 754 }
765 } 755 }
766 } 756 }
767 757
768 // Emit the signature on the class recording the runtime type information 758 // Emit the signature on the class recording the runtime type information
769 var extensions = _extensionsToImplement(classElem); 759 var extensions = _extensionsToImplement(classElem);
770 { 760 {
771 var tStatics = <JS.Property>[]; 761 var tStatics = <JS.Property>[];
772 var tMethods = <JS.Property>[]; 762 var tMethods = <JS.Property>[];
773 var sNames = <JS.Expression>[]; 763 var sNames = <JS.Expression>[];
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after
843 // TODO(vsm): Make this optional per #268. 833 // TODO(vsm): Make this optional per #268.
844 // Metadata 834 // Metadata
845 if (metadata.isNotEmpty) { 835 if (metadata.isNotEmpty) {
846 body.add(js.statement('#[dart.metadata] = () => #;', [ 836 body.add(js.statement('#[dart.metadata] = () => #;', [
847 name, 837 name,
848 new JS.ArrayInitializer( 838 new JS.ArrayInitializer(
849 new List<JS.Expression>.from(metadata.map(_instantiateAnnotation))) 839 new List<JS.Expression>.from(metadata.map(_instantiateAnnotation)))
850 ])); 840 ]));
851 } 841 }
852 842
843 // Emits static fields. These are eager initialized if possible, otherwise
844 // they are made static.
vsm 2016/01/27 14:29:29 "made lazy"?
Jennifer Messerly 2016/01/27 17:26:28 good catch, done!
845 var lazyStatics = <VariableDeclaration>[];
846 for (FieldDeclaration member in staticFields) {
847 for (VariableDeclaration field in member.fields.variables) {
848 JS.Statement eagerField = _emitConstantStaticField(classElem, field);
849 if (eagerField != null) {
850 body.add(eagerField);
851 } else {
852 lazyStatics.add(field);
853 }
854 }
855 }
856 if (lazyStatics.isNotEmpty) {
857 body.add(_emitLazyFields(classElem, lazyStatics));
858 }
859
853 return _statement(body); 860 return _statement(body);
854 } 861 }
855 862
856 List<ExecutableElement> _extensionsToImplement(ClassElement element) { 863 List<ExecutableElement> _extensionsToImplement(ClassElement element) {
857 var members = <ExecutableElement>[]; 864 var members = <ExecutableElement>[];
858 if (_extensionTypes.contains(element)) return members; 865 if (_extensionTypes.contains(element)) return members;
859 866
860 // Collect all extension types we implement. 867 // Collect all extension types we implement.
861 var type = element.type; 868 var type = element.type;
862 var types = new Set<ClassElement>(); 869 var types = new Set<ClassElement>();
(...skipping 1279 matching lines...) Expand 10 before | Expand all | Expand 10 after
2142 return new JS.Yield(_visit(node.expression)); 2149 return new JS.Yield(_visit(node.expression));
2143 } 2150 }
2144 2151
2145 @override 2152 @override
2146 visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { 2153 visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
2147 for (var v in node.variables.variables) { 2154 for (var v in node.variables.variables) {
2148 _loader.loadDeclaration(v, v.element); 2155 _loader.loadDeclaration(v, v.element);
2149 } 2156 }
2150 } 2157 }
2151 2158
2152 /// Emits static fields.
2153 ///
2154 /// Instance fields are emitted in [_initializeFields].
2155 ///
2156 /// These are generally treated the same as top-level fields, see
2157 /// [visitTopLevelVariableDeclaration].
2158 @override
2159 visitFieldDeclaration(FieldDeclaration node) {
2160 if (!node.isStatic) return;
2161
2162 for (var f in node.fields.variables) {
2163 _loader.loadDeclaration(f, f.element);
2164 }
2165 }
2166
2167 _addExport(String name) { 2159 _addExport(String name) {
2168 if (!_exports.add(name)) throw 'Duplicate top level name found: $name'; 2160 if (!_exports.add(name)) throw 'Duplicate top level name found: $name';
2169 } 2161 }
2170 2162
2171 @override 2163 @override
2172 JS.Statement visitVariableDeclarationStatement( 2164 JS.Statement visitVariableDeclarationStatement(
2173 VariableDeclarationStatement node) { 2165 VariableDeclarationStatement node) {
2174 // Special case a single variable with an initializer. 2166 // Special case a single variable with an initializer.
2175 // This helps emit cleaner code for things like: 2167 // This helps emit cleaner code for things like:
2176 // var result = []..add(1)..add(2); 2168 // var result = []..add(1)..add(2);
2177 if (node.variables.variables.length == 1) { 2169 if (node.variables.variables.length == 1) {
2178 var v = node.variables.variables.single; 2170 var v = node.variables.variables.single;
2179 if (v.initializer != null) { 2171 if (v.initializer != null) {
2180 var name = new JS.Identifier(v.name.name); 2172 var name = new JS.Identifier(v.name.name);
2181 return _visit(v.initializer).toVariableDeclaration(name); 2173 return _visit(v.initializer).toVariableDeclaration(name);
2182 } 2174 }
2183 } 2175 }
2184 return _visit(node.variables).toStatement(); 2176 return _visit(node.variables).toStatement();
2185 } 2177 }
2186 2178
2187 @override 2179 @override
2188 visitVariableDeclarationList(VariableDeclarationList node) { 2180 visitVariableDeclarationList(VariableDeclarationList node) {
2189 return new JS.VariableDeclarationList( 2181 return new JS.VariableDeclarationList(
2190 'let', _visitList(node.variables) as List<JS.VariableInitialization>); 2182 'let', _visitList(node.variables) as List<JS.VariableInitialization>);
2191 } 2183 }
2192 2184
2193 @override 2185 @override
2194 visitVariableDeclaration(VariableDeclaration node) { 2186 visitVariableDeclaration(VariableDeclaration node) {
2195 if (node.element is PropertyInducingElement) return _emitStaticField(node); 2187 if (node.element is PropertyInducingElement) {
2188 // Static and instance fields are handled elsewhere.
2189 assert(node.element is TopLevelVariableElement);
2190 return _emitTopLevelField(node);
2191 }
2196 2192
2197 var name = new JS.Identifier(node.name.name); 2193 var name = new JS.Identifier(node.name.name);
2198 return new JS.VariableInitialization(name, _visitInitializer(node)); 2194 return new JS.VariableInitialization(name, _visitInitializer(node));
2199 } 2195 }
2200 2196
2201 bool _isFinalJSDecl(AstNode field) => 2197 bool _isFinalJSDecl(AstNode field) =>
2202 field is VariableDeclaration && 2198 field is VariableDeclaration &&
2203 field.isFinal && 2199 field.isFinal &&
2204 _isJSInvocation(field.initializer); 2200 _isJSInvocation(field.initializer);
2205 2201
2206 /// Emits a static or top-level field. 2202 /// Emits a static field. If the field is a lazy static,
2207 JS.Statement _emitStaticField(VariableDeclaration field) { 2203 JS.Statement _emitConstantStaticField(
2204 ClassElement classElem, VariableDeclaration field) {
2208 PropertyInducingElement element = field.element; 2205 PropertyInducingElement element = field.element;
2209 assert(element.isStatic); 2206 assert(element.isStatic);
2210 2207
2208 // If the field is constant, try to avoid the lazy static.
2209 _loader.startCheckingReferences();
2210 JS.Expression jsInit = _visitInitializer(field);
2211 bool isLoaded = _loader.finishCheckingReferences();
2212 bool eagerInit =
2213 isLoaded && (field.isConst || _constField.isFieldInitConstant(field));
vsm 2016/01/27 14:29:29 Could this be an '||' instead of an '&&'? I.e., i
Jennifer Messerly 2016/01/27 17:26:28 The initializer must not cause visible side effect
2214
2215 var fieldName = field.name.name;
2216 if (eagerInit && !JS.invalidStaticFieldName(fieldName)) {
2217 return annotateVariable(
2218 js.statement('#.# = #;', [
2219 classElem.name,
2220 _emitMemberName(fieldName, isStatic: true),
2221 jsInit
2222 ]),
2223 field.element);
2224 }
2225
2226 // This means it should be treated as a lazy field.
2227 // TODO(jmesserly): we're throwing away the initializer expression,
2228 // which will force us to regenerate it.
2229 return null;
2230 }
2231
2232 /// Emits a top-level field.
2233 JS.Statement _emitTopLevelField(VariableDeclaration field) {
2234 TopLevelVariableElement element = field.element;
2235 assert(element.isStatic);
2236
2211 bool eagerInit; 2237 bool eagerInit;
2212 JS.Expression jsInit; 2238 JS.Expression jsInit;
2213 if (field.isConst || _constField.isFieldInitConstant(field)) { 2239 if (field.isConst || _constField.isFieldInitConstant(field)) {
2214 // If the field is constant, try and generate it at the top level. 2240 // If the field is constant, try and generate it at the top level.
2215 _loader.startTopLevel(element); 2241 _loader.startTopLevel(element);
2216 jsInit = _visitInitializer(field); 2242 jsInit = _visitInitializer(field);
2217 _loader.finishTopLevel(element); 2243 _loader.finishTopLevel(element);
2218 eagerInit = _loader.isLoaded(element); 2244 eagerInit = _loader.isLoaded(element);
2219 } else { 2245 } else {
2246 // TODO(jmesserly): we're visiting the initializer here, and again
2247 // later on when we emit lazy fields. That seems busted.
2220 jsInit = _visitInitializer(field); 2248 jsInit = _visitInitializer(field);
2221 eagerInit = false; 2249 eagerInit = false;
2222 } 2250 }
2223 2251
2224 // Treat `final x = JS('', '...')` as a const (non-lazy) to help compile 2252 // Treat `final x = JS('', '...')` as a const (non-lazy) to help compile
2225 // runtime helpers. 2253 // runtime helpers.
2226 var isJSTopLevel = field.isFinal && _isFinalJSDecl(field); 2254 var isJSTopLevel = field.isFinal && _isFinalJSDecl(field);
2227 if (isJSTopLevel) eagerInit = true; 2255 if (isJSTopLevel) eagerInit = true;
2228 2256
2229 var fieldName = field.name.name; 2257 var fieldName = _getJSExportName(element) ?? field.name.name;
2230 if (element is TopLevelVariableElement) { 2258 if (field.isConst && eagerInit || isJSTopLevel) {
2231 fieldName = _getJSExportName(element) ?? fieldName;
2232 }
2233 if ((field.isConst && eagerInit && element is TopLevelVariableElement) ||
2234 isJSTopLevel) {
2235 // constant fields don't change, so we can generate them as `let` 2259 // constant fields don't change, so we can generate them as `let`
2236 // but add them to the module's exports. However, make sure we generate 2260 // but add them to the module's exports. However, make sure we generate
2237 // anything they depend on first. 2261 // anything they depend on first.
2238
2239 if (isPublic(fieldName)) _addExport(fieldName); 2262 if (isPublic(fieldName)) _addExport(fieldName);
2240 var declKeyword = field.isConst || field.isFinal ? 'const' : 'let'; 2263 var declKeyword = field.isConst || field.isFinal ? 'const' : 'let';
2241 return annotateVariable( 2264 return annotateVariable(
2242 js.statement( 2265 js.statement(
2243 '$declKeyword # = #;', [new JS.Identifier(fieldName), jsInit]), 2266 '$declKeyword # = #;', [new JS.Identifier(fieldName), jsInit]),
2244 field.element); 2267 field.element);
2245 } 2268 }
2246 2269
2247 if (eagerInit && !JS.invalidStaticFieldName(fieldName)) { 2270 if (eagerInit && !JS.invalidStaticFieldName(fieldName)) {
2248 return annotateVariable( 2271 return annotateVariable(
2249 js.statement('# = #;', [_visit(field.name), jsInit]), field.element); 2272 js.statement('# = #;', [_visit(field.name), jsInit]), field.element);
2250 } 2273 }
2251 2274
2252 var body = <JS.Statement>[]; 2275 return _emitLazyFields(currentLibrary, [field]);
2253 if (_lazyFields.isNotEmpty) {
2254 var existingTarget = _lazyFields[0].element.enclosingElement;
2255 if (existingTarget != element.enclosingElement) {
2256 _flushLazyFields(body);
2257 }
2258 }
2259
2260 _lazyFields.add(field);
2261
2262 return _statement(body);
2263 } 2276 }
2264 2277
2265 JS.Expression _visitInitializer(VariableDeclaration node) { 2278 JS.Expression _visitInitializer(VariableDeclaration node) {
2266 var value = _visit(node.initializer); 2279 var value = _visit(node.initializer);
2267 // explicitly initialize to null, to avoid getting `undefined`. 2280 // explicitly initialize to null, to avoid getting `undefined`.
2268 // TODO(jmesserly): do this only for vars that aren't definitely assigned. 2281 // TODO(jmesserly): do this only for vars that aren't definitely assigned.
2269 return value ?? new JS.LiteralNull(); 2282 return value ?? new JS.LiteralNull();
2270 } 2283 }
2271 2284
2272 void _flushLazyFields(List<JS.Statement> body) { 2285 JS.Statement _emitLazyFields(
2273 if (_lazyFields.isEmpty) return; 2286 Element target, List<VariableDeclaration> fields) {
2274 body.add(_emitLazyFields(_lazyFields));
2275 _lazyFields.clear();
2276 }
2277
2278 JS.Statement _emitLazyFields(List<VariableDeclaration> fields) {
2279 var methods = []; 2287 var methods = [];
2280 for (var node in fields) { 2288 for (var node in fields) {
2281 var name = node.name.name; 2289 var name = node.name.name;
2282 var element = node.element; 2290 var element = node.element;
2283 var access = _emitMemberName(name, type: element.type, isStatic: true); 2291 var access = _emitMemberName(name, isStatic: true);
2284 methods.add(annotate( 2292 methods.add(annotate(
2285 new JS.Method( 2293 new JS.Method(
2286 access, 2294 access,
2287 js.call('function() { return #; }', _visit(node.initializer)) 2295 js.call('function() { return #; }', _visit(node.initializer))
2288 as JS.Fun, 2296 as JS.Fun,
2289 isGetter: true), 2297 isGetter: true),
2290 _findAccessor(element, getter: true))); 2298 _findAccessor(element, getter: true)));
2291 2299
2292 // TODO(jmesserly): use a dummy setter to indicate writable. 2300 // TODO(jmesserly): currently uses a dummy setter to indicate writable.
2293 if (!node.isFinal) { 2301 if (!node.isFinal && !node.isConst) {
2294 methods.add(annotate( 2302 methods.add(annotate(
2295 new JS.Method(access, js.call('function(_) {}') as JS.Fun, 2303 new JS.Method(access, js.call('function(_) {}') as JS.Fun,
2296 isSetter: true), 2304 isSetter: true),
2297 _findAccessor(element, getter: false))); 2305 _findAccessor(element, getter: false)));
2298 } 2306 }
2299 } 2307 }
2300 2308
2301 JS.Expression objExpr = _exportsVar; 2309 JS.Expression objExpr;
2302 var target = _lazyFields[0].element.enclosingElement;
2303 if (target is ClassElement) { 2310 if (target is ClassElement) {
2304 objExpr = new JS.Identifier(target.type.name); 2311 objExpr = new JS.Identifier(target.type.name);
2312 } else {
2313 objExpr = _libraryName(target);
2305 } 2314 }
2306 2315
2307 return js 2316 return js
2308 .statement('dart.defineLazyProperties(#, { # });', [objExpr, methods]); 2317 .statement('dart.defineLazyProperties(#, { # });', [objExpr, methods]);
2309 } 2318 }
2310 2319
2311 PropertyAccessorElement _findAccessor(VariableElement element, 2320 PropertyAccessorElement _findAccessor(VariableElement element,
2312 {bool getter}) { 2321 {bool getter}) {
2313 var parent = element.enclosingElement; 2322 var parent = element.enclosingElement;
2314 if (parent is ClassElement) { 2323 if (parent is ClassElement) {
(...skipping 527 matching lines...) Expand 10 before | Expand all | Expand 10 after
2842 return true; 2851 return true;
2843 } 2852 }
2844 2853
2845 /// Shared code for [PrefixedIdentifier] and [PropertyAccess]. 2854 /// Shared code for [PrefixedIdentifier] and [PropertyAccess].
2846 JS.Expression _emitGet(Expression target, SimpleIdentifier memberId) { 2855 JS.Expression _emitGet(Expression target, SimpleIdentifier memberId) {
2847 var member = memberId.staticElement; 2856 var member = memberId.staticElement;
2848 if (member is PropertyAccessorElement) { 2857 if (member is PropertyAccessorElement) {
2849 member = (member as PropertyAccessorElement).variable; 2858 member = (member as PropertyAccessorElement).variable;
2850 } 2859 }
2851 bool isStatic = member is ClassMemberElement && member.isStatic; 2860 bool isStatic = member is ClassMemberElement && member.isStatic;
2852 if (isStatic) {
2853 _loader.declareBeforeUse(member);
2854 }
2855 var name = _emitMemberName(memberId.name, 2861 var name = _emitMemberName(memberId.name,
2856 type: getStaticType(target), isStatic: isStatic); 2862 type: getStaticType(target), isStatic: isStatic);
2857 if (DynamicInvoke.get(target)) { 2863 if (DynamicInvoke.get(target)) {
2858 return js.call('dart.$DLOAD(#, #)', [_visit(target), name]); 2864 return js.call('dart.$DLOAD(#, #)', [_visit(target), name]);
2859 } 2865 }
2860 2866
2861 String code; 2867 String code;
2862 if (member != null && member is MethodElement && !isStatic) { 2868 if (member != null && member is MethodElement && !isStatic) {
2863 // Tear-off methods: explicitly bind it. 2869 // Tear-off methods: explicitly bind it.
2864 if (target is SuperExpression) { 2870 if (target is SuperExpression) {
(...skipping 755 matching lines...) Expand 10 before | Expand all | Expand 10 after
3620 3626
3621 /// A special kind of element created by the compiler, signifying a temporary 3627 /// A special kind of element created by the compiler, signifying a temporary
3622 /// variable. These objects use instance equality, and should be shared 3628 /// variable. These objects use instance equality, and should be shared
3623 /// everywhere in the tree where they are treated as the same variable. 3629 /// everywhere in the tree where they are treated as the same variable.
3624 class TemporaryVariableElement extends LocalVariableElementImpl { 3630 class TemporaryVariableElement extends LocalVariableElementImpl {
3625 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); 3631 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name);
3626 3632
3627 int get hashCode => identityHashCode(this); 3633 int get hashCode => identityHashCode(this);
3628 bool operator ==(Object other) => identical(this, other); 3634 bool operator ==(Object other) => identical(this, other);
3629 } 3635 }
OLDNEW
« no previous file with comments | « lib/runtime/dart/typed_data.js ('k') | lib/src/codegen/js_module_item_order.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698