| Index: pkg/compiler/lib/src/js/printer.dart
|
| diff --git a/pkg/compiler/lib/src/js/printer.dart b/pkg/compiler/lib/src/js/printer.dart
|
| index d359a05cf7f2e3dbf286f9645ba0de303458d94e..6ee3f3ecf4df70108506f7786ea3d789019b3c0c 100644
|
| --- a/pkg/compiler/lib/src/js/printer.dart
|
| +++ b/pkg/compiler/lib/src/js/printer.dart
|
| @@ -2,32 +2,77 @@
|
| // 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.
|
|
|
| -part of js;
|
| +part of js_ast;
|
|
|
| -class Printer extends Indentation implements NodeVisitor {
|
| +
|
| +class JavaScriptPrintingOptions {
|
| + final bool shouldCompressOutput;
|
| + final bool minifyLocalVariables;
|
| + final bool preferSemicolonToNewlineInMinifiedOutput;
|
| +
|
| + JavaScriptPrintingOptions(
|
| + {this.shouldCompressOutput: false,
|
| + this.minifyLocalVariables: false,
|
| + this.preferSemicolonToNewlineInMinifiedOutput: false});
|
| +}
|
| +
|
| +
|
| +/// An environment in which JavaScript printing is done. Provides emitting of
|
| +/// text and pre- and post-visit callbacks.
|
| +abstract class JavaScriptPrintingContext {
|
| + /// Signals an error. This should happen only for serious internal errors.
|
| + void error(String message) { throw message; }
|
| +
|
| + /// Adds [string] to the output.
|
| + void emit(String string);
|
| +
|
| + /// Callback immediately before printing [node]. Whitespace may be printed
|
| + /// after this callback before the first non-whitespace character for [node].
|
| + void enterNode(Node node) {}
|
| + /// Callback after printing the last character representing [node].
|
| + void exitNode(Node node) {}
|
| +}
|
| +
|
| +/// A simple implementation of [JavaScriptPrintingContext] suitable for tests.
|
| +class SimpleJavaScriptPrintingContext extends JavaScriptPrintingContext {
|
| + final StringBuffer buffer = new StringBuffer();
|
| +
|
| + void emit(String string) {
|
| + buffer.write(string);
|
| + }
|
| +
|
| + String getText() => buffer.toString();
|
| +}
|
| +
|
| +
|
| +class Printer implements NodeVisitor {
|
| + final JavaScriptPrintingOptions options;
|
| + final JavaScriptPrintingContext context;
|
| final bool shouldCompressOutput;
|
| - leg.DiagnosticListener diagnosticListener;
|
| - CodeBuffer outBuffer;
|
| - bool inForInit = false;
|
| - bool atStatementBegin = false;
|
| final DanglingElseVisitor danglingElseVisitor;
|
| final LocalNamer localNamer;
|
| +
|
| + bool inForInit = false;
|
| + bool atStatementBegin = false;
|
| bool pendingSemicolon = false;
|
| bool pendingSpace = false;
|
| - DumpInfoTask monitor = null;
|
| +
|
| + // The current indentation level.
|
| + int _indentLevel = 0;
|
| + // A cache of all indentation strings used so far.
|
| + List<String> _indentList = <String>[""];
|
|
|
| static final identifierCharacterRegExp = new RegExp(r'^[a-zA-Z_0-9$]');
|
| static final expressionContinuationRegExp = new RegExp(r'^[-+([]');
|
|
|
| - Printer(leg.DiagnosticListener diagnosticListener, DumpInfoTask monitor,
|
| - { bool enableMinification: false, allowVariableMinification: true })
|
| - : shouldCompressOutput = enableMinification,
|
| - monitor = monitor,
|
| - diagnosticListener = diagnosticListener,
|
| - outBuffer = new CodeBuffer(),
|
| - danglingElseVisitor = new DanglingElseVisitor(diagnosticListener),
|
| - localNamer = determineRenamer(enableMinification,
|
| - allowVariableMinification);
|
| + Printer(JavaScriptPrintingOptions options,
|
| + JavaScriptPrintingContext context)
|
| + : options = options,
|
| + context = context,
|
| + shouldCompressOutput = options.shouldCompressOutput,
|
| + danglingElseVisitor = new DanglingElseVisitor(context),
|
| + localNamer = determineRenamer(options.shouldCompressOutput,
|
| + options.minifyLocalVariables);
|
|
|
| static LocalNamer determineRenamer(bool shouldCompressOutput,
|
| bool allowVariableMinification) {
|
| @@ -35,6 +80,25 @@ class Printer extends Indentation implements NodeVisitor {
|
| ? new MinifyRenamer() : new IdentityNamer();
|
| }
|
|
|
| +
|
| + // The current indentation string.
|
| + String get indentation {
|
| + // Lazily add new indentation strings as required.
|
| + while (_indentList.length <= _indentLevel) {
|
| + _indentList.add(_indentList.last + " ");
|
| + }
|
| + return _indentList[_indentLevel];
|
| + }
|
| +
|
| + void indentMore() {
|
| + _indentLevel++;
|
| + }
|
| +
|
| + void indentLess() {
|
| + _indentLevel--;
|
| + }
|
| +
|
| +
|
| /// Always emit a newline, even under `enableMinification`.
|
| void forceLine() {
|
| out("\n");
|
| @@ -58,7 +122,7 @@ class Printer extends Indentation implements NodeVisitor {
|
| if (str != "") {
|
| if (pendingSemicolon) {
|
| if (!shouldCompressOutput) {
|
| - outBuffer.add(";");
|
| + context.emit(";");
|
| } else if (str != "}") {
|
| // We want to output newline instead of semicolon because it makes
|
| // the raw stack traces much easier to read and it also makes line-
|
| @@ -71,20 +135,21 @@ class Printer extends Indentation implements NodeVisitor {
|
| // If we're using the new emitter where most pretty printed code
|
| // is escaped in strings, it is a lot easier to deal with semicolons
|
| // than newlines because the former doesn't need escaping.
|
| - if (USE_NEW_EMITTER || expressionContinuationRegExp.hasMatch(str)) {
|
| - outBuffer.add(";");
|
| + if (options.preferSemicolonToNewlineInMinifiedOutput ||
|
| + expressionContinuationRegExp.hasMatch(str)) {
|
| + context.emit(";");
|
| } else {
|
| - outBuffer.add("\n");
|
| + context.emit("\n");
|
| }
|
| }
|
| }
|
| if (pendingSpace &&
|
| (!shouldCompressOutput || identifierCharacterRegExp.hasMatch(str))) {
|
| - outBuffer.add(" ");
|
| + context.emit(" ");
|
| }
|
| pendingSpace = false;
|
| pendingSemicolon = false;
|
| - outBuffer.add(str);
|
| + context.emit(str);
|
| lastAddedString = str;
|
| }
|
| }
|
| @@ -111,26 +176,10 @@ class Printer extends Indentation implements NodeVisitor {
|
| }
|
| }
|
|
|
| - void beginSourceRange(Node node) {
|
| - if (node.sourceInformation != null) {
|
| - node.sourceInformation.beginMapping(outBuffer);
|
| - }
|
| - }
|
| -
|
| - void endSourceRange(Node node) {
|
| - if (node.sourceInformation != null) {
|
| - node.sourceInformation.endMapping(outBuffer);
|
| - }
|
| - }
|
| -
|
| visit(Node node) {
|
| - beginSourceRange(node);
|
| - if (monitor != null) monitor.enteringAst(node, outBuffer.length);
|
| -
|
| + context.enterNode(node);
|
| node.accept(this);
|
| -
|
| - if (monitor != null) monitor.exitingAst(node, outBuffer.length);
|
| - endSourceRange(node);
|
| + context.exitNode(node);
|
| }
|
|
|
| visitCommaSeparated(List<Node> nodes, int hasRequiredType,
|
| @@ -155,10 +204,6 @@ class Printer extends Indentation implements NodeVisitor {
|
| visitAll(program.body);
|
| }
|
|
|
| - visitBlob(Blob node) {
|
| - outBuffer.addBuffer(node.buffer);
|
| - }
|
| -
|
| bool blockBody(Node body, {bool needsSeparation, bool needsNewline}) {
|
| if (body is Block) {
|
| spaceOut();
|
| @@ -172,16 +217,18 @@ class Printer extends Indentation implements NodeVisitor {
|
| } else {
|
| lineOut();
|
| }
|
| - indentBlock(() => visit(body));
|
| + indentMore();
|
| + visit(body);
|
| + indentLess();
|
| return false;
|
| }
|
|
|
| void blockOutWithoutBraces(Node node) {
|
| if (node is Block) {
|
| - beginSourceRange(node);
|
| + context.enterNode(node);
|
| Block block = node;
|
| block.statements.forEach(blockOutWithoutBraces);
|
| - endSourceRange(node);
|
| + context.exitNode(node);
|
| } else {
|
| visit(node);
|
| }
|
| @@ -189,13 +236,15 @@ class Printer extends Indentation implements NodeVisitor {
|
|
|
| void blockOut(Block node, bool shouldIndent, bool needsNewline) {
|
| if (shouldIndent) indent();
|
| - beginSourceRange(node);
|
| + context.enterNode(node);
|
| out("{");
|
| lineOut();
|
| - indentBlock(() => node.statements.forEach(blockOutWithoutBraces));
|
| + indentMore();
|
| + node.statements.forEach(blockOutWithoutBraces);
|
| + indentLess();
|
| indent();
|
| out("}");
|
| - endSourceRange(node);
|
| + context.exitNode(node);
|
| if (needsNewline) lineOut();
|
| }
|
|
|
| @@ -407,7 +456,9 @@ class Printer extends Indentation implements NodeVisitor {
|
| out(")");
|
| spaceOut();
|
| outLn("{");
|
| - indentBlock(() => visitAll(node.cases));
|
| + indentMore();
|
| + visitAll(node.cases);
|
| + indentLess();
|
| outIndentLn("}");
|
| }
|
|
|
| @@ -418,14 +469,18 @@ class Printer extends Indentation implements NodeVisitor {
|
| newInForInit: false, newAtStatementBegin: false);
|
| outLn(":");
|
| if (!node.body.statements.isEmpty) {
|
| - indentBlock(() => blockOutWithoutBraces(node.body));
|
| + indentMore();
|
| + blockOutWithoutBraces(node.body);
|
| + indentLess();
|
| }
|
| }
|
|
|
| visitDefault(Default node) {
|
| outIndentLn("default:");
|
| if (!node.body.statements.isEmpty) {
|
| - indentBlock(() => blockOutWithoutBraces(node.body));
|
| + indentMore();
|
| + blockOutWithoutBraces(node.body);
|
| + indentLess();
|
| }
|
| }
|
|
|
| @@ -642,8 +697,7 @@ class Printer extends Indentation implements NodeVisitor {
|
| rightPrecedenceRequirement = UNARY;
|
| break;
|
| default:
|
| - diagnosticListener
|
| - .internalError(NO_LOCATION_SPANNABLE, "Forgot operator: $op");
|
| + context.error("Forgot operator: $op");
|
| }
|
|
|
| visitNestedExpression(left, leftPrecedenceRequirement,
|
| @@ -880,8 +934,7 @@ class Printer extends Indentation implements NodeVisitor {
|
| List<String> parts = template.split('#');
|
| int inputsLength = inputs == null ? 0 : inputs.length;
|
| if (parts.length != inputsLength + 1) {
|
| - diagnosticListener.internalError(NO_LOCATION_SPANNABLE,
|
| - 'Wrong number of arguments for JS: $template');
|
| + context.error('Wrong number of arguments for JS: $template');
|
| }
|
| // Code that uses JS must take care of operator precedences, and
|
| // put parenthesis if needed.
|
| @@ -1008,15 +1061,14 @@ class VarCollector extends BaseVisitor {
|
| * as then-statement in an [If] that has an else branch.
|
| */
|
| class DanglingElseVisitor extends BaseVisitor<bool> {
|
| - leg.DiagnosticListener diagnosticListener;
|
| + JavaScriptPrintingContext context;
|
|
|
| - DanglingElseVisitor(this.diagnosticListener);
|
| + DanglingElseVisitor(this.context);
|
|
|
| bool visitProgram(Program node) => false;
|
|
|
| bool visitNode(Statement node) {
|
| - diagnosticListener
|
| - .internalError(NO_LOCATION_SPANNABLE, "Forgot node: $node");
|
| + context.error("Forgot node: $node");
|
| return null;
|
| }
|
|
|
| @@ -1055,18 +1107,6 @@ class DanglingElseVisitor extends BaseVisitor<bool> {
|
| }
|
|
|
|
|
| -CodeBuffer prettyPrint(Node node, leg.Compiler compiler,
|
| - {DumpInfoTask monitor,
|
| - bool allowVariableMinification: true}) {
|
| - Printer printer =
|
| - new Printer(compiler, monitor,
|
| - enableMinification: compiler.enableMinification,
|
| - allowVariableMinification: allowVariableMinification);
|
| - printer.visit(node);
|
| - return printer.outBuffer;
|
| -}
|
| -
|
| -
|
| abstract class LocalNamer {
|
| String getName(String oldName);
|
| String declareVariable(String oldName);
|
|
|