| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, 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 part of js_ast; | 5 part of js_ast; |
| 6 | 6 |
| 7 | |
| 8 typedef String Renamer(Name); | 7 typedef String Renamer(Name); |
| 9 | 8 |
| 10 class JavaScriptPrintingOptions { | 9 class JavaScriptPrintingOptions { |
| 11 final bool shouldCompressOutput; | 10 final bool shouldCompressOutput; |
| 12 final bool minifyLocalVariables; | 11 final bool minifyLocalVariables; |
| 13 final bool preferSemicolonToNewlineInMinifiedOutput; | 12 final bool preferSemicolonToNewlineInMinifiedOutput; |
| 14 final Renamer renamerForNames; | 13 final Renamer renamerForNames; |
| 15 | 14 |
| 16 JavaScriptPrintingOptions( | 15 JavaScriptPrintingOptions( |
| 17 {this.shouldCompressOutput: false, | 16 {this.shouldCompressOutput: false, |
| 18 this.minifyLocalVariables: false, | 17 this.minifyLocalVariables: false, |
| 19 this.preferSemicolonToNewlineInMinifiedOutput: false, | 18 this.preferSemicolonToNewlineInMinifiedOutput: false, |
| 20 this.renamerForNames: identityRenamer}); | 19 this.renamerForNames: identityRenamer}); |
| 21 | 20 |
| 22 static String identityRenamer(Name name) => name.name; | 21 static String identityRenamer(Name name) => name.name; |
| 23 } | 22 } |
| 24 | 23 |
| 25 | |
| 26 /// An environment in which JavaScript printing is done. Provides emitting of | 24 /// An environment in which JavaScript printing is done. Provides emitting of |
| 27 /// text and pre- and post-visit callbacks. | 25 /// text and pre- and post-visit callbacks. |
| 28 abstract class JavaScriptPrintingContext { | 26 abstract class JavaScriptPrintingContext { |
| 29 /// Signals an error. This should happen only for serious internal errors. | 27 /// Signals an error. This should happen only for serious internal errors. |
| 30 void error(String message) { throw message; } | 28 void error(String message) { |
| 29 throw message; |
| 30 } |
| 31 | 31 |
| 32 /// Adds [string] to the output. | 32 /// Adds [string] to the output. |
| 33 void emit(String string); | 33 void emit(String string); |
| 34 | 34 |
| 35 /// Callback for the start of printing of [node]. [startPosition] is the | 35 /// Callback for the start of printing of [node]. [startPosition] is the |
| 36 /// position of the first non-whitespace character of [node]. | 36 /// position of the first non-whitespace character of [node]. |
| 37 /// | 37 /// |
| 38 /// [enterNode] is called in pre-traversal order. | 38 /// [enterNode] is called in pre-traversal order. |
| 39 void enterNode(Node node, int startPosition) {} | 39 void enterNode(Node node, int startPosition) {} |
| 40 | 40 |
| 41 /// Callback for the end of printing of [node]. [startPosition] is the | 41 /// Callback for the end of printing of [node]. [startPosition] is the |
| 42 /// position of the first non-whitespace character of [node] (also provided | 42 /// position of the first non-whitespace character of [node] (also provided |
| 43 /// in the [enterNode] callback), [endPosition] is the position immediately | 43 /// in the [enterNode] callback), [endPosition] is the position immediately |
| 44 /// following the last character of [node]. [closingPosition] is the | 44 /// following the last character of [node]. [closingPosition] is the |
| 45 /// position of the ending delimiter of [node]. This is only provided for | 45 /// position of the ending delimiter of [node]. This is only provided for |
| 46 /// [Fun] nodes and is `null` otherwise. | 46 /// [Fun] nodes and is `null` otherwise. |
| 47 /// | 47 /// |
| 48 /// [enterNode] is called in post-traversal order. | 48 /// [enterNode] is called in post-traversal order. |
| 49 void exitNode(Node node, | 49 void exitNode( |
| 50 int startPosition, | 50 Node node, int startPosition, int endPosition, int closingPosition) {} |
| 51 int endPosition, | |
| 52 int closingPosition) {} | |
| 53 } | 51 } |
| 54 | 52 |
| 55 /// A simple implementation of [JavaScriptPrintingContext] suitable for tests. | 53 /// A simple implementation of [JavaScriptPrintingContext] suitable for tests. |
| 56 class SimpleJavaScriptPrintingContext extends JavaScriptPrintingContext { | 54 class SimpleJavaScriptPrintingContext extends JavaScriptPrintingContext { |
| 57 final StringBuffer buffer = new StringBuffer(); | 55 final StringBuffer buffer = new StringBuffer(); |
| 58 | 56 |
| 59 void emit(String string) { | 57 void emit(String string) { |
| 60 buffer.write(string); | 58 buffer.write(string); |
| 61 } | 59 } |
| 62 | 60 |
| 63 String getText() => buffer.toString(); | 61 String getText() => buffer.toString(); |
| 64 } | 62 } |
| 65 | 63 |
| 66 | |
| 67 class Printer implements NodeVisitor { | 64 class Printer implements NodeVisitor { |
| 68 final JavaScriptPrintingOptions options; | 65 final JavaScriptPrintingOptions options; |
| 69 final JavaScriptPrintingContext context; | 66 final JavaScriptPrintingContext context; |
| 70 final bool shouldCompressOutput; | 67 final bool shouldCompressOutput; |
| 71 final DanglingElseVisitor danglingElseVisitor; | 68 final DanglingElseVisitor danglingElseVisitor; |
| 72 final LocalNamer localNamer; | 69 final LocalNamer localNamer; |
| 73 | 70 |
| 74 int _charCount = 0; | 71 int _charCount = 0; |
| 75 bool inForInit = false; | 72 bool inForInit = false; |
| 76 bool atStatementBegin = false; | 73 bool atStatementBegin = false; |
| 77 bool pendingSemicolon = false; | 74 bool pendingSemicolon = false; |
| 78 bool pendingSpace = false; | 75 bool pendingSpace = false; |
| 79 | 76 |
| 80 // The current indentation level. | 77 // The current indentation level. |
| 81 int _indentLevel = 0; | 78 int _indentLevel = 0; |
| 82 // A cache of all indentation strings used so far. | 79 // A cache of all indentation strings used so far. |
| 83 List<String> _indentList = <String>[""]; | 80 List<String> _indentList = <String>[""]; |
| 84 | 81 |
| 85 static final identifierCharacterRegExp = new RegExp(r'^[a-zA-Z_0-9$]'); | 82 static final identifierCharacterRegExp = new RegExp(r'^[a-zA-Z_0-9$]'); |
| 86 static final expressionContinuationRegExp = new RegExp(r'^[-+([]'); | 83 static final expressionContinuationRegExp = new RegExp(r'^[-+([]'); |
| 87 | 84 |
| 88 Printer(JavaScriptPrintingOptions options, | 85 Printer(JavaScriptPrintingOptions options, JavaScriptPrintingContext context) |
| 89 JavaScriptPrintingContext context) | |
| 90 : options = options, | 86 : options = options, |
| 91 context = context, | 87 context = context, |
| 92 shouldCompressOutput = options.shouldCompressOutput, | 88 shouldCompressOutput = options.shouldCompressOutput, |
| 93 danglingElseVisitor = new DanglingElseVisitor(context), | 89 danglingElseVisitor = new DanglingElseVisitor(context), |
| 94 localNamer = determineRenamer(options.shouldCompressOutput, | 90 localNamer = determineRenamer( |
| 95 options.minifyLocalVariables); | 91 options.shouldCompressOutput, options.minifyLocalVariables); |
| 96 | 92 |
| 97 static LocalNamer determineRenamer(bool shouldCompressOutput, | 93 static LocalNamer determineRenamer( |
| 98 bool allowVariableMinification) { | 94 bool shouldCompressOutput, bool allowVariableMinification) { |
| 99 return (shouldCompressOutput && allowVariableMinification) | 95 return (shouldCompressOutput && allowVariableMinification) |
| 100 ? new MinifyRenamer() : new IdentityNamer(); | 96 ? new MinifyRenamer() |
| 97 : new IdentityNamer(); |
| 101 } | 98 } |
| 102 | 99 |
| 103 | |
| 104 // The current indentation string. | 100 // The current indentation string. |
| 105 String get indentation { | 101 String get indentation { |
| 106 // Lazily add new indentation strings as required. | 102 // Lazily add new indentation strings as required. |
| 107 while (_indentList.length <= _indentLevel) { | 103 while (_indentList.length <= _indentLevel) { |
| 108 _indentList.add(_indentList.last + " "); | 104 _indentList.add(_indentList.last + " "); |
| 109 } | 105 } |
| 110 return _indentList[_indentLevel]; | 106 return _indentList[_indentLevel]; |
| 111 } | 107 } |
| 112 | 108 |
| 113 void indentMore() { | 109 void indentMore() { |
| (...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 229 currentNode = currentNode.exitNode(context, _charCount); | 225 currentNode = currentNode.exitNode(context, _charCount); |
| 230 } | 226 } |
| 231 | 227 |
| 232 void visit(Node node) { | 228 void visit(Node node) { |
| 233 startNode(node); | 229 startNode(node); |
| 234 node.accept(this); | 230 node.accept(this); |
| 235 endNode(node); | 231 endNode(node); |
| 236 } | 232 } |
| 237 | 233 |
| 238 void visitCommaSeparated(List<Node> nodes, int hasRequiredType, | 234 void visitCommaSeparated(List<Node> nodes, int hasRequiredType, |
| 239 {bool newInForInit, bool newAtStatementBegin}) { | 235 {bool newInForInit, bool newAtStatementBegin}) { |
| 240 for (int i = 0; i < nodes.length; i++) { | 236 for (int i = 0; i < nodes.length; i++) { |
| 241 if (i != 0) { | 237 if (i != 0) { |
| 242 atStatementBegin = false; | 238 atStatementBegin = false; |
| 243 out(","); | 239 out(","); |
| 244 spaceOut(); | 240 spaceOut(); |
| 245 } | 241 } |
| 246 visitNestedExpression(nodes[i], hasRequiredType, | 242 visitNestedExpression(nodes[i], hasRequiredType, |
| 247 newInForInit: newInForInit, | 243 newInForInit: newInForInit, newAtStatementBegin: newAtStatementBegin); |
| 248 newAtStatementBegin: newAtStatementBegin); | |
| 249 } | 244 } |
| 250 } | 245 } |
| 251 | 246 |
| 252 void visitAll(List<Node> nodes) { | 247 void visitAll(List<Node> nodes) { |
| 253 nodes.forEach(visit); | 248 nodes.forEach(visit); |
| 254 } | 249 } |
| 255 | 250 |
| 256 @override | 251 @override |
| 257 void visitProgram(Program program) { | 252 void visitProgram(Program program) { |
| 258 if (program.body.isNotEmpty) { | 253 if (program.body.isNotEmpty) { |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 318 | 313 |
| 319 @override | 314 @override |
| 320 void visitBlock(Block block) { | 315 void visitBlock(Block block) { |
| 321 blockOut(block, shouldIndent: true, needsNewline: true); | 316 blockOut(block, shouldIndent: true, needsNewline: true); |
| 322 } | 317 } |
| 323 | 318 |
| 324 @override | 319 @override |
| 325 void visitExpressionStatement(ExpressionStatement node) { | 320 void visitExpressionStatement(ExpressionStatement node) { |
| 326 indent(); | 321 indent(); |
| 327 visitNestedExpression(node.expression, EXPRESSION, | 322 visitNestedExpression(node.expression, EXPRESSION, |
| 328 newInForInit: false, newAtStatementBegin: true); | 323 newInForInit: false, newAtStatementBegin: true); |
| 329 outSemicolonLn(); | 324 outSemicolonLn(); |
| 330 } | 325 } |
| 331 | 326 |
| 332 @override | 327 @override |
| 333 void visitEmptyStatement(EmptyStatement node) { | 328 void visitEmptyStatement(EmptyStatement node) { |
| 334 outIndentLn(";"); | 329 outIndentLn(";"); |
| 335 } | 330 } |
| 336 | 331 |
| 337 void ifOut(If node, bool shouldIndent) { | 332 void ifOut(If node, bool shouldIndent) { |
| 338 Statement then = unwrapBlockIfSingleStatement(node.then); | 333 Statement then = unwrapBlockIfSingleStatement(node.then); |
| 339 Statement elsePart = node.otherwise; | 334 Statement elsePart = node.otherwise; |
| 340 bool hasElse = node.hasElse; | 335 bool hasElse = node.hasElse; |
| 341 | 336 |
| 342 // Handle dangling elses and a work-around for Android 4.0 stock browser. | 337 // Handle dangling elses and a work-around for Android 4.0 stock browser. |
| 343 // Android 4.0 requires braces for a single do-while in the `then` branch. | 338 // Android 4.0 requires braces for a single do-while in the `then` branch. |
| 344 // See issue 10923. | 339 // See issue 10923. |
| 345 if (hasElse) { | 340 if (hasElse) { |
| 346 bool needsBraces = then.accept(danglingElseVisitor) || then is Do; | 341 bool needsBraces = then.accept(danglingElseVisitor) || then is Do; |
| 347 if (needsBraces) { | 342 if (needsBraces) { |
| 348 then = new Block(<Statement>[then]); | 343 then = new Block(<Statement>[then]); |
| 349 } | 344 } |
| 350 } | 345 } |
| 351 if (shouldIndent) indent(); | 346 if (shouldIndent) indent(); |
| 352 out("if"); | 347 out("if"); |
| 353 spaceOut(); | 348 spaceOut(); |
| 354 out("("); | 349 out("("); |
| 355 visitNestedExpression(node.condition, EXPRESSION, | 350 visitNestedExpression(node.condition, EXPRESSION, |
| 356 newInForInit: false, newAtStatementBegin: false); | 351 newInForInit: false, newAtStatementBegin: false); |
| 357 out(")"); | 352 out(")"); |
| 358 bool thenWasBlock = | 353 bool thenWasBlock = |
| 359 blockBody(then, needsSeparation: false, needsNewline: !hasElse); | 354 blockBody(then, needsSeparation: false, needsNewline: !hasElse); |
| 360 if (hasElse) { | 355 if (hasElse) { |
| 361 if (thenWasBlock) { | 356 if (thenWasBlock) { |
| 362 spaceOut(); | 357 spaceOut(); |
| 363 } else { | 358 } else { |
| 364 indent(); | 359 indent(); |
| 365 } | 360 } |
| 366 out("else"); | 361 out("else"); |
| 367 if (elsePart is If) { | 362 if (elsePart is If) { |
| 368 pendingSpace = true; | 363 pendingSpace = true; |
| 369 startNode(elsePart); | 364 startNode(elsePart); |
| 370 ifOut(elsePart, false); | 365 ifOut(elsePart, false); |
| 371 endNode(elsePart); | 366 endNode(elsePart); |
| 372 } else { | 367 } else { |
| 373 blockBody(unwrapBlockIfSingleStatement(elsePart), | 368 blockBody(unwrapBlockIfSingleStatement(elsePart), |
| 374 needsSeparation: true, needsNewline: true); | 369 needsSeparation: true, needsNewline: true); |
| 375 } | 370 } |
| 376 } | 371 } |
| 377 } | 372 } |
| 378 | 373 |
| 379 @override | 374 @override |
| 380 void visitIf(If node) { | 375 void visitIf(If node) { |
| 381 ifOut(node, true); | 376 ifOut(node, true); |
| 382 } | 377 } |
| 383 | 378 |
| 384 @override | 379 @override |
| 385 void visitFor(For loop) { | 380 void visitFor(For loop) { |
| 386 outIndent("for"); | 381 outIndent("for"); |
| 387 spaceOut(); | 382 spaceOut(); |
| 388 out("("); | 383 out("("); |
| 389 if (loop.init != null) { | 384 if (loop.init != null) { |
| 390 visitNestedExpression(loop.init, EXPRESSION, | 385 visitNestedExpression(loop.init, EXPRESSION, |
| 391 newInForInit: true, newAtStatementBegin: false); | 386 newInForInit: true, newAtStatementBegin: false); |
| 392 } | 387 } |
| 393 out(";"); | 388 out(";"); |
| 394 if (loop.condition != null) { | 389 if (loop.condition != null) { |
| 395 spaceOut(); | 390 spaceOut(); |
| 396 visitNestedExpression(loop.condition, EXPRESSION, | 391 visitNestedExpression(loop.condition, EXPRESSION, |
| 397 newInForInit: false, newAtStatementBegin: false); | 392 newInForInit: false, newAtStatementBegin: false); |
| 398 } | 393 } |
| 399 out(";"); | 394 out(";"); |
| 400 if (loop.update != null) { | 395 if (loop.update != null) { |
| 401 spaceOut(); | 396 spaceOut(); |
| 402 visitNestedExpression(loop.update, EXPRESSION, | 397 visitNestedExpression(loop.update, EXPRESSION, |
| 403 newInForInit: false, newAtStatementBegin: false); | 398 newInForInit: false, newAtStatementBegin: false); |
| 404 } | 399 } |
| 405 out(")"); | 400 out(")"); |
| 406 blockBody(unwrapBlockIfSingleStatement(loop.body), | 401 blockBody(unwrapBlockIfSingleStatement(loop.body), |
| 407 needsSeparation: false, needsNewline: true); | 402 needsSeparation: false, needsNewline: true); |
| 408 } | 403 } |
| 409 | 404 |
| 410 @override | 405 @override |
| 411 void visitForIn(ForIn loop) { | 406 void visitForIn(ForIn loop) { |
| 412 outIndent("for"); | 407 outIndent("for"); |
| 413 spaceOut(); | 408 spaceOut(); |
| 414 out("("); | 409 out("("); |
| 415 visitNestedExpression(loop.leftHandSide, EXPRESSION, | 410 visitNestedExpression(loop.leftHandSide, EXPRESSION, |
| 416 newInForInit: true, newAtStatementBegin: false); | 411 newInForInit: true, newAtStatementBegin: false); |
| 417 out(" in"); | 412 out(" in"); |
| 418 pendingSpace = true; | 413 pendingSpace = true; |
| 419 visitNestedExpression(loop.object, EXPRESSION, | 414 visitNestedExpression(loop.object, EXPRESSION, |
| 420 newInForInit: false, newAtStatementBegin: false); | 415 newInForInit: false, newAtStatementBegin: false); |
| 421 out(")"); | 416 out(")"); |
| 422 blockBody(unwrapBlockIfSingleStatement(loop.body), | 417 blockBody(unwrapBlockIfSingleStatement(loop.body), |
| 423 needsSeparation: false, needsNewline: true); | 418 needsSeparation: false, needsNewline: true); |
| 424 } | 419 } |
| 425 | 420 |
| 426 @override | 421 @override |
| 427 void visitWhile(While loop) { | 422 void visitWhile(While loop) { |
| 428 outIndent("while"); | 423 outIndent("while"); |
| 429 spaceOut(); | 424 spaceOut(); |
| 430 out("("); | 425 out("("); |
| 431 visitNestedExpression(loop.condition, EXPRESSION, | 426 visitNestedExpression(loop.condition, EXPRESSION, |
| 432 newInForInit: false, newAtStatementBegin: false); | 427 newInForInit: false, newAtStatementBegin: false); |
| 433 out(")"); | 428 out(")"); |
| 434 blockBody(unwrapBlockIfSingleStatement(loop.body), | 429 blockBody(unwrapBlockIfSingleStatement(loop.body), |
| 435 needsSeparation: false, needsNewline: true); | 430 needsSeparation: false, needsNewline: true); |
| 436 } | 431 } |
| 437 | 432 |
| 438 @override | 433 @override |
| 439 void visitDo(Do loop) { | 434 void visitDo(Do loop) { |
| 440 outIndent("do"); | 435 outIndent("do"); |
| 441 if (blockBody(unwrapBlockIfSingleStatement(loop.body), | 436 if (blockBody(unwrapBlockIfSingleStatement(loop.body), |
| 442 needsSeparation: true, needsNewline: false)) { | 437 needsSeparation: true, needsNewline: false)) { |
| 443 spaceOut(); | 438 spaceOut(); |
| 444 } else { | 439 } else { |
| 445 indent(); | 440 indent(); |
| 446 } | 441 } |
| 447 out("while"); | 442 out("while"); |
| 448 spaceOut(); | 443 spaceOut(); |
| 449 out("("); | 444 out("("); |
| 450 visitNestedExpression(loop.condition, EXPRESSION, | 445 visitNestedExpression(loop.condition, EXPRESSION, |
| 451 newInForInit: false, newAtStatementBegin: false); | 446 newInForInit: false, newAtStatementBegin: false); |
| 452 out(")"); | 447 out(")"); |
| 453 outSemicolonLn(); | 448 outSemicolonLn(); |
| 454 } | 449 } |
| 455 | 450 |
| 456 @override | 451 @override |
| 457 void visitContinue(Continue node) { | 452 void visitContinue(Continue node) { |
| 458 if (node.targetLabel == null) { | 453 if (node.targetLabel == null) { |
| 459 outIndent("continue"); | 454 outIndent("continue"); |
| 460 } else { | 455 } else { |
| 461 outIndent("continue ${node.targetLabel}"); | 456 outIndent("continue ${node.targetLabel}"); |
| (...skipping 12 matching lines...) Expand all Loading... |
| 474 } | 469 } |
| 475 | 470 |
| 476 @override | 471 @override |
| 477 void visitReturn(Return node) { | 472 void visitReturn(Return node) { |
| 478 if (node.value == null) { | 473 if (node.value == null) { |
| 479 outIndent("return"); | 474 outIndent("return"); |
| 480 } else { | 475 } else { |
| 481 outIndent("return"); | 476 outIndent("return"); |
| 482 pendingSpace = true; | 477 pendingSpace = true; |
| 483 visitNestedExpression(node.value, EXPRESSION, | 478 visitNestedExpression(node.value, EXPRESSION, |
| 484 newInForInit: false, newAtStatementBegin: false); | 479 newInForInit: false, newAtStatementBegin: false); |
| 485 } | 480 } |
| 486 outSemicolonLn(); | 481 outSemicolonLn(); |
| 487 } | 482 } |
| 488 | 483 |
| 489 @override | 484 @override |
| 490 void visitDartYield(DartYield node) { | 485 void visitDartYield(DartYield node) { |
| 491 if (node.hasStar) { | 486 if (node.hasStar) { |
| 492 outIndent("yield*"); | 487 outIndent("yield*"); |
| 493 } else { | 488 } else { |
| 494 outIndent("yield"); | 489 outIndent("yield"); |
| 495 } | 490 } |
| 496 pendingSpace = true; | 491 pendingSpace = true; |
| 497 visitNestedExpression(node.expression, EXPRESSION, | 492 visitNestedExpression(node.expression, EXPRESSION, |
| 498 newInForInit: false, newAtStatementBegin: false); | 493 newInForInit: false, newAtStatementBegin: false); |
| 499 outSemicolonLn(); | 494 outSemicolonLn(); |
| 500 } | 495 } |
| 501 | 496 |
| 502 @override | 497 @override |
| 503 void visitThrow(Throw node) { | 498 void visitThrow(Throw node) { |
| 504 outIndent("throw"); | 499 outIndent("throw"); |
| 505 pendingSpace = true; | 500 pendingSpace = true; |
| 506 visitNestedExpression(node.expression, EXPRESSION, | 501 visitNestedExpression(node.expression, EXPRESSION, |
| 507 newInForInit: false, newAtStatementBegin: false); | 502 newInForInit: false, newAtStatementBegin: false); |
| 508 outSemicolonLn(); | 503 outSemicolonLn(); |
| 509 } | 504 } |
| 510 | 505 |
| 511 @override | 506 @override |
| 512 void visitTry(Try node) { | 507 void visitTry(Try node) { |
| 513 outIndent("try"); | 508 outIndent("try"); |
| 514 blockBody(node.body, needsSeparation: true, needsNewline: false); | 509 blockBody(node.body, needsSeparation: true, needsNewline: false); |
| 515 if (node.catchPart != null) { | 510 if (node.catchPart != null) { |
| 516 visit(node.catchPart); | 511 visit(node.catchPart); |
| 517 } | 512 } |
| 518 if (node.finallyPart != null) { | 513 if (node.finallyPart != null) { |
| 519 spaceOut(); | 514 spaceOut(); |
| 520 out("finally"); | 515 out("finally"); |
| 521 blockBody(node.finallyPart, needsSeparation: true, needsNewline: true); | 516 blockBody(node.finallyPart, needsSeparation: true, needsNewline: true); |
| 522 } else { | 517 } else { |
| 523 lineOut(); | 518 lineOut(); |
| 524 } | 519 } |
| 525 } | 520 } |
| 526 | 521 |
| 527 @override | 522 @override |
| 528 void visitCatch(Catch node) { | 523 void visitCatch(Catch node) { |
| 529 spaceOut(); | 524 spaceOut(); |
| 530 out("catch"); | 525 out("catch"); |
| 531 spaceOut(); | 526 spaceOut(); |
| 532 out("("); | 527 out("("); |
| 533 visitNestedExpression(node.declaration, EXPRESSION, | 528 visitNestedExpression(node.declaration, EXPRESSION, |
| 534 newInForInit: false, newAtStatementBegin: false); | 529 newInForInit: false, newAtStatementBegin: false); |
| 535 out(")"); | 530 out(")"); |
| 536 blockBody(node.body, needsSeparation: false, needsNewline: false); | 531 blockBody(node.body, needsSeparation: false, needsNewline: false); |
| 537 } | 532 } |
| 538 | 533 |
| 539 @override | 534 @override |
| 540 void visitSwitch(Switch node) { | 535 void visitSwitch(Switch node) { |
| 541 outIndent("switch"); | 536 outIndent("switch"); |
| 542 spaceOut(); | 537 spaceOut(); |
| 543 out("("); | 538 out("("); |
| 544 visitNestedExpression(node.key, EXPRESSION, | 539 visitNestedExpression(node.key, EXPRESSION, |
| 545 newInForInit: false, newAtStatementBegin: false); | 540 newInForInit: false, newAtStatementBegin: false); |
| 546 out(")"); | 541 out(")"); |
| 547 spaceOut(); | 542 spaceOut(); |
| 548 outLn("{"); | 543 outLn("{"); |
| 549 indentMore(); | 544 indentMore(); |
| 550 visitAll(node.cases); | 545 visitAll(node.cases); |
| 551 indentLess(); | 546 indentLess(); |
| 552 outIndentLn("}"); | 547 outIndentLn("}"); |
| 553 } | 548 } |
| 554 | 549 |
| 555 @override | 550 @override |
| 556 void visitCase(Case node) { | 551 void visitCase(Case node) { |
| 557 outIndent("case"); | 552 outIndent("case"); |
| 558 pendingSpace = true; | 553 pendingSpace = true; |
| 559 visitNestedExpression(node.expression, EXPRESSION, | 554 visitNestedExpression(node.expression, EXPRESSION, |
| 560 newInForInit: false, newAtStatementBegin: false); | 555 newInForInit: false, newAtStatementBegin: false); |
| 561 outLn(":"); | 556 outLn(":"); |
| 562 if (!node.body.statements.isEmpty) { | 557 if (!node.body.statements.isEmpty) { |
| 563 indentMore(); | 558 indentMore(); |
| 564 blockOutWithoutBraces(node.body); | 559 blockOutWithoutBraces(node.body); |
| 565 indentLess(); | 560 indentLess(); |
| 566 } | 561 } |
| 567 } | 562 } |
| 568 | 563 |
| 569 @override | 564 @override |
| 570 void visitDefault(Default node) { | 565 void visitDefault(Default node) { |
| (...skipping 20 matching lines...) Expand all Loading... |
| 591 outIndent("${node.label}:"); | 586 outIndent("${node.label}:"); |
| 592 blockBody(body, needsSeparation: false, needsNewline: true); | 587 blockBody(body, needsSeparation: false, needsNewline: true); |
| 593 } | 588 } |
| 594 | 589 |
| 595 int functionOut(Fun fun, Node name, VarCollector vars) { | 590 int functionOut(Fun fun, Node name, VarCollector vars) { |
| 596 out("function"); | 591 out("function"); |
| 597 if (name != null) { | 592 if (name != null) { |
| 598 out(" "); | 593 out(" "); |
| 599 // Name must be a [Decl]. Therefore only test for primary expressions. | 594 // Name must be a [Decl]. Therefore only test for primary expressions. |
| 600 visitNestedExpression(name, PRIMARY, | 595 visitNestedExpression(name, PRIMARY, |
| 601 newInForInit: false, newAtStatementBegin: false); | 596 newInForInit: false, newAtStatementBegin: false); |
| 602 } | 597 } |
| 603 localNamer.enterScope(vars); | 598 localNamer.enterScope(vars); |
| 604 out("("); | 599 out("("); |
| 605 if (fun.params != null) { | 600 if (fun.params != null) { |
| 606 visitCommaSeparated(fun.params, PRIMARY, | 601 visitCommaSeparated(fun.params, PRIMARY, |
| 607 newInForInit: false, newAtStatementBegin: false); | 602 newInForInit: false, newAtStatementBegin: false); |
| 608 } | 603 } |
| 609 out(")"); | 604 out(")"); |
| 610 switch (fun.asyncModifier) { | 605 switch (fun.asyncModifier) { |
| 611 case const AsyncModifier.sync(): | 606 case const AsyncModifier.sync(): |
| 612 break; | 607 break; |
| 613 case const AsyncModifier.async(): | 608 case const AsyncModifier.async(): |
| 614 out(' ', isWhitespace: true); | 609 out(' ', isWhitespace: true); |
| 615 out('async'); | 610 out('async'); |
| 616 break; | 611 break; |
| 617 case const AsyncModifier.syncStar(): | 612 case const AsyncModifier.syncStar(): |
| 618 out(' ', isWhitespace: true); | 613 out(' ', isWhitespace: true); |
| 619 out('sync*'); | 614 out('sync*'); |
| 620 break; | 615 break; |
| 621 case const AsyncModifier.asyncStar(): | 616 case const AsyncModifier.asyncStar(): |
| 622 out(' ', isWhitespace: true); | 617 out(' ', isWhitespace: true); |
| 623 out('async*'); | 618 out('async*'); |
| 624 break; | 619 break; |
| 625 } | 620 } |
| 626 spaceOut(); | 621 spaceOut(); |
| 627 int closingPosition = | 622 int closingPosition = |
| 628 blockOut(fun.body, shouldIndent: false, needsNewline: false); | 623 blockOut(fun.body, shouldIndent: false, needsNewline: false); |
| 629 localNamer.leaveScope(); | 624 localNamer.leaveScope(); |
| 630 return closingPosition; | 625 return closingPosition; |
| 631 | |
| 632 } | 626 } |
| 633 | 627 |
| 634 @override | 628 @override |
| 635 visitFunctionDeclaration(FunctionDeclaration declaration) { | 629 visitFunctionDeclaration(FunctionDeclaration declaration) { |
| 636 VarCollector vars = new VarCollector(); | 630 VarCollector vars = new VarCollector(); |
| 637 vars.visitFunctionDeclaration(declaration); | 631 vars.visitFunctionDeclaration(declaration); |
| 638 indent(); | 632 indent(); |
| 639 startNode(declaration.function); | 633 startNode(declaration.function); |
| 640 currentNode.closingPosition = | 634 currentNode.closingPosition = |
| 641 functionOut(declaration.function, declaration.name, vars); | 635 functionOut(declaration.function, declaration.name, vars); |
| 642 endNode(declaration.function); | 636 endNode(declaration.function); |
| 643 lineOut(); | 637 lineOut(); |
| 644 } | 638 } |
| 645 | 639 |
| 646 visitNestedExpression(Expression node, int requiredPrecedence, | 640 visitNestedExpression(Expression node, int requiredPrecedence, |
| 647 {bool newInForInit, bool newAtStatementBegin}) { | 641 {bool newInForInit, bool newAtStatementBegin}) { |
| 648 bool needsParentheses = | 642 bool needsParentheses = |
| 649 // a - (b + c). | 643 // a - (b + c). |
| 650 (requiredPrecedence != EXPRESSION && | 644 (requiredPrecedence != EXPRESSION && |
| 651 node.precedenceLevel < requiredPrecedence) || | 645 node.precedenceLevel < requiredPrecedence) || |
| 652 // for (a = (x in o); ... ; ... ) { ... } | 646 // for (a = (x in o); ... ; ... ) { ... } |
| 653 (newInForInit && node is Binary && node.op == "in") || | 647 (newInForInit && node is Binary && node.op == "in") || |
| 654 // (function() { ... })(). | 648 // (function() { ... })(). |
| 655 // ({a: 2, b: 3}.toString()). | 649 // ({a: 2, b: 3}.toString()). |
| 656 (newAtStatementBegin && (node is NamedFunction || | 650 (newAtStatementBegin && |
| 657 node is Fun || | 651 (node is NamedFunction || |
| 658 node is ObjectInitializer)); | 652 node is Fun || |
| 653 node is ObjectInitializer)); |
| 659 if (needsParentheses) { | 654 if (needsParentheses) { |
| 660 inForInit = false; | 655 inForInit = false; |
| 661 atStatementBegin = false; | 656 atStatementBegin = false; |
| 662 out("("); | 657 out("("); |
| 663 visit(node); | 658 visit(node); |
| 664 out(")"); | 659 out(")"); |
| 665 } else { | 660 } else { |
| 666 inForInit = newInForInit; | 661 inForInit = newInForInit; |
| 667 atStatementBegin = newAtStatementBegin; | 662 atStatementBegin = newAtStatementBegin; |
| 668 visit(node); | 663 visit(node); |
| 669 } | 664 } |
| 670 } | 665 } |
| 671 | 666 |
| 672 @override | 667 @override |
| 673 visitVariableDeclarationList(VariableDeclarationList list) { | 668 visitVariableDeclarationList(VariableDeclarationList list) { |
| 674 out("var "); | 669 out("var "); |
| 675 visitCommaSeparated(list.declarations, ASSIGNMENT, | 670 visitCommaSeparated(list.declarations, ASSIGNMENT, |
| 676 newInForInit: inForInit, newAtStatementBegin: false); | 671 newInForInit: inForInit, newAtStatementBegin: false); |
| 677 } | 672 } |
| 678 | 673 |
| 679 @override | 674 @override |
| 680 visitAssignment(Assignment assignment) { | 675 visitAssignment(Assignment assignment) { |
| 681 visitNestedExpression(assignment.leftHandSide, CALL, | 676 visitNestedExpression(assignment.leftHandSide, CALL, |
| 682 newInForInit: inForInit, | 677 newInForInit: inForInit, newAtStatementBegin: atStatementBegin); |
| 683 newAtStatementBegin: atStatementBegin); | |
| 684 if (assignment.value != null) { | 678 if (assignment.value != null) { |
| 685 spaceOut(); | 679 spaceOut(); |
| 686 String op = assignment.op; | 680 String op = assignment.op; |
| 687 if (op != null) out(op); | 681 if (op != null) out(op); |
| 688 out("="); | 682 out("="); |
| 689 spaceOut(); | 683 spaceOut(); |
| 690 visitNestedExpression(assignment.value, ASSIGNMENT, | 684 visitNestedExpression(assignment.value, ASSIGNMENT, |
| 691 newInForInit: inForInit, | 685 newInForInit: inForInit, newAtStatementBegin: false); |
| 692 newAtStatementBegin: false); | |
| 693 } | 686 } |
| 694 } | 687 } |
| 695 | 688 |
| 696 @override | 689 @override |
| 697 visitVariableInitialization(VariableInitialization initialization) { | 690 visitVariableInitialization(VariableInitialization initialization) { |
| 698 visitAssignment(initialization); | 691 visitAssignment(initialization); |
| 699 } | 692 } |
| 700 | 693 |
| 701 @override | 694 @override |
| 702 visitConditional(Conditional cond) { | 695 visitConditional(Conditional cond) { |
| 703 visitNestedExpression(cond.condition, LOGICAL_OR, | 696 visitNestedExpression(cond.condition, LOGICAL_OR, |
| 704 newInForInit: inForInit, | 697 newInForInit: inForInit, newAtStatementBegin: atStatementBegin); |
| 705 newAtStatementBegin: atStatementBegin); | |
| 706 spaceOut(); | 698 spaceOut(); |
| 707 out("?"); | 699 out("?"); |
| 708 spaceOut(); | 700 spaceOut(); |
| 709 // The then part is allowed to have an 'in'. | 701 // The then part is allowed to have an 'in'. |
| 710 visitNestedExpression(cond.then, ASSIGNMENT, | 702 visitNestedExpression(cond.then, ASSIGNMENT, |
| 711 newInForInit: false, newAtStatementBegin: false); | 703 newInForInit: false, newAtStatementBegin: false); |
| 712 spaceOut(); | 704 spaceOut(); |
| 713 out(":"); | 705 out(":"); |
| 714 spaceOut(); | 706 spaceOut(); |
| 715 visitNestedExpression(cond.otherwise, ASSIGNMENT, | 707 visitNestedExpression(cond.otherwise, ASSIGNMENT, |
| 716 newInForInit: inForInit, newAtStatementBegin: false); | 708 newInForInit: inForInit, newAtStatementBegin: false); |
| 717 } | 709 } |
| 718 | 710 |
| 719 @override | 711 @override |
| 720 visitNew(New node) { | 712 visitNew(New node) { |
| 721 out("new "); | 713 out("new "); |
| 722 visitNestedExpression(node.target, LEFT_HAND_SIDE, | 714 visitNestedExpression(node.target, LEFT_HAND_SIDE, |
| 723 newInForInit: inForInit, newAtStatementBegin: false); | 715 newInForInit: inForInit, newAtStatementBegin: false); |
| 724 out("("); | 716 out("("); |
| 725 visitCommaSeparated(node.arguments, ASSIGNMENT, | 717 visitCommaSeparated(node.arguments, ASSIGNMENT, |
| 726 newInForInit: false, newAtStatementBegin: false); | 718 newInForInit: false, newAtStatementBegin: false); |
| 727 out(")"); | 719 out(")"); |
| 728 } | 720 } |
| 729 | 721 |
| 730 @override | 722 @override |
| 731 visitCall(Call call) { | 723 visitCall(Call call) { |
| 732 visitNestedExpression(call.target, CALL, | 724 visitNestedExpression(call.target, CALL, |
| 733 newInForInit: inForInit, | 725 newInForInit: inForInit, newAtStatementBegin: atStatementBegin); |
| 734 newAtStatementBegin: atStatementBegin); | |
| 735 out("("); | 726 out("("); |
| 736 visitCommaSeparated(call.arguments, ASSIGNMENT, | 727 visitCommaSeparated(call.arguments, ASSIGNMENT, |
| 737 newInForInit: false, newAtStatementBegin: false); | 728 newInForInit: false, newAtStatementBegin: false); |
| 738 out(")"); | 729 out(")"); |
| 739 } | 730 } |
| 740 | 731 |
| 741 @override | 732 @override |
| 742 void visitBinary(Binary binary) { | 733 void visitBinary(Binary binary) { |
| 743 Expression left = binary.left; | 734 Expression left = binary.left; |
| 744 Expression right = binary.right; | 735 Expression right = binary.right; |
| 745 String op = binary.op; | 736 String op = binary.op; |
| 746 int leftPrecedenceRequirement; | 737 int leftPrecedenceRequirement; |
| 747 int rightPrecedenceRequirement; | 738 int rightPrecedenceRequirement; |
| 748 bool leftSpace = true; // left<HERE>op right | 739 bool leftSpace = true; // left<HERE>op right |
| 749 switch (op) { | 740 switch (op) { |
| 750 case ',': | 741 case ',': |
| 751 // x, (y, z) <=> (x, y), z. | 742 // x, (y, z) <=> (x, y), z. |
| 752 leftPrecedenceRequirement = EXPRESSION; | 743 leftPrecedenceRequirement = EXPRESSION; |
| 753 rightPrecedenceRequirement = EXPRESSION; | 744 rightPrecedenceRequirement = EXPRESSION; |
| 754 leftSpace = false; | 745 leftSpace = false; |
| 755 break; | 746 break; |
| 756 case "||": | 747 case "||": |
| 757 leftPrecedenceRequirement = LOGICAL_OR; | 748 leftPrecedenceRequirement = LOGICAL_OR; |
| 758 // x || (y || z) <=> (x || y) || z. | 749 // x || (y || z) <=> (x || y) || z. |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 815 case "%": | 806 case "%": |
| 816 leftPrecedenceRequirement = MULTIPLICATIVE; | 807 leftPrecedenceRequirement = MULTIPLICATIVE; |
| 817 // We cannot remove parenthesis for "*" because of precision issues. | 808 // We cannot remove parenthesis for "*" because of precision issues. |
| 818 rightPrecedenceRequirement = UNARY; | 809 rightPrecedenceRequirement = UNARY; |
| 819 break; | 810 break; |
| 820 default: | 811 default: |
| 821 context.error("Forgot operator: $op"); | 812 context.error("Forgot operator: $op"); |
| 822 } | 813 } |
| 823 | 814 |
| 824 visitNestedExpression(left, leftPrecedenceRequirement, | 815 visitNestedExpression(left, leftPrecedenceRequirement, |
| 825 newInForInit: inForInit, | 816 newInForInit: inForInit, newAtStatementBegin: atStatementBegin); |
| 826 newAtStatementBegin: atStatementBegin); | |
| 827 | 817 |
| 828 if (op == "in" || op == "instanceof") { | 818 if (op == "in" || op == "instanceof") { |
| 829 // There are cases where the space is not required but without further | 819 // There are cases where the space is not required but without further |
| 830 // analysis we cannot know. | 820 // analysis we cannot know. |
| 831 out(" ", isWhitespace: true); | 821 out(" ", isWhitespace: true); |
| 832 out(op); | 822 out(op); |
| 833 out(" ", isWhitespace: true); | 823 out(" ", isWhitespace: true); |
| 834 } else { | 824 } else { |
| 835 if (leftSpace) spaceOut(); | 825 if (leftSpace) spaceOut(); |
| 836 out(op); | 826 out(op); |
| 837 spaceOut(); | 827 spaceOut(); |
| 838 } | 828 } |
| 839 visitNestedExpression(right, rightPrecedenceRequirement, | 829 visitNestedExpression(right, rightPrecedenceRequirement, |
| 840 newInForInit: inForInit, | 830 newInForInit: inForInit, newAtStatementBegin: false); |
| 841 newAtStatementBegin: false); | |
| 842 } | 831 } |
| 843 | 832 |
| 844 @override | 833 @override |
| 845 void visitPrefix(Prefix unary) { | 834 void visitPrefix(Prefix unary) { |
| 846 String op = unary.op; | 835 String op = unary.op; |
| 847 switch (op) { | 836 switch (op) { |
| 848 case "delete": | 837 case "delete": |
| 849 case "void": | 838 case "void": |
| 850 case "typeof": | 839 case "typeof": |
| 851 // There are cases where the space is not required but without further | 840 // There are cases where the space is not required but without further |
| 852 // analysis we cannot know. | 841 // analysis we cannot know. |
| 853 out(op); | 842 out(op); |
| 854 out(" ", isWhitespace: true); | 843 out(" ", isWhitespace: true); |
| 855 break; | 844 break; |
| 856 case "+": | 845 case "+": |
| 857 case "++": | 846 case "++": |
| 858 if (lastCharCode == charCodes.$PLUS) out(" ", isWhitespace: true); | 847 if (lastCharCode == charCodes.$PLUS) out(" ", isWhitespace: true); |
| 859 out(op); | 848 out(op); |
| 860 break; | 849 break; |
| 861 case "-": | 850 case "-": |
| 862 case "--": | 851 case "--": |
| 863 if (lastCharCode == charCodes.$MINUS) out(" ", isWhitespace: true); | 852 if (lastCharCode == charCodes.$MINUS) out(" ", isWhitespace: true); |
| 864 out(op); | 853 out(op); |
| 865 break; | 854 break; |
| 866 default: | 855 default: |
| 867 out(op); | 856 out(op); |
| 868 } | 857 } |
| 869 visitNestedExpression(unary.argument, UNARY, | 858 visitNestedExpression(unary.argument, UNARY, |
| 870 newInForInit: inForInit, newAtStatementBegin: false); | 859 newInForInit: inForInit, newAtStatementBegin: false); |
| 871 } | 860 } |
| 872 | 861 |
| 873 @override | 862 @override |
| 874 void visitPostfix(Postfix postfix) { | 863 void visitPostfix(Postfix postfix) { |
| 875 visitNestedExpression(postfix.argument, CALL, | 864 visitNestedExpression(postfix.argument, CALL, |
| 876 newInForInit: inForInit, | 865 newInForInit: inForInit, newAtStatementBegin: atStatementBegin); |
| 877 newAtStatementBegin: atStatementBegin); | |
| 878 out(postfix.op); | 866 out(postfix.op); |
| 879 } | 867 } |
| 880 | 868 |
| 881 @override | 869 @override |
| 882 void visitVariableUse(VariableUse ref) { | 870 void visitVariableUse(VariableUse ref) { |
| 883 out(localNamer.getName(ref.name)); | 871 out(localNamer.getName(ref.name)); |
| 884 } | 872 } |
| 885 | 873 |
| 886 @override | 874 @override |
| 887 void visitThis(This node) { | 875 void visitThis(This node) { |
| (...skipping 14 matching lines...) Expand all Loading... |
| 902 return charCodes.$0 <= charCode && charCode <= charCodes.$9; | 890 return charCodes.$0 <= charCode && charCode <= charCodes.$9; |
| 903 } | 891 } |
| 904 | 892 |
| 905 bool isValidJavaScriptId(String field) { | 893 bool isValidJavaScriptId(String field) { |
| 906 if (field.length < 3) return false; | 894 if (field.length < 3) return false; |
| 907 // Ignore the leading and trailing string-delimiter. | 895 // Ignore the leading and trailing string-delimiter. |
| 908 for (int i = 1; i < field.length - 1; i++) { | 896 for (int i = 1; i < field.length - 1; i++) { |
| 909 // TODO(floitsch): allow more characters. | 897 // TODO(floitsch): allow more characters. |
| 910 int charCode = field.codeUnitAt(i); | 898 int charCode = field.codeUnitAt(i); |
| 911 if (!(charCodes.$a <= charCode && charCode <= charCodes.$z || | 899 if (!(charCodes.$a <= charCode && charCode <= charCodes.$z || |
| 912 charCodes.$A <= charCode && charCode <= charCodes.$Z || | 900 charCodes.$A <= charCode && charCode <= charCodes.$Z || |
| 913 charCode == charCodes.$$ || | 901 charCode == charCodes.$$ || |
| 914 charCode == charCodes.$_ || | 902 charCode == charCodes.$_ || |
| 915 i != 1 && isDigit(charCode))) { | 903 i != 1 && isDigit(charCode))) { |
| 916 return false; | 904 return false; |
| 917 } | 905 } |
| 918 } | 906 } |
| 919 // TODO(floitsch): normally we should also check that the field is not a | 907 // TODO(floitsch): normally we should also check that the field is not a |
| 920 // reserved word. We don't generate fields with reserved word names except | 908 // reserved word. We don't generate fields with reserved word names except |
| 921 // for 'super'. | 909 // for 'super'. |
| 922 if (field == '"super"') return false; | 910 if (field == '"super"') return false; |
| 923 if (field == '"catch"') return false; | 911 if (field == '"catch"') return false; |
| 924 return true; | 912 return true; |
| 925 } | 913 } |
| 926 | 914 |
| 927 @override | 915 @override |
| 928 void visitAccess(PropertyAccess access) { | 916 void visitAccess(PropertyAccess access) { |
| 929 visitNestedExpression(access.receiver, CALL, | 917 visitNestedExpression(access.receiver, CALL, |
| 930 newInForInit: inForInit, | 918 newInForInit: inForInit, newAtStatementBegin: atStatementBegin); |
| 931 newAtStatementBegin: atStatementBegin); | |
| 932 Node selector = access.selector; | 919 Node selector = access.selector; |
| 933 if (selector is LiteralString) { | 920 if (selector is LiteralString) { |
| 934 LiteralString selectorString = selector; | 921 LiteralString selectorString = selector; |
| 935 String fieldWithQuotes = selectorString.value; | 922 String fieldWithQuotes = selectorString.value; |
| 936 if (isValidJavaScriptId(fieldWithQuotes)) { | 923 if (isValidJavaScriptId(fieldWithQuotes)) { |
| 937 if (access.receiver is LiteralNumber && | 924 if (access.receiver is LiteralNumber && |
| 938 lastCharCode != charCodes.$CLOSE_PAREN) { | 925 lastCharCode != charCodes.$CLOSE_PAREN) { |
| 939 out(" ", isWhitespace: true); | 926 out(" ", isWhitespace: true); |
| 940 } | 927 } |
| 941 out("."); | 928 out("."); |
| 942 startNode(selector); | 929 startNode(selector); |
| 943 out(fieldWithQuotes.substring(1, fieldWithQuotes.length - 1)); | 930 out(fieldWithQuotes.substring(1, fieldWithQuotes.length - 1)); |
| 944 endNode(selector); | 931 endNode(selector); |
| 945 return; | 932 return; |
| 946 } | 933 } |
| 947 } else if (selector is Name) { | 934 } else if (selector is Name) { |
| 948 if (access.receiver is LiteralNumber && | 935 if (access.receiver is LiteralNumber && |
| 949 lastCharCode != charCodes.$CLOSE_PAREN) { | 936 lastCharCode != charCodes.$CLOSE_PAREN) { |
| 950 out(" ", isWhitespace: true); | 937 out(" ", isWhitespace: true); |
| 951 } | 938 } |
| 952 out("."); | 939 out("."); |
| 953 startNode(selector); | 940 startNode(selector); |
| 954 selector.accept(this); | 941 selector.accept(this); |
| 955 endNode(selector); | 942 endNode(selector); |
| 956 return; | 943 return; |
| 957 } | 944 } |
| 958 out("["); | 945 out("["); |
| 959 visitNestedExpression(selector, EXPRESSION, | 946 visitNestedExpression(selector, EXPRESSION, |
| 960 newInForInit: false, newAtStatementBegin: false); | 947 newInForInit: false, newAtStatementBegin: false); |
| 961 out("]"); | 948 out("]"); |
| 962 } | 949 } |
| 963 | 950 |
| 964 @override | 951 @override |
| 965 void visitNamedFunction(NamedFunction namedFunction) { | 952 void visitNamedFunction(NamedFunction namedFunction) { |
| 966 VarCollector vars = new VarCollector(); | 953 VarCollector vars = new VarCollector(); |
| 967 vars.visitNamedFunction(namedFunction); | 954 vars.visitNamedFunction(namedFunction); |
| 968 startNode(namedFunction.function); | 955 startNode(namedFunction.function); |
| 969 currentNode.closingPosition = | 956 currentNode.closingPosition = |
| 970 functionOut(namedFunction.function, namedFunction.name, vars); | 957 functionOut(namedFunction.function, namedFunction.name, vars); |
| (...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1044 // in last position. Otherwise `[,]` (having length 1) would become | 1031 // in last position. Otherwise `[,]` (having length 1) would become |
| 1045 // equal to `[]` (the empty array) | 1032 // equal to `[]` (the empty array) |
| 1046 // and [1,,] (array with 1 and a hole) would become [1,] = [1]. | 1033 // and [1,,] (array with 1 and a hole) would become [1,] = [1]. |
| 1047 startNode(element); | 1034 startNode(element); |
| 1048 out(","); | 1035 out(","); |
| 1049 endNode(element); | 1036 endNode(element); |
| 1050 continue; | 1037 continue; |
| 1051 } | 1038 } |
| 1052 if (i != 0) spaceOut(); | 1039 if (i != 0) spaceOut(); |
| 1053 visitNestedExpression(element, ASSIGNMENT, | 1040 visitNestedExpression(element, ASSIGNMENT, |
| 1054 newInForInit: false, newAtStatementBegin: false); | 1041 newInForInit: false, newAtStatementBegin: false); |
| 1055 // We can skip the trailing "," for the last element (since it's not | 1042 // We can skip the trailing "," for the last element (since it's not |
| 1056 // an array hole). | 1043 // an array hole). |
| 1057 if (i != elements.length - 1) out(","); | 1044 if (i != elements.length - 1) out(","); |
| 1058 } | 1045 } |
| 1059 out("]"); | 1046 out("]"); |
| 1060 } | 1047 } |
| 1061 | 1048 |
| 1062 @override | 1049 @override |
| 1063 void visitArrayHole(ArrayHole node) { | 1050 void visitArrayHole(ArrayHole node) { |
| 1064 context.error("Unreachable"); | 1051 context.error("Unreachable"); |
| 1065 } | 1052 } |
| 1066 | 1053 |
| 1067 @override | 1054 @override |
| 1068 void visitObjectInitializer(ObjectInitializer node) { | 1055 void visitObjectInitializer(ObjectInitializer node) { |
| 1069 // Print all the properties on one line until we see a function-valued | 1056 // Print all the properties on one line until we see a function-valued |
| 1070 // property. Ideally, we would use a proper pretty-printer to make the | 1057 // property. Ideally, we would use a proper pretty-printer to make the |
| 1071 // decision based on layout. | 1058 // decision based on layout. |
| 1072 bool exitOneLinerMode(Expression value) { | 1059 bool exitOneLinerMode(Expression value) { |
| 1073 return | 1060 return value is Fun || |
| 1074 value is Fun || | |
| 1075 value is ArrayInitializer && value.elements.any((e) => e is Fun); | 1061 value is ArrayInitializer && value.elements.any((e) => e is Fun); |
| 1076 } | 1062 } |
| 1077 | 1063 |
| 1078 bool isOneLiner = node.isOneLiner || shouldCompressOutput; | 1064 bool isOneLiner = node.isOneLiner || shouldCompressOutput; |
| 1079 List<Property> properties = node.properties; | 1065 List<Property> properties = node.properties; |
| 1080 out("{"); | 1066 out("{"); |
| 1081 indentMore(); | 1067 indentMore(); |
| 1082 for (int i = 0; i < properties.length; i++) { | 1068 for (int i = 0; i < properties.length; i++) { |
| 1083 Node value = properties[i].value; | 1069 Node value = properties[i].value; |
| 1084 if (isOneLiner && exitOneLinerMode(value)) isOneLiner = false; | 1070 if (isOneLiner && exitOneLinerMode(value)) isOneLiner = false; |
| (...skipping 30 matching lines...) Expand all Loading... |
| 1115 node.name.accept(this); | 1101 node.name.accept(this); |
| 1116 } else { | 1102 } else { |
| 1117 assert(node.name is LiteralNumber); | 1103 assert(node.name is LiteralNumber); |
| 1118 LiteralNumber nameNumber = node.name; | 1104 LiteralNumber nameNumber = node.name; |
| 1119 out(nameNumber.value); | 1105 out(nameNumber.value); |
| 1120 } | 1106 } |
| 1121 endNode(node.name); | 1107 endNode(node.name); |
| 1122 out(":"); | 1108 out(":"); |
| 1123 spaceOut(); | 1109 spaceOut(); |
| 1124 visitNestedExpression(node.value, ASSIGNMENT, | 1110 visitNestedExpression(node.value, ASSIGNMENT, |
| 1125 newInForInit: false, newAtStatementBegin: false); | 1111 newInForInit: false, newAtStatementBegin: false); |
| 1126 } | 1112 } |
| 1127 | 1113 |
| 1128 @override | 1114 @override |
| 1129 void visitRegExpLiteral(RegExpLiteral node) { | 1115 void visitRegExpLiteral(RegExpLiteral node) { |
| 1130 out(node.pattern); | 1116 out(node.pattern); |
| 1131 } | 1117 } |
| 1132 | 1118 |
| 1133 @override | 1119 @override |
| 1134 void visitLiteralExpression(LiteralExpression node) { | 1120 void visitLiteralExpression(LiteralExpression node) { |
| 1135 String template = node.template; | 1121 String template = node.template; |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1198 } | 1184 } |
| 1199 } | 1185 } |
| 1200 | 1186 |
| 1201 @override | 1187 @override |
| 1202 void visitAwait(Await node) { | 1188 void visitAwait(Await node) { |
| 1203 out("await "); | 1189 out("await "); |
| 1204 visit(node.expression); | 1190 visit(node.expression); |
| 1205 } | 1191 } |
| 1206 } | 1192 } |
| 1207 | 1193 |
| 1208 | |
| 1209 class OrderedSet<T> { | 1194 class OrderedSet<T> { |
| 1210 final Set<T> set; | 1195 final Set<T> set; |
| 1211 final List<T> list; | 1196 final List<T> list; |
| 1212 | 1197 |
| 1213 OrderedSet() : set = new Set<T>(), list = <T>[]; | 1198 OrderedSet() |
| 1199 : set = new Set<T>(), |
| 1200 list = <T>[]; |
| 1214 | 1201 |
| 1215 void add(T x) { | 1202 void add(T x) { |
| 1216 if (set.add(x)) { | 1203 if (set.add(x)) { |
| 1217 // [Set.add] returns `true` if 'x' was added. | 1204 // [Set.add] returns `true` if 'x' was added. |
| 1218 list.add(x); | 1205 list.add(x); |
| 1219 } | 1206 } |
| 1220 } | 1207 } |
| 1221 | 1208 |
| 1222 void forEach(void fun(T x)) { | 1209 void forEach(void fun(T x)) { |
| 1223 list.forEach(fun); | 1210 list.forEach(fun); |
| 1224 } | 1211 } |
| 1225 } | 1212 } |
| 1226 | 1213 |
| 1227 // Collects all the var declarations in the function. We need to do this in a | 1214 // Collects all the var declarations in the function. We need to do this in a |
| 1228 // separate pass because JS vars are lifted to the top of the function. | 1215 // separate pass because JS vars are lifted to the top of the function. |
| 1229 class VarCollector extends BaseVisitor { | 1216 class VarCollector extends BaseVisitor { |
| 1230 bool nested; | 1217 bool nested; |
| 1231 bool enableRenaming = true; | 1218 bool enableRenaming = true; |
| 1232 final OrderedSet<String> vars; | 1219 final OrderedSet<String> vars; |
| 1233 final OrderedSet<String> params; | 1220 final OrderedSet<String> params; |
| 1234 | 1221 |
| 1235 static final String disableVariableMinificationPattern = "::norenaming::"; | 1222 static final String disableVariableMinificationPattern = "::norenaming::"; |
| 1236 static final String enableVariableMinificationPattern = "::dorenaming::"; | 1223 static final String enableVariableMinificationPattern = "::dorenaming::"; |
| 1237 | 1224 |
| 1238 VarCollector() : nested = false, | 1225 VarCollector() |
| 1239 vars = new OrderedSet<String>(), | 1226 : nested = false, |
| 1240 params = new OrderedSet<String>(); | 1227 vars = new OrderedSet<String>(), |
| 1228 params = new OrderedSet<String>(); |
| 1241 | 1229 |
| 1242 void forEachVar(void fn(String v)) => vars.forEach(fn); | 1230 void forEachVar(void fn(String v)) => vars.forEach(fn); |
| 1243 void forEachParam(void fn(String p)) => params.forEach(fn); | 1231 void forEachParam(void fn(String p)) => params.forEach(fn); |
| 1244 | 1232 |
| 1245 void collectVarsInFunction(Fun fun) { | 1233 void collectVarsInFunction(Fun fun) { |
| 1246 if (!nested) { | 1234 if (!nested) { |
| 1247 nested = true; | 1235 nested = true; |
| 1248 if (fun.params != null) { | 1236 if (fun.params != null) { |
| 1249 for (int i = 0; i < fun.params.length; i++) { | 1237 for (int i = 0; i < fun.params.length; i++) { |
| 1250 params.add(fun.params[i].name); | 1238 params.add(fun.params[i].name); |
| (...skipping 26 matching lines...) Expand all Loading... |
| 1277 } else if (node.comment.contains(enableVariableMinificationPattern)) { | 1265 } else if (node.comment.contains(enableVariableMinificationPattern)) { |
| 1278 enableRenaming = true; | 1266 enableRenaming = true; |
| 1279 } | 1267 } |
| 1280 } | 1268 } |
| 1281 | 1269 |
| 1282 void visitVariableDeclaration(VariableDeclaration decl) { | 1270 void visitVariableDeclaration(VariableDeclaration decl) { |
| 1283 if (enableRenaming && decl.allowRename) vars.add(decl.name); | 1271 if (enableRenaming && decl.allowRename) vars.add(decl.name); |
| 1284 } | 1272 } |
| 1285 } | 1273 } |
| 1286 | 1274 |
| 1287 | |
| 1288 /** | 1275 /** |
| 1289 * Returns true, if the given node must be wrapped into braces when used | 1276 * Returns true, if the given node must be wrapped into braces when used |
| 1290 * as then-statement in an [If] that has an else branch. | 1277 * as then-statement in an [If] that has an else branch. |
| 1291 */ | 1278 */ |
| 1292 class DanglingElseVisitor extends BaseVisitor<bool> { | 1279 class DanglingElseVisitor extends BaseVisitor<bool> { |
| 1293 JavaScriptPrintingContext context; | 1280 JavaScriptPrintingContext context; |
| 1294 | 1281 |
| 1295 DanglingElseVisitor(this.context); | 1282 DanglingElseVisitor(this.context); |
| 1296 | 1283 |
| 1297 bool visitProgram(Program node) => false; | 1284 bool visitProgram(Program node) => false; |
| 1298 | 1285 |
| 1299 bool visitNode(Statement node) { | 1286 bool visitNode(Statement node) { |
| 1300 context.error("Forgot node: $node"); | 1287 context.error("Forgot node: $node"); |
| 1301 return null; | 1288 return null; |
| 1302 } | 1289 } |
| 1303 | 1290 |
| 1304 bool visitBlock(Block node) => false; | 1291 bool visitBlock(Block node) => false; |
| 1305 bool visitExpressionStatement(ExpressionStatement node) => false; | 1292 bool visitExpressionStatement(ExpressionStatement node) => false; |
| 1306 bool visitEmptyStatement(EmptyStatement node) => false; | 1293 bool visitEmptyStatement(EmptyStatement node) => false; |
| 1307 bool visitIf(If node) { | 1294 bool visitIf(If node) { |
| 1308 if (!node.hasElse) return true; | 1295 if (!node.hasElse) return true; |
| 1309 return node.otherwise.accept(this); | 1296 return node.otherwise.accept(this); |
| 1310 } | 1297 } |
| 1298 |
| 1311 bool visitFor(For node) => node.body.accept(this); | 1299 bool visitFor(For node) => node.body.accept(this); |
| 1312 bool visitForIn(ForIn node) => node.body.accept(this); | 1300 bool visitForIn(ForIn node) => node.body.accept(this); |
| 1313 bool visitWhile(While node) => node.body.accept(this); | 1301 bool visitWhile(While node) => node.body.accept(this); |
| 1314 bool visitDo(Do node) => false; | 1302 bool visitDo(Do node) => false; |
| 1315 bool visitContinue(Continue node) => false; | 1303 bool visitContinue(Continue node) => false; |
| 1316 bool visitBreak(Break node) => false; | 1304 bool visitBreak(Break node) => false; |
| 1317 bool visitReturn(Return node) => false; | 1305 bool visitReturn(Return node) => false; |
| 1318 bool visitThrow(Throw node) => false; | 1306 bool visitThrow(Throw node) => false; |
| 1319 bool visitTry(Try node) { | 1307 bool visitTry(Try node) { |
| 1320 if (node.finallyPart != null) { | 1308 if (node.finallyPart != null) { |
| 1321 return node.finallyPart.accept(this); | 1309 return node.finallyPart.accept(this); |
| 1322 } else { | 1310 } else { |
| 1323 return node.catchPart.accept(this); | 1311 return node.catchPart.accept(this); |
| 1324 } | 1312 } |
| 1325 } | 1313 } |
| 1314 |
| 1326 bool visitCatch(Catch node) => node.body.accept(this); | 1315 bool visitCatch(Catch node) => node.body.accept(this); |
| 1327 bool visitSwitch(Switch node) => false; | 1316 bool visitSwitch(Switch node) => false; |
| 1328 bool visitCase(Case node) => false; | 1317 bool visitCase(Case node) => false; |
| 1329 bool visitDefault(Default node) => false; | 1318 bool visitDefault(Default node) => false; |
| 1330 bool visitFunctionDeclaration(FunctionDeclaration node) => false; | 1319 bool visitFunctionDeclaration(FunctionDeclaration node) => false; |
| 1331 bool visitLabeledStatement(LabeledStatement node) | 1320 bool visitLabeledStatement(LabeledStatement node) => node.body.accept(this); |
| 1332 => node.body.accept(this); | |
| 1333 bool visitLiteralStatement(LiteralStatement node) => true; | 1321 bool visitLiteralStatement(LiteralStatement node) => true; |
| 1334 | 1322 |
| 1335 bool visitExpression(Expression node) => false; | 1323 bool visitExpression(Expression node) => false; |
| 1336 } | 1324 } |
| 1337 | 1325 |
| 1338 | |
| 1339 abstract class LocalNamer { | 1326 abstract class LocalNamer { |
| 1340 String getName(String oldName); | 1327 String getName(String oldName); |
| 1341 String declareVariable(String oldName); | 1328 String declareVariable(String oldName); |
| 1342 String declareParameter(String oldName); | 1329 String declareParameter(String oldName); |
| 1343 void enterScope(VarCollector vars); | 1330 void enterScope(VarCollector vars); |
| 1344 void leaveScope(); | 1331 void leaveScope(); |
| 1345 } | 1332 } |
| 1346 | 1333 |
| 1347 | |
| 1348 class IdentityNamer implements LocalNamer { | 1334 class IdentityNamer implements LocalNamer { |
| 1349 String getName(String oldName) => oldName; | 1335 String getName(String oldName) => oldName; |
| 1350 String declareVariable(String oldName) => oldName; | 1336 String declareVariable(String oldName) => oldName; |
| 1351 String declareParameter(String oldName) => oldName; | 1337 String declareParameter(String oldName) => oldName; |
| 1352 void enterScope(VarCollector vars) {} | 1338 void enterScope(VarCollector vars) {} |
| 1353 void leaveScope() {} | 1339 void leaveScope() {} |
| 1354 } | 1340 } |
| 1355 | 1341 |
| 1356 | |
| 1357 class MinifyRenamer implements LocalNamer { | 1342 class MinifyRenamer implements LocalNamer { |
| 1358 final List<Map<String, String>> maps = []; | 1343 final List<Map<String, String>> maps = []; |
| 1359 final List<int> parameterNumberStack = []; | 1344 final List<int> parameterNumberStack = []; |
| 1360 final List<int> variableNumberStack = []; | 1345 final List<int> variableNumberStack = []; |
| 1361 int parameterNumber = 0; | 1346 int parameterNumber = 0; |
| 1362 int variableNumber = 0; | 1347 int variableNumber = 0; |
| 1363 | 1348 |
| 1364 MinifyRenamer(); | 1349 MinifyRenamer(); |
| 1365 | 1350 |
| 1366 void enterScope(VarCollector vars) { | 1351 void enterScope(VarCollector vars) { |
| (...skipping 18 matching lines...) Expand all Loading... |
| 1385 if (replacement != null) return replacement; | 1370 if (replacement != null) return replacement; |
| 1386 } | 1371 } |
| 1387 return oldName; | 1372 return oldName; |
| 1388 } | 1373 } |
| 1389 | 1374 |
| 1390 static const LOWER_CASE_LETTERS = 26; | 1375 static const LOWER_CASE_LETTERS = 26; |
| 1391 static const LETTERS = LOWER_CASE_LETTERS; | 1376 static const LETTERS = LOWER_CASE_LETTERS; |
| 1392 static const DIGITS = 10; | 1377 static const DIGITS = 10; |
| 1393 | 1378 |
| 1394 static int nthLetter(int n) { | 1379 static int nthLetter(int n) { |
| 1395 return (n < LOWER_CASE_LETTERS) ? | 1380 return (n < LOWER_CASE_LETTERS) |
| 1396 charCodes.$a + n : | 1381 ? charCodes.$a + n |
| 1397 charCodes.$A + n - LOWER_CASE_LETTERS; | 1382 : charCodes.$A + n - LOWER_CASE_LETTERS; |
| 1398 } | 1383 } |
| 1399 | 1384 |
| 1400 // Parameters go from a to z and variables go from z to a. This makes each | 1385 // Parameters go from a to z and variables go from z to a. This makes each |
| 1401 // argument list and each top-of-function var declaration look similar and | 1386 // argument list and each top-of-function var declaration look similar and |
| 1402 // helps gzip compress the file. If we have more than 26 arguments and | 1387 // helps gzip compress the file. If we have more than 26 arguments and |
| 1403 // variables then we meet somewhere in the middle of the alphabet. After | 1388 // variables then we meet somewhere in the middle of the alphabet. After |
| 1404 // that we give up trying to be nice to the compression algorithm and just | 1389 // that we give up trying to be nice to the compression algorithm and just |
| 1405 // use the same namespace for arguments and variables, starting with A, and | 1390 // use the same namespace for arguments and variables, starting with A, and |
| 1406 // moving on to a0, a1, etc. | 1391 // moving on to a0, a1, etc. |
| 1407 String declareVariable(String oldName) { | 1392 String declareVariable(String oldName) { |
| (...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1497 } | 1482 } |
| 1498 } | 1483 } |
| 1499 | 1484 |
| 1500 EnterExitNode exitNode(JavaScriptPrintingContext context, int position) { | 1485 EnterExitNode exitNode(JavaScriptPrintingContext context, int position) { |
| 1501 // Enter must happen before exit. | 1486 // Enter must happen before exit. |
| 1502 addToNode(context, position); | 1487 addToNode(context, position); |
| 1503 context.exitNode(node, startPosition, position, closingPosition); | 1488 context.exitNode(node, startPosition, position, closingPosition); |
| 1504 return parent; | 1489 return parent; |
| 1505 } | 1490 } |
| 1506 } | 1491 } |
| OLD | NEW |