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 |