OLD | NEW |
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 library dev_compiler.src.codegen.js_codegen; | 5 library dev_compiler.src.codegen.js_codegen; |
6 | 6 |
7 import 'dart:collection' show HashSet, HashMap, SplayTreeSet; | 7 import 'dart:collection' show HashSet, HashMap, SplayTreeSet; |
8 | 8 |
9 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; | 9 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; |
10 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator; | 10 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator; |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
48 const DSETINDEX = 'dsetindex'; | 48 const DSETINDEX = 'dsetindex'; |
49 const DCALL = 'dcall'; | 49 const DCALL = 'dcall'; |
50 const DSEND = 'dsend'; | 50 const DSEND = 'dsend'; |
51 | 51 |
52 class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { | 52 class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor { |
53 final AbstractCompiler compiler; | 53 final AbstractCompiler compiler; |
54 final CompilerOptions options; | 54 final CompilerOptions options; |
55 final TypeRules rules; | 55 final TypeRules rules; |
56 final LibraryInfo libraryInfo; | 56 final LibraryInfo libraryInfo; |
57 | 57 |
| 58 /// Entry point uri |
| 59 final Uri root; |
| 60 |
58 /// The global extension type table. | 61 /// The global extension type table. |
59 final HashSet<ClassElement> _extensionTypes; | 62 final HashSet<ClassElement> _extensionTypes; |
60 | 63 |
61 /// Information that is precomputed for this library, indicates which fields | 64 /// Information that is precomputed for this library, indicates which fields |
62 /// need storage slots. | 65 /// need storage slots. |
63 final HashSet<FieldElement> _fieldsNeedingStorage; | 66 final HashSet<FieldElement> _fieldsNeedingStorage; |
64 | 67 |
65 /// The variable for the target of the current `..` cascade expression. | 68 /// The variable for the target of the current `..` cascade expression. |
66 SimpleIdentifier _cascadeTarget; | 69 SimpleIdentifier _cascadeTarget; |
67 | 70 |
(...skipping 17 matching lines...) Expand all Loading... |
85 final _namedArgTemp = new JS.TemporaryId('opts'); | 88 final _namedArgTemp = new JS.TemporaryId('opts'); |
86 | 89 |
87 ConstFieldVisitor _constField; | 90 ConstFieldVisitor _constField; |
88 | 91 |
89 ModuleItemLoadOrder _loader; | 92 ModuleItemLoadOrder _loader; |
90 | 93 |
91 JSCodegenVisitor(AbstractCompiler compiler, this.libraryInfo, | 94 JSCodegenVisitor(AbstractCompiler compiler, this.libraryInfo, |
92 this._extensionTypes, this._fieldsNeedingStorage) | 95 this._extensionTypes, this._fieldsNeedingStorage) |
93 : compiler = compiler, | 96 : compiler = compiler, |
94 options = compiler.options, | 97 options = compiler.options, |
95 rules = compiler.rules { | 98 rules = compiler.rules, |
| 99 root = compiler.entryPointUri { |
96 _loader = new ModuleItemLoadOrder(_emitModuleItem); | 100 _loader = new ModuleItemLoadOrder(_emitModuleItem); |
97 } | 101 } |
98 | 102 |
99 LibraryElement get currentLibrary => libraryInfo.library; | 103 LibraryElement get currentLibrary => libraryInfo.library; |
100 TypeProvider get types => rules.provider; | 104 TypeProvider get types => rules.provider; |
101 | 105 |
102 JS.Program emitLibrary(LibraryUnit library) { | 106 JS.Program emitLibrary(LibraryUnit library) { |
103 String jsDefaultValue = null; | 107 String jsDefaultValue = null; |
104 | 108 |
105 // Modify the AST to make coercions explicit. | 109 // Modify the AST to make coercions explicit. |
106 new CoercionReifier(library, compiler).reify(); | 110 new CoercionReifier(library, compiler).reify(); |
107 | 111 |
108 var unit = library.library; | 112 var unit = library.library; |
109 if (unit.directives.isNotEmpty) { | 113 if (unit.directives.isNotEmpty) { |
110 var libraryDir = unit.directives.first; | 114 var libraryDir = unit.directives.first; |
111 if (libraryDir is LibraryDirective) { | 115 if (libraryDir is LibraryDirective) { |
112 var jsName = findAnnotation(libraryDir.element, _isJsNameAnnotation); | 116 var jsName = findAnnotation(libraryDir.element, _isJsNameAnnotation); |
113 jsDefaultValue = getConstantField(jsName, 'name', types.stringType); | 117 jsDefaultValue = getConstantField(jsName, 'name', types.stringType); |
114 } | 118 } |
115 } | 119 } |
116 if (jsDefaultValue == null) jsDefaultValue = '{}'; | |
117 | 120 |
118 // TODO(jmesserly): visit scriptTag, directives? | 121 // TODO(jmesserly): visit scriptTag, directives? |
119 | 122 |
120 _loader.collectElements(currentLibrary, library.partsThenLibrary); | 123 _loader.collectElements(currentLibrary, library.partsThenLibrary); |
121 | 124 |
122 for (var unit in library.partsThenLibrary) { | 125 for (var unit in library.partsThenLibrary) { |
123 _constField = new ConstFieldVisitor(types, unit); | 126 _constField = new ConstFieldVisitor(types, unit); |
124 | 127 |
125 for (var decl in unit.declarations) { | 128 for (var decl in unit.declarations) { |
126 if (decl is TopLevelVariableDeclaration) { | 129 if (decl is TopLevelVariableDeclaration) { |
(...skipping 27 matching lines...) Expand all Loading... |
154 _qualifiedIds.forEach(unqualifyIfNeeded); | 157 _qualifiedIds.forEach(unqualifyIfNeeded); |
155 _qualifiedGenericIds.forEach(unqualifyIfNeeded); | 158 _qualifiedGenericIds.forEach(unqualifyIfNeeded); |
156 | 159 |
157 if (_exports.isNotEmpty) _moduleItems.add(js.comment('Exports:')); | 160 if (_exports.isNotEmpty) _moduleItems.add(js.comment('Exports:')); |
158 | 161 |
159 // TODO(jmesserly): make these immutable in JS? | 162 // TODO(jmesserly): make these immutable in JS? |
160 for (var name in _exports) { | 163 for (var name in _exports) { |
161 _moduleItems.add(js.statement('#.# = #;', [_exportsVar, name, name])); | 164 _moduleItems.add(js.statement('#.# = #;', [_exportsVar, name, name])); |
162 } | 165 } |
163 | 166 |
164 var name = new JS.Identifier(jsLibraryName(currentLibrary)); | 167 var jsPath = js.string(jsOutputBase(currentLibrary, root), "'"); |
165 | 168 |
166 // TODO(jmesserly): it would be great to run the renamer on the body, | 169 // TODO(jmesserly): it would be great to run the renamer on the body, |
167 // then figure out if we really need each of these parameters. | 170 // then figure out if we really need each of these parameters. |
168 // See ES6 modules: https://github.com/dart-lang/dev_compiler/issues/34 | 171 // See ES6 modules: https://github.com/dart-lang/dev_compiler/issues/34 |
| 172 var params = [_exportsVar]; |
| 173 var processImport = (LibraryElement library, JS.TemporaryId temp, |
| 174 List list) { |
| 175 params.add(temp); |
| 176 list.add(js.string(jsOutputBase(library, root), "'")); |
| 177 }; |
| 178 |
| 179 var imports = []; |
| 180 _imports.forEach((library, temp) { |
| 181 if (_loader.libraryIsLoaded(library)) { |
| 182 processImport(library, temp, imports); |
| 183 } |
| 184 }); |
| 185 |
| 186 var lazyImports = []; |
| 187 _imports.forEach((library, temp) { |
| 188 if (!_loader.libraryIsLoaded(library)) { |
| 189 processImport(library, temp, lazyImports); |
| 190 } |
| 191 }); |
| 192 |
| 193 var module = |
| 194 js.call("function(#) { 'use strict'; #; }", [params, _moduleItems]); |
| 195 |
169 var program = [ | 196 var program = [ |
170 js.statement('var # = dart.defineLibrary(#, #);', [ | 197 js.statement("dart.library(#, #, #, #, #)", [ |
171 name, | 198 jsPath, |
172 name, | 199 jsDefaultValue != null ? jsDefaultValue : new JS.LiteralNull(), |
173 js.call(jsDefaultValue) | 200 js.commentExpression( |
| 201 "Imports", new JS.ArrayInitializer(imports, multiline: true)), |
| 202 js.commentExpression("Lazy imports", |
| 203 new JS.ArrayInitializer(lazyImports, multiline: true)), |
| 204 module |
174 ]) | 205 ]) |
175 ]; | 206 ]; |
176 | 207 |
177 var params = [_exportsVar]; | |
178 var args = [name]; | |
179 _imports.forEach((library, temp) { | |
180 var name = new JS.Identifier(temp.name); | |
181 params.add(temp); | |
182 args.add(name); | |
183 var helper = _loader.libraryIsLoaded(library) ? 'import' : 'lazyImport'; | |
184 program.add(js.statement('var # = dart.#(#);', [name, helper, name])); | |
185 }); | |
186 | |
187 program.add(js.statement("(function(#) { 'use strict'; #; })(#);", [ | |
188 params, | |
189 _moduleItems, | |
190 args | |
191 ])); | |
192 | |
193 return new JS.Program(program); | 208 return new JS.Program(program); |
194 } | 209 } |
195 | 210 |
196 void _emitModuleItem(AstNode node) { | 211 void _emitModuleItem(AstNode node) { |
197 // Attempt to group adjacent fields/properties. | 212 // Attempt to group adjacent fields/properties. |
198 if (node is! VariableDeclaration) _flushLazyFields(_moduleItems); | 213 if (node is! VariableDeclaration) _flushLazyFields(_moduleItems); |
199 if (node is! FunctionDeclaration) _flushLibraryProperties(_moduleItems); | 214 if (node is! FunctionDeclaration) _flushLibraryProperties(_moduleItems); |
200 | 215 |
201 var code = _visit(node); | 216 var code = _visit(node); |
202 if (code != null) _moduleItems.add(code); | 217 if (code != null) _moduleItems.add(code); |
(...skipping 2463 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2666 t = fillDynamicTypeArgs(t, rules.provider); | 2681 t = fillDynamicTypeArgs(t, rules.provider); |
2667 t.interfaces.forEach(_addExtensionType); | 2682 t.interfaces.forEach(_addExtensionType); |
2668 t.mixins.forEach(_addExtensionType); | 2683 t.mixins.forEach(_addExtensionType); |
2669 _addExtensionType(t.superclass); | 2684 _addExtensionType(t.superclass); |
2670 } | 2685 } |
2671 | 2686 |
2672 String generateLibrary(LibraryUnit unit, LibraryInfo info) { | 2687 String generateLibrary(LibraryUnit unit, LibraryInfo info) { |
2673 var fields = findFieldsNeedingStorage(unit); | 2688 var fields = findFieldsNeedingStorage(unit); |
2674 var codegen = new JSCodegenVisitor(compiler, info, _extensionTypes, fields); | 2689 var codegen = new JSCodegenVisitor(compiler, info, _extensionTypes, fields); |
2675 var module = codegen.emitLibrary(unit); | 2690 var module = codegen.emitLibrary(unit); |
2676 var dir = path.join(outDir, jsOutputPath(info, root)); | 2691 var dir = path.join(outDir, jsOutputPath(info.library, root)); |
2677 return writeJsLibrary(module, dir, emitSourceMaps: options.emitSourceMaps); | 2692 return writeJsLibrary(module, dir, emitSourceMaps: options.emitSourceMaps); |
2678 } | 2693 } |
2679 } | 2694 } |
2680 | 2695 |
2681 /// Choose a canonical name from the library element. | 2696 /// Choose a canonical name from the library element. |
2682 /// This never uses the library's name (the identifier in the `library` | 2697 /// This never uses the library's name (the identifier in the `library` |
2683 /// declaration) as it doesn't have any meaningful rules enforced. | 2698 /// declaration) as it doesn't have any meaningful rules enforced. |
2684 String jsLibraryName(LibraryElement library) => canonicalLibraryName(library); | 2699 String jsLibraryName(LibraryElement library) => canonicalLibraryName(library); |
2685 | 2700 |
2686 /// Shorthand for identifier-like property names. | 2701 /// Shorthand for identifier-like property names. |
2687 /// For now, we emit them as strings and the printer restores them to | 2702 /// For now, we emit them as strings and the printer restores them to |
2688 /// identifiers if it can. | 2703 /// identifiers if it can. |
2689 // TODO(jmesserly): avoid the round tripping through quoted form. | 2704 // TODO(jmesserly): avoid the round tripping through quoted form. |
2690 JS.LiteralString _propertyName(String name) => js.string(name, "'"); | 2705 JS.LiteralString _propertyName(String name) => js.string(name, "'"); |
2691 | 2706 |
2692 /// Path to file that will be generated for [info]. In case it's url is a | 2707 /// Path to file that will be generated for [info]. In case it's url is a |
2693 /// `file:` url, we use [root] to determine the relative path from the entry | 2708 /// `file:` url, we use [root] to determine the relative path from the entry |
2694 /// point file. | 2709 /// point file. |
2695 String jsOutputPath(LibraryInfo info, Uri root) { | 2710 String jsOutputPath(LibraryElement library, Uri root) { |
2696 var uri = info.library.source.uri; | 2711 return '${jsOutputBase(library, root)}.js'; |
2697 var filepath = '${path.withoutExtension(uri.path)}.js'; | 2712 } |
| 2713 |
| 2714 String jsOutputBase(LibraryElement library, Uri root) { |
| 2715 var uri = library.source.uri; |
| 2716 var filepath = path.withoutExtension(uri.path); |
2698 if (uri.scheme == 'dart') { | 2717 if (uri.scheme == 'dart') { |
2699 filepath = 'dart/$filepath'; | 2718 filepath = 'dart/$filepath'; |
2700 } else if (uri.scheme == 'file') { | 2719 } else if (uri.scheme == 'file') { |
2701 filepath = path.relative(filepath, from: path.dirname(root.path)); | 2720 filepath = path.relative(filepath, from: path.dirname(root.path)); |
2702 } else { | 2721 } else { |
2703 assert(uri.scheme == 'package'); | 2722 assert(uri.scheme == 'package'); |
2704 // filepath is good here, we want the output to start with a directory | 2723 // filepath is good here, we want the output to start with a directory |
2705 // matching the package name. | 2724 // matching the package name. |
2706 } | 2725 } |
2707 return filepath; | 2726 return filepath; |
(...skipping 12 matching lines...) Expand all Loading... |
2720 | 2739 |
2721 /// A special kind of element created by the compiler, signifying a temporary | 2740 /// A special kind of element created by the compiler, signifying a temporary |
2722 /// variable. These objects use instance equality, and should be shared | 2741 /// variable. These objects use instance equality, and should be shared |
2723 /// everywhere in the tree where they are treated as the same variable. | 2742 /// everywhere in the tree where they are treated as the same variable. |
2724 class TemporaryVariableElement extends LocalVariableElementImpl { | 2743 class TemporaryVariableElement extends LocalVariableElementImpl { |
2725 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); | 2744 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); |
2726 | 2745 |
2727 int get hashCode => identityHashCode(this); | 2746 int get hashCode => identityHashCode(this); |
2728 bool operator ==(Object other) => identical(this, other); | 2747 bool operator ==(Object other) => identical(this, other); |
2729 } | 2748 } |
OLD | NEW |