OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 } |
OLD | NEW |