Chromium Code Reviews| 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 |