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; | 7 import 'dart:collection' show HashSet, HashMap; |
| 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; |
| 11 import 'package:analyzer/src/generated/constant.dart'; | 11 import 'package:analyzer/src/generated/constant.dart'; |
| 12 import 'package:analyzer/src/generated/element.dart'; | 12 import 'package:analyzer/src/generated/element.dart'; |
| 13 import 'package:analyzer/src/generated/engine.dart' show RecordingErrorListener; | |
| 13 import 'package:analyzer/src/generated/resolver.dart' show TypeProvider; | 14 import 'package:analyzer/src/generated/resolver.dart' show TypeProvider; |
| 14 import 'package:analyzer/src/generated/scanner.dart' | 15 import 'package:analyzer/src/generated/scanner.dart' |
| 15 show StringToken, Token, TokenType; | 16 show StringToken, Token, TokenType; |
| 16 import 'package:path/path.dart' as path; | 17 import 'package:path/path.dart' as path; |
| 17 | 18 |
| 18 import 'package:dev_compiler/src/codegen/ast_builder.dart' show AstBuilder; | 19 import 'package:dev_compiler/src/codegen/ast_builder.dart' show AstBuilder; |
| 19 import 'package:dev_compiler/src/codegen/reify_coercions.dart' | 20 import 'package:dev_compiler/src/codegen/reify_coercions.dart' |
| 20 show CoercionReifier; | 21 show CoercionReifier; |
| 21 | 22 |
| 22 // TODO(jmesserly): import from its own package | 23 // TODO(jmesserly): import from its own package |
| 23 import 'package:dev_compiler/src/js/js_ast.dart' as JS; | 24 import 'package:dev_compiler/src/js/js_ast.dart' as JS; |
| 24 import 'package:dev_compiler/src/js/js_ast.dart' show js; | 25 import 'package:dev_compiler/src/js/js_ast.dart' show js; |
| 25 | 26 |
| 26 import 'package:dev_compiler/src/checker/rules.dart'; | 27 import 'package:dev_compiler/src/checker/rules.dart'; |
| 27 import 'package:dev_compiler/src/dependency_graph.dart'; | |
| 28 import 'package:dev_compiler/src/info.dart'; | 28 import 'package:dev_compiler/src/info.dart'; |
| 29 import 'package:dev_compiler/src/options.dart'; | 29 import 'package:dev_compiler/src/options.dart'; |
| 30 import 'package:dev_compiler/src/utils.dart'; | 30 import 'package:dev_compiler/src/utils.dart'; |
| 31 | 31 |
| 32 import 'code_generator.dart'; | 32 import 'code_generator.dart'; |
| 33 import 'js_field_storage.dart'; | 33 import 'js_field_storage.dart'; |
| 34 import 'js_names.dart' as JS; | 34 import 'js_names.dart' as JS; |
| 35 import 'js_metalet.dart' as JS; | 35 import 'js_metalet.dart' as JS; |
| 36 import 'js_module_item_order.dart'; | |
| 36 import 'js_printer.dart' show writeJsLibrary; | 37 import 'js_printer.dart' show writeJsLibrary; |
| 37 import 'side_effect_analysis.dart'; | 38 import 'side_effect_analysis.dart'; |
| 38 | 39 |
| 39 // Various dynamic helpers we call. | 40 // Various dynamic helpers we call. |
| 40 // If renaming these, make sure to check other places like the | 41 // If renaming these, make sure to check other places like the |
| 41 // dart_runtime.js file and comments. | 42 // dart_runtime.js file and comments. |
| 42 // TODO(jmesserly): ideally we'd have a "dynamic call" dart library we can | 43 // TODO(jmesserly): ideally we'd have a "dynamic call" dart library we can |
| 43 // import and generate calls to, rather than dart_runtime.js | 44 // import and generate calls to, rather than dart_runtime.js |
| 44 const DPUT = 'dput'; | 45 const DPUT = 'dput'; |
| 45 const DLOAD = 'dload'; | 46 const DLOAD = 'dload'; |
| 46 const DINDEX = 'dindex'; | 47 const DINDEX = 'dindex'; |
| 47 const DSETINDEX = 'dsetindex'; | 48 const DSETINDEX = 'dsetindex'; |
| 48 const DCALL = 'dcall'; | 49 const DCALL = 'dcall'; |
| 49 const DSEND = 'dsend'; | 50 const DSEND = 'dsend'; |
| 50 | 51 |
| 52 class ConstFieldVisitor { | |
|
Jennifer Messerly
2015/05/12 16:23:45
I forgot to move this to side_effect_analysis.dart
vsm
2015/05/12 18:00:11
+1 to moving out. Not really js_cg specific.
Jennifer Messerly
2015/05/12 18:47:04
Done.
| |
| 53 final ConstantVisitor _constantVisitor; | |
| 54 | |
| 55 ConstFieldVisitor(TypeProvider types, CompilationUnit unit) | |
| 56 : _constantVisitor = new ConstantVisitor.con1(types, | |
| 57 new ErrorReporter(new RecordingErrorListener(), unit.element.source)); | |
| 58 | |
| 59 // TODO(jmesserly): this is used to determine if the field initialization is | |
| 60 // side effect free. We should make the check more general, as things like | |
| 61 // list/map literals/regexp are also side effect free and fairly common | |
| 62 // to use as field initializers. | |
| 63 bool isFieldInitConstant(VariableDeclaration field) => | |
| 64 field.initializer == null || computeConstant(field) != null; | |
| 65 | |
| 66 DartObjectImpl computeConstant(VariableDeclaration field) { | |
| 67 // If the constant is already computed by ConstantEvaluator, just return it. | |
| 68 VariableElementImpl element = field.element; | |
| 69 var result = element.evaluationResult; | |
| 70 if (result != null) return result.value; | |
| 71 | |
| 72 // ConstantEvaluator will not compute constants for non-const fields | |
| 73 // at least for cases like `int x = 0;`, so run ConstantVisitor for those. | |
| 74 // TODO(jmesserly): ideally we'd only do this if we're sure it was skipped | |
| 75 // by ConstantEvaluator. | |
|
vsm
2015/05/12 18:00:11
Is that equivalent to only doing this if !field.is
Jennifer Messerly
2015/05/12 18:47:04
Ah, yes. I think so. I'll add an assert in place o
| |
| 76 var initializer = field.initializer; | |
| 77 if (initializer == null) return null; | |
| 78 return initializer.accept(_constantVisitor); | |
| 79 } | |
| 80 } | |
| 81 | |
| 51 class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { | 82 class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
| 52 final CompilerOptions options; | 83 final CompilerOptions options; |
| 53 final TypeRules rules; | 84 final TypeRules rules; |
| 54 final LibraryInfo libraryInfo; | 85 final LibraryInfo libraryInfo; |
| 55 | 86 |
| 56 /// The global extension method table. | 87 /// The global extension method table. |
| 57 final HashMap<String, List<InterfaceType>> _extensionMethods; | 88 final HashMap<String, List<InterfaceType>> _extensionMethods; |
| 58 | 89 |
| 59 /// Information that is precomputed for this library, indicates which fields | 90 /// Information that is precomputed for this library, indicates which fields |
| 60 /// need storage slots. | 91 /// need storage slots. |
| 61 final HashSet<FieldElement> _fieldsNeedingStorage; | 92 final HashSet<FieldElement> _fieldsNeedingStorage; |
| 62 | 93 |
| 63 /// The variable for the target of the current `..` cascade expression. | 94 /// The variable for the target of the current `..` cascade expression. |
| 64 SimpleIdentifier _cascadeTarget; | 95 SimpleIdentifier _cascadeTarget; |
| 65 | 96 |
| 66 /// The variable for the current catch clause | 97 /// The variable for the current catch clause |
| 67 SimpleIdentifier _catchParameter; | 98 SimpleIdentifier _catchParameter; |
| 68 | 99 |
| 69 ConstantEvaluator _constEvaluator; | |
| 70 | |
| 71 ClassElement _currentClassElement = null; | |
| 72 | |
| 73 /// Imported libraries, and the temporaries used to refer to them. | 100 /// Imported libraries, and the temporaries used to refer to them. |
| 74 final _imports = new Map<LibraryElement, JS.TemporaryId>(); | 101 final _imports = new Map<LibraryElement, JS.TemporaryId>(); |
| 75 final _exports = new Set<String>(); | 102 final _exports = new Set<String>(); |
| 76 final _lazyFields = <VariableDeclaration>[]; | 103 final _lazyFields = <VariableDeclaration>[]; |
| 77 final _properties = <FunctionDeclaration>[]; | 104 final _properties = <FunctionDeclaration>[]; |
| 78 final _privateNames = new HashMap<String, JS.TemporaryId>(); | 105 final _privateNames = new HashMap<String, JS.TemporaryId>(); |
| 79 final _extensionMethodNames = new HashSet<String>(); | 106 final _extensionMethodNames = new HashSet<String>(); |
| 80 final _pendingStatements = <JS.Statement>[]; | 107 final _moduleItems = <JS.Statement>[]; |
| 81 final _temps = new HashMap<Element, JS.TemporaryId>(); | 108 final _temps = new HashMap<Element, JS.TemporaryId>.identity(); |
| 109 final _qualifiedIds = new HashMap<Element, JS.MaybeQualifiedId>(); | |
| 110 final _qualifiedGenericIds = new HashMap<Element, JS.MaybeQualifiedId>(); | |
| 82 | 111 |
| 83 /// The name for the library's exports inside itself. | 112 /// The name for the library's exports inside itself. |
| 84 /// `exports` was chosen as the most similar to ES module patterns. | 113 /// `exports` was chosen as the most similar to ES module patterns. |
| 85 final _exportsVar = new JS.TemporaryId('exports'); | 114 final _exportsVar = new JS.TemporaryId('exports'); |
| 86 final _namedArgTemp = new JS.TemporaryId('opts'); | 115 final _namedArgTemp = new JS.TemporaryId('opts'); |
| 87 | 116 |
| 88 /// Classes we have not emitted yet. Values can be [ClassDeclaration] or | 117 ConstFieldVisitor _constField; |
| 89 /// [ClassTypeAlias]. | |
| 90 final _pendingClasses = new HashMap<Element, CompilationUnitMember>(); | |
| 91 | 118 |
| 92 /// Memoized results of [_lazyClass]. | 119 ModuleItemLoadOrder _loader; |
| 93 final _lazyClassMemo = new HashMap<Element, bool>(); | |
| 94 | |
| 95 /// Memoized results of [_inLibraryCycle]. | |
| 96 final _libraryCycleMemo = new HashMap<LibraryElement, bool>(); | |
| 97 | 120 |
| 98 JSCodegenVisitor(this.options, this.rules, this.libraryInfo, | 121 JSCodegenVisitor(this.options, this.rules, this.libraryInfo, |
| 99 this._extensionMethods, this._fieldsNeedingStorage); | 122 this._extensionMethods, this._fieldsNeedingStorage) { |
| 123 _loader = new ModuleItemLoadOrder(_emitModuleItem); | |
|
Jennifer Messerly
2015/05/12 16:23:45
alternate design: this could be a mixin which decl
| |
| 124 } | |
| 100 | 125 |
| 101 LibraryElement get currentLibrary => libraryInfo.library; | 126 LibraryElement get currentLibrary => libraryInfo.library; |
| 102 TypeProvider get types => rules.provider; | 127 TypeProvider get types => rules.provider; |
| 103 | 128 |
| 104 JS.Program emitLibrary(LibraryUnit library) { | 129 JS.Program emitLibrary(LibraryUnit library) { |
| 105 String jsDefaultValue = null; | 130 String jsDefaultValue = null; |
| 106 | 131 |
| 107 // Modify the AST to make coercions explicit. | 132 // Modify the AST to make coercions explicit. |
| 108 new CoercionReifier(library, rules, options).reify(); | 133 new CoercionReifier(library, rules, options).reify(); |
| 109 | 134 |
| 110 var unit = library.library; | 135 var unit = library.library; |
| 111 if (unit.directives.isNotEmpty) { | 136 if (unit.directives.isNotEmpty) { |
| 112 var libraryDir = unit.directives.first; | 137 var libraryDir = unit.directives.first; |
| 113 if (libraryDir is LibraryDirective) { | 138 if (libraryDir is LibraryDirective) { |
| 114 var jsName = getAnnotationValue(libraryDir, _isJsNameAnnotation); | 139 var jsName = getAnnotationValue(libraryDir, _isJsNameAnnotation); |
| 115 jsDefaultValue = getConstantField(jsName, 'name', types.stringType); | 140 jsDefaultValue = getConstantField(jsName, 'name', types.stringType); |
| 116 } | 141 } |
| 117 } | 142 } |
| 118 if (jsDefaultValue == null) jsDefaultValue = '{}'; | 143 if (jsDefaultValue == null) jsDefaultValue = '{}'; |
| 119 | 144 |
| 120 var body = <JS.Statement>[]; | 145 // TODO(jmesserly): visit scriptTag, directives? |
|
Jennifer Messerly
2015/05/12 16:23:45
this TODO just moved from somewhere else
| |
| 121 | 146 |
| 122 // Collect classes we need to emit, used for: | 147 _loader.collectElements(currentLibrary, library.partsThenLibrary); |
| 123 // * tracks what we've emitted so we don't emit twice | 148 |
| 124 // * provides a mapping from ClassElement back to the ClassDeclaration. | |
| 125 for (var unit in library.partsThenLibrary) { | 149 for (var unit in library.partsThenLibrary) { |
| 150 _constField = new ConstFieldVisitor(types, unit); | |
| 151 | |
| 126 for (var decl in unit.declarations) { | 152 for (var decl in unit.declarations) { |
| 127 if (decl is ClassDeclaration || | 153 if (decl is TopLevelVariableDeclaration) { |
| 128 decl is ClassTypeAlias || | 154 _visit(decl); |
| 129 decl is FunctionTypeAlias) { | 155 } else { |
| 130 _pendingClasses[decl.element] = decl; | 156 _loader.loadDeclaration(decl, decl.element); |
| 157 } | |
| 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 && member.isStatic) { | |
| 163 for (var f in member.fields.variables) { | |
| 164 _loader.loadDeclaration(f, f.element); | |
| 165 } | |
| 166 } | |
| 167 } | |
| 131 } | 168 } |
| 132 } | 169 } |
| 133 } | 170 } |
| 134 | 171 |
| 135 for (var unit in library.partsThenLibrary) body.add(_visit(unit)); | 172 // Flush any unwritten fields/properties. |
| 173 _flushLazyFields(_moduleItems); | |
| 174 _flushLibraryProperties(_moduleItems); | |
| 136 | 175 |
| 137 assert(_pendingClasses.isEmpty); | 176 // Mark all qualified names as qualified or not, depending on if they need |
| 177 // to be loaded lazily or not. | |
| 178 unqualifyIfNeeded(Element e, JS.MaybeQualifiedId id) { | |
| 179 id.setQualified(!_loader.isLoaded(e)); | |
| 180 } | |
| 181 _qualifiedIds.forEach(unqualifyIfNeeded); | |
| 182 _qualifiedGenericIds.forEach(unqualifyIfNeeded); | |
| 138 | 183 |
| 139 if (_exports.isNotEmpty) body.add(js.comment('Exports:')); | 184 if (_exports.isNotEmpty) _moduleItems.add(js.comment('Exports:')); |
| 140 | 185 |
| 141 // TODO(jmesserly): make these immutable in JS? | 186 // TODO(jmesserly): make these immutable in JS? |
| 142 for (var name in _exports) { | 187 for (var name in _exports) { |
| 143 body.add(js.statement('#.# = #;', [_exportsVar, name, name])); | 188 _moduleItems.add(js.statement('#.# = #;', [_exportsVar, name, name])); |
| 144 } | 189 } |
| 145 | 190 |
| 146 var name = new JS.Identifier(jsLibraryName(currentLibrary)); | 191 var name = new JS.Identifier(jsLibraryName(currentLibrary)); |
| 147 | 192 |
| 148 // TODO(jmesserly): it would be great to run the renamer on the body, | 193 // TODO(jmesserly): it would be great to run the renamer on the body, |
| 149 // then figure out if we really need each of these parameters. | 194 // then figure out if we really need each of these parameters. |
| 150 // See ES6 modules: https://github.com/dart-lang/dev_compiler/issues/34 | 195 // See ES6 modules: https://github.com/dart-lang/dev_compiler/issues/34 |
| 151 var program = [ | 196 var program = [ |
| 152 js.statement('var # = dart.defineLibrary(#, #);', [ | 197 js.statement('var # = dart.defineLibrary(#, #);', [ |
| 153 name, | 198 name, |
| 154 name, | 199 name, |
| 155 js.call(jsDefaultValue) | 200 js.call(jsDefaultValue) |
| 156 ]) | 201 ]) |
| 157 ]; | 202 ]; |
| 158 | 203 |
| 159 var params = [_exportsVar]; | 204 var params = [_exportsVar]; |
| 160 var args = [name]; | 205 var args = [name]; |
| 161 _imports.forEach((library, temp) { | 206 _imports.forEach((library, temp) { |
| 162 var name = new JS.Identifier(temp.name); | 207 var name = new JS.Identifier(temp.name); |
| 163 params.add(temp); | 208 params.add(temp); |
| 164 args.add(name); | 209 args.add(name); |
| 165 var helper = _libraryMightNotBeLoaded(library) ? 'lazyImport' : 'import'; | 210 var helper = _loader.libraryIsLoaded(library) ? 'import' : 'lazyImport'; |
| 166 program.add(js.statement('var # = dart.#(#);', [name, helper, name])); | 211 program.add(js.statement('var # = dart.#(#);', [name, helper, name])); |
| 167 }); | 212 }); |
| 168 | 213 |
| 169 program.add(js.statement( | 214 program.add(js.statement("(function(#) { 'use strict'; #; })(#);", [ |
| 170 "(function(#) { 'use strict'; #; })(#);", [params, body, args])); | 215 params, |
| 216 _moduleItems, | |
| 217 args | |
| 218 ])); | |
| 171 | 219 |
| 172 return new JS.Program(program); | 220 return new JS.Program(program); |
| 173 } | 221 } |
| 174 | 222 |
| 223 void _emitModuleItem(AstNode node) { | |
| 224 // Attempt to group adjacent fields/properties. | |
| 225 if (node is! VariableDeclaration) _flushLazyFields(_moduleItems); | |
| 226 if (node is! FunctionDeclaration) _flushLibraryProperties(_moduleItems); | |
| 227 | |
| 228 var code = _visit(node); | |
| 229 if (code != null) _moduleItems.add(code); | |
| 230 } | |
| 231 | |
| 175 JS.Identifier _initSymbol(JS.Identifier id) { | 232 JS.Identifier _initSymbol(JS.Identifier id) { |
| 176 var s = js.statement('let # = $_SYMBOL(#);', [id, js.string(id.name, "'")]); | 233 var s = js.statement('let # = $_SYMBOL(#);', [id, js.string(id.name, "'")]); |
| 177 _pendingStatements.add(s); | 234 _moduleItems.add(s); |
| 178 return id; | 235 return id; |
| 179 } | 236 } |
| 180 | 237 |
| 181 // TODO(jmesserly): this is a temporary workaround for `Symbol` in core, | 238 // TODO(jmesserly): this is a temporary workaround for `Symbol` in core, |
| 182 // until we have better name tracking. | 239 // until we have better name tracking. |
| 183 String get _SYMBOL { | 240 String get _SYMBOL { |
| 184 var name = currentLibrary.name; | 241 var name = currentLibrary.name; |
| 185 if (name == 'dart.core' || name == 'dart._internal') return 'dart.JsSymbol'; | 242 if (name == 'dart.core' || name == 'dart._internal') return 'dart.JsSymbol'; |
| 186 return 'Symbol'; | 243 return 'Symbol'; |
| 187 } | 244 } |
| 188 | 245 |
| 189 @override | |
| 190 JS.Statement visitCompilationUnit(CompilationUnit node) { | |
| 191 var source = node.element.source; | |
| 192 | |
| 193 _constEvaluator = new ConstantEvaluator(source, types); | |
| 194 | |
| 195 // TODO(jmesserly): scriptTag, directives. | |
| 196 var body = <JS.Statement>[]; | |
| 197 for (var child in node.declarations) { | |
| 198 // Attempt to group adjacent fields/properties. | |
| 199 if (child is! TopLevelVariableDeclaration) _flushLazyFields(body); | |
| 200 if (child is! FunctionDeclaration) _flushLibraryProperties(body); | |
| 201 | |
| 202 var code = _visit(child); | |
| 203 if (code != null) { | |
| 204 _flushPendingStatements(body); | |
| 205 body.add(code); | |
| 206 } | |
| 207 } | |
| 208 | |
| 209 // Flush any unwritten fields/properties. | |
| 210 _flushLazyFields(body); | |
| 211 _flushLibraryProperties(body); | |
| 212 | |
| 213 assert(_pendingStatements.isEmpty); | |
| 214 return _statement(body); | |
| 215 } | |
| 216 | |
| 217 bool isPublic(String name) => !name.startsWith('_'); | 246 bool isPublic(String name) => !name.startsWith('_'); |
| 218 | 247 |
| 219 /// Conversions that we don't handle end up here. | 248 /// Conversions that we don't handle end up here. |
| 220 @override | 249 @override |
| 221 visitConversion(Conversion node) { | 250 visitConversion(Conversion node) { |
| 222 throw 'Unlowered conversion ${node.runtimeType}: $node'; | 251 throw 'Unlowered conversion ${node.runtimeType}: $node'; |
| 223 } | 252 } |
| 224 | 253 |
| 225 @override | 254 @override |
| 226 visitAsExpression(AsExpression node) { | 255 visitAsExpression(AsExpression node) { |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 273 String _jsTypeofName(DartType t) { | 302 String _jsTypeofName(DartType t) { |
| 274 if (rules.isIntType(t) || rules.isDoubleType(t)) return 'number'; | 303 if (rules.isIntType(t) || rules.isDoubleType(t)) return 'number'; |
| 275 if (rules.isStringType(t)) return 'string'; | 304 if (rules.isStringType(t)) return 'string'; |
| 276 if (rules.isBoolType(t)) return 'boolean'; | 305 if (rules.isBoolType(t)) return 'boolean'; |
| 277 return null; | 306 return null; |
| 278 } | 307 } |
| 279 | 308 |
| 280 @override | 309 @override |
| 281 visitFunctionTypeAlias(FunctionTypeAlias node) { | 310 visitFunctionTypeAlias(FunctionTypeAlias node) { |
| 282 // If we've already emitted this class, skip it. | 311 // If we've already emitted this class, skip it. |
| 283 var type = node.element.type; | 312 var element = node.element; |
| 284 if (_pendingClasses.remove(node.element) == null) return null; | 313 var type = element.type; |
| 314 var name = element.name; | |
| 285 | 315 |
| 286 var name = type.name; | 316 _loader.startTopLevel(element); |
| 287 var result = js.statement('let # = dart.typedef(#, () => #);', [ | 317 var fnType = js.statement('let # = dart.typedef(#, #);', [ |
| 288 new JS.Identifier(name), | 318 name, |
| 289 js.string(name, "'"), | 319 js.string(name, "'"), |
| 290 _emitTypeName(node.element.type, lowerTypedef: true) | 320 _emitTypeName(type, lowerTypedef: true) |
| 291 ]); | 321 ]); |
| 322 _loader.finishTopLevel(element); | |
| 292 | 323 |
| 293 return _finishClassDef(type, result); | 324 return _finishClassDef(type, fnType); |
| 294 } | 325 } |
| 295 | 326 |
| 296 @override | 327 @override |
| 297 JS.Expression visitTypeName(TypeName node) => _emitTypeName(node.type); | 328 JS.Expression visitTypeName(TypeName node) => _emitTypeName(node.type); |
| 298 | 329 |
| 299 @override | 330 @override |
| 300 JS.Statement visitClassTypeAlias(ClassTypeAlias node) { | 331 JS.Statement visitClassTypeAlias(ClassTypeAlias node) { |
| 301 // If we've already emitted this class, skip it. | 332 // If we've already emitted this class, skip it. |
| 302 var type = node.element.type; | 333 var element = node.element; |
| 303 if (_pendingClasses.remove(node.element) == null) return null; | |
| 304 | 334 |
| 305 var name = node.name.name; | |
| 306 var classDecl = new JS.ClassDeclaration(new JS.ClassExpression( | 335 var classDecl = new JS.ClassDeclaration(new JS.ClassExpression( |
| 307 new JS.Identifier(name), _classHeritage(node), [])); | 336 new JS.Identifier(element.name), _classHeritage(element), [])); |
| 308 | 337 |
| 309 return _finishClassDef(type, classDecl); | 338 return _finishClassDef(element.type, classDecl); |
| 310 } | 339 } |
| 311 | 340 |
| 312 JS.Statement _emitJsType(String dartClassName, DartObjectImpl jsName) { | 341 JS.Statement _emitJsType(String dartClassName, DartObjectImpl jsName) { |
| 313 var jsTypeName = getConstantField(jsName, 'name', types.stringType); | 342 var jsTypeName = getConstantField(jsName, 'name', types.stringType); |
| 314 | 343 |
| 315 if (jsTypeName != null && jsTypeName != dartClassName) { | 344 if (jsTypeName != null && jsTypeName != dartClassName) { |
| 316 // We export the JS type as if it was a Dart type. For example this allows | 345 // We export the JS type as if it was a Dart type. For example this allows |
| 317 // `dom.InputElement` to actually be HTMLInputElement. | 346 // `dom.InputElement` to actually be HTMLInputElement. |
| 318 // TODO(jmesserly): if we had the JsName on the Element, we could just | 347 // TODO(jmesserly): if we had the JsName on the Element, we could just |
| 319 // generate it correctly when we refer to it. | 348 // generate it correctly when we refer to it. |
| 320 if (isPublic(dartClassName)) _addExport(dartClassName); | 349 if (isPublic(dartClassName)) _addExport(dartClassName); |
| 321 return js.statement('let # = #;', [dartClassName, jsTypeName]); | 350 return js.statement('let # = #;', [dartClassName, jsTypeName]); |
| 322 } | 351 } |
| 323 return null; | 352 return null; |
| 324 } | 353 } |
| 325 | 354 |
| 326 @override | 355 @override |
| 327 JS.Statement visitClassDeclaration(ClassDeclaration node) { | 356 JS.Statement visitClassDeclaration(ClassDeclaration node) { |
| 328 // If we've already emitted this class, skip it. | 357 // If we've already emitted this class, skip it. |
| 329 var classElem = node.element; | 358 var classElem = node.element; |
| 330 var type = classElem.type; | 359 var type = classElem.type; |
| 331 if (_pendingClasses.remove(classElem) == null) return null; | |
| 332 | |
| 333 var jsName = getAnnotationValue(node, _isJsNameAnnotation); | 360 var jsName = getAnnotationValue(node, _isJsNameAnnotation); |
| 334 | 361 |
| 335 if (jsName != null) return _emitJsType(node.name.name, jsName); | 362 if (jsName != null) return _emitJsType(node.name.name, jsName); |
| 336 | 363 |
| 337 // Set current class | |
| 338 assert(_currentClassElement == null); | |
| 339 _currentClassElement = classElem; | |
| 340 | |
| 341 var ctors = <ConstructorDeclaration>[]; | 364 var ctors = <ConstructorDeclaration>[]; |
| 342 var fields = <FieldDeclaration>[]; | 365 var fields = <FieldDeclaration>[]; |
| 343 var staticFields = <FieldDeclaration>[]; | |
| 344 for (var member in node.members) { | 366 for (var member in node.members) { |
| 345 if (member is ConstructorDeclaration) { | 367 if (member is ConstructorDeclaration) { |
| 346 ctors.add(member); | 368 ctors.add(member); |
| 347 } else if (member is FieldDeclaration) { | 369 } else if (member is FieldDeclaration && !member.isStatic) { |
| 348 (member.isStatic ? staticFields : fields).add(member); | 370 fields.add(member); |
| 349 } | 371 } |
| 350 } | 372 } |
| 351 | 373 |
| 352 var classExpr = new JS.ClassExpression(new JS.Identifier(type.name), | 374 var classExpr = new JS.ClassExpression(new JS.Identifier(type.name), |
| 353 _classHeritage(node), _emitClassMethods(node, ctors, fields)); | 375 _classHeritage(classElem), _emitClassMethods(node, ctors, fields)); |
| 354 | 376 |
| 355 String jsPeerName; | 377 String jsPeerName; |
| 356 var jsPeer = getAnnotationValue(node, _isJsPeerInterface); | 378 var jsPeer = getAnnotationValue(node, _isJsPeerInterface); |
| 357 if (jsPeer != null) { | 379 if (jsPeer != null) { |
| 358 jsPeerName = getConstantField(jsPeer, 'name', types.stringType); | 380 jsPeerName = getConstantField(jsPeer, 'name', types.stringType); |
| 359 } | 381 } |
| 360 | 382 |
| 361 var body = _finishClassMembers( | 383 var body = |
| 362 classElem, classExpr, ctors, fields, staticFields, jsPeerName); | 384 _finishClassMembers(classElem, classExpr, ctors, fields, jsPeerName); |
| 363 | |
| 364 // Unset current class | |
| 365 assert(_currentClassElement == classElem); | |
| 366 _currentClassElement = null; | |
| 367 | 385 |
| 368 var result = _finishClassDef(type, body); | 386 var result = _finishClassDef(type, body); |
| 369 | 387 |
| 370 if (jsPeerName != null) { | 388 if (jsPeerName != null) { |
| 371 // This class isn't allowed to be lazy, because we need to set up | 389 // This class isn't allowed to be lazy, because we need to set up |
| 372 // the native JS type eagerly at this point. | 390 // the native JS type eagerly at this point. |
| 373 // If we wanted to support laziness, we could defer the hookup until | 391 // If we wanted to support laziness, we could defer the hookup until |
| 374 // the end of the Dart library cycle load. | 392 // the end of the Dart library cycle load. |
| 375 assert(!_lazyClass(type)); | 393 assert(_loader.isLoaded(classElem)); |
| 376 | 394 |
| 377 // TODO(jmesserly): this copies the dynamic members. | 395 // TODO(jmesserly): this copies the dynamic members. |
| 378 // Probably fine for objects coming from JS, but not if we actually | 396 // Probably fine for objects coming from JS, but not if we actually |
| 379 // want to support construction of instances with generic types other | 397 // want to support construction of instances with generic types other |
| 380 // than dynamic. See issue #154 for Array and List<E> related bug. | 398 // than dynamic. See issue #154 for Array and List<E> related bug. |
| 381 var copyMembers = js.statement( | 399 var copyMembers = js.statement( |
| 382 'dart.registerExtension(dart.global.#, #);', [ | 400 'dart.registerExtension(dart.global.#, #);', [ |
| 383 _propertyName(jsPeerName), | 401 _propertyName(jsPeerName), |
| 384 classElem.name | 402 classElem.name |
| 385 ]); | 403 ]); |
| 386 return _statement([result, copyMembers]); | 404 return _statement([result, copyMembers]); |
| 387 } | 405 } |
| 388 return result; | 406 return result; |
| 389 } | 407 } |
| 390 | 408 |
| 391 @override | 409 @override |
| 392 JS.Statement visitEnumDeclaration(EnumDeclaration node) => | 410 JS.Statement visitEnumDeclaration(EnumDeclaration node) => |
| 393 _unimplementedCall("Unimplemented enum: $node").toStatement(); | 411 _unimplementedCall("Unimplemented enum: $node").toStatement(); |
| 394 | 412 |
| 395 /// Given a class element and body, complete the class declaration. | 413 /// Given a class element and body, complete the class declaration. |
| 396 /// This handles generic type parameters, laziness (in library-cycle cases), | 414 /// This handles generic type parameters, laziness (in library-cycle cases), |
| 397 /// and ensuring dependencies are loaded first. | 415 /// and ensuring dependencies are loaded first. |
| 398 JS.Statement _finishClassDef(ParameterizedType type, JS.Statement body) { | 416 JS.Statement _finishClassDef(ParameterizedType type, JS.Statement body) { |
| 399 var name = type.name; | 417 var name = type.name; |
| 400 var genericName = '$name\$'; | 418 var genericName = '$name\$'; |
| 401 | 419 |
| 402 JS.Statement genericDef; | 420 JS.Statement genericDef = null; |
| 403 JS.Expression genericInst; | |
| 404 if (type.typeParameters.isNotEmpty) { | 421 if (type.typeParameters.isNotEmpty) { |
| 405 genericDef = _emitGenericClassDef(type, body); | 422 genericDef = _emitGenericClassDef(type, body); |
| 406 var target = genericName; | |
| 407 if (_needQualifiedName(type.element)) { | |
| 408 target = js.call('#.#', [_exportsVar, genericName]); | |
| 409 } | |
| 410 genericInst = js.call('#()', [target]); | |
| 411 } | 423 } |
| 412 | 424 |
| 413 // The base class and all mixins must be declared before this class. | 425 // The base class and all mixins must be declared before this class. |
| 414 if (_lazyClass(type)) { | 426 if (!_loader.isLoaded(type.element)) { |
| 415 // TODO(jmesserly): the lazy class def is a simple solution for now. | 427 // TODO(jmesserly): the lazy class def is a simple solution for now. |
| 416 // We may want to consider other options in the future. | 428 // We may want to consider other options in the future. |
| 417 | 429 |
| 418 if (genericDef != null) { | 430 if (genericDef != null) { |
| 419 return js.statement( | 431 return js.statement( |
| 420 '{ #; dart.defineLazyClassGeneric(#, #, { get: # }); }', [ | 432 '{ #; dart.defineLazyClassGeneric(#, #, { get: # }); }', [ |
| 421 genericDef, | 433 genericDef, |
| 422 _exportsVar, | 434 _exportsVar, |
| 423 _propertyName(name), | 435 _propertyName(name), |
| 424 genericName | 436 genericName |
| 425 ]); | 437 ]); |
| 426 } | 438 } |
| 427 | 439 |
| 428 return js.statement( | 440 return js.statement( |
| 429 'dart.defineLazyClass(#, { get #() { #; return #; } });', [ | 441 'dart.defineLazyClass(#, { get #() { #; return #; } });', [ |
| 430 _exportsVar, | 442 _exportsVar, |
| 431 _propertyName(name), | 443 _propertyName(name), |
| 432 body, | 444 body, |
| 433 name | 445 name |
| 434 ]); | 446 ]); |
| 435 } | 447 } |
| 436 | 448 |
| 437 if (isPublic(name)) _addExport(name); | 449 if (isPublic(name)) _addExport(name); |
| 438 | 450 |
| 439 if (genericDef != null) { | 451 if (genericDef != null) { |
| 440 body = js.statement('{ #; let # = #; }', [genericDef, name, genericInst]); | 452 var dynType = fillDynamicTypeArgs(type, types); |
| 453 var genericInst = _emitTypeName(dynType, lowerGeneric: true); | |
| 454 return js.statement('{ #; let # = #; }', [genericDef, name, genericInst]); | |
| 441 } | 455 } |
| 442 | 456 return body; |
| 443 if (type.isObject) return body; | |
| 444 | |
| 445 // If we're not lazy, we still need to ensure our dependencies are | |
| 446 // generated first. | |
| 447 var classDefs = <JS.Statement>[]; | |
| 448 if (type is InterfaceType) { | |
| 449 _emitClassIfNeeded(classDefs, type.superclass); | |
| 450 for (var m in type.element.mixins) { | |
| 451 _emitClassIfNeeded(classDefs, m); | |
| 452 } | |
| 453 } else if (type is FunctionType) { | |
| 454 _emitClassIfNeeded(classDefs, types.functionType); | |
| 455 } | |
| 456 classDefs.add(body); | |
| 457 return _statement(classDefs); | |
| 458 } | |
| 459 | |
| 460 void _emitClassIfNeeded(List<JS.Statement> defs, DartType base) { | |
| 461 // We can only emit classes from this library. | |
| 462 if (base.element.library != currentLibrary) return; | |
| 463 | |
| 464 var baseNode = _pendingClasses[base.element]; | |
| 465 if (baseNode != null) defs.add(visitClassDeclaration(baseNode)); | |
| 466 } | |
| 467 | |
| 468 /// Returns true if the supertype or mixins aren't loaded. | |
| 469 /// If that is the case, we'll emit a lazy class definition. | |
| 470 bool _lazyClass(DartType type) { | |
| 471 if (type.isObject) return false; | |
| 472 | |
| 473 // Use the element as the key, as those are unique whereas generic types | |
| 474 // can have their arguments substituted. | |
| 475 assert(type.element.library == currentLibrary); | |
| 476 var result = _lazyClassMemo[type.element]; | |
| 477 if (result != null) return result; | |
| 478 | |
| 479 if (type is InterfaceType) { | |
| 480 result = _typeMightNotBeLoaded(type.superclass) || | |
| 481 type.mixins.any(_typeMightNotBeLoaded); | |
| 482 } else if (type is FunctionType) { | |
| 483 result = _typeMightNotBeLoaded(types.functionType); | |
| 484 } | |
| 485 return _lazyClassMemo[type.element] = result; | |
| 486 } | |
| 487 | |
| 488 /// Returns true if the class might not be loaded. | |
| 489 /// | |
| 490 /// If the class is from our library, this can happen because it's lazy. | |
| 491 /// | |
| 492 /// If the class is from a different library, it could happen if we're in | |
| 493 /// a library cycle. In other words, if that different library depends back | |
| 494 /// on this library via some transitive import path. | |
| 495 /// | |
| 496 /// If we could control the global import ordering, we could eliminate some | |
| 497 /// of these cases, by ordering the imports of the cyclic libraries in an | |
| 498 /// optimal way. For example, we could order the libraries in a cycle to | |
| 499 /// minimize laziness. However, we currently assume we cannot control the | |
| 500 /// order that the cycle of libraries will be loaded in. | |
| 501 bool _typeMightNotBeLoaded(DartType type) { | |
| 502 var library = type.element.library; | |
| 503 if (library == currentLibrary) return _lazyClass(type); | |
| 504 return _libraryMightNotBeLoaded(library); | |
| 505 } | |
| 506 | |
| 507 bool _libraryMightNotBeLoaded(LibraryElement library) { | |
| 508 // The SDK is a special case: we optimize the order to prevent laziness. | |
| 509 if (library.isInSdk) { | |
| 510 // SDK is loaded before non-SDK libraies | |
| 511 if (!currentLibrary.isInSdk) return false; | |
| 512 | |
| 513 // Compute the order of both SDK libraries. If unknown, assume it's after. | |
| 514 var classOrder = corelibOrder.indexOf(library.name); | |
| 515 if (classOrder == -1) classOrder = corelibOrder.length; | |
| 516 | |
| 517 var currentOrder = corelibOrder.indexOf(currentLibrary.name); | |
| 518 if (currentOrder == -1) currentOrder = corelibOrder.length; | |
| 519 | |
| 520 // If the dart:* library we are currently compiling is loaded after the | |
| 521 // class's library, then we know the class is available. | |
| 522 if (classOrder != currentOrder) return currentOrder < classOrder; | |
| 523 | |
| 524 // If we don't know the order of the class's library or the current | |
| 525 // library, do the normal cycle check. (Not all SDK libs are cycles.) | |
| 526 } | |
| 527 | |
| 528 return _inLibraryCycle(library); | |
| 529 } | |
| 530 | |
| 531 /// Returns true if [library] depends on the [currentLibrary] via some | |
| 532 /// transitive import. | |
| 533 bool _inLibraryCycle(LibraryElement library) { | |
| 534 // SDK libs don't depend on things outside the SDK. | |
| 535 // (We can reach this via the recursive call below.) | |
| 536 if (library.isInSdk && !currentLibrary.isInSdk) return false; | |
| 537 | |
| 538 var result = _libraryCycleMemo[library]; | |
| 539 if (result != null) return result; | |
| 540 | |
| 541 result = library == currentLibrary; | |
| 542 _libraryCycleMemo[library] = result; | |
| 543 for (var e in library.imports) { | |
| 544 if (result) break; | |
| 545 result = _inLibraryCycle(e.importedLibrary); | |
| 546 } | |
| 547 for (var e in library.exports) { | |
| 548 if (result) break; | |
| 549 result = _inLibraryCycle(e.exportedLibrary); | |
| 550 } | |
| 551 return _libraryCycleMemo[library] = result; | |
| 552 } | 457 } |
| 553 | 458 |
| 554 JS.Statement _emitGenericClassDef(ParameterizedType type, JS.Statement body) { | 459 JS.Statement _emitGenericClassDef(ParameterizedType type, JS.Statement body) { |
| 555 var name = type.name; | 460 var name = type.name; |
| 556 var genericName = '$name\$'; | 461 var genericName = '$name\$'; |
| 557 var typeParams = type.typeParameters.map((p) => p.name); | 462 var typeParams = type.typeParameters.map((p) => p.name); |
| 558 if (isPublic(name)) _exports.add(genericName); | 463 if (isPublic(name)) _exports.add(genericName); |
| 559 return js.statement('let # = dart.generic(function(#) { #; return #; });', [ | 464 return js.statement('let # = dart.generic(function(#) { #; return #; });', [ |
| 560 genericName, | 465 genericName, |
| 561 typeParams, | 466 typeParams, |
| 562 body, | 467 body, |
| 563 name | 468 name |
| 564 ]); | 469 ]); |
| 565 } | 470 } |
| 566 | 471 |
| 567 JS.Expression _classHeritage(node) { | 472 JS.Expression _classHeritage(ClassElement element) { |
| 568 if (node.element.type.isObject) return null; | 473 var type = element.type; |
| 474 if (type.isObject) return null; | |
| 569 | 475 |
| 570 DartType supertype; | 476 // Assume we can load eagerly, until proven otherwise. |
| 571 if (node is ClassDeclaration) { | 477 _loader.startTopLevel(element); |
| 572 var ext = node.extendsClause; | |
| 573 supertype = ext != null ? ext.superclass.type : types.objectType; | |
| 574 } else { | |
| 575 supertype = (node as ClassTypeAlias).superclass.type; | |
| 576 } | |
| 577 | 478 |
| 578 JS.Expression heritage = _emitTypeName(supertype); | 479 JS.Expression heritage = _emitTypeName(type.superclass); |
| 579 | 480 if (type.mixins.isNotEmpty) { |
| 580 if (node.withClause != null) { | 481 var mixins = type.mixins.map(_emitTypeName).toList(); |
| 581 var mixins = _visitList(node.withClause.mixinTypes); | |
| 582 mixins.insert(0, heritage); | 482 mixins.insert(0, heritage); |
| 583 heritage = js.call('dart.mixin(#)', [mixins]); | 483 heritage = js.call('dart.mixin(#)', [mixins]); |
| 584 } | 484 } |
| 585 | 485 |
| 486 _loader.finishTopLevel(element); | |
| 586 return heritage; | 487 return heritage; |
| 587 } | 488 } |
| 588 | 489 |
| 589 List<JS.Method> _emitClassMethods(ClassDeclaration node, | 490 List<JS.Method> _emitClassMethods(ClassDeclaration node, |
| 590 List<ConstructorDeclaration> ctors, List<FieldDeclaration> fields) { | 491 List<ConstructorDeclaration> ctors, List<FieldDeclaration> fields) { |
| 591 var element = node.element; | 492 var element = node.element; |
| 592 var type = element.type; | 493 var type = element.type; |
| 593 var isObject = type.isObject; | 494 var isObject = type.isObject; |
| 594 var name = node.name.name; | 495 var name = node.name.name; |
| 595 | 496 |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 656 return new JS.Method(js.call('$_SYMBOL.iterator'), js.call( | 557 return new JS.Method(js.call('$_SYMBOL.iterator'), js.call( |
| 657 'function() { return new dart.JsIterator(this.#); }', | 558 'function() { return new dart.JsIterator(this.#); }', |
| 658 [_emitMemberName('iterator', type: t)])); | 559 [_emitMemberName('iterator', type: t)])); |
| 659 } | 560 } |
| 660 | 561 |
| 661 /// Emit class members that need to come after the class declaration, such | 562 /// Emit class members that need to come after the class declaration, such |
| 662 /// as static fields. See [_emitClassMethods] for things that are emitted | 563 /// as static fields. See [_emitClassMethods] for things that are emitted |
| 663 /// inside the ES6 `class { ... }` node. | 564 /// inside the ES6 `class { ... }` node. |
| 664 JS.Statement _finishClassMembers(ClassElement classElem, | 565 JS.Statement _finishClassMembers(ClassElement classElem, |
| 665 JS.ClassExpression cls, List<ConstructorDeclaration> ctors, | 566 JS.ClassExpression cls, List<ConstructorDeclaration> ctors, |
| 666 List<FieldDeclaration> fields, List<FieldDeclaration> staticFields, | 567 List<FieldDeclaration> fields, String jsPeerName) { |
| 667 String jsPeerName) { | |
| 668 var name = classElem.name; | 568 var name = classElem.name; |
| 669 var body = <JS.Statement>[]; | 569 var body = <JS.Statement>[]; |
| 670 body.add(new JS.ClassDeclaration(cls)); | 570 body.add(new JS.ClassDeclaration(cls)); |
| 671 | 571 |
| 672 // TODO(jmesserly): we should really just extend native Array. | 572 // TODO(jmesserly): we should really just extend native Array. |
| 673 if (jsPeerName != null && classElem.typeParameters.isNotEmpty) { | 573 if (jsPeerName != null && classElem.typeParameters.isNotEmpty) { |
| 674 body.add(js.statement('dart.setBaseClass(#, dart.global.#);', [ | 574 body.add(js.statement('dart.setBaseClass(#, dart.global.#);', [ |
| 675 classElem.name, | 575 classElem.name, |
| 676 _propertyName(jsPeerName) | 576 _propertyName(jsPeerName) |
| 677 ])); | 577 ])); |
| (...skipping 21 matching lines...) Expand all Loading... | |
| 699 // Instance fields, if they override getter/setter pairs | 599 // Instance fields, if they override getter/setter pairs |
| 700 for (FieldDeclaration member in fields) { | 600 for (FieldDeclaration member in fields) { |
| 701 for (VariableDeclaration fieldDecl in member.fields.variables) { | 601 for (VariableDeclaration fieldDecl in member.fields.variables) { |
| 702 var field = fieldDecl.element; | 602 var field = fieldDecl.element; |
| 703 if (_fieldsNeedingStorage.contains(field)) { | 603 if (_fieldsNeedingStorage.contains(field)) { |
| 704 body.add(_overrideField(field)); | 604 body.add(_overrideField(field)); |
| 705 } | 605 } |
| 706 } | 606 } |
| 707 } | 607 } |
| 708 | 608 |
| 709 // Static fields | |
| 710 var lazyStatics = <VariableDeclaration>[]; | |
| 711 for (FieldDeclaration member in staticFields) { | |
| 712 for (VariableDeclaration field in member.fields.variables) { | |
| 713 var fieldName = field.name.name; | |
| 714 if ((field.isConst || _isFieldInitConstant(field)) && | |
| 715 !JS.invalidStaticFieldName(fieldName)) { | |
| 716 var init = _visit(field.initializer); | |
| 717 if (init == null) init = new JS.LiteralNull(); | |
| 718 body.add(js.statement('#.# = #;', [name, fieldName, init])); | |
| 719 } else { | |
| 720 lazyStatics.add(field); | |
| 721 } | |
| 722 } | |
| 723 } | |
| 724 var lazy = _emitLazyFields(new JS.Identifier(name), lazyStatics); | |
| 725 if (lazy != null) body.add(lazy); | |
| 726 return _statement(body); | 609 return _statement(body); |
| 727 } | 610 } |
| 728 | 611 |
| 729 JS.Statement _overrideField(FieldElement e) { | 612 JS.Statement _overrideField(FieldElement e) { |
| 730 var cls = e.enclosingElement; | 613 var cls = e.enclosingElement; |
| 731 return js.statement('dart.virtualField(#, #)', [ | 614 return js.statement('dart.virtualField(#, #)', [ |
| 732 cls.name, | 615 cls.name, |
| 733 _emitMemberName(e.name, type: cls.type) | 616 _emitMemberName(e.name, type: cls.type) |
| 734 ]); | 617 ]); |
| 735 } | 618 } |
| 736 | 619 |
| 737 /// Generates the implicit default constructor for class C of the form | 620 /// Generates the implicit default constructor for class C of the form |
| 738 /// `C() : super() {}`. | 621 /// `C() : super() {}`. |
| 739 JS.Method _emitImplicitConstructor( | 622 JS.Method _emitImplicitConstructor( |
| 740 ClassDeclaration node, String name, List<FieldDeclaration> fields) { | 623 ClassDeclaration node, String name, List<FieldDeclaration> fields) { |
| 741 assert(_hasUnnamedConstructor(node.element) == fields.isNotEmpty); | 624 assert(_hasUnnamedConstructor(node.element) == fields.isNotEmpty); |
| 742 | 625 |
| 743 // If we don't have a method body, skip this. | 626 // If we don't have a method body, skip this. |
| 744 var superCall = _superConstructorCall(node); | 627 var superCall = _superConstructorCall(node); |
| 745 if (fields.isEmpty && superCall == null) return null; | 628 if (fields.isEmpty && superCall == null) return null; |
| 746 | 629 |
| 747 dynamic body = _initializeFields(fields); | 630 dynamic body = _initializeFields(node, fields); |
| 748 if (superCall != null) body = [[body, superCall]]; | 631 if (superCall != null) body = [[body, superCall]]; |
| 749 return new JS.Method( | 632 return new JS.Method( |
| 750 _propertyName(name), js.call('function() { #; }', body)); | 633 _propertyName(name), js.call('function() { #; }', body)); |
| 751 } | 634 } |
| 752 | 635 |
| 753 JS.Method _emitConstructor(ConstructorDeclaration node, String className, | 636 JS.Method _emitConstructor(ConstructorDeclaration node, String className, |
| 754 List<FieldDeclaration> fields, bool isObject) { | 637 List<FieldDeclaration> fields, bool isObject) { |
| 755 if (_externalOrNative(node)) return null; | 638 if (_externalOrNative(node)) return null; |
| 756 | 639 |
| 757 var name = _constructorName(className, node.name); | 640 var name = _constructorName(className, node.name); |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 826 body.add(_visit(redirectCall)); | 709 body.add(_visit(redirectCall)); |
| 827 return new JS.Block(body); | 710 return new JS.Block(body); |
| 828 } | 711 } |
| 829 | 712 |
| 830 // Initializers only run for non-factory constructors. | 713 // Initializers only run for non-factory constructors. |
| 831 if (node.factoryKeyword == null) { | 714 if (node.factoryKeyword == null) { |
| 832 // Generate field initializers. | 715 // Generate field initializers. |
| 833 // These are expanded into each non-redirecting constructor. | 716 // These are expanded into each non-redirecting constructor. |
| 834 // In the future we may want to create an initializer function if we have | 717 // In the future we may want to create an initializer function if we have |
| 835 // multiple constructors, but it needs to be balanced against readability. | 718 // multiple constructors, but it needs to be balanced against readability. |
| 836 body.add(_initializeFields(fields, node.parameters, node.initializers)); | 719 body.add(_initializeFields(node, fields)); |
| 837 | 720 |
| 838 var superCall = node.initializers.firstWhere( | 721 var superCall = node.initializers.firstWhere( |
| 839 (i) => i is SuperConstructorInvocation, orElse: () => null); | 722 (i) => i is SuperConstructorInvocation, orElse: () => null); |
| 840 | 723 |
| 841 // If no superinitializer is provided, an implicit superinitializer of the | 724 // If no superinitializer is provided, an implicit superinitializer of the |
| 842 // form `super()` is added at the end of the initializer list, unless the | 725 // form `super()` is added at the end of the initializer list, unless the |
| 843 // enclosing class is class Object. | 726 // enclosing class is class Object. |
| 844 var jsSuper = _superConstructorCall(node.parent, superCall); | 727 var jsSuper = _superConstructorCall(node.parent, superCall); |
| 845 if (jsSuper != null) body.add(jsSuper); | 728 if (jsSuper != null) body.add(jsSuper); |
| 846 } | 729 } |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 890 if (!e.unnamedConstructor.isSynthetic) return true; | 773 if (!e.unnamedConstructor.isSynthetic) return true; |
| 891 return e.fields.any((f) => !f.isStatic && !f.isSynthetic); | 774 return e.fields.any((f) => !f.isStatic && !f.isSynthetic); |
| 892 } | 775 } |
| 893 | 776 |
| 894 /// Initialize fields. They follow the sequence: | 777 /// Initialize fields. They follow the sequence: |
| 895 /// | 778 /// |
| 896 /// 1. field declaration initializer if non-const, | 779 /// 1. field declaration initializer if non-const, |
| 897 /// 2. field initializing parameters, | 780 /// 2. field initializing parameters, |
| 898 /// 3. constructor field initializers, | 781 /// 3. constructor field initializers, |
| 899 /// 4. initialize fields not covered in 1-3 | 782 /// 4. initialize fields not covered in 1-3 |
| 900 JS.Statement _initializeFields(List<FieldDeclaration> fieldDecls, | 783 JS.Statement _initializeFields( |
| 901 [FormalParameterList parameters, | 784 AstNode node, List<FieldDeclaration> fieldDecls) { |
| 902 NodeList<ConstructorInitializer> initializers]) { | 785 var unit = node.getAncestor((a) => a is CompilationUnit); |
| 786 var constField = new ConstFieldVisitor(types, unit); | |
| 903 | 787 |
| 904 // Run field initializers if they can have side-effects. | 788 // Run field initializers if they can have side-effects. |
| 905 var fields = new Map<FieldElement, JS.Expression>(); | 789 var fields = new Map<FieldElement, JS.Expression>(); |
| 906 var unsetFields = new Map<FieldElement, VariableDeclaration>(); | 790 var unsetFields = new Map<FieldElement, VariableDeclaration>(); |
| 907 for (var declaration in fieldDecls) { | 791 for (var declaration in fieldDecls) { |
| 908 for (var fieldNode in declaration.fields.variables) { | 792 for (var fieldNode in declaration.fields.variables) { |
| 909 var element = fieldNode.element; | 793 var element = fieldNode.element; |
| 910 if (_isFieldInitConstant(fieldNode)) { | 794 if (constField.isFieldInitConstant(fieldNode)) { |
| 911 unsetFields[element] = fieldNode; | 795 unsetFields[element] = fieldNode; |
| 912 } else { | 796 } else { |
| 913 fields[element] = _visitInitializer(fieldNode); | 797 fields[element] = _visitInitializer(fieldNode); |
| 914 } | 798 } |
| 915 } | 799 } |
| 916 } | 800 } |
| 917 | 801 |
| 918 // Initialize fields from `this.fieldName` parameters. | 802 // Initialize fields from `this.fieldName` parameters. |
| 919 if (parameters != null) { | 803 if (node is ConstructorDeclaration) { |
| 804 var parameters = node.parameters; | |
| 805 var initializers = node.initializers; | |
| 806 | |
| 920 for (var p in parameters.parameters) { | 807 for (var p in parameters.parameters) { |
| 921 var element = p.element; | 808 var element = p.element; |
| 922 if (element is FieldFormalParameterElement) { | 809 if (element is FieldFormalParameterElement) { |
| 923 fields[element.field] = _visit(p); | 810 fields[element.field] = _visit(p); |
| 924 } | 811 } |
| 925 } | 812 } |
| 926 } | |
| 927 | 813 |
| 928 // Run constructor field initializers such as `: foo = bar.baz` | 814 // Run constructor field initializers such as `: foo = bar.baz` |
| 929 if (initializers != null) { | |
| 930 for (var init in initializers) { | 815 for (var init in initializers) { |
| 931 if (init is ConstructorFieldInitializer) { | 816 if (init is ConstructorFieldInitializer) { |
| 932 fields[init.fieldName.staticElement] = _visit(init.expression); | 817 fields[init.fieldName.staticElement] = _visit(init.expression); |
| 933 } | 818 } |
| 934 } | 819 } |
| 935 } | 820 } |
| 936 | 821 |
| 937 for (var f in fields.keys) unsetFields.remove(f); | 822 for (var f in fields.keys) unsetFields.remove(f); |
| 938 | 823 |
| 939 // Initialize all remaining fields | 824 // Initialize all remaining fields |
| (...skipping 182 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1122 var accessor = node.staticElement; | 1007 var accessor = node.staticElement; |
| 1123 if (accessor == null) { | 1008 if (accessor == null) { |
| 1124 return js.commentExpression( | 1009 return js.commentExpression( |
| 1125 'Unimplemented unknown name', new JS.Identifier(node.name)); | 1010 'Unimplemented unknown name', new JS.Identifier(node.name)); |
| 1126 } | 1011 } |
| 1127 | 1012 |
| 1128 // Get the original declaring element. If we had a property accessor, this | 1013 // Get the original declaring element. If we had a property accessor, this |
| 1129 // indirects back to a (possibly synthetic) field. | 1014 // indirects back to a (possibly synthetic) field. |
| 1130 var element = accessor; | 1015 var element = accessor; |
| 1131 if (element is PropertyAccessorElement) element = accessor.variable; | 1016 if (element is PropertyAccessorElement) element = accessor.variable; |
| 1017 | |
| 1018 _loader.declareBeforeUse(element); | |
| 1019 | |
| 1132 var name = element.name; | 1020 var name = element.name; |
| 1133 | 1021 |
| 1134 // library member | 1022 // library member |
| 1135 if (element.enclosingElement is CompilationUnitElement && | 1023 if (element.enclosingElement is CompilationUnitElement) { |
| 1136 (element.library != currentLibrary || | 1024 return _maybeQualifiedName( |
| 1137 element is TopLevelVariableElement && !element.isConst)) { | 1025 element, _emitMemberName(name, isStatic: true)); |
| 1138 var memberName = _emitMemberName(name, isStatic: true); | |
| 1139 return js.call('#.#', [_libraryName(element.library), memberName]); | |
| 1140 } | 1026 } |
| 1141 | 1027 |
| 1142 // Unqualified class member. This could mean implicit-this, or implicit | 1028 // Unqualified class member. This could mean implicit-this, or implicit |
| 1143 // call to a static from the same class. | 1029 // call to a static from the same class. |
| 1144 if (element is ClassMemberElement && element is! ConstructorElement) { | 1030 if (element is ClassMemberElement && element is! ConstructorElement) { |
| 1145 bool isStatic = element.isStatic; | 1031 bool isStatic = element.isStatic; |
| 1146 var type = element.enclosingElement.type; | 1032 var type = element.enclosingElement.type; |
| 1147 var member = _emitMemberName(name, isStatic: isStatic, type: type); | 1033 var member = _emitMemberName(name, isStatic: isStatic, type: type); |
| 1148 | 1034 |
| 1149 // For static methods, we add the raw type name, without generics or | 1035 // For static methods, we add the raw type name, without generics or |
| 1150 // library prefix. We don't need those because static calls can't use | 1036 // library prefix. We don't need those because static calls can't use |
| 1151 // the generic type. | 1037 // the generic type. |
| 1152 if (isStatic) { | 1038 if (isStatic) { |
| 1153 return js.call('#.#', [type.name, member]); | 1039 var dynType = _emitTypeName(fillDynamicTypeArgs(type, types)); |
| 1040 return new JS.PropertyAccess(dynType, member); | |
| 1154 } | 1041 } |
| 1155 | 1042 |
| 1156 // For instance members, we add implicit-this. | 1043 // For instance members, we add implicit-this. |
| 1157 // For method tear-offs, we ensure it's a bound method. | 1044 // For method tear-offs, we ensure it's a bound method. |
| 1158 var code = 'this.#'; | 1045 var code = 'this.#'; |
| 1159 if (element is MethodElement && !inInvocationContext(node)) { | 1046 if (element is MethodElement && !inInvocationContext(node)) { |
| 1160 code += '.bind(this)'; | 1047 code += '.bind(this)'; |
| 1161 } | 1048 } |
| 1162 return js.call(code, member); | 1049 return js.call(code, member); |
| 1163 } | 1050 } |
| (...skipping 29 matching lines...) Expand all Loading... | |
| 1193 JS.ObjectInitializer _emitTypeProperties(Map<String, DartType> types) { | 1080 JS.ObjectInitializer _emitTypeProperties(Map<String, DartType> types) { |
| 1194 var properties = <JS.Property>[]; | 1081 var properties = <JS.Property>[]; |
| 1195 types.forEach((name, type) { | 1082 types.forEach((name, type) { |
| 1196 var key = new JS.LiteralString(name); | 1083 var key = new JS.LiteralString(name); |
| 1197 var value = _emitTypeName(type); | 1084 var value = _emitTypeName(type); |
| 1198 properties.add(new JS.Property(key, value)); | 1085 properties.add(new JS.Property(key, value)); |
| 1199 }); | 1086 }); |
| 1200 return new JS.ObjectInitializer(properties); | 1087 return new JS.ObjectInitializer(properties); |
| 1201 } | 1088 } |
| 1202 | 1089 |
| 1203 JS.Expression _emitTypeName(DartType type, {bool lowerTypedef: false}) { | 1090 /// Emits a Dart [type] into code. |
| 1091 /// | |
| 1092 /// If [lowerTypedef] is set, a typedef will be expanded as if it were a | |
| 1093 /// function type. Similarly if [lowerGeneric] is set, the `List$()` form | |
| 1094 /// will be used instead of `List`. These flags are used when generating | |
| 1095 /// the definitions for typedefs and generic types, respectively. | |
| 1096 JS.Expression _emitTypeName(DartType type, | |
| 1097 {bool lowerTypedef: false, bool lowerGeneric: false}) { | |
| 1098 | |
| 1204 // The void and dynamic types are not defined in core. | 1099 // The void and dynamic types are not defined in core. |
| 1205 if (type.isVoid) { | 1100 if (type.isVoid) { |
| 1206 return js.call('dart.void'); | 1101 return js.call('dart.void'); |
| 1207 } else if (type.isDynamic) { | 1102 } else if (type.isDynamic) { |
| 1208 return js.call('dart.dynamic'); | 1103 return js.call('dart.dynamic'); |
| 1209 } | 1104 } |
| 1210 | 1105 |
| 1106 _loader.declareBeforeUse(type.element); | |
| 1107 | |
| 1108 // TODO(jmesserly): like constants, should we hoist function types out of | |
| 1109 // methods? Similar issue with generic types. For all of these, we may want | |
| 1110 // to canonicalize them too, at least when inside the same library. | |
| 1211 var name = type.name; | 1111 var name = type.name; |
| 1212 var element = type.element; | 1112 var element = type.element; |
| 1213 if (name == '' || lowerTypedef && type is FunctionType) { | 1113 if (name == '' || lowerTypedef) { |
| 1214 if (type is FunctionType) { | 1114 var fnType = type as FunctionType; |
|
Jennifer Messerly
2015/05/12 16:23:45
if we get here, type has to be FunctionType.
| |
| 1215 var returnType = type.returnType; | 1115 var returnType = fnType.returnType; |
| 1216 var parameterTypes = type.normalParameterTypes; | 1116 var parameterTypes = fnType.normalParameterTypes; |
| 1217 var optionalTypes = type.optionalParameterTypes; | 1117 var optionalTypes = fnType.optionalParameterTypes; |
| 1218 var namedTypes = type.namedParameterTypes; | 1118 var namedTypes = fnType.namedParameterTypes; |
| 1219 if (namedTypes.isEmpty) { | 1119 if (namedTypes.isEmpty) { |
| 1220 if (optionalTypes.isEmpty) { | 1120 if (optionalTypes.isEmpty) { |
| 1221 return js.call('dart.functionType(#, #)', [ | 1121 return js.call('dart.functionType(#, #)', [ |
| 1222 _emitTypeName(returnType), | 1122 _emitTypeName(returnType), |
| 1223 _emitTypeNames(parameterTypes) | 1123 _emitTypeNames(parameterTypes) |
| 1224 ]); | 1124 ]); |
| 1225 } else { | |
| 1226 return js.call('dart.functionType(#, #, #)', [ | |
| 1227 _emitTypeName(returnType), | |
| 1228 _emitTypeNames(parameterTypes), | |
| 1229 _emitTypeNames(optionalTypes) | |
| 1230 ]); | |
| 1231 } | |
| 1232 } else { | 1125 } else { |
| 1233 assert(optionalTypes.isEmpty); | |
| 1234 return js.call('dart.functionType(#, #, #)', [ | 1126 return js.call('dart.functionType(#, #, #)', [ |
| 1235 _emitTypeName(returnType), | 1127 _emitTypeName(returnType), |
| 1236 _emitTypeNames(parameterTypes), | 1128 _emitTypeNames(parameterTypes), |
| 1237 _emitTypeProperties(namedTypes) | 1129 _emitTypeNames(optionalTypes) |
| 1238 ]); | 1130 ]); |
| 1239 } | 1131 } |
| 1132 } else { | |
| 1133 assert(optionalTypes.isEmpty); | |
| 1134 return js.call('dart.functionType(#, #, #)', [ | |
| 1135 _emitTypeName(returnType), | |
| 1136 _emitTypeNames(parameterTypes), | |
| 1137 _emitTypeProperties(namedTypes) | |
| 1138 ]); | |
| 1240 } | 1139 } |
| 1241 // TODO(jmesserly): remove when we're using coercion reifier. | |
| 1242 return _unimplementedCall('Unimplemented type $type'); | |
| 1243 } | 1140 } |
| 1244 | 1141 |
| 1245 var typeArgs = null; | 1142 if (type is TypeParameterType) { |
| 1143 return new JS.Identifier(name); | |
| 1144 } | |
| 1145 | |
| 1246 if (type is ParameterizedType) { | 1146 if (type is ParameterizedType) { |
| 1247 var args = type.typeArguments; | 1147 var args = type.typeArguments; |
| 1248 var isCurrentClass = | 1148 var isCurrentClass = |
| 1249 type is InterfaceType ? type.element == _currentClassElement : false; | 1149 args.isNotEmpty && _loader.isCurrentElement(type.element); |
| 1150 Iterable jsArgs = null; | |
| 1250 if (args.any((a) => a != types.dynamicType)) { | 1151 if (args.any((a) => a != types.dynamicType)) { |
| 1251 name = '$name\$'; | 1152 jsArgs = args.map(_emitTypeName); |
| 1252 typeArgs = args.map(_emitTypeName); | 1153 } else if (lowerGeneric || isCurrentClass) { |
| 1253 } else if (args.isNotEmpty && isCurrentClass) { | |
| 1254 // When creating a `new S<dynamic>` we try and use the raw form | 1154 // When creating a `new S<dynamic>` we try and use the raw form |
| 1255 // `new S()`, but this does not work if we're inside the same class, | 1155 // `new S()`, but this does not work if we're inside the same class, |
| 1256 // because `S` refers to the current S<T> we are generating. | 1156 // because `S` refers to the current S<T> we are generating. |
| 1257 name = '$name\$'; | 1157 jsArgs = []; |
| 1258 typeArgs = []; | 1158 } |
| 1159 if (jsArgs != null) { | |
| 1160 var genericName = _maybeQualifiedName( | |
| 1161 element, _propertyName('$name\$'), _qualifiedGenericIds); | |
| 1162 return js.call('#(#)', [genericName, jsArgs]); | |
| 1259 } | 1163 } |
| 1260 } | 1164 } |
| 1261 | 1165 |
| 1262 JS.Expression result; | 1166 return _maybeQualifiedName(element, _propertyName(name)); |
| 1263 if (_needQualifiedName(element)) { | 1167 } |
| 1264 result = js.call('#.#', [_libraryName(element.library), name]); | 1168 |
| 1265 } else { | 1169 JS.Expression _maybeQualifiedName(Element e, JS.Expression name, |
|
Jennifer Messerly
2015/05/12 16:23:45
we can now make name qualification decisions after
| |
| 1266 result = new JS.Identifier(name); | 1170 [Map<Element, JS.MaybeQualifiedId> idTable]) { |
| 1171 var libName = _libraryName(e.library); | |
| 1172 if (idTable == null) idTable = _qualifiedIds; | |
| 1173 | |
| 1174 // Mutable top-level fields should always be qualified. | |
| 1175 bool mutableTopLevel = e is TopLevelVariableElement && !e.isConst; | |
| 1176 if (e.library != currentLibrary || mutableTopLevel) { | |
| 1177 return new JS.PropertyAccess(libName, name); | |
| 1267 } | 1178 } |
| 1268 | 1179 |
| 1269 if (typeArgs != null) { | 1180 return idTable.putIfAbsent(e, () => new JS.MaybeQualifiedId(libName, name)); |
| 1270 result = js.call('#(#)', [result, typeArgs]); | |
| 1271 } | |
| 1272 return result; | |
| 1273 } | |
| 1274 | |
| 1275 bool _needQualifiedName(Element element) { | |
| 1276 var lib = element.library; | |
| 1277 if (lib == null) return false; | |
| 1278 if (lib != currentLibrary) return true; | |
| 1279 if (element is ClassElement) return _lazyClass(element.type); | |
| 1280 if (element is FunctionTypeAliasElement) return _lazyClass(element.type); | |
| 1281 return false; | |
| 1282 } | 1181 } |
| 1283 | 1182 |
| 1284 @override | 1183 @override |
| 1285 JS.Expression visitAssignmentExpression(AssignmentExpression node) { | 1184 JS.Expression visitAssignmentExpression(AssignmentExpression node) { |
| 1286 var left = node.leftHandSide; | 1185 var left = node.leftHandSide; |
| 1287 var right = node.rightHandSide; | 1186 var right = node.rightHandSide; |
| 1288 if (node.operator.type == TokenType.EQ) return _emitSet(left, right); | 1187 if (node.operator.type == TokenType.EQ) return _emitSet(left, right); |
| 1289 return _emitOpAssign( | 1188 return _emitOpAssign( |
| 1290 left, right, node.operator.lexeme[0], node.staticElement, | 1189 left, right, node.operator.lexeme[0], node.staticElement, |
| 1291 context: node); | 1190 context: node); |
| (...skipping 189 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1481 | 1380 |
| 1482 @override | 1381 @override |
| 1483 JS.Statement visitReturnStatement(ReturnStatement node) { | 1382 JS.Statement visitReturnStatement(ReturnStatement node) { |
| 1484 var e = node.expression; | 1383 var e = node.expression; |
| 1485 if (e == null) return new JS.Return(); | 1384 if (e == null) return new JS.Return(); |
| 1486 return _visit(e).toReturn(); | 1385 return _visit(e).toReturn(); |
| 1487 } | 1386 } |
| 1488 | 1387 |
| 1489 @override | 1388 @override |
| 1490 visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { | 1389 visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { |
| 1491 var body = <JS.Statement>[]; | 1390 for (var v in node.variables.variables) { |
| 1492 | 1391 _loader.loadDeclaration(v, v.element); |
| 1493 for (var field in node.variables.variables) { | |
| 1494 if (field.isConst) { | |
| 1495 // constant fields don't change, so we can generate them as `let` | |
| 1496 // but add them to the module's exports | |
| 1497 var name = field.name.name; | |
| 1498 body.add(js.statement( | |
| 1499 'let # = #;', [new JS.Identifier(name), _visitInitializer(field)])); | |
| 1500 if (isPublic(name)) _addExport(name); | |
| 1501 } else if (_isFieldInitConstant(field)) { | |
| 1502 body.add(js.statement( | |
| 1503 '# = #;', [_visit(field.name), _visitInitializer(field)])); | |
| 1504 } else { | |
| 1505 _lazyFields.add(field); | |
| 1506 } | |
| 1507 } | 1392 } |
| 1508 | |
| 1509 return _statement(body); | |
| 1510 } | 1393 } |
| 1511 | 1394 |
| 1512 _addExport(String name) { | 1395 _addExport(String name) { |
| 1513 if (!_exports.add(name)) throw 'Duplicate top level name found: $name'; | 1396 if (!_exports.add(name)) throw 'Duplicate top level name found: $name'; |
| 1514 } | 1397 } |
| 1515 | 1398 |
| 1516 @override | 1399 @override |
| 1517 JS.Statement visitVariableDeclarationStatement( | 1400 JS.Statement visitVariableDeclarationStatement( |
| 1518 VariableDeclarationStatement node) { | 1401 VariableDeclarationStatement node) { |
| 1519 // Special case a single variable with an initializer. | 1402 // Special case a single variable with an initializer. |
| 1520 // This helps emit cleaner code for things like: | 1403 // This helps emit cleaner code for things like: |
| 1521 // var result = []..add(1)..add(2); | 1404 // var result = []..add(1)..add(2); |
| 1522 if (node.variables.variables.length == 1) { | 1405 if (node.variables.variables.length == 1) { |
| 1523 var v = node.variables.variables.single; | 1406 var v = node.variables.variables.single; |
| 1524 if (v.initializer != null) { | 1407 if (v.initializer != null) { |
| 1525 var name = new JS.Identifier(v.name.name); | 1408 var name = new JS.Identifier(v.name.name); |
| 1526 return _visit(v.initializer).toVariableDeclaration(name); | 1409 return _visit(v.initializer).toVariableDeclaration(name); |
| 1527 } | 1410 } |
| 1528 } | 1411 } |
| 1529 return _visit(node.variables).toStatement(); | 1412 return _visit(node.variables).toStatement(); |
| 1530 } | 1413 } |
| 1531 | 1414 |
| 1532 @override | 1415 @override |
| 1533 visitVariableDeclarationList(VariableDeclarationList node) { | 1416 visitVariableDeclarationList(VariableDeclarationList node) { |
| 1534 return new JS.VariableDeclarationList('let', _visitList(node.variables)); | 1417 return new JS.VariableDeclarationList('let', _visitList(node.variables)); |
| 1535 } | 1418 } |
| 1536 | 1419 |
| 1537 @override | 1420 @override |
| 1538 JS.VariableInitialization visitVariableDeclaration(VariableDeclaration node) { | 1421 visitVariableDeclaration(VariableDeclaration node) { |
| 1422 if (node.element is PropertyInducingElement) return _emitStaticField(node); | |
| 1423 | |
| 1539 var name = new JS.Identifier(node.name.name); | 1424 var name = new JS.Identifier(node.name.name); |
| 1540 return new JS.VariableInitialization(name, _visitInitializer(node)); | 1425 return new JS.VariableInitialization(name, _visitInitializer(node)); |
| 1541 } | 1426 } |
| 1542 | 1427 |
| 1428 /// Emits a static or top-level field. | |
| 1429 JS.Statement _emitStaticField(VariableDeclaration field) { | |
| 1430 PropertyInducingElement element = field.element; | |
| 1431 assert(element.isStatic); | |
| 1432 | |
| 1433 bool eagerInit; | |
| 1434 JS.Expression jsInit; | |
| 1435 if (field.isConst || _constField.isFieldInitConstant(field)) { | |
| 1436 // If the field is constant, try and generate it at the top level. | |
| 1437 _loader.startTopLevel(element); | |
| 1438 jsInit = _visitInitializer(field); | |
| 1439 _loader.finishTopLevel(element); | |
| 1440 eagerInit = _loader.isLoaded(element); | |
| 1441 } else { | |
| 1442 jsInit = _visitInitializer(field); | |
| 1443 eagerInit = false; | |
| 1444 } | |
| 1445 | |
| 1446 var fieldName = field.name.name; | |
| 1447 if (field.isConst && eagerInit && element is TopLevelVariableElement) { | |
| 1448 // constant fields don't change, so we can generate them as `let` | |
| 1449 // but add them to the module's exports. However, make sure we generate | |
| 1450 // anything they depend on first. | |
| 1451 | |
| 1452 if (isPublic(fieldName)) _addExport(fieldName); | |
| 1453 return js.statement('let # = #;', [new JS.Identifier(fieldName), jsInit]); | |
| 1454 } | |
| 1455 | |
| 1456 if (eagerInit && !JS.invalidStaticFieldName(fieldName)) { | |
| 1457 return js.statement('# = #;', [_visit(field.name), jsInit]); | |
| 1458 } | |
| 1459 | |
| 1460 var body = []; | |
| 1461 if (_lazyFields.isNotEmpty) { | |
| 1462 var existingTarget = _lazyFields[0].element.enclosingElement; | |
| 1463 if (existingTarget != element.enclosingElement) { | |
| 1464 _flushLazyFields(body); | |
| 1465 } | |
| 1466 } | |
| 1467 | |
| 1468 _lazyFields.add(field); | |
| 1469 | |
| 1470 return _statement(body); | |
| 1471 } | |
| 1472 | |
| 1543 JS.Expression _visitInitializer(VariableDeclaration node) { | 1473 JS.Expression _visitInitializer(VariableDeclaration node) { |
| 1544 var value = _visit(node.initializer); | 1474 var value = _visit(node.initializer); |
| 1545 // explicitly initialize to null, to avoid getting `undefined`. | 1475 // explicitly initialize to null, to avoid getting `undefined`. |
| 1546 // TODO(jmesserly): do this only for vars that aren't definitely assigned. | 1476 // TODO(jmesserly): do this only for vars that aren't definitely assigned. |
| 1547 return value != null ? value : new JS.LiteralNull(); | 1477 return value != null ? value : new JS.LiteralNull(); |
| 1548 } | 1478 } |
| 1549 | 1479 |
| 1550 void _flushPendingStatements(List<JS.Statement> body) { | |
| 1551 if (_pendingStatements.isNotEmpty) { | |
| 1552 body.addAll(_pendingStatements); | |
| 1553 _pendingStatements.clear(); | |
| 1554 } | |
| 1555 } | |
| 1556 | |
| 1557 void _flushLazyFields(List<JS.Statement> body) { | 1480 void _flushLazyFields(List<JS.Statement> body) { |
| 1558 var code = _emitLazyFields(_exportsVar, _lazyFields); | 1481 if (_lazyFields.isEmpty) return; |
| 1559 if (code != null) { | 1482 body.add(_emitLazyFields(_lazyFields)); |
| 1560 // Ensure symbols for private fields are defined. | |
| 1561 _flushPendingStatements(body); | |
| 1562 body.add(code); | |
| 1563 } | |
| 1564 _lazyFields.clear(); | 1483 _lazyFields.clear(); |
| 1565 } | 1484 } |
| 1566 | 1485 |
| 1567 JS.Statement _emitLazyFields( | 1486 JS.Statement _emitLazyFields(List<VariableDeclaration> fields) { |
| 1568 JS.Expression objExpr, List<VariableDeclaration> fields) { | |
| 1569 if (fields.isEmpty) return null; | |
| 1570 | |
| 1571 var methods = []; | 1487 var methods = []; |
| 1572 for (var node in fields) { | 1488 for (var node in fields) { |
| 1573 var name = node.name.name; | 1489 var name = node.name.name; |
| 1574 var element = node.element; | 1490 var element = node.element; |
| 1575 var access = _emitMemberName(name, type: element.type, isStatic: true); | 1491 var access = _emitMemberName(name, type: element.type, isStatic: true); |
| 1576 methods.add(new JS.Method( | 1492 methods.add(new JS.Method( |
| 1577 access, js.call('function() { return #; }', _visit(node.initializer)), | 1493 access, js.call('function() { return #; }', _visit(node.initializer)), |
| 1578 isGetter: true)); | 1494 isGetter: true)); |
| 1579 | 1495 |
| 1580 // TODO(jmesserly): use a dummy setter to indicate writable. | 1496 // TODO(jmesserly): use a dummy setter to indicate writable. |
| 1581 if (!node.isFinal) { | 1497 if (!node.isFinal) { |
| 1582 methods.add( | 1498 methods.add( |
| 1583 new JS.Method(access, js.call('function(_) {}'), isSetter: true)); | 1499 new JS.Method(access, js.call('function(_) {}'), isSetter: true)); |
| 1584 } | 1500 } |
| 1585 } | 1501 } |
| 1586 | 1502 |
| 1503 JS.Expression objExpr = _exportsVar; | |
| 1504 var target = _lazyFields[0].element.enclosingElement; | |
| 1505 if (target is ClassElement) { | |
| 1506 objExpr = new JS.Identifier(target.type.name); | |
| 1507 } | |
| 1508 | |
| 1587 return js.statement( | 1509 return js.statement( |
| 1588 'dart.defineLazyProperties(#, { # });', [objExpr, methods]); | 1510 'dart.defineLazyProperties(#, { # });', [objExpr, methods]); |
| 1589 } | 1511 } |
| 1590 | 1512 |
| 1591 void _flushLibraryProperties(List<JS.Statement> body) { | 1513 void _flushLibraryProperties(List<JS.Statement> body) { |
| 1592 if (_properties.isEmpty) return; | 1514 if (_properties.isEmpty) return; |
| 1593 body.add(js.statement('dart.copyProperties(#, { # });', [ | 1515 body.add(js.statement('dart.copyProperties(#, { # });', [ |
| 1594 _exportsVar, | 1516 _exportsVar, |
| 1595 _properties.map(_emitTopLevelProperty) | 1517 _properties.map(_emitTopLevelProperty) |
| 1596 ])); | 1518 ])); |
| 1597 _properties.clear(); | 1519 _properties.clear(); |
| 1598 } | 1520 } |
| 1599 | 1521 |
| 1600 @override | 1522 @override |
| 1601 visitConstructorName(ConstructorName node) { | 1523 visitConstructorName(ConstructorName node) { |
| 1602 var typeName = _visit(node.type); | 1524 var typeName = _visit(node.type); |
| 1603 if (node.name != null) { | 1525 if (node.name != null) { |
| 1604 return js.call( | 1526 return js.call( |
| 1605 '#.#', [typeName, _emitMemberName(node.name.name, isStatic: true)]); | 1527 '#.#', [typeName, _emitMemberName(node.name.name, isStatic: true)]); |
| 1606 } | 1528 } |
| 1607 return typeName; | 1529 return typeName; |
| 1608 } | 1530 } |
| 1609 | 1531 |
| 1610 @override | 1532 @override |
| 1611 visitInstanceCreationExpression(InstanceCreationExpression node) { | 1533 visitInstanceCreationExpression(InstanceCreationExpression node) { |
| 1612 var newExpr = js.call( | 1534 emitNew() => js.call( |
| 1613 'new #(#)', [_visit(node.constructorName), _visit(node.argumentList)]); | 1535 'new #(#)', [_visit(node.constructorName), _visit(node.argumentList)]); |
| 1614 if (node.isConst) return _const(node, newExpr); | 1536 if (node.isConst) return _emitConst(node, emitNew); |
| 1615 return newExpr; | 1537 return emitNew(); |
| 1616 } | 1538 } |
| 1617 | 1539 |
| 1618 /// True if this type is built-in to JS, and we use the values unwrapped. | 1540 /// True if this type is built-in to JS, and we use the values unwrapped. |
| 1619 /// For these types we generate a calling convention via static | 1541 /// For these types we generate a calling convention via static |
| 1620 /// "extension methods". This allows types to be extended without adding | 1542 /// "extension methods". This allows types to be extended without adding |
| 1621 /// extensions directly on the prototype. | 1543 /// extensions directly on the prototype. |
| 1622 bool _isJSBuiltinType(DartType t) => | 1544 bool _isJSBuiltinType(DartType t) => |
| 1623 typeIsPrimitiveInJS(t) || rules.isStringType(t); | 1545 typeIsPrimitiveInJS(t) || rules.isStringType(t); |
| 1624 | 1546 |
| 1625 bool typeIsPrimitiveInJS(DartType t) => rules.isIntType(t) || | 1547 bool typeIsPrimitiveInJS(DartType t) => rules.isIntType(t) || |
| (...skipping 147 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1773 // LocalVariableElementImpl, so we could repurpose to mean "temp". | 1695 // LocalVariableElementImpl, so we could repurpose to mean "temp". |
| 1774 // * add a new property to LocalVariableElementImpl. | 1696 // * add a new property to LocalVariableElementImpl. |
| 1775 // * create a new subtype of LocalVariableElementImpl to mark a temp. | 1697 // * create a new subtype of LocalVariableElementImpl to mark a temp. |
| 1776 var id = | 1698 var id = |
| 1777 new SimpleIdentifier(new StringToken(TokenType.IDENTIFIER, name, -1)); | 1699 new SimpleIdentifier(new StringToken(TokenType.IDENTIFIER, name, -1)); |
| 1778 id.staticElement = new TemporaryVariableElement.forNode(id); | 1700 id.staticElement = new TemporaryVariableElement.forNode(id); |
| 1779 id.staticType = type; | 1701 id.staticType = type; |
| 1780 return id; | 1702 return id; |
| 1781 } | 1703 } |
| 1782 | 1704 |
| 1783 JS.Expression _const(Expression node, JS.Expression expr, [String nameHint]) { | 1705 JS.Expression _emitConst(Expression node, JS.Expression expr()) { |
| 1784 var value = js.call('dart.const(#)', expr); | 1706 // TODO(jmesserly): emit the constants at top level if possible. |
| 1785 | 1707 // This wasn't quite working, so disabled for now. |
| 1786 // If we're inside a method or function, capture the value into a | 1708 return js.call('dart.const(#)', expr()); |
|
Jennifer Messerly
2015/05/12 16:23:45
the idea here is: _emitConst can tell _loader it's
| |
| 1787 // global temporary, so we don't do the expensive canonicalization step. | |
| 1788 var ancestor = node.getAncestor((n) => n is FunctionBody || | |
| 1789 (n is FieldDeclaration && n.staticKeyword == null)); | |
| 1790 if (ancestor == null) return value; | |
| 1791 | |
| 1792 if (nameHint == null) { | |
| 1793 nameHint = 'const_' + getStaticType(node).name; | |
| 1794 } | |
| 1795 | |
| 1796 // TODO(jmesserly): enable this once we fix | |
| 1797 // https://github.com/dart-lang/dev_compiler/issues/131 | |
| 1798 /*var temp = new JSTemporary(nameHint); | |
| 1799 _pendingStatements.add(js.statement('let # = #;', [temp, value])); | |
| 1800 return temp;*/ | |
| 1801 assert(nameHint != null); // so it's not marked unused | |
| 1802 return value; | |
| 1803 } | 1709 } |
| 1804 | 1710 |
| 1805 /// Returns a new expression, which can be be used safely *once* on the | 1711 /// Returns a new expression, which can be be used safely *once* on the |
| 1806 /// left hand side, and *once* on the right side of an assignment. | 1712 /// left hand side, and *once* on the right side of an assignment. |
| 1807 /// For example: `expr1[expr2] += y` can be compiled as | 1713 /// For example: `expr1[expr2] += y` can be compiled as |
| 1808 /// `expr1[expr2] = expr1[expr2] + y`. | 1714 /// `expr1[expr2] = expr1[expr2] + y`. |
| 1809 /// | 1715 /// |
| 1810 /// The temporary scope will ensure `expr1` and `expr2` are only evaluated | 1716 /// The temporary scope will ensure `expr1` and `expr2` are only evaluated |
| 1811 /// once: `((x1, x2) => x1[x2] = x1[x2] + y)(expr1, expr2)`. | 1717 /// once: `((x1, x2) => x1[x2] = x1[x2] + y)(expr1, expr2)`. |
| 1812 /// | 1718 /// |
| (...skipping 457 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2270 visitIntegerLiteral(IntegerLiteral node) => js.number(node.value); | 2176 visitIntegerLiteral(IntegerLiteral node) => js.number(node.value); |
| 2271 | 2177 |
| 2272 @override | 2178 @override |
| 2273 visitDoubleLiteral(DoubleLiteral node) => js.number(node.value); | 2179 visitDoubleLiteral(DoubleLiteral node) => js.number(node.value); |
| 2274 | 2180 |
| 2275 @override | 2181 @override |
| 2276 visitNullLiteral(NullLiteral node) => new JS.LiteralNull(); | 2182 visitNullLiteral(NullLiteral node) => new JS.LiteralNull(); |
| 2277 | 2183 |
| 2278 @override | 2184 @override |
| 2279 visitSymbolLiteral(SymbolLiteral node) { | 2185 visitSymbolLiteral(SymbolLiteral node) { |
| 2280 // TODO(vsm): When we canonicalize, we need to treat private symbols | 2186 emitSymbol() { |
| 2281 // correctly. | 2187 // TODO(vsm): When we canonicalize, we need to treat private symbols |
| 2282 var name = js.string(node.components.join('.'), "'"); | 2188 // correctly. |
| 2283 var nameHint = 'symbol_' + node.components.join('_'); | 2189 var name = js.string(node.components.join('.'), "'"); |
| 2284 return _const( | 2190 return new JS.New(_emitTypeName(types.symbolType), [name]); |
| 2285 node, new JS.New(_emitTypeName(types.symbolType), [name]), nameHint); | 2191 } |
| 2192 return _emitConst(node, emitSymbol); | |
| 2286 } | 2193 } |
| 2287 | 2194 |
| 2288 @override | 2195 @override |
| 2289 visitListLiteral(ListLiteral node) { | 2196 visitListLiteral(ListLiteral node) { |
| 2290 JS.Expression list = new JS.ArrayInitializer(_visitList(node.elements)); | 2197 emitList() { |
| 2291 | 2198 JS.Expression list = new JS.ArrayInitializer(_visitList(node.elements)); |
| 2292 ParameterizedType type = node.staticType; | 2199 ParameterizedType type = node.staticType; |
| 2293 if (type.typeArguments.any((a) => a != types.dynamicType)) { | 2200 if (type.typeArguments.any((a) => a != types.dynamicType)) { |
| 2294 list = js.call('dart.setType(#, #)', [list, _emitTypeName(type)]); | 2201 list = js.call('dart.setType(#, #)', [list, _emitTypeName(type)]); |
| 2202 } | |
| 2203 return list; | |
| 2295 } | 2204 } |
| 2296 if (node.constKeyword != null) return _const(node, list); | 2205 if (node.constKeyword != null) return _emitConst(node, emitList); |
| 2297 return list; | 2206 return emitList(); |
| 2298 } | 2207 } |
| 2299 | 2208 |
| 2300 @override | 2209 @override |
| 2301 visitMapLiteral(MapLiteral node) { | 2210 visitMapLiteral(MapLiteral node) { |
| 2302 // TODO(jmesserly): we can likely make these faster. | 2211 // TODO(jmesserly): we can likely make these faster. |
| 2303 var entries = node.entries; | 2212 emitMap() { |
| 2304 var mapArguments = null; | 2213 var entries = node.entries; |
| 2305 if (entries.isEmpty) { | 2214 var mapArguments = null; |
| 2306 mapArguments = []; | 2215 if (entries.isEmpty) { |
| 2307 } else if (entries.every((e) => e.key is StringLiteral)) { | 2216 mapArguments = []; |
| 2308 // Use JS object literal notation if possible, otherwise use an array. | 2217 } else if (entries.every((e) => e.key is StringLiteral)) { |
| 2309 // We could do this any time all keys are non-nullable String type. | 2218 // Use JS object literal notation if possible, otherwise use an array. |
| 2310 // For now, support StringLiteral as the common non-nullable String case. | 2219 // We could do this any time all keys are non-nullable String type. |
| 2311 var props = []; | 2220 // For now, support StringLiteral as the common non-nullable String case . |
| 2312 for (var e in entries) { | 2221 var props = []; |
| 2313 props.add(new JS.Property(_visit(e.key), _visit(e.value))); | 2222 for (var e in entries) { |
| 2223 props.add(new JS.Property(_visit(e.key), _visit(e.value))); | |
| 2224 } | |
| 2225 mapArguments = new JS.ObjectInitializer(props); | |
| 2226 } else { | |
| 2227 var values = []; | |
| 2228 for (var e in entries) { | |
| 2229 values.add(_visit(e.key)); | |
| 2230 values.add(_visit(e.value)); | |
| 2231 } | |
| 2232 mapArguments = new JS.ArrayInitializer(values); | |
| 2314 } | 2233 } |
| 2315 mapArguments = new JS.ObjectInitializer(props); | 2234 // TODO(jmesserly): add generic types args. |
| 2316 } else { | 2235 return js.call('dart.map(#)', [mapArguments]); |
| 2317 var values = []; | |
| 2318 for (var e in entries) { | |
| 2319 values.add(_visit(e.key)); | |
| 2320 values.add(_visit(e.value)); | |
| 2321 } | |
| 2322 mapArguments = new JS.ArrayInitializer(values); | |
| 2323 } | 2236 } |
| 2324 // TODO(jmesserly): add generic types args. | 2237 if (node.constKeyword != null) return _emitConst(node, emitMap); |
| 2325 var map = js.call('dart.map(#)', [mapArguments]); | 2238 return emitMap(); |
| 2326 if (node.constKeyword != null) return _const(node, map); | |
| 2327 return map; | |
| 2328 } | 2239 } |
| 2329 | 2240 |
| 2330 @override | 2241 @override |
| 2331 JS.LiteralString visitSimpleStringLiteral(SimpleStringLiteral node) => | 2242 JS.LiteralString visitSimpleStringLiteral(SimpleStringLiteral node) => |
| 2332 js.escapedString(node.value, node.isSingleQuoted ? "'" : '"'); | 2243 js.escapedString(node.value, node.isSingleQuoted ? "'" : '"'); |
| 2333 | 2244 |
| 2334 @override | 2245 @override |
| 2335 JS.Expression visitAdjacentStrings(AdjacentStrings node) => | 2246 JS.Expression visitAdjacentStrings(AdjacentStrings node) => |
| 2336 _visitListToBinary(node.strings, '+'); | 2247 _visitListToBinary(node.strings, '+'); |
| 2337 | 2248 |
| (...skipping 30 matching lines...) Expand all Loading... | |
| 2368 JS.Expression _unimplementedCall(String comment) { | 2279 JS.Expression _unimplementedCall(String comment) { |
| 2369 return js.call('dart.throw_(#)', [js.escapedString(comment)]); | 2280 return js.call('dart.throw_(#)', [js.escapedString(comment)]); |
| 2370 } | 2281 } |
| 2371 | 2282 |
| 2372 @override | 2283 @override |
| 2373 visitNode(AstNode node) { | 2284 visitNode(AstNode node) { |
| 2374 // TODO(jmesserly): verify this is unreachable. | 2285 // TODO(jmesserly): verify this is unreachable. |
| 2375 throw 'Unimplemented ${node.runtimeType}: $node'; | 2286 throw 'Unimplemented ${node.runtimeType}: $node'; |
| 2376 } | 2287 } |
| 2377 | 2288 |
| 2378 // TODO(jmesserly): this is used to determine if the field initialization is | |
| 2379 // side effect free. We should make the check more general, as things like | |
| 2380 // list/map literals/regexp are also side effect free and fairly common | |
| 2381 // to use as field initializers. | |
| 2382 bool _isFieldInitConstant(VariableDeclaration field) => | |
| 2383 field.initializer == null || _computeConstant(field).isValid; | |
| 2384 | |
| 2385 EvaluationResult _computeConstant(VariableDeclaration field) { | |
| 2386 // If the constant is already computed by ConstantEvaluator, just return it. | |
| 2387 VariableElementImpl element = field.element; | |
| 2388 var result = element.evaluationResult; | |
| 2389 if (result != null) return result; | |
| 2390 | |
| 2391 // ConstantEvaluator will not compute constants for non-const fields | |
| 2392 // at least for cases like `int x = 0;`, so run ConstantVisitor for those. | |
| 2393 // TODO(jmesserly): ideally we'd only do this if we're sure it was skipped | |
| 2394 // by ConstantEvaluator. | |
| 2395 var initializer = field.initializer; | |
| 2396 if (initializer == null) return null; | |
| 2397 | |
| 2398 return _constEvaluator.evaluate(initializer); | |
| 2399 } | |
| 2400 | |
| 2401 _visit(AstNode node) { | 2289 _visit(AstNode node) { |
| 2402 if (node == null) return null; | 2290 if (node == null) return null; |
| 2403 var result = node.accept(this); | 2291 var result = node.accept(this); |
| 2404 if (result is JS.Node) result.sourceInformation = node; | 2292 if (result is JS.Node) result.sourceInformation = node; |
| 2405 return result; | 2293 return result; |
| 2406 } | 2294 } |
| 2407 | 2295 |
| 2408 List _visitList(Iterable<AstNode> nodes) { | 2296 List _visitList(Iterable<AstNode> nodes) { |
| 2409 if (nodes == null) return null; | 2297 if (nodes == null) return null; |
| 2410 var result = []; | 2298 var result = []; |
| (...skipping 209 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2620 | 2508 |
| 2621 /// A special kind of element created by the compiler, signifying a temporary | 2509 /// A special kind of element created by the compiler, signifying a temporary |
| 2622 /// variable. These objects use instance equality, and should be shared | 2510 /// variable. These objects use instance equality, and should be shared |
| 2623 /// everywhere in the tree where they are treated as the same variable. | 2511 /// everywhere in the tree where they are treated as the same variable. |
| 2624 class TemporaryVariableElement extends LocalVariableElementImpl { | 2512 class TemporaryVariableElement extends LocalVariableElementImpl { |
| 2625 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); | 2513 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); |
| 2626 | 2514 |
| 2627 int get hashCode => identityHashCode(this); | 2515 int get hashCode => identityHashCode(this); |
| 2628 bool operator ==(Object other) => identical(this, other); | 2516 bool operator ==(Object other) => identical(this, other); |
| 2629 } | 2517 } |
| OLD | NEW |