| 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 |