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:path/path.dart' as path; | |
17 | 16 |
18 import 'package:dev_compiler/src/codegen/ast_builder.dart' show AstBuilder; | 17 import 'package:dev_compiler/src/codegen/ast_builder.dart' show AstBuilder; |
19 import 'package:dev_compiler/src/codegen/reify_coercions.dart' | 18 import 'package:dev_compiler/src/codegen/reify_coercions.dart' |
20 show CoercionReifier; | 19 show CoercionReifier; |
21 | 20 |
22 // TODO(jmesserly): import from its own package | 21 // TODO(jmesserly): import from its own package |
23 import 'package:dev_compiler/src/js/js_ast.dart' as JS; | 22 import 'package:dev_compiler/src/js/js_ast.dart' as JS; |
24 import 'package:dev_compiler/src/js/js_ast.dart' show js; | 23 import 'package:dev_compiler/src/js/js_ast.dart' show js; |
25 | 24 |
26 import 'package:dev_compiler/devc.dart' show AbstractCompiler; | 25 import 'package:dev_compiler/src/compiler.dart' show AbstractCompiler; |
27 import 'package:dev_compiler/src/checker/rules.dart'; | 26 import 'package:dev_compiler/src/checker/rules.dart'; |
28 import 'package:dev_compiler/src/info.dart'; | 27 import 'package:dev_compiler/src/info.dart'; |
29 import 'package:dev_compiler/src/options.dart' show CodegenOptions; | 28 import 'package:dev_compiler/src/options.dart' show CodegenOptions; |
30 import 'package:dev_compiler/src/utils.dart'; | 29 import 'package:dev_compiler/src/utils.dart'; |
31 | 30 |
32 import 'code_generator.dart'; | 31 import 'code_generator.dart'; |
33 import 'js_field_storage.dart'; | 32 import 'js_field_storage.dart'; |
34 import 'js_names.dart' as JS; | 33 import 'js_names.dart' as JS; |
35 import 'js_metalet.dart' as JS; | 34 import 'js_metalet.dart' as JS; |
36 import 'js_module_item_order.dart'; | 35 import 'js_module_item_order.dart'; |
37 import 'js_printer.dart' show writeJsLibrary; | 36 import 'js_printer.dart' show writeJsLibrary; |
38 import 'side_effect_analysis.dart'; | 37 import 'side_effect_analysis.dart'; |
39 | 38 |
40 // Various dynamic helpers we call. | 39 // Various dynamic helpers we call. |
41 // If renaming these, make sure to check other places like the | 40 // If renaming these, make sure to check other places like the |
42 // dart_runtime.js file and comments. | 41 // dart_runtime.js file and comments. |
43 // TODO(jmesserly): ideally we'd have a "dynamic call" dart library we can | 42 // TODO(jmesserly): ideally we'd have a "dynamic call" dart library we can |
44 // import and generate calls to, rather than dart_runtime.js | 43 // import and generate calls to, rather than dart_runtime.js |
45 const DPUT = 'dput'; | 44 const DPUT = 'dput'; |
46 const DLOAD = 'dload'; | 45 const DLOAD = 'dload'; |
47 const DINDEX = 'dindex'; | 46 const DINDEX = 'dindex'; |
48 const DSETINDEX = 'dsetindex'; | 47 const DSETINDEX = 'dsetindex'; |
49 const DCALL = 'dcall'; | 48 const DCALL = 'dcall'; |
50 const DSEND = 'dsend'; | 49 const DSEND = 'dsend'; |
51 | 50 |
52 class JSCodegenVisitor extends GeneralizingAstVisitor { | 51 class JSCodegenVisitor extends GeneralizingAstVisitor { |
53 final AbstractCompiler compiler; | 52 final AbstractCompiler compiler; |
54 final CodegenOptions options; | 53 final CodegenOptions options; |
55 final TypeRules rules; | 54 final TypeRules rules; |
56 final LibraryInfo libraryInfo; | 55 final LibraryElement currentLibrary; |
57 | |
58 /// Entry point uri | |
59 final Uri root; | |
60 | 56 |
61 /// The global extension type table. | 57 /// The global extension type table. |
62 final HashSet<ClassElement> _extensionTypes; | 58 final HashSet<ClassElement> _extensionTypes; |
63 | 59 |
64 /// Information that is precomputed for this library, indicates which fields | 60 /// Information that is precomputed for this library, indicates which fields |
65 /// need storage slots. | 61 /// need storage slots. |
66 final HashSet<FieldElement> _fieldsNeedingStorage; | 62 final HashSet<FieldElement> _fieldsNeedingStorage; |
67 | 63 |
68 /// The variable for the target of the current `..` cascade expression. | 64 /// The variable for the target of the current `..` cascade expression. |
69 SimpleIdentifier _cascadeTarget; | 65 SimpleIdentifier _cascadeTarget; |
(...skipping 21 matching lines...) Expand all Loading... |
91 | 87 |
92 ConstFieldVisitor _constField; | 88 ConstFieldVisitor _constField; |
93 | 89 |
94 ModuleItemLoadOrder _loader; | 90 ModuleItemLoadOrder _loader; |
95 | 91 |
96 /// _interceptors.JSArray<E>, used for List literals. | 92 /// _interceptors.JSArray<E>, used for List literals. |
97 ClassElement _jsArray; | 93 ClassElement _jsArray; |
98 | 94 |
99 Map<String, DartType> _objectMembers; | 95 Map<String, DartType> _objectMembers; |
100 | 96 |
101 JSCodegenVisitor(AbstractCompiler compiler, this.libraryInfo, | 97 JSCodegenVisitor(AbstractCompiler compiler, this.currentLibrary, |
102 this._extensionTypes, this._fieldsNeedingStorage) | 98 this._extensionTypes, this._fieldsNeedingStorage) |
103 : compiler = compiler, | 99 : compiler = compiler, |
104 options = compiler.options.codegenOptions, | 100 options = compiler.options.codegenOptions, |
105 rules = compiler.rules, | 101 rules = compiler.rules { |
106 root = compiler.entryPointUri { | |
107 _loader = new ModuleItemLoadOrder(_emitModuleItem); | 102 _loader = new ModuleItemLoadOrder(_emitModuleItem); |
108 | 103 |
109 var context = compiler.context; | 104 var context = compiler.context; |
110 var src = context.sourceFactory.forUri('dart:_interceptors'); | 105 var src = context.sourceFactory.forUri('dart:_interceptors'); |
111 var interceptors = context.computeLibraryElement(src); | 106 var interceptors = context.computeLibraryElement(src); |
112 _jsArray = interceptors.getType('JSArray'); | 107 _jsArray = interceptors.getType('JSArray'); |
113 | 108 |
114 _objectMembers = getObjectMemberMap(types); | 109 _objectMembers = getObjectMemberMap(types); |
115 } | 110 } |
116 | 111 |
117 LibraryElement get currentLibrary => libraryInfo.library; | |
118 TypeProvider get types => rules.provider; | 112 TypeProvider get types => rules.provider; |
119 | 113 |
120 JS.Program emitLibrary(LibraryUnit library) { | 114 JS.Program emitLibrary(LibraryUnit library) { |
121 String jsDefaultValue = null; | 115 String jsDefaultValue = null; |
122 | 116 |
123 // Modify the AST to make coercions explicit. | 117 // Modify the AST to make coercions explicit. |
124 new CoercionReifier(library, compiler).reify(); | 118 new CoercionReifier(library, compiler).reify(); |
125 | 119 |
126 var unit = library.library; | 120 var unit = library.library; |
127 if (unit.directives.isNotEmpty) { | 121 if (unit.directives.isNotEmpty) { |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
171 _qualifiedIds.forEach(unqualifyIfNeeded); | 165 _qualifiedIds.forEach(unqualifyIfNeeded); |
172 _qualifiedGenericIds.forEach(unqualifyIfNeeded); | 166 _qualifiedGenericIds.forEach(unqualifyIfNeeded); |
173 | 167 |
174 if (_exports.isNotEmpty) _moduleItems.add(js.comment('Exports:')); | 168 if (_exports.isNotEmpty) _moduleItems.add(js.comment('Exports:')); |
175 | 169 |
176 // TODO(jmesserly): make these immutable in JS? | 170 // TODO(jmesserly): make these immutable in JS? |
177 for (var name in _exports) { | 171 for (var name in _exports) { |
178 _moduleItems.add(js.statement('#.# = #;', [_exportsVar, name, name])); | 172 _moduleItems.add(js.statement('#.# = #;', [_exportsVar, name, name])); |
179 } | 173 } |
180 | 174 |
181 var jsPath = js.string(jsOutputBase(currentLibrary, root), "'"); | 175 var jsPath = compiler.getModuleName(currentLibrary.source.uri); |
182 | 176 |
183 // TODO(jmesserly): it would be great to run the renamer on the body, | 177 // TODO(jmesserly): it would be great to run the renamer on the body, |
184 // then figure out if we really need each of these parameters. | 178 // then figure out if we really need each of these parameters. |
185 // See ES6 modules: https://github.com/dart-lang/dev_compiler/issues/34 | 179 // See ES6 modules: https://github.com/dart-lang/dev_compiler/issues/34 |
186 var params = [_exportsVar, _runtimeLibVar]; | 180 var params = [_exportsVar, _runtimeLibVar]; |
187 var processImport = (LibraryElement library, JS.TemporaryId temp, | 181 var processImport = (LibraryElement library, JS.TemporaryId temp, |
188 List list) { | 182 List list) { |
189 params.add(temp); | 183 params.add(temp); |
190 list.add(js.string(jsOutputBase(library, root), "'")); | 184 list.add(js.string(compiler.getModuleName(library.source.uri), "'")); |
191 }; | 185 }; |
192 | 186 |
193 var imports = [js.string('dart_runtime/dart')]; | 187 var imports = [js.string('dart_runtime/dart')]; |
194 _imports.forEach((library, temp) { | 188 _imports.forEach((library, temp) { |
195 if (_loader.libraryIsLoaded(library)) { | 189 if (_loader.libraryIsLoaded(library)) { |
196 processImport(library, temp, imports); | 190 processImport(library, temp, imports); |
197 } | 191 } |
198 }); | 192 }); |
199 | 193 |
200 var lazyImports = []; | 194 var lazyImports = []; |
201 _imports.forEach((library, temp) { | 195 _imports.forEach((library, temp) { |
202 if (!_loader.libraryIsLoaded(library)) { | 196 if (!_loader.libraryIsLoaded(library)) { |
203 processImport(library, temp, lazyImports); | 197 processImport(library, temp, lazyImports); |
204 } | 198 } |
205 }); | 199 }); |
206 | 200 |
207 var dartxImport = | 201 var dartxImport = |
208 js.statement("let # = #.dartx;", [_dartxVar, _runtimeLibVar]); | 202 js.statement("let # = #.dartx;", [_dartxVar, _runtimeLibVar]); |
209 | 203 |
210 var module = js.call("function(#) { 'use strict'; #; #; }", [ | 204 var module = js.call("function(#) { 'use strict'; #; #; }", [ |
211 params, | 205 params, |
212 dartxImport, | 206 dartxImport, |
213 _moduleItems | 207 _moduleItems |
214 ]); | 208 ]); |
215 | 209 |
216 var program = [ | 210 var program = [ |
217 js.statement("dart_library.library(#, #, #, #, #)", [ | 211 js.statement("dart_library.library(#, #, #, #, #)", [ |
218 jsPath, | 212 js.string(jsPath, "'"), |
219 jsDefaultValue != null ? jsDefaultValue : new JS.LiteralNull(), | 213 jsDefaultValue != null ? jsDefaultValue : new JS.LiteralNull(), |
220 js.commentExpression( | 214 js.commentExpression( |
221 "Imports", new JS.ArrayInitializer(imports, multiline: true)), | 215 "Imports", new JS.ArrayInitializer(imports, multiline: true)), |
222 js.commentExpression("Lazy imports", | 216 js.commentExpression("Lazy imports", |
223 new JS.ArrayInitializer(lazyImports, multiline: true)), | 217 new JS.ArrayInitializer(lazyImports, multiline: true)), |
224 module | 218 module |
225 ]) | 219 ]) |
226 ]; | 220 ]; |
227 | 221 |
228 return new JS.Program(program); | 222 return new JS.Program(program); |
(...skipping 2571 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2800 } | 2794 } |
2801 | 2795 |
2802 void _addExtensionType(InterfaceType t) { | 2796 void _addExtensionType(InterfaceType t) { |
2803 if (t.isObject || !_extensionTypes.add(t.element)) return; | 2797 if (t.isObject || !_extensionTypes.add(t.element)) return; |
2804 t = fillDynamicTypeArgs(t, rules.provider); | 2798 t = fillDynamicTypeArgs(t, rules.provider); |
2805 t.interfaces.forEach(_addExtensionType); | 2799 t.interfaces.forEach(_addExtensionType); |
2806 t.mixins.forEach(_addExtensionType); | 2800 t.mixins.forEach(_addExtensionType); |
2807 _addExtensionType(t.superclass); | 2801 _addExtensionType(t.superclass); |
2808 } | 2802 } |
2809 | 2803 |
2810 String generateLibrary(LibraryUnit unit, LibraryInfo info) { | 2804 String generateLibrary(LibraryUnit unit) { |
| 2805 var library = unit.library.element.library; |
2811 var fields = findFieldsNeedingStorage(unit); | 2806 var fields = findFieldsNeedingStorage(unit); |
2812 var codegen = new JSCodegenVisitor(compiler, info, _extensionTypes, fields); | 2807 var codegen = |
| 2808 new JSCodegenVisitor(compiler, library, _extensionTypes, fields); |
2813 var module = codegen.emitLibrary(unit); | 2809 var module = codegen.emitLibrary(unit); |
2814 var dir = path.join(outDir, jsOutputPath(info.library, root)); | 2810 var out = compiler.getOutputPath(library.source.uri); |
2815 return writeJsLibrary(module, dir, emitSourceMaps: options.emitSourceMaps); | 2811 return writeJsLibrary(module, out, emitSourceMaps: options.emitSourceMaps); |
2816 } | 2812 } |
2817 } | 2813 } |
2818 | 2814 |
2819 /// Choose a canonical name from the library element. | 2815 /// Choose a canonical name from the library element. |
2820 /// This never uses the library's name (the identifier in the `library` | 2816 /// This never uses the library's name (the identifier in the `library` |
2821 /// declaration) as it doesn't have any meaningful rules enforced. | 2817 /// declaration) as it doesn't have any meaningful rules enforced. |
2822 String jsLibraryName(LibraryElement library) => canonicalLibraryName(library); | 2818 String jsLibraryName(LibraryElement library) => canonicalLibraryName(library); |
2823 | 2819 |
2824 /// Shorthand for identifier-like property names. | 2820 /// Shorthand for identifier-like property names. |
2825 /// For now, we emit them as strings and the printer restores them to | 2821 /// For now, we emit them as strings and the printer restores them to |
2826 /// identifiers if it can. | 2822 /// identifiers if it can. |
2827 // TODO(jmesserly): avoid the round tripping through quoted form. | 2823 // TODO(jmesserly): avoid the round tripping through quoted form. |
2828 JS.LiteralString _propertyName(String name) => js.string(name, "'"); | 2824 JS.LiteralString _propertyName(String name) => js.string(name, "'"); |
2829 | 2825 |
2830 /// Path to file that will be generated for [info]. In case it's url is a | |
2831 /// `file:` url, we use [root] to determine the relative path from the entry | |
2832 /// point file. | |
2833 String jsOutputPath(LibraryElement library, Uri root) { | |
2834 return '${jsOutputBase(library, root)}.js'; | |
2835 } | |
2836 | |
2837 String jsOutputBase(LibraryElement library, Uri root) { | |
2838 var uri = library.source.uri; | |
2839 var filepath = path.withoutExtension(uri.path); | |
2840 if (uri.scheme == 'dart') { | |
2841 filepath = 'dart/$filepath'; | |
2842 } else if (uri.scheme == 'file') { | |
2843 filepath = path.relative(filepath, from: path.dirname(root.path)); | |
2844 } else { | |
2845 assert(uri.scheme == 'package'); | |
2846 // filepath is good here, we want the output to start with a directory | |
2847 // matching the package name. | |
2848 } | |
2849 return filepath; | |
2850 } | |
2851 | |
2852 // TODO(jmesserly): validate the library. See issue #135. | 2826 // TODO(jmesserly): validate the library. See issue #135. |
2853 bool _isJsNameAnnotation(DartObjectImpl value) => value.type.name == 'JsName'; | 2827 bool _isJsNameAnnotation(DartObjectImpl value) => value.type.name == 'JsName'; |
2854 | 2828 |
2855 bool _isJsPeerInterface(DartObjectImpl value) => | 2829 bool _isJsPeerInterface(DartObjectImpl value) => |
2856 value.type.name == 'JsPeerInterface'; | 2830 value.type.name == 'JsPeerInterface'; |
2857 | 2831 |
2858 // TODO(jacobr): we would like to do something like the following | 2832 // TODO(jacobr): we would like to do something like the following |
2859 // but we don't have summary support yet. | 2833 // but we don't have summary support yet. |
2860 // bool _supportJsExtensionMethod(AnnotatedNode node) => | 2834 // bool _supportJsExtensionMethod(AnnotatedNode node) => |
2861 // _getAnnotation(node, "SupportJsExtensionMethod") != null; | 2835 // _getAnnotation(node, "SupportJsExtensionMethod") != null; |
(...skipping 10 matching lines...) Expand all Loading... |
2872 | 2846 |
2873 class _JsThisFinder extends JS.BaseVisitor { | 2847 class _JsThisFinder extends JS.BaseVisitor { |
2874 bool found = false; | 2848 bool found = false; |
2875 visitThis(JS.This node) { | 2849 visitThis(JS.This node) { |
2876 found = true; | 2850 found = true; |
2877 } | 2851 } |
2878 visitNode(JS.Node node) { | 2852 visitNode(JS.Node node) { |
2879 if (!found) super.visitNode(node); | 2853 if (!found) super.visitNode(node); |
2880 } | 2854 } |
2881 } | 2855 } |
OLD | NEW |