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

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: merged2 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 63 matching lines...) Expand 10 before | Expand all | Expand 10 after
74 74
75 /// The variable for the current catch clause 75 /// The variable for the current catch clause
76 SimpleIdentifier _catchParameter; 76 SimpleIdentifier _catchParameter;
77 77
78 /// In an async* function, this represents the stream controller parameter. 78 /// In an async* function, this represents the stream controller parameter.
79 JS.TemporaryId _asyncStarController; 79 JS.TemporaryId _asyncStarController;
80 80
81 /// Imported libraries, and the temporaries used to refer to them. 81 /// Imported libraries, and the temporaries used to refer to them.
82 final _imports = new Map<LibraryElement, JS.TemporaryId>(); 82 final _imports = new Map<LibraryElement, JS.TemporaryId>();
83 final _exports = new Set<String>(); 83 final _exports = new Set<String>();
84 final _lazyFields = <VariableDeclaration>[];
85 final _properties = <FunctionDeclaration>[]; 84 final _properties = <FunctionDeclaration>[];
86 final _privateNames = new HashMap<String, JS.TemporaryId>(); 85 final _privateNames = new HashMap<String, JS.TemporaryId>();
87 final _moduleItems = <JS.Statement>[]; 86 final _moduleItems = <JS.Statement>[];
88 final _temps = new HashMap<Element, JS.TemporaryId>(); 87 final _temps = new HashMap<Element, JS.TemporaryId>();
89 final _qualifiedIds = new List<Tuple2<Element, JS.MaybeQualifiedId>>(); 88 final _qualifiedIds = new List<Tuple2<Element, JS.MaybeQualifiedId>>();
90 89
91 /// The name for the library's exports inside itself. 90 /// The name for the library's exports inside itself.
92 /// `exports` was chosen as the most similar to ES module patterns. 91 /// `exports` was chosen as the most similar to ES module patterns.
93 final _dartxVar = new JS.Identifier('dartx'); 92 final _dartxVar = new JS.Identifier('dartx');
94 final _exportsVar = new JS.TemporaryId('exports'); 93 final _exportsVar = new JS.TemporaryId('exports');
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
148 147
149 for (var unit in library.partsThenLibrary) { 148 for (var unit in library.partsThenLibrary) {
150 _constField = new ConstFieldVisitor(types, unit); 149 _constField = new ConstFieldVisitor(types, unit);
151 150
152 for (var decl in unit.declarations) { 151 for (var decl in unit.declarations) {
153 if (decl is TopLevelVariableDeclaration) { 152 if (decl is TopLevelVariableDeclaration) {
154 visitTopLevelVariableDeclaration(decl); 153 visitTopLevelVariableDeclaration(decl);
155 } else { 154 } else {
156 _loader.loadDeclaration(decl, decl.element); 155 _loader.loadDeclaration(decl, decl.element);
157 } 156 }
158 if (decl is ClassDeclaration) {
159 // Static fields can be emitted into the top-level code, so they need
160 // to potentially be ordered independently of the class.
161 for (var member in decl.members) {
162 if (member is FieldDeclaration) {
163 visitFieldDeclaration(member);
164 }
165 }
166 }
167 } 157 }
168 } 158 }
169 159
170 // Flush any unwritten fields/properties. 160 // Flush any unwritten fields/properties.
171 _flushLazyFields(_moduleItems);
172 _flushLibraryProperties(_moduleItems); 161 _flushLibraryProperties(_moduleItems);
173 162
174 // Mark all qualified names as qualified or not, depending on if they need 163 // Mark all qualified names as qualified or not, depending on if they need
175 // to be loaded lazily or not. 164 // to be loaded lazily or not.
176 for (var elementIdPairs in _qualifiedIds) { 165 for (var elementIdPairs in _qualifiedIds) {
177 var element = elementIdPairs.e0; 166 var element = elementIdPairs.e0;
178 var id = elementIdPairs.e1; 167 var id = elementIdPairs.e1;
179 id.setQualified(!_loader.isLoaded(element)); 168 id.setQualified(!_loader.isLoaded(element));
180 } 169 }
181 170
(...skipping 23 matching lines...) Expand all
205 // String scriptTag = null; 194 // String scriptTag = null;
206 // if (library.library.scriptTag != null) scriptTag = '/usr/bin/env $jsBin'; 195 // if (library.library.scriptTag != null) scriptTag = '/usr/bin/env $jsBin';
207 return moduleBuilder.build( 196 return moduleBuilder.build(
208 compiler.getModuleName(currentLibrary.source.uri), 197 compiler.getModuleName(currentLibrary.source.uri),
209 _jsModuleValue, 198 _jsModuleValue,
210 _exportsVar, 199 _exportsVar,
211 items); 200 items);
212 } 201 }
213 202
214 void _emitModuleItem(AstNode node) { 203 void _emitModuleItem(AstNode node) {
215 // Attempt to group adjacent fields/properties. 204 // Attempt to group adjacent properties.
216 if (node is! VariableDeclaration) _flushLazyFields(_moduleItems);
217 if (node is! FunctionDeclaration) _flushLibraryProperties(_moduleItems); 205 if (node is! FunctionDeclaration) _flushLibraryProperties(_moduleItems);
218 206
219 var code = _visit(node); 207 var code = _visit(node);
220 if (code != null) _moduleItems.add(code); 208 if (code != null) _moduleItems.add(code);
221 } 209 }
222 210
223 @override 211 @override
224 void visitLibraryDirective(LibraryDirective node) { 212 void visitLibraryDirective(LibraryDirective node) {
225 assert(_jsModuleValue == null); 213 assert(_jsModuleValue == null);
226 214
(...skipping 172 matching lines...) Expand 10 before | Expand all | Expand 10 after
399 @override 387 @override
400 JS.Statement visitClassDeclaration(ClassDeclaration node) { 388 JS.Statement visitClassDeclaration(ClassDeclaration node) {
401 var classElem = node.element; 389 var classElem = node.element;
402 var type = classElem.type; 390 var type = classElem.type;
403 var jsName = findAnnotation(classElem, isJSAnnotation); 391 var jsName = findAnnotation(classElem, isJSAnnotation);
404 392
405 if (jsName != null) return _emitJsType(node.name.name, jsName); 393 if (jsName != null) return _emitJsType(node.name.name, jsName);
406 394
407 var ctors = <ConstructorDeclaration>[]; 395 var ctors = <ConstructorDeclaration>[];
408 var fields = <FieldDeclaration>[]; 396 var fields = <FieldDeclaration>[];
397 var staticFields = <FieldDeclaration>[];
409 var methods = <MethodDeclaration>[]; 398 var methods = <MethodDeclaration>[];
410 for (var member in node.members) { 399 for (var member in node.members) {
411 if (member is ConstructorDeclaration) { 400 if (member is ConstructorDeclaration) {
412 ctors.add(member); 401 ctors.add(member);
413 } else if (member is FieldDeclaration && !member.isStatic) { 402 } else if (member is FieldDeclaration) {
414 fields.add(member); 403 (member.isStatic ? staticFields : fields).add(member);
415 } else if (member is MethodDeclaration) { 404 } else if (member is MethodDeclaration) {
416 methods.add(member); 405 methods.add(member);
417 } 406 }
418 } 407 }
419 408
420 var classExpr = new JS.ClassExpression(new JS.Identifier(type.name), 409 var classExpr = new JS.ClassExpression(new JS.Identifier(type.name),
421 _classHeritage(classElem), _emitClassMethods(node, ctors, fields)); 410 _classHeritage(classElem), _emitClassMethods(node, ctors, fields));
422 411
423 String jsPeerName; 412 String jsPeerName;
424 var jsPeer = findAnnotation(classElem, isJsPeerInterface); 413 var jsPeer = findAnnotation(classElem, isJsPeerInterface);
425 if (jsPeer != null) { 414 if (jsPeer != null) {
426 jsPeerName = 415 jsPeerName =
427 getConstantField(jsPeer, 'name', types.stringType)?.toStringValue(); 416 getConstantField(jsPeer, 'name', types.stringType)?.toStringValue();
428 } 417 }
429 418
430 var body = _finishClassMembers(classElem, classExpr, ctors, fields, methods, 419 var body = _finishClassMembers(classElem, classExpr, ctors, fields,
431 node.metadata, jsPeerName); 420 staticFields, methods, node.metadata, jsPeerName);
432 421
433 var result = _finishClassDef(type, body); 422 var result = _finishClassDef(type, body);
434 423
435 if (jsPeerName != null) { 424 if (jsPeerName != null) {
436 // This class isn't allowed to be lazy, because we need to set up 425 // This class isn't allowed to be lazy, because we need to set up
437 // the native JS type eagerly at this point. 426 // the native JS type eagerly at this point.
438 // If we wanted to support laziness, we could defer the hookup until 427 // If we wanted to support laziness, we could defer the hookup until
439 // the end of the Dart library cycle load. 428 // the end of the Dart library cycle load.
440 assert(_loader.isLoaded(classElem)); 429 assert(_loader.isLoaded(classElem));
441 430
(...skipping 228 matching lines...) Expand 10 before | Expand all | Expand 10 after
670 } 659 }
671 660
672 /// Emit class members that need to come after the class declaration, such 661 /// Emit class members that need to come after the class declaration, such
673 /// as static fields. See [_emitClassMethods] for things that are emitted 662 /// as static fields. See [_emitClassMethods] for things that are emitted
674 /// inside the ES6 `class { ... }` node. 663 /// inside the ES6 `class { ... }` node.
675 JS.Statement _finishClassMembers( 664 JS.Statement _finishClassMembers(
676 ClassElement classElem, 665 ClassElement classElem,
677 JS.ClassExpression cls, 666 JS.ClassExpression cls,
678 List<ConstructorDeclaration> ctors, 667 List<ConstructorDeclaration> ctors,
679 List<FieldDeclaration> fields, 668 List<FieldDeclaration> fields,
669 List<FieldDeclaration> staticFields,
680 List<MethodDeclaration> methods, 670 List<MethodDeclaration> methods,
681 List<Annotation> metadata, 671 List<Annotation> metadata,
682 String jsPeerName) { 672 String jsPeerName) {
683 var name = classElem.name; 673 var name = classElem.name;
684 var body = <JS.Statement>[]; 674 var body = <JS.Statement>[];
685 675
686 if (_extensionTypes.contains(classElem)) { 676 if (_extensionTypes.contains(classElem)) {
687 var dartxNames = <JS.Expression>[]; 677 var dartxNames = <JS.Expression>[];
688 for (var m in methods) { 678 for (var m in methods) {
689 if (!m.isAbstract && !m.isStatic && m.element.isPublic) { 679 if (!m.isAbstract && !m.isStatic && m.element.isPublic) {
(...skipping 30 matching lines...) Expand all
720 } 710 }
721 711
722 // Named constructors 712 // Named constructors
723 for (ConstructorDeclaration member in ctors) { 713 for (ConstructorDeclaration member in ctors) {
724 if (member.name != null && member.factoryKeyword == null) { 714 if (member.name != null && member.factoryKeyword == null) {
725 body.add(js.statement('dart.defineNamedConstructor(#, #);', 715 body.add(js.statement('dart.defineNamedConstructor(#, #);',
726 [name, _emitMemberName(member.name.name, isStatic: true)])); 716 [name, _emitMemberName(member.name.name, isStatic: true)]));
727 } 717 }
728 } 718 }
729 719
730 // Instance fields, if they override getter/setter pairs 720 // Emits instance fields, if they are virtual
721 // (in other words, they override a getter/setter pair).
731 for (FieldDeclaration member in fields) { 722 for (FieldDeclaration member in fields) {
732 for (VariableDeclaration fieldDecl in member.fields.variables) { 723 for (VariableDeclaration field in member.fields.variables) {
733 var field = fieldDecl.element as FieldElement; 724 if (_fieldsNeedingStorage.contains(field.element)) {
734 if (_fieldsNeedingStorage.contains(field)) { 725 body.add(_overrideField(field.element));
735 body.add(_overrideField(field));
736 } 726 }
737 } 727 }
738 } 728 }
739 729
740 // Emit the signature on the class recording the runtime type information 730 // Emit the signature on the class recording the runtime type information
741 var extensions = _extensionsToImplement(classElem); 731 var extensions = _extensionsToImplement(classElem);
742 { 732 {
743 var tStatics = <JS.Property>[]; 733 var tStatics = <JS.Property>[];
744 var tMethods = <JS.Property>[]; 734 var tMethods = <JS.Property>[];
745 var sNames = <JS.Expression>[]; 735 var sNames = <JS.Expression>[];
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after
815 // TODO(vsm): Make this optional per #268. 805 // TODO(vsm): Make this optional per #268.
816 // Metadata 806 // Metadata
817 if (metadata.isNotEmpty) { 807 if (metadata.isNotEmpty) {
818 body.add(js.statement('#[dart.metadata] = () => #;', [ 808 body.add(js.statement('#[dart.metadata] = () => #;', [
819 name, 809 name,
820 new JS.ArrayInitializer( 810 new JS.ArrayInitializer(
821 new List<JS.Expression>.from(metadata.map(_instantiateAnnotation))) 811 new List<JS.Expression>.from(metadata.map(_instantiateAnnotation)))
822 ])); 812 ]));
823 } 813 }
824 814
815 // Emits static fields. These are eager initialized if possible, otherwise
816 // they are made lazy.
817 var lazyStatics = <VariableDeclaration>[];
818 for (FieldDeclaration member in staticFields) {
819 for (VariableDeclaration field in member.fields.variables) {
820 JS.Statement eagerField = _emitConstantStaticField(classElem, field);
821 if (eagerField != null) {
822 body.add(eagerField);
823 } else {
824 lazyStatics.add(field);
825 }
826 }
827 }
828 if (lazyStatics.isNotEmpty) {
829 body.add(_emitLazyFields(classElem, lazyStatics));
830 }
831
825 return _statement(body); 832 return _statement(body);
826 } 833 }
827 834
828 List<ExecutableElement> _extensionsToImplement(ClassElement element) { 835 List<ExecutableElement> _extensionsToImplement(ClassElement element) {
829 var members = <ExecutableElement>[]; 836 var members = <ExecutableElement>[];
830 if (_extensionTypes.contains(element)) return members; 837 if (_extensionTypes.contains(element)) return members;
831 838
832 // Collect all extension types we implement. 839 // Collect all extension types we implement.
833 var type = element.type; 840 var type = element.type;
834 var types = new Set<ClassElement>(); 841 var types = new Set<ClassElement>();
(...skipping 1279 matching lines...) Expand 10 before | Expand all | Expand 10 after
2114 return new JS.Yield(_visit(node.expression)); 2121 return new JS.Yield(_visit(node.expression));
2115 } 2122 }
2116 2123
2117 @override 2124 @override
2118 visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { 2125 visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
2119 for (var v in node.variables.variables) { 2126 for (var v in node.variables.variables) {
2120 _loader.loadDeclaration(v, v.element); 2127 _loader.loadDeclaration(v, v.element);
2121 } 2128 }
2122 } 2129 }
2123 2130
2124 /// Emits static fields.
2125 ///
2126 /// Instance fields are emitted in [_initializeFields].
2127 ///
2128 /// These are generally treated the same as top-level fields, see
2129 /// [visitTopLevelVariableDeclaration].
2130 @override
2131 visitFieldDeclaration(FieldDeclaration node) {
2132 if (!node.isStatic) return;
2133
2134 for (var f in node.fields.variables) {
2135 _loader.loadDeclaration(f, f.element);
2136 }
2137 }
2138
2139 _addExport(String name) { 2131 _addExport(String name) {
2140 if (!_exports.add(name)) throw 'Duplicate top level name found: $name'; 2132 if (!_exports.add(name)) throw 'Duplicate top level name found: $name';
2141 } 2133 }
2142 2134
2143 @override 2135 @override
2144 JS.Statement visitVariableDeclarationStatement( 2136 JS.Statement visitVariableDeclarationStatement(
2145 VariableDeclarationStatement node) { 2137 VariableDeclarationStatement node) {
2146 // Special case a single variable with an initializer. 2138 // Special case a single variable with an initializer.
2147 // This helps emit cleaner code for things like: 2139 // This helps emit cleaner code for things like:
2148 // var result = []..add(1)..add(2); 2140 // var result = []..add(1)..add(2);
2149 if (node.variables.variables.length == 1) { 2141 if (node.variables.variables.length == 1) {
2150 var v = node.variables.variables.single; 2142 var v = node.variables.variables.single;
2151 if (v.initializer != null) { 2143 if (v.initializer != null) {
2152 var name = new JS.Identifier(v.name.name); 2144 var name = new JS.Identifier(v.name.name);
2153 return _visit(v.initializer).toVariableDeclaration(name); 2145 return _visit(v.initializer).toVariableDeclaration(name);
2154 } 2146 }
2155 } 2147 }
2156 return _visit(node.variables).toStatement(); 2148 return _visit(node.variables).toStatement();
2157 } 2149 }
2158 2150
2159 @override 2151 @override
2160 visitVariableDeclarationList(VariableDeclarationList node) { 2152 visitVariableDeclarationList(VariableDeclarationList node) {
2161 return new JS.VariableDeclarationList( 2153 return new JS.VariableDeclarationList(
2162 'let', _visitList(node.variables) as List<JS.VariableInitialization>); 2154 'let', _visitList(node.variables) as List<JS.VariableInitialization>);
2163 } 2155 }
2164 2156
2165 @override 2157 @override
2166 visitVariableDeclaration(VariableDeclaration node) { 2158 visitVariableDeclaration(VariableDeclaration node) {
2167 if (node.element is PropertyInducingElement) return _emitStaticField(node); 2159 if (node.element is PropertyInducingElement) {
2160 // Static and instance fields are handled elsewhere.
2161 assert(node.element is TopLevelVariableElement);
2162 return _emitTopLevelField(node);
2163 }
2168 2164
2169 var name = new JS.Identifier(node.name.name); 2165 var name = new JS.Identifier(node.name.name);
2170 return new JS.VariableInitialization(name, _visitInitializer(node)); 2166 return new JS.VariableInitialization(name, _visitInitializer(node));
2171 } 2167 }
2172 2168
2173 bool _isFinalJSDecl(AstNode field) => 2169 bool _isFinalJSDecl(AstNode field) =>
2174 field is VariableDeclaration && 2170 field is VariableDeclaration &&
2175 field.isFinal && 2171 field.isFinal &&
2176 _isJSInvocation(field.initializer); 2172 _isJSInvocation(field.initializer);
2177 2173
2178 /// Emits a static or top-level field. 2174 /// Try to emit a constant static field.
2179 JS.Statement _emitStaticField(VariableDeclaration field) { 2175 ///
2176 /// If the field's initializer does not cause side effects, and if all of
2177 /// dependencies are safe to refer to while we are initializing the class,
2178 /// then we can initialize it eagerly:
2179 ///
2180 /// // Baz must be const constructor, and the name "Baz" must be defined
2181 /// // by this point.
2182 /// Foo.bar = dart.const(new Baz(42));
2183 ///
2184 /// Otherwise, we'll need to generate a lazy-static field. That ensures
2185 /// correct visible behavior, as well as avoiding referencing something that
2186 /// isn't defined yet (because it is defined later in the module).
2187 JS.Statement _emitConstantStaticField(
2188 ClassElement classElem, VariableDeclaration field) {
2180 PropertyInducingElement element = field.element; 2189 PropertyInducingElement element = field.element;
2181 assert(element.isStatic); 2190 assert(element.isStatic);
2182 2191
2192 _loader.startCheckingReferences();
2193 JS.Expression jsInit = _visitInitializer(field);
2194 bool isLoaded = _loader.finishCheckingReferences();
2195
2196 bool eagerInit =
2197 isLoaded && (field.isConst || _constField.isFieldInitConstant(field));
2198
2199 var fieldName = field.name.name;
2200 if (eagerInit && !JS.invalidStaticFieldName(fieldName)) {
2201 return annotateVariable(
2202 js.statement('#.# = #;', [
2203 classElem.name,
2204 _emitMemberName(fieldName, isStatic: true),
2205 jsInit
2206 ]),
2207 field.element);
2208 }
2209
2210 // This means it should be treated as a lazy field.
2211 // TODO(jmesserly): we're throwing away the initializer expression,
2212 // which will force us to regenerate it.
2213 return null;
2214 }
2215
2216 /// Emits a top-level field.
2217 JS.Statement _emitTopLevelField(VariableDeclaration field) {
2218 TopLevelVariableElement element = field.element;
2219 assert(element.isStatic);
2220
2183 bool eagerInit; 2221 bool eagerInit;
2184 JS.Expression jsInit; 2222 JS.Expression jsInit;
2185 if (field.isConst || _constField.isFieldInitConstant(field)) { 2223 if (field.isConst || _constField.isFieldInitConstant(field)) {
2186 // If the field is constant, try and generate it at the top level. 2224 // If the field is constant, try and generate it at the top level.
2187 _loader.startTopLevel(element); 2225 _loader.startTopLevel(element);
2188 jsInit = _visitInitializer(field); 2226 jsInit = _visitInitializer(field);
2189 _loader.finishTopLevel(element); 2227 _loader.finishTopLevel(element);
2190 eagerInit = _loader.isLoaded(element); 2228 eagerInit = _loader.isLoaded(element);
2191 } else { 2229 } else {
2230 // TODO(jmesserly): we're visiting the initializer here, and again
2231 // later on when we emit lazy fields. That seems busted.
2192 jsInit = _visitInitializer(field); 2232 jsInit = _visitInitializer(field);
2193 eagerInit = false; 2233 eagerInit = false;
2194 } 2234 }
2195 2235
2196 // Treat `final x = JS('', '...')` as a const (non-lazy) to help compile 2236 // Treat `final x = JS('', '...')` as a const (non-lazy) to help compile
2197 // runtime helpers. 2237 // runtime helpers.
2198 var isJSTopLevel = field.isFinal && _isFinalJSDecl(field); 2238 var isJSTopLevel = field.isFinal && _isFinalJSDecl(field);
2199 if (isJSTopLevel) eagerInit = true; 2239 if (isJSTopLevel) eagerInit = true;
2200 2240
2201 var fieldName = field.name.name; 2241 var fieldName = _getJSExportName(element) ?? field.name.name;
2202 if (element is TopLevelVariableElement) { 2242 if (field.isConst && eagerInit || isJSTopLevel) {
2203 fieldName = _getJSExportName(element) ?? fieldName;
2204 }
2205 if ((field.isConst && eagerInit && element is TopLevelVariableElement) ||
2206 isJSTopLevel) {
2207 // constant fields don't change, so we can generate them as `let` 2243 // constant fields don't change, so we can generate them as `let`
2208 // but add them to the module's exports. However, make sure we generate 2244 // but add them to the module's exports. However, make sure we generate
2209 // anything they depend on first. 2245 // anything they depend on first.
2210
2211 if (isPublic(fieldName)) _addExport(fieldName); 2246 if (isPublic(fieldName)) _addExport(fieldName);
2212 var declKeyword = field.isConst || field.isFinal ? 'const' : 'let'; 2247 var declKeyword = field.isConst || field.isFinal ? 'const' : 'let';
2213 return annotateVariable( 2248 return annotateVariable(
2214 js.statement( 2249 js.statement(
2215 '$declKeyword # = #;', [new JS.Identifier(fieldName), jsInit]), 2250 '$declKeyword # = #;', [new JS.Identifier(fieldName), jsInit]),
2216 field.element); 2251 field.element);
2217 } 2252 }
2218 2253
2219 if (eagerInit && !JS.invalidStaticFieldName(fieldName)) { 2254 if (eagerInit && !JS.invalidStaticFieldName(fieldName)) {
2220 return annotateVariable( 2255 return annotateVariable(
2221 js.statement('# = #;', [_visit(field.name), jsInit]), field.element); 2256 js.statement('# = #;', [_visit(field.name), jsInit]), field.element);
2222 } 2257 }
2223 2258
2224 var body = <JS.Statement>[]; 2259 return _emitLazyFields(currentLibrary, [field]);
2225 if (_lazyFields.isNotEmpty) {
2226 var existingTarget = _lazyFields[0].element.enclosingElement;
2227 if (existingTarget != element.enclosingElement) {
2228 _flushLazyFields(body);
2229 }
2230 }
2231
2232 _lazyFields.add(field);
2233
2234 return _statement(body);
2235 } 2260 }
2236 2261
2237 JS.Expression _visitInitializer(VariableDeclaration node) { 2262 JS.Expression _visitInitializer(VariableDeclaration node) {
2238 var value = _visit(node.initializer); 2263 var value = _visit(node.initializer);
2239 // explicitly initialize to null, to avoid getting `undefined`. 2264 // explicitly initialize to null, to avoid getting `undefined`.
2240 // TODO(jmesserly): do this only for vars that aren't definitely assigned. 2265 // TODO(jmesserly): do this only for vars that aren't definitely assigned.
2241 return value ?? new JS.LiteralNull(); 2266 return value ?? new JS.LiteralNull();
2242 } 2267 }
2243 2268
2244 void _flushLazyFields(List<JS.Statement> body) { 2269 JS.Statement _emitLazyFields(
2245 if (_lazyFields.isEmpty) return; 2270 Element target, List<VariableDeclaration> fields) {
2246 body.add(_emitLazyFields(_lazyFields));
2247 _lazyFields.clear();
2248 }
2249
2250 JS.Statement _emitLazyFields(List<VariableDeclaration> fields) {
2251 var methods = []; 2271 var methods = [];
2252 for (var node in fields) { 2272 for (var node in fields) {
2253 var name = node.name.name; 2273 var name = node.name.name;
2254 var element = node.element; 2274 var element = node.element;
2255 var access = _emitMemberName(name, type: element.type, isStatic: true); 2275 var access = _emitMemberName(name, isStatic: true);
2256 methods.add(annotate( 2276 methods.add(annotate(
2257 new JS.Method( 2277 new JS.Method(
2258 access, 2278 access,
2259 js.call('function() { return #; }', _visit(node.initializer)) 2279 js.call('function() { return #; }', _visit(node.initializer))
2260 as JS.Fun, 2280 as JS.Fun,
2261 isGetter: true), 2281 isGetter: true),
2262 _findAccessor(element, getter: true))); 2282 _findAccessor(element, getter: true)));
2263 2283
2264 // TODO(jmesserly): use a dummy setter to indicate writable. 2284 // TODO(jmesserly): currently uses a dummy setter to indicate writable.
2265 if (!node.isFinal) { 2285 if (!node.isFinal && !node.isConst) {
2266 methods.add(annotate( 2286 methods.add(annotate(
2267 new JS.Method(access, js.call('function(_) {}') as JS.Fun, 2287 new JS.Method(access, js.call('function(_) {}') as JS.Fun,
2268 isSetter: true), 2288 isSetter: true),
2269 _findAccessor(element, getter: false))); 2289 _findAccessor(element, getter: false)));
2270 } 2290 }
2271 } 2291 }
2272 2292
2273 JS.Expression objExpr = _exportsVar; 2293 JS.Expression objExpr;
2274 var target = _lazyFields[0].element.enclosingElement;
2275 if (target is ClassElement) { 2294 if (target is ClassElement) {
2276 objExpr = new JS.Identifier(target.type.name); 2295 objExpr = new JS.Identifier(target.type.name);
2296 } else {
2297 objExpr = _libraryName(target);
2277 } 2298 }
2278 2299
2279 return js 2300 return js
2280 .statement('dart.defineLazyProperties(#, { # });', [objExpr, methods]); 2301 .statement('dart.defineLazyProperties(#, { # });', [objExpr, methods]);
2281 } 2302 }
2282 2303
2283 PropertyAccessorElement _findAccessor(VariableElement element, 2304 PropertyAccessorElement _findAccessor(VariableElement element,
2284 {bool getter}) { 2305 {bool getter}) {
2285 var parent = element.enclosingElement; 2306 var parent = element.enclosingElement;
2286 if (parent is ClassElement) { 2307 if (parent is ClassElement) {
(...skipping 527 matching lines...) Expand 10 before | Expand all | Expand 10 after
2814 return true; 2835 return true;
2815 } 2836 }
2816 2837
2817 /// Shared code for [PrefixedIdentifier] and [PropertyAccess]. 2838 /// Shared code for [PrefixedIdentifier] and [PropertyAccess].
2818 JS.Expression _emitGet(Expression target, SimpleIdentifier memberId) { 2839 JS.Expression _emitGet(Expression target, SimpleIdentifier memberId) {
2819 var member = memberId.staticElement; 2840 var member = memberId.staticElement;
2820 if (member is PropertyAccessorElement) { 2841 if (member is PropertyAccessorElement) {
2821 member = (member as PropertyAccessorElement).variable; 2842 member = (member as PropertyAccessorElement).variable;
2822 } 2843 }
2823 bool isStatic = member is ClassMemberElement && member.isStatic; 2844 bool isStatic = member is ClassMemberElement && member.isStatic;
2824 if (isStatic) {
2825 _loader.declareBeforeUse(member);
2826 }
2827 var name = _emitMemberName(memberId.name, 2845 var name = _emitMemberName(memberId.name,
2828 type: getStaticType(target), isStatic: isStatic); 2846 type: getStaticType(target), isStatic: isStatic);
2829 if (DynamicInvoke.get(target)) { 2847 if (DynamicInvoke.get(target)) {
2830 return js.call('dart.$DLOAD(#, #)', [_visit(target), name]); 2848 return js.call('dart.$DLOAD(#, #)', [_visit(target), name]);
2831 } 2849 }
2832 2850
2833 String code; 2851 String code;
2834 if (member != null && member is MethodElement && !isStatic) { 2852 if (member != null && member is MethodElement && !isStatic) {
2835 // Tear-off methods: explicitly bind it. 2853 // Tear-off methods: explicitly bind it.
2836 if (target is SuperExpression) { 2854 if (target is SuperExpression) {
(...skipping 755 matching lines...) Expand 10 before | Expand all | Expand 10 after
3592 3610
3593 /// A special kind of element created by the compiler, signifying a temporary 3611 /// A special kind of element created by the compiler, signifying a temporary
3594 /// variable. These objects use instance equality, and should be shared 3612 /// variable. These objects use instance equality, and should be shared
3595 /// everywhere in the tree where they are treated as the same variable. 3613 /// everywhere in the tree where they are treated as the same variable.
3596 class TemporaryVariableElement extends LocalVariableElementImpl { 3614 class TemporaryVariableElement extends LocalVariableElementImpl {
3597 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); 3615 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name);
3598 3616
3599 int get hashCode => identityHashCode(this); 3617 int get hashCode => identityHashCode(this);
3600 bool operator ==(Object other) => identical(this, other); 3618 bool operator ==(Object other) => identical(this, other);
3601 } 3619 }
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