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

Unified 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 side-by-side diff with in-line comments
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 »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: lib/src/compiler/module_builder.dart
diff --git a/lib/src/compiler/module_builder.dart b/lib/src/compiler/module_builder.dart
new file mode 100644
index 0000000000000000000000000000000000000000..7a6866c6463c53269bc03c0f817ff11c01b59a5a
--- /dev/null
+++ b/lib/src/compiler/module_builder.dart
@@ -0,0 +1,170 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:path/path.dart' as path;
+
+import '../js_ast/js_ast.dart';
+import 'js_names.dart';
+
+/// Base class for compiling ES6 modules into various ES5 module patterns.
+///
+/// This is a helper class for utilities and state that is shared by several
+/// module transformers.
+// TODO(jmesserly): "module transformer" might be a better name than builder.
+abstract class _ModuleBuilder {
+ final imports = <ImportDeclaration>[];
+ final exports = <ExportDeclaration>[];
+ final statements = <Statement>[];
+
+ /// Collect [imports], [exports] and [statements] from the ES6 [module].
+ ///
+ /// For exports, this will also add their body to [statements] in the
+ /// appropriate position.
+ void visitProgram(Program module) {
+ for (var item in module.body) {
+ if (item is ImportDeclaration) {
+ visitImportDeclaration(item);
+ } else if (item is ExportDeclaration) {
+ visitExportDeclaration(item);
+ } else if (item is Statement) {
+ visitStatement(item);
+ }
+ }
+ }
+
+ visitImportDeclaration(ImportDeclaration node) {
+ imports.add(node);
+ }
+
+ visitExportDeclaration(ExportDeclaration node) {
+ exports.add(node);
+ statements.add(node.exported.toStatement());
+ }
+
+ visitStatement(Statement node) {
+ statements.add(node);
+ }
+}
+
+/// Generates modules for with our legacy `dart_library.js` loading mechanism.
+// TODO(jmesserly): remove this and replace with something that interoperates.
+class LegacyModuleBuilder extends _ModuleBuilder {
+ Program build(Program module) {
+ // Collect imports/exports/statements.
+ visitProgram(module);
+
+ // Build import parameters.
+ var exportsVar = new TemporaryId('exports');
+ var parameters = <TemporaryId>[exportsVar];
+ var importNames = <Expression>[];
+ var importStatements = <Statement>[];
+ for (var import in imports) {
+ importNames.add(import.from);
+ // TODO(jmesserly): we could use destructuring once Atom supports it.
+ var moduleVar =
+ new TemporaryId(pathToJSIdentifier(import.from.valueWithoutQuotes));
+ parameters.add(moduleVar);
+ for (var importName in import.namedImports) {
+ assert(!importName.isStar); // import * not supported in legacy modules.
+ var asName = importName.asName ?? importName.name;
+ importStatements.add(js.statement(
+ 'const # = #.#', [asName, moduleVar, importName.name.name]));
+ }
+ }
+ statements.insertAll(0, importStatements);
+
+ if (exports.isNotEmpty) {
+ statements.add(js.comment('Exports:'));
+ // TODO(jmesserly): make these immutable in JS?
+ for (var export in exports) {
+ var names = export.exportedNames;
+ assert(names != null); // export * not supported in legacy modules.
+ for (var name in names) {
+ statements
+ .add(js.statement('#.# = #;', [exportsVar, name.name, name]));
+ }
+ }
+ }
+
+ var resultModule =
+ js.call("function(#) { 'use strict'; #; }", [parameters, statements]);
+
+ var moduleDef = js.statement("dart_library.library(#, #, #, #)", [
+ js.string(module.name, "'"),
+ new LiteralNull(),
+ js.commentExpression(
+ "Imports", new ArrayInitializer(importNames, multiline: true)),
+ resultModule
+ ]);
+ return new Program(<ModuleItem>[moduleDef]);
+ }
+}
+
+/// Generates node modules.
+class NodeModuleBuilder extends _ModuleBuilder {
+ Program build(Program module) {
+ var importStatements = [js.statement("'use strict';"),];
+
+ for (var import in imports) {
+ // TODO(jmesserly): we could use destructuring once Atom supports it.
+ var moduleVar =
+ new TemporaryId(pathToJSIdentifier(import.from.valueWithoutQuotes));
+ importStatements
+ .add(js.statement('const # = require(#);', [moduleVar, import.from]));
+
+ // TODO(jmesserly): optimize for the common case of a single import.
+ for (var importName in import.namedImports) {
+ assert(!importName.isStar); // import * not supported yet.
+ var asName = importName.asName ?? importName.name;
+ importStatements.add(js.statement(
+ 'const # = #.#', [asName, moduleVar, importName.name.name]));
+ }
+ }
+ statements.insertAll(0, importStatements);
+
+ if (exports.isNotEmpty) {
+ var exportsVar = new Identifier('exports');
+ statements.add(js.comment('Exports:'));
+ for (var export in exports) {
+ var names = export.exportedNames;
+ assert(names != null); // export * not supported in legacy modules.
+ for (var name in names) {
+ statements
+ .add(js.statement('#.# = #;', [exportsVar, name.name, name]));
+ }
+ }
+ }
+ return new Program(statements);
+ }
+}
+
+/// Escape [name] to make it into a valid identifier.
+String pathToJSIdentifier(String name) {
+ name = path.basenameWithoutExtension(name);
+ if (name.length == 0) return r'$';
+
+ // Escape any invalid characters
+ StringBuffer buffer = null;
+ for (int i = 0; i < name.length; i++) {
+ var ch = name[i];
+ var needsEscape = ch == r'$' || _invalidCharInIdentifier.hasMatch(ch);
+ if (needsEscape && buffer == null) {
+ buffer = new StringBuffer(name.substring(0, i));
+ }
+ if (buffer != null) {
+ buffer.write(needsEscape ? '\$${ch.codeUnits.join("")}' : ch);
+ }
+ }
+
+ var result = buffer != null ? '$buffer' : name;
+ // Ensure the identifier first character is not numeric and that the whole
+ // identifier is not a keyword.
+ if (result.startsWith(new RegExp('[0-9]')) || invalidVariableName(result)) {
+ return '\$$result';
+ }
+ return result;
+}
+
+// Invalid characters for identifiers, which would need to be escaped.
+final _invalidCharInIdentifier = new RegExp(r'[^A-Za-z_$0-9]');
« 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