Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(131)

Side by Side Diff: lib/src/codegen/module_builder.dart

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

Powered by Google App Engine
This is Rietveld 408576698