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

Side by Side Diff: lib/src/compiler/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/compiler/js_typeref_codegen.dart ('k') | lib/src/compiler/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_ast/js_ast.dart';
8 import 'js_names.dart';
9
10 /// Base class for compiling ES6 modules into various ES5 module patterns.
11 ///
12 /// This is a helper class for utilities and state that is shared by several
13 /// module transformers.
14 // TODO(jmesserly): "module transformer" might be a better name than builder.
15 abstract class _ModuleBuilder {
16 final imports = <ImportDeclaration>[];
17 final exports = <ExportDeclaration>[];
18 final statements = <Statement>[];
19
20 /// Collect [imports], [exports] and [statements] from the ES6 [module].
21 ///
22 /// For exports, this will also add their body to [statements] in the
23 /// appropriate position.
24 void visitProgram(Program module) {
25 for (var item in module.body) {
26 if (item is ImportDeclaration) {
27 visitImportDeclaration(item);
28 } else if (item is ExportDeclaration) {
29 visitExportDeclaration(item);
30 } else if (item is Statement) {
31 visitStatement(item);
32 }
33 }
34 }
35
36 visitImportDeclaration(ImportDeclaration node) {
37 imports.add(node);
38 }
39
40 visitExportDeclaration(ExportDeclaration node) {
41 exports.add(node);
42 statements.add(node.exported.toStatement());
43 }
44
45 visitStatement(Statement node) {
46 statements.add(node);
47 }
48 }
49
50 /// Generates modules for with our legacy `dart_library.js` loading mechanism.
51 // TODO(jmesserly): remove this and replace with something that interoperates.
52 class LegacyModuleBuilder extends _ModuleBuilder {
53 Program build(Program module) {
54 // Collect imports/exports/statements.
55 visitProgram(module);
56
57 // Build import parameters.
58 var exportsVar = new TemporaryId('exports');
59 var parameters = <TemporaryId>[exportsVar];
60 var importNames = <Expression>[];
61 var importStatements = <Statement>[];
62 for (var import in imports) {
63 importNames.add(import.from);
64 // TODO(jmesserly): we could use destructuring once Atom supports it.
65 var moduleVar =
66 new TemporaryId(pathToJSIdentifier(import.from.valueWithoutQuotes));
67 parameters.add(moduleVar);
68 for (var importName in import.namedImports) {
69 assert(!importName.isStar); // import * not supported in legacy modules.
70 var asName = importName.asName ?? importName.name;
71 importStatements.add(js.statement(
72 'const # = #.#', [asName, moduleVar, importName.name.name]));
73 }
74 }
75 statements.insertAll(0, importStatements);
76
77 if (exports.isNotEmpty) {
78 statements.add(js.comment('Exports:'));
79 // TODO(jmesserly): make these immutable in JS?
80 for (var export in exports) {
81 var names = export.exportedNames;
82 assert(names != null); // export * not supported in legacy modules.
83 for (var name in names) {
84 statements
85 .add(js.statement('#.# = #;', [exportsVar, name.name, name]));
86 }
87 }
88 }
89
90 var resultModule =
91 js.call("function(#) { 'use strict'; #; }", [parameters, statements]);
92
93 var moduleDef = js.statement("dart_library.library(#, #, #, #)", [
94 js.string(module.name, "'"),
95 new LiteralNull(),
96 js.commentExpression(
97 "Imports", new ArrayInitializer(importNames, multiline: true)),
98 resultModule
99 ]);
100 return new Program(<ModuleItem>[moduleDef]);
101 }
102 }
103
104 /// Generates node modules.
105 class NodeModuleBuilder extends _ModuleBuilder {
106 Program build(Program module) {
107 var importStatements = [js.statement("'use strict';"),];
108
109 for (var import in imports) {
110 // TODO(jmesserly): we could use destructuring once Atom supports it.
111 var moduleVar =
112 new TemporaryId(pathToJSIdentifier(import.from.valueWithoutQuotes));
113 importStatements
114 .add(js.statement('const # = require(#);', [moduleVar, import.from]));
115
116 // TODO(jmesserly): optimize for the common case of a single import.
117 for (var importName in import.namedImports) {
118 assert(!importName.isStar); // import * not supported yet.
119 var asName = importName.asName ?? importName.name;
120 importStatements.add(js.statement(
121 'const # = #.#', [asName, moduleVar, importName.name.name]));
122 }
123 }
124 statements.insertAll(0, importStatements);
125
126 if (exports.isNotEmpty) {
127 var exportsVar = new Identifier('exports');
128 statements.add(js.comment('Exports:'));
129 for (var export in exports) {
130 var names = export.exportedNames;
131 assert(names != null); // export * not supported in legacy modules.
132 for (var name in names) {
133 statements
134 .add(js.statement('#.# = #;', [exportsVar, name.name, name]));
135 }
136 }
137 }
138 return new Program(statements);
139 }
140 }
141
142 /// Escape [name] to make it into a valid identifier.
143 String pathToJSIdentifier(String name) {
144 name = path.basenameWithoutExtension(name);
145 if (name.length == 0) return r'$';
146
147 // Escape any invalid characters
148 StringBuffer buffer = null;
149 for (int i = 0; i < name.length; i++) {
150 var ch = name[i];
151 var needsEscape = ch == r'$' || _invalidCharInIdentifier.hasMatch(ch);
152 if (needsEscape && buffer == null) {
153 buffer = new StringBuffer(name.substring(0, i));
154 }
155 if (buffer != null) {
156 buffer.write(needsEscape ? '\$${ch.codeUnits.join("")}' : ch);
157 }
158 }
159
160 var result = buffer != null ? '$buffer' : name;
161 // Ensure the identifier first character is not numeric and that the whole
162 // identifier is not a keyword.
163 if (result.startsWith(new RegExp('[0-9]')) || invalidVariableName(result)) {
164 return '\$$result';
165 }
166 return result;
167 }
168
169 // Invalid characters for identifiers, which would need to be escaped.
170 final _invalidCharInIdentifier = new RegExp(r'[^A-Za-z_$0-9]');
OLDNEW
« no previous file with comments | « lib/src/compiler/js_typeref_codegen.dart ('k') | lib/src/compiler/nullable_type_inference.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698