Chromium Code Reviews| 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.module_builder; | 5 library dev_compiler.src.codegen.module_builder; |
| 6 | 6 |
| 7 import 'package:path/path.dart' as path; | 7 import 'package:path/path.dart' as path; |
| 8 | 8 |
| 9 import '../js/js_ast.dart' as JS; | 9 import '../js/js_ast.dart' as JS; |
| 10 import '../js/js_ast.dart' show js; | 10 import '../js/js_ast.dart' show js; |
| 11 import '../options.dart' show ModuleFormat; | 11 import '../options.dart' show ModuleFormat; |
| 12 | 12 |
| 13 /// Helper that builds JS modules in a given [ModuleFormat]. | 13 /// Helper that builds JS modules in a given [ModuleFormat]. |
| 14 abstract class ModuleBuilder { | 14 abstract class ModuleBuilder { |
| 15 final List<String> _exports = <String>[]; | 15 final _exports = <String, String>{}; |
| 16 final List<_ModuleImport> _imports = <_ModuleImport>[]; | 16 final _imports = <_ModuleImport>[]; |
| 17 | 17 |
| 18 ModuleBuilder._(); | 18 ModuleBuilder._(); |
| 19 | 19 |
| 20 /// Returns a [format]-specific [ModuleBuilder]. | 20 /// Returns a [format]-specific [ModuleBuilder]. |
| 21 /// - [jsPath] is the path of the module being built. | 21 /// - [jsPath] is the path of the module being built. |
| 22 /// - [jsModuleValue] is the default value to use for the library, in case of | 22 /// - [jsModuleValue] is the default value to use for the library, in case of |
| 23 /// js interop (comes from the @js.JS(jsModuleValue) annotation on the | 23 /// js interop (comes from the @js.JS(jsModuleValue) annotation on the |
| 24 /// library directive). It is null in any other case. | 24 /// library directive). It is null in any other case. |
| 25 /// - [exportsVar] is the name of the object on which items are exported. Lazy | 25 /// - [exportsVar] is the name of the object on which items are exported. Lazy |
| 26 /// variables and constants are assumed to be declared on this instance. | 26 /// variables and constants are assumed to be declared on this instance. |
| 27 factory ModuleBuilder(ModuleFormat format) { | 27 factory ModuleBuilder(ModuleFormat format) { |
| 28 switch (format) { | 28 switch (format) { |
| 29 case ModuleFormat.legacy: | 29 case ModuleFormat.legacy: |
| 30 return new LegacyModuleBuilder(); | 30 return new LegacyModuleBuilder(); |
| 31 case ModuleFormat.es6: | 31 case ModuleFormat.es6: |
| 32 return new ES6ModuleBuilder(); | 32 return new ES6ModuleBuilder(); |
| 33 case ModuleFormat.node: | |
| 34 return new NodeModuleBuilder(); | |
| 33 } | 35 } |
| 34 } | 36 } |
| 35 | 37 |
| 36 /// Adds [name] to the list of names to be exported from the module. | 38 /// Adds [name] to the list of names to be exported from the module. |
| 37 void addExport(String name) { | 39 void addExport(String name, String exportName) { |
| 38 _exports.add(name); | 40 _exports[name] = exportName; |
| 39 } | 41 } |
| 40 | 42 |
| 41 /// Adds an import from a module named [name] and locally aliased as [libVar]. | 43 /// Adds an import from a module named [name] and locally aliased as [libVar]. |
| 42 /// When [isLazy] is true, the import should be lazy (i.e. there is some | 44 /// When [isLazy] is `true`, the import should be lazy (i.e. there is some |
| 43 /// cyclic dependency of imports). | 45 /// cyclic dependency of imports). |
| 46 /// When [libVar] is `null`, the import is there just to force the import | |
| 47 /// order. | |
| 44 void addImport(String name, JS.Identifier libVar, {bool isLazy: false}) { | 48 void addImport(String name, JS.Identifier libVar, {bool isLazy: false}) { |
| 45 _imports.add(new _ModuleImport(name, libVar, isLazy)); | 49 _imports.add(new _ModuleImport(name, libVar, isLazy)); |
| 46 } | 50 } |
| 47 | 51 |
| 48 /// Builds a program out of menu items. | 52 /// Builds a program out of menu items. |
| 49 JS.Program build(String jsPath, String jsModuleValue, | 53 JS.Program build(String jsPath, String jsModuleValue, |
| 50 JS.Identifier exportsVar, Iterable<JS.ModuleItem> moduleItems); | 54 JS.Identifier exportsVar, Iterable<JS.ModuleItem> moduleItems); |
| 51 } | 55 } |
| 52 | 56 |
| 53 class _ModuleImport { | 57 class _ModuleImport { |
| 54 final String name; | 58 final String name; |
| 55 final JS.Identifier libVar; | 59 final JS.Identifier libVar; |
| 56 // TODO(jmesserly): Assess whether we can remove this (we shouldn't need it | 60 // TODO(jmesserly): Assess whether we can remove this (we shouldn't need it |
| 57 // even in our legacy module format, but it might still be useful for Closure | 61 // even in our legacy module format, but it might still be useful for Closure |
| 58 // with ES6 modules). | 62 // with ES6 modules). |
| 59 final bool isLazy; | 63 final bool isLazy; |
| 60 _ModuleImport(this.name, this.libVar, this.isLazy); | 64 _ModuleImport(this.name, this.libVar, this.isLazy); |
| 61 } | 65 } |
| 62 | 66 |
| 63 /// Generates modules for with DDC's `dart_library.js` loading mechanism. | 67 /// Generates modules for with DDC's `dart_library.js` loading mechanism. |
| 64 class LegacyModuleBuilder extends ModuleBuilder { | 68 class LegacyModuleBuilder extends ModuleBuilder { |
| 65 LegacyModuleBuilder() : super._(); | 69 LegacyModuleBuilder() : super._(); |
| 66 | 70 |
| 67 JS.Program build(String jsPath, String jsModuleValue, | 71 JS.Program build(String jsPath, String jsModuleValue, |
| 68 JS.Identifier exportsVar, List<JS.ModuleItem> moduleItems) { | 72 JS.Identifier exportsVar, Iterable<JS.ModuleItem> moduleItems) { |
| 69 // TODO(jmesserly): it would be great to run the renamer on the body, | 73 // TODO(jmesserly): it would be great to run the renamer on the body, |
| 70 // then figure out if we really need each of these parameters. | 74 // then figure out if we really need each of these parameters. |
| 71 // See ES6 modules: https://github.com/dart-lang/dev_compiler/issues/34 | 75 // See ES6 modules: https://github.com/dart-lang/dev_compiler/issues/34 |
| 72 var params = [exportsVar]; | 76 var params = [exportsVar]; |
| 73 var lazyParams = []; | 77 var lazyParams = []; |
| 74 | 78 |
| 75 var imports = <JS.Expression>[]; | 79 var imports = <JS.Expression>[]; |
| 76 var lazyImports = <JS.Expression>[]; | 80 var lazyImports = <JS.Expression>[]; |
| 77 var moduleStatements = <JS.Statement>[]; | 81 var moduleStatements = <JS.Statement>[]; |
| 78 | 82 |
| 79 for (var i in _imports) { | 83 for (var i in _imports) { |
| 84 // No need to force the import order for the legacy library mechanism. | |
| 85 if (i.libVar == null) continue; | |
| 80 (i.isLazy ? lazyImports : imports).add(js.string(i.name, "'")); | 86 (i.isLazy ? lazyImports : imports).add(js.string(i.name, "'")); |
| 81 (i.isLazy ? lazyParams : params).add(i.libVar); | 87 (i.isLazy ? lazyParams : params).add(i.libVar); |
| 82 } | 88 } |
| 83 params.addAll(lazyParams); | 89 params.addAll(lazyParams); |
| 84 | 90 |
| 85 moduleStatements.addAll(_flattenBlocks(moduleItems)); | 91 moduleStatements.addAll(_flattenBlocks(moduleItems)); |
| 86 | 92 |
| 87 if (_exports.isNotEmpty) { | 93 if (_exports.isNotEmpty) { |
| 88 moduleStatements.add(js.comment('Exports:')); | 94 moduleStatements.add(js.comment('Exports:')); |
| 89 // TODO(jmesserly): make these immutable in JS? | 95 // TODO(jmesserly): make these immutable in JS? |
| 90 for (var name in _exports) { | 96 _exports.forEach((name, exportName) { |
| 91 moduleStatements | 97 moduleStatements |
| 92 .add(js.statement('#.# = #;', [exportsVar, name, name])); | 98 .add(js.statement('#.# = #;', [exportsVar, exportName, name])); |
| 93 } | 99 }); |
| 94 } | 100 } |
| 95 | 101 |
| 96 var module = | 102 var module = |
| 97 js.call("function(#) { 'use strict'; #; }", [params, moduleStatements]); | 103 js.call("function(#) { 'use strict'; #; }", [params, moduleStatements]); |
| 98 | 104 |
| 99 var moduleDef = js.statement("dart_library.library(#, #, #, #, #)", [ | 105 var moduleDef = js.statement("dart_library.library(#, #, #, #, #)", [ |
| 100 js.string(jsPath, "'"), | 106 js.string(jsPath, "'"), |
| 101 jsModuleValue ?? new JS.LiteralNull(), | 107 jsModuleValue ?? new JS.LiteralNull(), |
| 102 js.commentExpression( | 108 js.commentExpression( |
| 103 "Imports", new JS.ArrayInitializer(imports, multiline: true)), | 109 "Imports", new JS.ArrayInitializer(imports, multiline: true)), |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 116 } | 122 } |
| 117 | 123 |
| 118 /// Generates ES6 modules. | 124 /// Generates ES6 modules. |
| 119 // TODO(ochafik): Break strong dep cycles to accommodate the Closure Compiler. | 125 // TODO(ochafik): Break strong dep cycles to accommodate the Closure Compiler. |
| 120 class ES6ModuleBuilder extends ModuleBuilder { | 126 class ES6ModuleBuilder extends ModuleBuilder { |
| 121 ES6ModuleBuilder() : super._(); | 127 ES6ModuleBuilder() : super._(); |
| 122 | 128 |
| 123 JS.Program build(String jsPath, String jsModuleValue, | 129 JS.Program build(String jsPath, String jsModuleValue, |
| 124 JS.Identifier exportsVar, Iterable<JS.ModuleItem> moduleItems) { | 130 JS.Identifier exportsVar, Iterable<JS.ModuleItem> moduleItems) { |
| 125 var moduleStatements = <JS.ModuleItem>[ | 131 var moduleStatements = <JS.ModuleItem>[ |
| 126 // Lazy declarations may reference exports. | |
| 127 js.statement("const # = {};", [exportsVar]) | 132 js.statement("const # = {};", [exportsVar]) |
| 128 ]; | 133 ]; |
| 129 | 134 |
| 130 // TODO(jmesserly): it would be great to run the renamer on the body, | 135 // TODO(jmesserly): it would be great to run the renamer on the body, |
| 131 // then figure out if we really need each of these parameters. | 136 // then figure out if we really need each of these parameters. |
| 132 // See ES6 modules: https://github.com/dart-lang/dev_compiler/issues/34 | 137 // See ES6 modules: https://github.com/dart-lang/dev_compiler/issues/34 |
| 133 for (var i in _imports) { | 138 for (var i in _imports) { |
| 134 var moduleName = js.string(_relativeModuleName(i.name, from: jsPath)); | 139 var moduleName = js.string(_relativeModuleName(i.name, from: jsPath)); |
| 135 // TODO(ochafik): laziness, late binding, etc, to support Closure... | 140 // TODO(ochafik): laziness, late binding, etc, to support Closure... |
| 136 moduleStatements.add( | 141 if (i.libVar == null) { |
| 137 new JS.ImportDeclaration(defaultBinding: i.libVar, from: moduleName)); | 142 moduleStatements |
| 143 .add(new JS.ImportDeclaration(namedImports: [], from: moduleName)); | |
| 144 } else { | |
| 145 moduleStatements.add(new JS.ImportDeclaration( | |
| 146 defaultBinding: i.libVar, from: moduleName)); | |
| 147 } | |
| 138 } | 148 } |
| 139 | 149 |
| 140 moduleStatements.addAll(_flattenBlocks(moduleItems)); | 150 moduleStatements.addAll(_flattenBlocks(moduleItems)); |
| 141 | 151 |
| 142 if (_exports.isNotEmpty) { | 152 if (_exports.isNotEmpty) { |
| 143 moduleStatements.add(js.comment('Exports:')); | 153 moduleStatements.add(js.comment('Exports:')); |
| 144 // TODO(jmesserly): make these immutable in JS? | 154 // TODO(jmesserly): make these immutable in JS? |
| 145 for (var name in _exports) { | 155 _exports.forEach((name, exportName) { |
| 146 moduleStatements | 156 moduleStatements |
| 147 .add(js.statement('#.# = #;', [exportsVar, name, name])); | 157 .add(js.statement('#.# = #;', [exportsVar, exportName, name])); |
| 148 } | 158 }); |
| 149 moduleStatements | 159 moduleStatements |
| 150 .add(new JS.ExportDeclaration(exportsVar, isDefault: true)); | 160 .add(new JS.ExportDeclaration(exportsVar, isDefault: true)); |
| 151 } | 161 } |
| 152 // TODO(ochafik): What to do of jsModuleValue? | 162 // TODO(ochafik): What to do of jsModuleValue? |
| 153 return new JS.Program(moduleStatements); | 163 return new JS.Program(moduleStatements); |
| 154 } | 164 } |
| 155 } | 165 } |
| 156 | 166 |
| 167 /// Generates node modules. | |
| 168 class NodeModuleBuilder extends ModuleBuilder { | |
| 169 NodeModuleBuilder() : super._(); | |
| 170 | |
| 171 JS.Program build(String jsPath, String jsModuleValue, | |
| 172 JS.Identifier exportsVar, Iterable<JS.ModuleItem> moduleItems) { | |
| 173 var moduleStatements = <JS.ModuleItem>[js.statement("'use strict';"),]; | |
| 174 | |
| 175 for (var i in _imports) { | |
| 176 if (i.libVar == null) { | |
| 177 moduleStatements.add(js.statement('require(#);', [js.string(i.name)])); | |
| 178 } else { | |
| 179 moduleStatements.add( | |
| 180 js.statement('let # = require(#);', [i.libVar, js.string(i.name)])); | |
| 181 } | |
| 182 } | |
| 183 | |
| 184 moduleStatements.addAll(_flattenBlocks(moduleItems)); | |
| 185 | |
| 186 if (_exports.isNotEmpty) { | |
| 187 moduleStatements.add(js.comment('Exports:')); | |
| 188 _exports.forEach((name, exportName) { | |
| 189 moduleStatements | |
| 190 .add(js.statement('#.# = #;', [exportsVar, exportName, name])); | |
| 191 }); | |
| 192 } | |
| 193 // TODO(ochafik): What to do of jsModuleValue? | |
|
Jennifer Messerly
2016/01/29 00:32:31
Wording suggestion: "what to do with jsModuleValue
ochafik
2016/01/29 09:38:17
Thanks for the explanation! Upgraded the comment
| |
| 194 return new JS.Program(moduleStatements); | |
| 195 } | |
| 196 } | |
| 197 | |
| 157 /// Flattens blocks in [stats] to a single list of module items. | 198 /// Flattens blocks in [stats] to a single list of module items. |
| 158 /// Note that in general, blocks should not be flattened, because it can | 199 /// Note that in general, blocks should not be flattened, because it can |
| 159 /// mess up with block-level scoping (let, const). | 200 /// mess up with block-level scoping (let, const). |
| 160 // TODO(ochafik): Remove this / find better pattern (adding statements as they | 201 // TODO(ochafik): Remove this / find better pattern (adding statements as they |
| 161 // are generated from [JSCodegenVisitor], instead of composing them with | 202 // are generated from [JSCodegenVisitor], instead of composing them with |
| 162 // [_statements]). | 203 // [_statements]). |
| 163 Iterable<JS.ModuleItem> _flattenBlocks(List<JS.ModuleItem> stats) => | 204 Iterable<JS.ModuleItem> _flattenBlocks(List<JS.ModuleItem> stats) => |
| 164 stats.expand( | 205 stats.expand( |
| 165 (item) => item is JS.Block ? _flattenBlocks(item.statements) : [item]); | 206 (item) => item is JS.Block ? _flattenBlocks(item.statements) : [item]); |
| OLD | NEW |