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