| 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 |