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; |
| 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/resolver.dart' show TypeProvider; | 13 import 'package:analyzer/src/generated/resolver.dart' show TypeProvider; |
| 14 import 'package:analyzer/src/generated/scanner.dart' | 14 import 'package:analyzer/src/generated/scanner.dart' |
| 15 show StringToken, Token, TokenType; | 15 show StringToken, Token, TokenType; |
| 16 import 'package:analyzer/src/task/dart.dart' show PublicNamespaceBuilder; | |
| 16 | 17 |
| 17 import 'package:dev_compiler/src/codegen/ast_builder.dart' show AstBuilder; | 18 import 'package:dev_compiler/src/codegen/ast_builder.dart' show AstBuilder; |
| 18 import 'package:dev_compiler/src/codegen/reify_coercions.dart' | 19 import 'package:dev_compiler/src/codegen/reify_coercions.dart' |
| 19 show CoercionReifier; | 20 show CoercionReifier; |
| 20 | 21 |
| 21 // TODO(jmesserly): import from its own package | 22 // TODO(jmesserly): import from its own package |
| 22 import 'package:dev_compiler/src/js/js_ast.dart' as JS; | 23 import 'package:dev_compiler/src/js/js_ast.dart' as JS; |
| 23 import 'package:dev_compiler/src/js/js_ast.dart' show js; | 24 import 'package:dev_compiler/src/js/js_ast.dart' show js; |
| 24 | 25 |
| 25 import 'package:dev_compiler/src/compiler.dart' show AbstractCompiler; | 26 import 'package:dev_compiler/src/compiler.dart' show AbstractCompiler; |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 88 final _runtimeLibVar = new JS.Identifier('dart'); | 89 final _runtimeLibVar = new JS.Identifier('dart'); |
| 89 final _namedArgTemp = new JS.TemporaryId('opts'); | 90 final _namedArgTemp = new JS.TemporaryId('opts'); |
| 90 | 91 |
| 91 ConstFieldVisitor _constField; | 92 ConstFieldVisitor _constField; |
| 92 | 93 |
| 93 ModuleItemLoadOrder _loader; | 94 ModuleItemLoadOrder _loader; |
| 94 | 95 |
| 95 /// _interceptors.JSArray<E>, used for List literals. | 96 /// _interceptors.JSArray<E>, used for List literals. |
| 96 ClassElement _jsArray; | 97 ClassElement _jsArray; |
| 97 | 98 |
| 99 /// The default value of the module object. See [visitLibraryDirective]. | |
| 100 String _jsModuleValue; | |
| 101 | |
| 98 Map<String, DartType> _objectMembers; | 102 Map<String, DartType> _objectMembers; |
| 99 | 103 |
| 100 JSCodegenVisitor(AbstractCompiler compiler, this.currentLibrary, | 104 JSCodegenVisitor(AbstractCompiler compiler, this.currentLibrary, |
| 101 this._extensionTypes, this._fieldsNeedingStorage) | 105 this._extensionTypes, this._fieldsNeedingStorage) |
| 102 : compiler = compiler, | 106 : compiler = compiler, |
| 103 options = compiler.options.codegenOptions, | 107 options = compiler.options.codegenOptions, |
| 104 rules = compiler.rules { | 108 rules = compiler.rules { |
| 105 _loader = new ModuleItemLoadOrder(_emitModuleItem); | 109 _loader = new ModuleItemLoadOrder(_emitModuleItem); |
| 106 | 110 |
| 107 var context = compiler.context; | 111 var context = compiler.context; |
| 108 var src = context.sourceFactory.forUri('dart:_interceptors'); | 112 var src = context.sourceFactory.forUri('dart:_interceptors'); |
| 109 var interceptors = context.computeLibraryElement(src); | 113 var interceptors = context.computeLibraryElement(src); |
| 110 _jsArray = interceptors.getType('JSArray'); | 114 _jsArray = interceptors.getType('JSArray'); |
| 111 | 115 |
| 112 _objectMembers = getObjectMemberMap(types); | 116 _objectMembers = getObjectMemberMap(types); |
| 113 } | 117 } |
| 114 | 118 |
| 115 TypeProvider get types => rules.provider; | 119 TypeProvider get types => rules.provider; |
| 116 | 120 |
| 117 JS.Program emitLibrary(LibraryUnit library) { | 121 JS.Program emitLibrary(LibraryUnit library) { |
| 118 String jsDefaultValue = null; | |
| 119 | |
| 120 // Modify the AST to make coercions explicit. | 122 // Modify the AST to make coercions explicit. |
| 121 new CoercionReifier(library, compiler).reify(); | 123 new CoercionReifier(library, compiler).reify(); |
| 122 | 124 |
| 123 var unit = library.library; | 125 // Build the public namespace for this library. This allows us to do |
| 124 if (unit.directives.isNotEmpty) { | 126 // constant time lookups (contrast with `Element.getChild(name)`). |
| 125 var libraryDir = unit.directives.first; | 127 if (currentLibrary.publicNamespace == null) { |
| 126 if (libraryDir is LibraryDirective) { | 128 (currentLibrary as LibraryElementImpl).publicNamespace = |
| 127 var jsName = findAnnotation(libraryDir.element, _isJsNameAnnotation); | 129 new PublicNamespaceBuilder().build(currentLibrary); |
| 128 jsDefaultValue = | |
| 129 getConstantField(jsName, 'name', types.stringType) as String; | |
| 130 } | |
| 131 } | 130 } |
| 132 | 131 |
| 133 // TODO(jmesserly): visit scriptTag, directives? | 132 library.library.directives.forEach(_visit); |
|
Jennifer Messerly
2015/08/03 21:11:50
went ahead and fixed scriptTag too
| |
| 134 | 133 |
| 134 // Rather than directly visit declarations, we instead use [_loader] to | |
| 135 // visit them. It has the ability to sort elements on demand, so | |
| 136 // dependencies between top level items are handled with a minimal | |
| 137 // reordering of the user's input code. The loader will call back into | |
| 138 // this visitor via [_emitModuleItem] when it's ready to visit the item | |
| 139 // for real. | |
| 135 _loader.collectElements(currentLibrary, library.partsThenLibrary); | 140 _loader.collectElements(currentLibrary, library.partsThenLibrary); |
| 136 | 141 |
| 137 for (var unit in library.partsThenLibrary) { | 142 for (var unit in library.partsThenLibrary) { |
| 138 _constField = new ConstFieldVisitor(types, unit); | 143 _constField = new ConstFieldVisitor(types, unit); |
| 139 | 144 |
| 140 for (var decl in unit.declarations) { | 145 for (var decl in unit.declarations) { |
| 141 if (decl is TopLevelVariableDeclaration) { | 146 if (decl is TopLevelVariableDeclaration) { |
| 142 _visit(decl); | 147 visitTopLevelVariableDeclaration(decl); |
| 143 } else { | 148 } else { |
| 144 _loader.loadDeclaration(decl, decl.element); | 149 _loader.loadDeclaration(decl, decl.element); |
| 145 } | 150 } |
| 146 if (decl is ClassDeclaration) { | 151 if (decl is ClassDeclaration) { |
| 147 // Static fields can be emitted into the top-level code, so they need | 152 // Static fields can be emitted into the top-level code, so they need |
| 148 // to potentially be ordered independently of the class. | 153 // to potentially be ordered independently of the class. |
| 149 for (var member in decl.members) { | 154 for (var member in decl.members) { |
| 150 if (member is FieldDeclaration && member.isStatic) { | 155 if (member is FieldDeclaration) { |
| 151 for (var f in member.fields.variables) { | 156 visitFieldDeclaration(member); |
| 152 _loader.loadDeclaration(f, f.element); | |
| 153 } | |
| 154 } | 157 } |
| 155 } | 158 } |
| 156 } | 159 } |
| 157 } | 160 } |
| 158 } | 161 } |
| 159 | 162 |
| 160 // Flush any unwritten fields/properties. | 163 // Flush any unwritten fields/properties. |
| 161 _flushLazyFields(_moduleItems); | 164 _flushLazyFields(_moduleItems); |
| 162 _flushLibraryProperties(_moduleItems); | 165 _flushLibraryProperties(_moduleItems); |
| 163 | 166 |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 201 processImport(library, temp, lazyImports); | 204 processImport(library, temp, lazyImports); |
| 202 } | 205 } |
| 203 }); | 206 }); |
| 204 | 207 |
| 205 var dartxImport = | 208 var dartxImport = |
| 206 js.statement("let # = #.dartx;", [_dartxVar, _runtimeLibVar]); | 209 js.statement("let # = #.dartx;", [_dartxVar, _runtimeLibVar]); |
| 207 | 210 |
| 208 var module = js.call("function(#) { 'use strict'; #; #; }", | 211 var module = js.call("function(#) { 'use strict'; #; #; }", |
| 209 [params, dartxImport, _moduleItems]); | 212 [params, dartxImport, _moduleItems]); |
| 210 | 213 |
| 211 var program = <JS.Statement>[ | 214 var moduleDef = js.statement("dart_library.library(#, #, #, #, #)", [ |
| 212 js.statement("dart_library.library(#, #, #, #, #)", [ | 215 js.string(jsPath, "'"), |
| 213 js.string(jsPath, "'"), | 216 _jsModuleValue ?? new JS.LiteralNull(), |
| 214 jsDefaultValue ?? new JS.LiteralNull(), | 217 js.commentExpression( |
| 215 js.commentExpression( | 218 "Imports", new JS.ArrayInitializer(imports, multiline: true)), |
| 216 "Imports", new JS.ArrayInitializer(imports, multiline: true)), | 219 js.commentExpression("Lazy imports", |
| 217 js.commentExpression("Lazy imports", | 220 new JS.ArrayInitializer(lazyImports, multiline: true)), |
| 218 new JS.ArrayInitializer(lazyImports, multiline: true)), | 221 module |
| 219 module | 222 ]); |
| 220 ]) | |
| 221 ]; | |
| 222 | 223 |
| 223 return new JS.Program(program); | 224 var jsBin = compiler.options.runnerOptions.v8Binary; |
| 225 | |
| 226 String scriptTag = null; | |
| 227 if (library.library.scriptTag != null) scriptTag = '/usr/bin/env $jsBin'; | |
| 228 return new JS.Program(<JS.Statement>[moduleDef], scriptTag: scriptTag); | |
| 224 } | 229 } |
| 225 | 230 |
| 226 void _emitModuleItem(AstNode node) { | 231 void _emitModuleItem(AstNode node) { |
| 227 // Attempt to group adjacent fields/properties. | 232 // Attempt to group adjacent fields/properties. |
| 228 if (node is! VariableDeclaration) _flushLazyFields(_moduleItems); | 233 if (node is! VariableDeclaration) _flushLazyFields(_moduleItems); |
| 229 if (node is! FunctionDeclaration) _flushLibraryProperties(_moduleItems); | 234 if (node is! FunctionDeclaration) _flushLibraryProperties(_moduleItems); |
| 230 | 235 |
| 231 var code = _visit(node); | 236 var code = _visit(node); |
| 232 if (code != null) _moduleItems.add(code); | 237 if (code != null) _moduleItems.add(code); |
| 233 } | 238 } |
| 234 | 239 |
| 240 @override | |
| 241 void visitLibraryDirective(LibraryDirective node) { | |
| 242 assert(_jsModuleValue == null); | |
| 243 | |
| 244 var jsName = findAnnotation(node.element, _isJsNameAnnotation); | |
| 245 _jsModuleValue = | |
| 246 getConstantField(jsName, 'name', types.stringType) as String; | |
| 247 } | |
| 248 | |
| 249 @override | |
| 250 void visitImportDirective(ImportDirective node) { | |
| 251 // Nothing to do yet, but we'll want to convert this to an ES6 import once | |
| 252 // we have support for modules. | |
| 253 } | |
| 254 | |
| 255 @override void visitPartDirective(PartDirective node) {} | |
| 256 @override void visitPartOfDirective(PartOfDirective node) {} | |
| 257 | |
| 258 @override | |
| 259 void visitExportDirective(ExportDirective node) { | |
| 260 var exportName = _libraryName(node.uriElement); | |
| 261 | |
| 262 var currentLibNames = currentLibrary.publicNamespace.definedNames; | |
| 263 | |
| 264 var args = [_exportsVar, exportName]; | |
| 265 if (node.combinators.isNotEmpty) { | |
| 266 var shownNames = <JS.Expression>[]; | |
| 267 var hiddenNames = <JS.Expression>[]; | |
| 268 | |
| 269 var show = node.combinators.firstWhere((c) => c is ShowCombinator, | |
| 270 orElse: () => null) as ShowCombinator; | |
| 271 var hide = node.combinators.firstWhere((c) => c is HideCombinator, | |
| 272 orElse: () => null) as HideCombinator; | |
| 273 if (show != null) { | |
| 274 shownNames.addAll(show.shownNames | |
| 275 .map((i) => i.name) | |
| 276 .where((s) => !currentLibNames.containsKey(s)) | |
| 277 .map((s) => js.string(s, "'"))); | |
| 278 } | |
| 279 if (hide != null) { | |
| 280 hiddenNames.addAll(hide.hiddenNames.map((i) => js.string(i.name, "'"))); | |
| 281 } | |
| 282 args.add(new JS.ArrayInitializer(shownNames)); | |
| 283 args.add(new JS.ArrayInitializer(hiddenNames)); | |
| 284 } | |
| 285 _moduleItems.add(js.statement('dart.export(#);', [args])); | |
| 286 } | |
| 287 | |
| 235 JS.Identifier _initSymbol(JS.Identifier id) { | 288 JS.Identifier _initSymbol(JS.Identifier id) { |
| 236 var s = js.statement('let # = $_SYMBOL(#);', [id, js.string(id.name, "'")]); | 289 var s = js.statement('let # = $_SYMBOL(#);', [id, js.string(id.name, "'")]); |
| 237 _moduleItems.add(s); | 290 _moduleItems.add(s); |
| 238 return id; | 291 return id; |
| 239 } | 292 } |
| 240 | 293 |
| 241 // TODO(jmesserly): this is a temporary workaround for `Symbol` in core, | 294 // TODO(jmesserly): this is a temporary workaround for `Symbol` in core, |
| 242 // until we have better name tracking. | 295 // until we have better name tracking. |
| 243 String get _SYMBOL { | 296 String get _SYMBOL { |
| 244 var name = currentLibrary.name; | 297 var name = currentLibrary.name; |
| (...skipping 1524 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1769 return new JS.Yield(_visit(node.expression)); | 1822 return new JS.Yield(_visit(node.expression)); |
| 1770 } | 1823 } |
| 1771 | 1824 |
| 1772 @override | 1825 @override |
| 1773 visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { | 1826 visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { |
| 1774 for (var v in node.variables.variables) { | 1827 for (var v in node.variables.variables) { |
| 1775 _loader.loadDeclaration(v, v.element); | 1828 _loader.loadDeclaration(v, v.element); |
| 1776 } | 1829 } |
| 1777 } | 1830 } |
| 1778 | 1831 |
| 1832 /// Emits static fields. | |
| 1833 /// | |
| 1834 /// Instance fields are emitted in [_initializeFields]. | |
| 1835 /// | |
| 1836 /// These are generally treated the same as top-level fields, see | |
| 1837 /// [visitTopLevelVariableDeclaration]. | |
| 1838 @override | |
| 1839 visitFieldDeclaration(FieldDeclaration node) { | |
| 1840 if (!node.isStatic) return; | |
| 1841 | |
| 1842 for (var f in node.fields.variables) { | |
| 1843 _loader.loadDeclaration(f, f.element); | |
| 1844 } | |
| 1845 } | |
| 1846 | |
| 1779 _addExport(String name) { | 1847 _addExport(String name) { |
| 1780 if (!_exports.add(name)) throw 'Duplicate top level name found: $name'; | 1848 if (!_exports.add(name)) throw 'Duplicate top level name found: $name'; |
| 1781 } | 1849 } |
| 1782 | 1850 |
| 1783 @override | 1851 @override |
| 1784 JS.Statement visitVariableDeclarationStatement( | 1852 JS.Statement visitVariableDeclarationStatement( |
| 1785 VariableDeclarationStatement node) { | 1853 VariableDeclarationStatement node) { |
| 1786 // Special case a single variable with an initializer. | 1854 // Special case a single variable with an initializer. |
| 1787 // This helps emit cleaner code for things like: | 1855 // This helps emit cleaner code for things like: |
| 1788 // var result = []..add(1)..add(2); | 1856 // var result = []..add(1)..add(2); |
| (...skipping 1190 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2979 | 3047 |
| 2980 /// A special kind of element created by the compiler, signifying a temporary | 3048 /// A special kind of element created by the compiler, signifying a temporary |
| 2981 /// variable. These objects use instance equality, and should be shared | 3049 /// variable. These objects use instance equality, and should be shared |
| 2982 /// everywhere in the tree where they are treated as the same variable. | 3050 /// everywhere in the tree where they are treated as the same variable. |
| 2983 class TemporaryVariableElement extends LocalVariableElementImpl { | 3051 class TemporaryVariableElement extends LocalVariableElementImpl { |
| 2984 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); | 3052 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); |
| 2985 | 3053 |
| 2986 int get hashCode => identityHashCode(this); | 3054 int get hashCode => identityHashCode(this); |
| 2987 bool operator ==(Object other) => identical(this, other); | 3055 bool operator ==(Object other) => identical(this, other); |
| 2988 } | 3056 } |
| OLD | NEW |