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 |