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 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 } |
OLD | NEW |