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 class JavaScriptPrintingOptions { | 7 class JavaScriptPrintingOptions { |
9 final bool shouldCompressOutput; | 8 final bool shouldCompressOutput; |
10 final bool minifyLocalVariables; | 9 final bool minifyLocalVariables; |
11 final bool preferSemicolonToNewlineInMinifiedOutput; | 10 final bool preferSemicolonToNewlineInMinifiedOutput; |
12 final bool emitTypes; | 11 final bool emitTypes; |
13 final bool allowSingleLineIfStatements; | 12 final bool allowSingleLineIfStatements; |
14 | 13 |
15 /// True to allow keywords in properties, such as `obj.var` or `obj.function` | 14 /// True to allow keywords in properties, such as `obj.var` or `obj.function` |
16 /// Modern JS engines support this. | 15 /// Modern JS engines support this. |
17 final bool allowKeywordsInProperties; | 16 final bool allowKeywordsInProperties; |
18 | 17 |
19 JavaScriptPrintingOptions( | 18 JavaScriptPrintingOptions( |
20 {this.shouldCompressOutput: false, | 19 {this.shouldCompressOutput: false, |
21 this.minifyLocalVariables: false, | 20 this.minifyLocalVariables: false, |
22 this.preferSemicolonToNewlineInMinifiedOutput: false, | 21 this.preferSemicolonToNewlineInMinifiedOutput: false, |
23 this.emitTypes: false, | 22 this.emitTypes: false, |
24 this.allowKeywordsInProperties: false, | 23 this.allowKeywordsInProperties: false, |
25 this.allowSingleLineIfStatements: false}); | 24 this.allowSingleLineIfStatements: false}); |
26 } | 25 } |
27 | 26 |
28 | |
29 /// An environment in which JavaScript printing is done. Provides emitting of | 27 /// An environment in which JavaScript printing is done. Provides emitting of |
30 /// text and pre- and post-visit callbacks. | 28 /// text and pre- and post-visit callbacks. |
31 abstract class JavaScriptPrintingContext { | 29 abstract class JavaScriptPrintingContext { |
32 /// Signals an error. This should happen only for serious internal errors. | 30 /// Signals an error. This should happen only for serious internal errors. |
33 void error(String message) { throw message; } | 31 void error(String message) { |
| 32 throw message; |
| 33 } |
34 | 34 |
35 /// Adds [string] to the output. | 35 /// Adds [string] to the output. |
36 void emit(String string); | 36 void emit(String string); |
37 | 37 |
38 /// Callback immediately before printing [node]. Whitespace may be printed | 38 /// Callback immediately before printing [node]. Whitespace may be printed |
39 /// after this callback before the first non-whitespace character for [node]. | 39 /// after this callback before the first non-whitespace character for [node]. |
40 void enterNode(Node node) {} | 40 void enterNode(Node node) {} |
| 41 |
41 /// Callback after printing the last character representing [node]. | 42 /// Callback after printing the last character representing [node]. |
42 void exitNode(Node node) {} | 43 void exitNode(Node node) {} |
43 } | 44 } |
44 | 45 |
45 /// A simple implementation of [JavaScriptPrintingContext] suitable for tests. | 46 /// A simple implementation of [JavaScriptPrintingContext] suitable for tests. |
46 class SimpleJavaScriptPrintingContext extends JavaScriptPrintingContext { | 47 class SimpleJavaScriptPrintingContext extends JavaScriptPrintingContext { |
47 final StringBuffer buffer = new StringBuffer(); | 48 final StringBuffer buffer = new StringBuffer(); |
48 | 49 |
49 void emit(String string) { | 50 void emit(String string) { |
50 buffer.write(string); | 51 buffer.write(string); |
(...skipping 14 matching lines...) Expand all Loading... |
65 bool inForInit = false; | 66 bool inForInit = false; |
66 bool atStatementBegin = false; | 67 bool atStatementBegin = false; |
67 bool inNewTarget = false; | 68 bool inNewTarget = false; |
68 bool pendingSemicolon = false; | 69 bool pendingSemicolon = false; |
69 bool pendingSpace = false; | 70 bool pendingSpace = false; |
70 | 71 |
71 // The current indentation level. | 72 // The current indentation level. |
72 int _indentLevel = 0; | 73 int _indentLevel = 0; |
73 // A cache of all indentation strings used so far. | 74 // A cache of all indentation strings used so far. |
74 List<String> _indentList = <String>[""]; | 75 List<String> _indentList = <String>[""]; |
| 76 |
75 /// Whether the next call to [indent] should just be a no-op. | 77 /// Whether the next call to [indent] should just be a no-op. |
76 bool _skipNextIndent = false; | 78 bool _skipNextIndent = false; |
77 | 79 |
78 static final identifierCharacterRegExp = new RegExp(r'^[a-zA-Z_0-9$]'); | 80 static final identifierCharacterRegExp = new RegExp(r'^[a-zA-Z_0-9$]'); |
79 static final expressionContinuationRegExp = new RegExp(r'^[-+([]'); | 81 static final expressionContinuationRegExp = new RegExp(r'^[-+([]'); |
80 | 82 |
81 Printer(JavaScriptPrintingOptions options, | 83 Printer(JavaScriptPrintingOptions options, JavaScriptPrintingContext context, |
82 JavaScriptPrintingContext context, | 84 {LocalNamer localNamer}) |
83 {LocalNamer localNamer}) | |
84 : options = options, | 85 : options = options, |
85 context = context, | 86 context = context, |
86 shouldCompressOutput = options.shouldCompressOutput, | 87 shouldCompressOutput = options.shouldCompressOutput, |
87 danglingElseVisitor = new DanglingElseVisitor(context), | 88 danglingElseVisitor = new DanglingElseVisitor(context), |
88 localNamer = determineRenamer(localNamer, options); | 89 localNamer = determineRenamer(localNamer, options); |
89 | 90 |
90 static LocalNamer determineRenamer(LocalNamer localNamer, | 91 static LocalNamer determineRenamer( |
91 JavaScriptPrintingOptions options) { | 92 LocalNamer localNamer, JavaScriptPrintingOptions options) { |
92 if (localNamer != null) return localNamer; | 93 if (localNamer != null) return localNamer; |
93 return (options.shouldCompressOutput && options.minifyLocalVariables) | 94 return (options.shouldCompressOutput && options.minifyLocalVariables) |
94 ? new MinifyRenamer() : new IdentityNamer(); | 95 ? new MinifyRenamer() |
| 96 : new IdentityNamer(); |
95 } | 97 } |
96 | 98 |
97 | |
98 // The current indentation string. | 99 // The current indentation string. |
99 String get indentation { | 100 String get indentation { |
100 // Lazily add new indentation strings as required. | 101 // Lazily add new indentation strings as required. |
101 while (_indentList.length <= _indentLevel) { | 102 while (_indentList.length <= _indentLevel) { |
102 _indentList.add(_indentList.last + " "); | 103 _indentList.add(_indentList.last + " "); |
103 } | 104 } |
104 return _indentList[_indentLevel]; | 105 return _indentList[_indentLevel]; |
105 } | 106 } |
106 | 107 |
107 void indentMore() { | 108 void indentMore() { |
108 _indentLevel++; | 109 _indentLevel++; |
109 } | 110 } |
110 | 111 |
111 void indentLess() { | 112 void indentLess() { |
112 _indentLevel--; | 113 _indentLevel--; |
113 } | 114 } |
114 | 115 |
115 | |
116 /// Always emit a newline, even under `enableMinification`. | 116 /// Always emit a newline, even under `enableMinification`. |
117 void forceLine() { | 117 void forceLine() { |
118 out("\n"); | 118 out("\n"); |
119 } | 119 } |
| 120 |
120 /// Emits a newline for readability. | 121 /// Emits a newline for readability. |
121 void lineOut() { | 122 void lineOut() { |
122 if (!shouldCompressOutput) forceLine(); | 123 if (!shouldCompressOutput) forceLine(); |
123 } | 124 } |
| 125 |
124 void spaceOut() { | 126 void spaceOut() { |
125 if (!shouldCompressOutput) out(" "); | 127 if (!shouldCompressOutput) out(" "); |
126 } | 128 } |
127 | 129 |
128 String lastAddedString = null; | 130 String lastAddedString = null; |
129 int get lastCharCode { | 131 int get lastCharCode { |
130 if (lastAddedString == null) return 0; | 132 if (lastAddedString == null) return 0; |
131 assert(lastAddedString.length != ""); | 133 assert(lastAddedString.length != ""); |
132 return lastAddedString.codeUnitAt(lastAddedString.length - 1); | 134 return lastAddedString.codeUnitAt(lastAddedString.length - 1); |
133 } | 135 } |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
175 | 177 |
176 void outSemicolonLn() { | 178 void outSemicolonLn() { |
177 if (shouldCompressOutput) { | 179 if (shouldCompressOutput) { |
178 pendingSemicolon = true; | 180 pendingSemicolon = true; |
179 } else { | 181 } else { |
180 out(";"); | 182 out(";"); |
181 forceLine(); | 183 forceLine(); |
182 } | 184 } |
183 } | 185 } |
184 | 186 |
185 void outIndent(String str) { indent(); out(str); } | 187 void outIndent(String str) { |
186 void outIndentLn(String str) { indent(); outLn(str); } | 188 indent(); |
| 189 out(str); |
| 190 } |
| 191 |
| 192 void outIndentLn(String str) { |
| 193 indent(); |
| 194 outLn(str); |
| 195 } |
187 | 196 |
188 void skipNextIndent() { | 197 void skipNextIndent() { |
189 _skipNextIndent = true; | 198 _skipNextIndent = true; |
190 } | 199 } |
191 | 200 |
192 void indent() { | 201 void indent() { |
193 if (_skipNextIndent) { | 202 if (_skipNextIndent) { |
194 _skipNextIndent = false; | 203 _skipNextIndent = false; |
195 return; | 204 return; |
196 } | 205 } |
197 if (!shouldCompressOutput) { | 206 if (!shouldCompressOutput) { |
198 out(indentation); | 207 out(indentation); |
199 } | 208 } |
200 } | 209 } |
201 | 210 |
202 visit(Node node) { | 211 visit(Node node) { |
203 context.enterNode(node); | 212 context.enterNode(node); |
204 node.accept(this); | 213 node.accept(this); |
205 context.exitNode(node); | 214 context.exitNode(node); |
206 } | 215 } |
207 | 216 |
208 visitCommaSeparated(List<Node> nodes, int hasRequiredType, | 217 visitCommaSeparated(List<Node> nodes, int hasRequiredType, |
209 {bool newInForInit, bool newAtStatementBegin}) { | 218 {bool newInForInit, bool newAtStatementBegin}) { |
210 for (int i = 0; i < nodes.length; i++) { | 219 for (int i = 0; i < nodes.length; i++) { |
211 if (i != 0) { | 220 if (i != 0) { |
212 atStatementBegin = false; | 221 atStatementBegin = false; |
213 out(","); | 222 out(","); |
214 spaceOut(); | 223 spaceOut(); |
215 } | 224 } |
216 visitNestedExpression(nodes[i], hasRequiredType, | 225 visitNestedExpression(nodes[i], hasRequiredType, |
217 newInForInit: newInForInit, | 226 newInForInit: newInForInit, newAtStatementBegin: newAtStatementBegin); |
218 newAtStatementBegin: newAtStatementBegin); | |
219 } | 227 } |
220 } | 228 } |
221 | 229 |
222 visitAll(List<Node> nodes) { | 230 visitAll(List<Node> nodes) { |
223 nodes.forEach(visit); | 231 nodes.forEach(visit); |
224 } | 232 } |
225 | 233 |
226 visitProgram(Program program) { | 234 visitProgram(Program program) { |
227 if (program.scriptTag != null) { | 235 if (program.scriptTag != null) { |
228 out('#!${program.scriptTag}\n'); | 236 out('#!${program.scriptTag}\n'); |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
275 } | 283 } |
276 | 284 |
277 visitBlock(Block block) { | 285 visitBlock(Block block) { |
278 blockOut(block, true, true); | 286 blockOut(block, true, true); |
279 } | 287 } |
280 | 288 |
281 visitExpressionStatement(ExpressionStatement expressionStatement) { | 289 visitExpressionStatement(ExpressionStatement expressionStatement) { |
282 indent(); | 290 indent(); |
283 outClosureAnnotation(expressionStatement); | 291 outClosureAnnotation(expressionStatement); |
284 visitNestedExpression(expressionStatement.expression, EXPRESSION, | 292 visitNestedExpression(expressionStatement.expression, EXPRESSION, |
285 newInForInit: false, newAtStatementBegin: true); | 293 newInForInit: false, newAtStatementBegin: true); |
286 outSemicolonLn(); | 294 outSemicolonLn(); |
287 } | 295 } |
288 | 296 |
289 visitEmptyStatement(EmptyStatement nop) { | 297 visitEmptyStatement(EmptyStatement nop) { |
290 outIndentLn(";"); | 298 outIndentLn(";"); |
291 } | 299 } |
292 | 300 |
293 void ifOut(If node, bool shouldIndent) { | 301 void ifOut(If node, bool shouldIndent) { |
294 Node then = node.then; | 302 Node then = node.then; |
295 Node elsePart = node.otherwise; | 303 Node elsePart = node.otherwise; |
296 bool hasElse = node.hasElse; | 304 bool hasElse = node.hasElse; |
297 | 305 |
298 // Handle dangling elses and a work-around for Android 4.0 stock browser. | 306 // Handle dangling elses and a work-around for Android 4.0 stock browser. |
299 // Android 4.0 requires braces for a single do-while in the `then` branch. | 307 // Android 4.0 requires braces for a single do-while in the `then` branch. |
300 // See issue 10923. | 308 // See issue 10923. |
301 if (hasElse) { | 309 if (hasElse) { |
302 bool needsBraces = node.then.accept(danglingElseVisitor) || then is Do; | 310 bool needsBraces = node.then.accept(danglingElseVisitor) || then is Do; |
303 if (needsBraces) { | 311 if (needsBraces) { |
304 then = new Block(<Statement>[then]); | 312 then = new Block(<Statement>[then]); |
305 } | 313 } |
306 } | 314 } |
307 if (shouldIndent) indent(); | 315 if (shouldIndent) indent(); |
308 out("if"); | 316 out("if"); |
309 spaceOut(); | 317 spaceOut(); |
310 out("("); | 318 out("("); |
311 visitNestedExpression(node.condition, EXPRESSION, | 319 visitNestedExpression(node.condition, EXPRESSION, |
312 newInForInit: false, newAtStatementBegin: false); | 320 newInForInit: false, newAtStatementBegin: false); |
313 out(")"); | 321 out(")"); |
314 bool thenWasBlock; | 322 bool thenWasBlock; |
315 if (options.allowSingleLineIfStatements && !hasElse && then is! Block) { | 323 if (options.allowSingleLineIfStatements && !hasElse && then is! Block) { |
316 thenWasBlock = false; | 324 thenWasBlock = false; |
317 spaceOut(); | 325 spaceOut(); |
318 skipNextIndent(); | 326 skipNextIndent(); |
319 visit(then); | 327 visit(then); |
320 } else { | 328 } else { |
321 thenWasBlock = | 329 thenWasBlock = |
322 blockBody(then, needsSeparation: false, needsNewline: !hasElse); | 330 blockBody(then, needsSeparation: false, needsNewline: !hasElse); |
(...skipping 17 matching lines...) Expand all Loading... |
340 visitIf(If node) { | 348 visitIf(If node) { |
341 ifOut(node, true); | 349 ifOut(node, true); |
342 } | 350 } |
343 | 351 |
344 visitFor(For loop) { | 352 visitFor(For loop) { |
345 outIndent("for"); | 353 outIndent("for"); |
346 spaceOut(); | 354 spaceOut(); |
347 out("("); | 355 out("("); |
348 if (loop.init != null) { | 356 if (loop.init != null) { |
349 visitNestedExpression(loop.init, EXPRESSION, | 357 visitNestedExpression(loop.init, EXPRESSION, |
350 newInForInit: true, newAtStatementBegin: false); | 358 newInForInit: true, newAtStatementBegin: false); |
351 } | 359 } |
352 out(";"); | 360 out(";"); |
353 if (loop.condition != null) { | 361 if (loop.condition != null) { |
354 spaceOut(); | 362 spaceOut(); |
355 visitNestedExpression(loop.condition, EXPRESSION, | 363 visitNestedExpression(loop.condition, EXPRESSION, |
356 newInForInit: false, newAtStatementBegin: false); | 364 newInForInit: false, newAtStatementBegin: false); |
357 } | 365 } |
358 out(";"); | 366 out(";"); |
359 if (loop.update != null) { | 367 if (loop.update != null) { |
360 spaceOut(); | 368 spaceOut(); |
361 visitNestedExpression(loop.update, EXPRESSION, | 369 visitNestedExpression(loop.update, EXPRESSION, |
362 newInForInit: false, newAtStatementBegin: false); | 370 newInForInit: false, newAtStatementBegin: false); |
363 } | 371 } |
364 out(")"); | 372 out(")"); |
365 blockBody(loop.body, needsSeparation: false, needsNewline: true); | 373 blockBody(loop.body, needsSeparation: false, needsNewline: true); |
366 } | 374 } |
367 | 375 |
368 visitForIn(ForIn loop) { | 376 visitForIn(ForIn loop) { |
369 outIndent("for"); | 377 outIndent("for"); |
370 spaceOut(); | 378 spaceOut(); |
371 out("("); | 379 out("("); |
372 visitNestedExpression(loop.leftHandSide, EXPRESSION, | 380 visitNestedExpression(loop.leftHandSide, EXPRESSION, |
373 newInForInit: true, newAtStatementBegin: false); | 381 newInForInit: true, newAtStatementBegin: false); |
374 out(" in"); | 382 out(" in"); |
375 pendingSpace = true; | 383 pendingSpace = true; |
376 visitNestedExpression(loop.object, EXPRESSION, | 384 visitNestedExpression(loop.object, EXPRESSION, |
377 newInForInit: false, newAtStatementBegin: false); | 385 newInForInit: false, newAtStatementBegin: false); |
378 out(")"); | 386 out(")"); |
379 blockBody(loop.body, needsSeparation: false, needsNewline: true); | 387 blockBody(loop.body, needsSeparation: false, needsNewline: true); |
380 } | 388 } |
381 | 389 |
382 visitForOf(ForOf loop) { | 390 visitForOf(ForOf loop) { |
383 outIndent("for"); | 391 outIndent("for"); |
384 spaceOut(); | 392 spaceOut(); |
385 out("("); | 393 out("("); |
386 visitNestedExpression(loop.leftHandSide, EXPRESSION, | 394 visitNestedExpression(loop.leftHandSide, EXPRESSION, |
387 newInForInit: true, newAtStatementBegin: false); | 395 newInForInit: true, newAtStatementBegin: false); |
388 out(" of"); | 396 out(" of"); |
389 pendingSpace = true; | 397 pendingSpace = true; |
390 visitNestedExpression(loop.iterable, EXPRESSION, | 398 visitNestedExpression(loop.iterable, EXPRESSION, |
391 newInForInit: false, newAtStatementBegin: false); | 399 newInForInit: false, newAtStatementBegin: false); |
392 out(")"); | 400 out(")"); |
393 blockBody(loop.body, needsSeparation: false, needsNewline: true); | 401 blockBody(loop.body, needsSeparation: false, needsNewline: true); |
394 } | 402 } |
395 | 403 |
396 visitWhile(While loop) { | 404 visitWhile(While loop) { |
397 outIndent("while"); | 405 outIndent("while"); |
398 spaceOut(); | 406 spaceOut(); |
399 out("("); | 407 out("("); |
400 visitNestedExpression(loop.condition, EXPRESSION, | 408 visitNestedExpression(loop.condition, EXPRESSION, |
401 newInForInit: false, newAtStatementBegin: false); | 409 newInForInit: false, newAtStatementBegin: false); |
402 out(")"); | 410 out(")"); |
403 blockBody(loop.body, needsSeparation: false, needsNewline: true); | 411 blockBody(loop.body, needsSeparation: false, needsNewline: true); |
404 } | 412 } |
405 | 413 |
406 visitDo(Do loop) { | 414 visitDo(Do loop) { |
407 outIndent("do"); | 415 outIndent("do"); |
408 if (blockBody(loop.body, needsSeparation: true, needsNewline: false)) { | 416 if (blockBody(loop.body, needsSeparation: true, needsNewline: false)) { |
409 spaceOut(); | 417 spaceOut(); |
410 } else { | 418 } else { |
411 indent(); | 419 indent(); |
412 } | 420 } |
413 out("while"); | 421 out("while"); |
414 spaceOut(); | 422 spaceOut(); |
415 out("("); | 423 out("("); |
416 visitNestedExpression(loop.condition, EXPRESSION, | 424 visitNestedExpression(loop.condition, EXPRESSION, |
417 newInForInit: false, newAtStatementBegin: false); | 425 newInForInit: false, newAtStatementBegin: false); |
418 out(")"); | 426 out(")"); |
419 outSemicolonLn(); | 427 outSemicolonLn(); |
420 } | 428 } |
421 | 429 |
422 visitContinue(Continue node) { | 430 visitContinue(Continue node) { |
423 if (node.targetLabel == null) { | 431 if (node.targetLabel == null) { |
424 outIndent("continue"); | 432 outIndent("continue"); |
425 } else { | 433 } else { |
426 outIndent("continue ${node.targetLabel}"); | 434 outIndent("continue ${node.targetLabel}"); |
427 } | 435 } |
428 outSemicolonLn(); | 436 outSemicolonLn(); |
429 } | 437 } |
430 | 438 |
431 visitBreak(Break node) { | 439 visitBreak(Break node) { |
432 if (node.targetLabel == null) { | 440 if (node.targetLabel == null) { |
433 outIndent("break"); | 441 outIndent("break"); |
434 } else { | 442 } else { |
435 outIndent("break ${node.targetLabel}"); | 443 outIndent("break ${node.targetLabel}"); |
436 } | 444 } |
437 outSemicolonLn(); | 445 outSemicolonLn(); |
438 } | 446 } |
439 | 447 |
440 visitReturn(Return node) { | 448 visitReturn(Return node) { |
441 if (node.value == null) { | 449 if (node.value == null) { |
442 outIndent("return"); | 450 outIndent("return"); |
443 } else { | 451 } else { |
444 outIndent("return"); | 452 outIndent("return"); |
445 pendingSpace = true; | 453 pendingSpace = true; |
446 visitNestedExpression(node.value, EXPRESSION, | 454 visitNestedExpression(node.value, EXPRESSION, |
447 newInForInit: false, newAtStatementBegin: false); | 455 newInForInit: false, newAtStatementBegin: false); |
448 } | 456 } |
449 outSemicolonLn(); | 457 outSemicolonLn(); |
450 } | 458 } |
451 | 459 |
452 visitDartYield(DartYield node) { | 460 visitDartYield(DartYield node) { |
453 if (node.hasStar) { | 461 if (node.hasStar) { |
454 outIndent("yield*"); | 462 outIndent("yield*"); |
455 } else { | 463 } else { |
456 outIndent("yield"); | 464 outIndent("yield"); |
457 } | 465 } |
458 pendingSpace = true; | 466 pendingSpace = true; |
459 visitNestedExpression(node.expression, EXPRESSION, | 467 visitNestedExpression(node.expression, EXPRESSION, |
460 newInForInit: false, newAtStatementBegin: false); | 468 newInForInit: false, newAtStatementBegin: false); |
461 outSemicolonLn(); | 469 outSemicolonLn(); |
462 } | 470 } |
463 | 471 |
464 | |
465 visitThrow(Throw node) { | 472 visitThrow(Throw node) { |
466 outIndent("throw"); | 473 outIndent("throw"); |
467 pendingSpace = true; | 474 pendingSpace = true; |
468 visitNestedExpression(node.expression, EXPRESSION, | 475 visitNestedExpression(node.expression, EXPRESSION, |
469 newInForInit: false, newAtStatementBegin: false); | 476 newInForInit: false, newAtStatementBegin: false); |
470 outSemicolonLn(); | 477 outSemicolonLn(); |
471 } | 478 } |
472 | 479 |
473 visitTry(Try node) { | 480 visitTry(Try node) { |
474 outIndent("try"); | 481 outIndent("try"); |
475 blockBody(node.body, needsSeparation: true, needsNewline: false); | 482 blockBody(node.body, needsSeparation: true, needsNewline: false); |
476 if (node.catchPart != null) { | 483 if (node.catchPart != null) { |
477 visit(node.catchPart); | 484 visit(node.catchPart); |
478 } | 485 } |
479 if (node.finallyPart != null) { | 486 if (node.finallyPart != null) { |
480 spaceOut(); | 487 spaceOut(); |
481 out("finally"); | 488 out("finally"); |
482 blockBody(node.finallyPart, needsSeparation: true, needsNewline: true); | 489 blockBody(node.finallyPart, needsSeparation: true, needsNewline: true); |
483 } else { | 490 } else { |
484 lineOut(); | 491 lineOut(); |
485 } | 492 } |
486 } | 493 } |
487 | 494 |
488 visitCatch(Catch node) { | 495 visitCatch(Catch node) { |
489 spaceOut(); | 496 spaceOut(); |
490 out("catch"); | 497 out("catch"); |
491 spaceOut(); | 498 spaceOut(); |
492 out("("); | 499 out("("); |
493 visitNestedExpression(node.declaration, EXPRESSION, | 500 visitNestedExpression(node.declaration, EXPRESSION, |
494 newInForInit: false, newAtStatementBegin: false); | 501 newInForInit: false, newAtStatementBegin: false); |
495 out(")"); | 502 out(")"); |
496 blockBody(node.body, needsSeparation: false, needsNewline: true); | 503 blockBody(node.body, needsSeparation: false, needsNewline: true); |
497 } | 504 } |
498 | 505 |
499 visitSwitch(Switch node) { | 506 visitSwitch(Switch node) { |
500 outIndent("switch"); | 507 outIndent("switch"); |
501 spaceOut(); | 508 spaceOut(); |
502 out("("); | 509 out("("); |
503 visitNestedExpression(node.key, EXPRESSION, | 510 visitNestedExpression(node.key, EXPRESSION, |
504 newInForInit: false, newAtStatementBegin: false); | 511 newInForInit: false, newAtStatementBegin: false); |
505 out(")"); | 512 out(")"); |
506 spaceOut(); | 513 spaceOut(); |
507 outLn("{"); | 514 outLn("{"); |
508 indentMore(); | 515 indentMore(); |
509 visitAll(node.cases); | 516 visitAll(node.cases); |
510 indentLess(); | 517 indentLess(); |
511 outIndentLn("}"); | 518 outIndentLn("}"); |
512 } | 519 } |
513 | 520 |
514 visitCase(Case node) { | 521 visitCase(Case node) { |
515 outIndent("case"); | 522 outIndent("case"); |
516 pendingSpace = true; | 523 pendingSpace = true; |
517 visitNestedExpression(node.expression, EXPRESSION, | 524 visitNestedExpression(node.expression, EXPRESSION, |
518 newInForInit: false, newAtStatementBegin: false); | 525 newInForInit: false, newAtStatementBegin: false); |
519 outLn(":"); | 526 outLn(":"); |
520 if (!node.body.statements.isEmpty) { | 527 if (!node.body.statements.isEmpty) { |
521 blockOut(node.body, true, true); | 528 blockOut(node.body, true, true); |
522 } | 529 } |
523 } | 530 } |
524 | 531 |
525 visitDefault(Default node) { | 532 visitDefault(Default node) { |
526 outIndentLn("default:"); | 533 outIndentLn("default:"); |
527 if (!node.body.statements.isEmpty) { | 534 if (!node.body.statements.isEmpty) { |
528 blockOut(node.body, true, true); | 535 blockOut(node.body, true, true); |
529 } | 536 } |
530 } | 537 } |
531 | 538 |
532 visitLabeledStatement(LabeledStatement node) { | 539 visitLabeledStatement(LabeledStatement node) { |
533 outIndent("${node.label}:"); | 540 outIndent("${node.label}:"); |
534 blockBody(node.body, needsSeparation: false, needsNewline: true); | 541 blockBody(node.body, needsSeparation: false, needsNewline: true); |
535 } | 542 } |
536 | 543 |
537 void functionOut(Fun fun, Node name) { | 544 void functionOut(Fun fun, Node name) { |
538 out("function"); | 545 out("function"); |
539 if (fun.isGenerator) out("*"); | 546 if (fun.isGenerator) out("*"); |
540 if (name != null) { | 547 if (name != null) { |
541 out(" "); | 548 out(" "); |
542 // Name must be a [Decl]. Therefore only test for primary expressions. | 549 // Name must be a [Decl]. Therefore only test for primary expressions. |
543 visitNestedExpression(name, PRIMARY, | 550 visitNestedExpression(name, PRIMARY, |
544 newInForInit: false, newAtStatementBegin: false); | 551 newInForInit: false, newAtStatementBegin: false); |
545 } | 552 } |
546 localNamer.enterScope(fun); | 553 localNamer.enterScope(fun); |
547 outTypeParams(fun.typeParams); | 554 outTypeParams(fun.typeParams); |
548 out("("); | 555 out("("); |
549 if (fun.params != null) { | 556 if (fun.params != null) { |
550 visitCommaSeparated(fun.params, PRIMARY, | 557 visitCommaSeparated(fun.params, PRIMARY, |
551 newInForInit: false, newAtStatementBegin: false); | 558 newInForInit: false, newAtStatementBegin: false); |
552 } | 559 } |
553 out(")"); | 560 out(")"); |
554 outTypeAnnotation(fun.returnType); | 561 outTypeAnnotation(fun.returnType); |
555 switch (fun.asyncModifier) { | 562 switch (fun.asyncModifier) { |
556 case const AsyncModifier.sync(): | 563 case const AsyncModifier.sync(): |
557 break; | 564 break; |
558 case const AsyncModifier.async(): | 565 case const AsyncModifier.async(): |
559 out(' async'); | 566 out(' async'); |
560 break; | 567 break; |
561 case const AsyncModifier.syncStar(): | 568 case const AsyncModifier.syncStar(): |
562 out(' sync*'); | 569 out(' sync*'); |
563 break; | 570 break; |
564 case const AsyncModifier.asyncStar(): | 571 case const AsyncModifier.asyncStar(): |
565 out(' async*'); | 572 out(' async*'); |
566 break; | 573 break; |
567 } | 574 } |
568 blockBody(fun.body, needsSeparation: false, needsNewline: false); | 575 blockBody(fun.body, needsSeparation: false, needsNewline: false); |
569 localNamer.leaveScope(); | 576 localNamer.leaveScope(); |
570 } | 577 } |
571 | 578 |
572 visitFunctionDeclaration(FunctionDeclaration declaration) { | 579 visitFunctionDeclaration(FunctionDeclaration declaration) { |
573 indent(); | 580 indent(); |
574 outClosureAnnotation(declaration); | 581 outClosureAnnotation(declaration); |
575 functionOut(declaration.function, declaration.name); | 582 functionOut(declaration.function, declaration.name); |
576 lineOut(); | 583 lineOut(); |
577 } | 584 } |
578 | 585 |
579 visitNestedExpression(Expression node, int requiredPrecedence, | 586 visitNestedExpression(Expression node, int requiredPrecedence, |
580 {bool newInForInit, bool newAtStatementBegin}) { | 587 {bool newInForInit, bool newAtStatementBegin}) { |
581 int nodePrecedence = node.precedenceLevel; | 588 int nodePrecedence = node.precedenceLevel; |
582 bool needsParentheses = | 589 bool needsParentheses = |
583 // a - (b + c). | 590 // a - (b + c). |
584 (requiredPrecedence != EXPRESSION && | 591 (requiredPrecedence != EXPRESSION && |
585 nodePrecedence < requiredPrecedence) || | 592 nodePrecedence < requiredPrecedence) || |
586 // for (a = (x in o); ... ; ... ) { ... } | 593 // for (a = (x in o); ... ; ... ) { ... } |
587 (newInForInit && node is Binary && node.op == "in") || | 594 (newInForInit && node is Binary && node.op == "in") || |
588 // (function() { ... })(). | 595 // (function() { ... })(). |
589 // ({a: 2, b: 3}.toString()). | 596 // ({a: 2, b: 3}.toString()). |
590 (newAtStatementBegin && (node is NamedFunction || | 597 (newAtStatementBegin && |
591 node is FunctionExpression || | 598 (node is NamedFunction || |
592 node is ObjectInitializer)); | 599 node is FunctionExpression || |
| 600 node is ObjectInitializer)); |
593 if (needsParentheses) { | 601 if (needsParentheses) { |
594 inForInit = false; | 602 inForInit = false; |
595 atStatementBegin = false; | 603 atStatementBegin = false; |
596 inNewTarget = false; | 604 inNewTarget = false; |
597 out("("); | 605 out("("); |
598 visit(node); | 606 visit(node); |
599 out(")"); | 607 out(")"); |
600 } else { | 608 } else { |
601 inForInit = newInForInit; | 609 inForInit = newInForInit; |
602 atStatementBegin = newAtStatementBegin; | 610 atStatementBegin = newAtStatementBegin; |
603 visit(node); | 611 visit(node); |
604 } | 612 } |
605 } | 613 } |
606 | 614 |
607 visitVariableDeclarationList(VariableDeclarationList list) { | 615 visitVariableDeclarationList(VariableDeclarationList list) { |
608 outClosureAnnotation(list); | 616 outClosureAnnotation(list); |
609 // Note: keyword can be null for non-static field declarations. | 617 // Note: keyword can be null for non-static field declarations. |
610 if (list.keyword != null) { | 618 if (list.keyword != null) { |
611 out(list.keyword); | 619 out(list.keyword); |
612 out(" "); | 620 out(" "); |
613 } | 621 } |
614 visitCommaSeparated(list.declarations, ASSIGNMENT, | 622 visitCommaSeparated(list.declarations, ASSIGNMENT, |
615 newInForInit: inForInit, newAtStatementBegin: false); | 623 newInForInit: inForInit, newAtStatementBegin: false); |
616 } | 624 } |
617 | 625 |
618 visitArrayBindingPattern(ArrayBindingPattern node) { | 626 visitArrayBindingPattern(ArrayBindingPattern node) { |
619 out("["); | 627 out("["); |
620 visitCommaSeparated(node.variables, EXPRESSION, | 628 visitCommaSeparated(node.variables, EXPRESSION, |
621 newInForInit: false, newAtStatementBegin: false); | 629 newInForInit: false, newAtStatementBegin: false); |
622 out("]"); | 630 out("]"); |
623 } | 631 } |
| 632 |
624 visitObjectBindingPattern(ObjectBindingPattern node) { | 633 visitObjectBindingPattern(ObjectBindingPattern node) { |
625 out("{"); | 634 out("{"); |
626 visitCommaSeparated(node.variables, EXPRESSION, | 635 visitCommaSeparated(node.variables, EXPRESSION, |
627 newInForInit: false, newAtStatementBegin: false); | 636 newInForInit: false, newAtStatementBegin: false); |
628 out("}"); | 637 out("}"); |
629 } | 638 } |
630 | 639 |
631 visitDestructuredVariable(DestructuredVariable node) { | 640 visitDestructuredVariable(DestructuredVariable node) { |
632 var name = node.name; | 641 var name = node.name; |
633 var hasName = name != null; | 642 var hasName = name != null; |
634 if (hasName) { | 643 if (hasName) { |
635 if (name is LiteralString) { | 644 if (name is LiteralString) { |
636 out("["); | 645 out("["); |
637 out(name.value); | 646 out(name.value); |
638 out("]"); | 647 out("]"); |
639 } else { | 648 } else { |
640 visit(name); | 649 visit(name); |
641 } | 650 } |
642 } | 651 } |
643 if (node.structure != null) { | 652 if (node.structure != null) { |
644 if (hasName) { | 653 if (hasName) { |
645 out(":"); | 654 out(":"); |
646 spaceOut(); | 655 spaceOut(); |
647 } | 656 } |
648 visit(node.structure); | 657 visit(node.structure); |
649 } | 658 } |
650 outTypeAnnotation(node.type); | 659 outTypeAnnotation(node.type); |
651 if (node.defaultValue != null) { | 660 if (node.defaultValue != null) { |
652 spaceOut(); | 661 spaceOut(); |
653 out("="); | 662 out("="); |
654 spaceOut(); | 663 spaceOut(); |
655 visitNestedExpression(node.defaultValue, EXPRESSION, | 664 visitNestedExpression(node.defaultValue, EXPRESSION, |
656 newInForInit: false, newAtStatementBegin: false); | 665 newInForInit: false, newAtStatementBegin: false); |
657 } | 666 } |
658 } | 667 } |
659 | 668 |
660 visitSimpleBindingPattern(SimpleBindingPattern node) { | 669 visitSimpleBindingPattern(SimpleBindingPattern node) { |
661 visit(node.name); | 670 visit(node.name); |
662 } | 671 } |
663 | 672 |
664 visitAssignment(Assignment assignment) { | 673 visitAssignment(Assignment assignment) { |
665 visitNestedExpression(assignment.leftHandSide, LEFT_HAND_SIDE, | 674 visitNestedExpression(assignment.leftHandSide, LEFT_HAND_SIDE, |
666 newInForInit: inForInit, | 675 newInForInit: inForInit, newAtStatementBegin: atStatementBegin); |
667 newAtStatementBegin: atStatementBegin); | |
668 if (assignment.value != null) { | 676 if (assignment.value != null) { |
669 spaceOut(); | 677 spaceOut(); |
670 String op = assignment.op; | 678 String op = assignment.op; |
671 if (op != null) out(op); | 679 if (op != null) out(op); |
672 out("="); | 680 out("="); |
673 spaceOut(); | 681 spaceOut(); |
674 visitNestedExpression(assignment.value, ASSIGNMENT, | 682 visitNestedExpression(assignment.value, ASSIGNMENT, |
675 newInForInit: inForInit, | 683 newInForInit: inForInit, newAtStatementBegin: false); |
676 newAtStatementBegin: false); | |
677 } | 684 } |
678 } | 685 } |
679 | 686 |
680 visitVariableInitialization(VariableInitialization initialization) { | 687 visitVariableInitialization(VariableInitialization initialization) { |
681 outClosureAnnotation(initialization); | 688 outClosureAnnotation(initialization); |
682 visitAssignment(initialization); | 689 visitAssignment(initialization); |
683 } | 690 } |
684 | 691 |
685 visitConditional(Conditional cond) { | 692 visitConditional(Conditional cond) { |
686 visitNestedExpression(cond.condition, LOGICAL_OR, | 693 visitNestedExpression(cond.condition, LOGICAL_OR, |
687 newInForInit: inForInit, | 694 newInForInit: inForInit, newAtStatementBegin: atStatementBegin); |
688 newAtStatementBegin: atStatementBegin); | |
689 spaceOut(); | 695 spaceOut(); |
690 out("?"); | 696 out("?"); |
691 spaceOut(); | 697 spaceOut(); |
692 // The then part is allowed to have an 'in'. | 698 // The then part is allowed to have an 'in'. |
693 visitNestedExpression(cond.then, ASSIGNMENT, | 699 visitNestedExpression(cond.then, ASSIGNMENT, |
694 newInForInit: false, newAtStatementBegin: false); | 700 newInForInit: false, newAtStatementBegin: false); |
695 spaceOut(); | 701 spaceOut(); |
696 out(":"); | 702 out(":"); |
697 spaceOut(); | 703 spaceOut(); |
698 visitNestedExpression(cond.otherwise, ASSIGNMENT, | 704 visitNestedExpression(cond.otherwise, ASSIGNMENT, |
699 newInForInit: inForInit, newAtStatementBegin: false); | 705 newInForInit: inForInit, newAtStatementBegin: false); |
700 } | 706 } |
701 | 707 |
702 visitNew(New node) { | 708 visitNew(New node) { |
703 out("new "); | 709 out("new "); |
704 inNewTarget = true; | 710 inNewTarget = true; |
705 visitNestedExpression(node.target, ACCESS, | 711 visitNestedExpression(node.target, ACCESS, |
706 newInForInit: inForInit, newAtStatementBegin: false); | 712 newInForInit: inForInit, newAtStatementBegin: false); |
707 inNewTarget = false; | 713 inNewTarget = false; |
708 out("("); | 714 out("("); |
709 visitCommaSeparated(node.arguments, SPREAD, | 715 visitCommaSeparated(node.arguments, SPREAD, |
710 newInForInit: false, newAtStatementBegin: false); | 716 newInForInit: false, newAtStatementBegin: false); |
711 out(")"); | 717 out(")"); |
712 } | 718 } |
713 | 719 |
714 visitCall(Call call) { | 720 visitCall(Call call) { |
715 visitNestedExpression(call.target, LEFT_HAND_SIDE, | 721 visitNestedExpression(call.target, LEFT_HAND_SIDE, |
716 newInForInit: inForInit, | 722 newInForInit: inForInit, newAtStatementBegin: atStatementBegin); |
717 newAtStatementBegin: atStatementBegin); | |
718 out("("); | 723 out("("); |
719 visitCommaSeparated(call.arguments, SPREAD, | 724 visitCommaSeparated(call.arguments, SPREAD, |
720 newInForInit: false, newAtStatementBegin: false); | 725 newInForInit: false, newAtStatementBegin: false); |
721 out(")"); | 726 out(")"); |
722 } | 727 } |
723 | 728 |
724 visitBinary(Binary binary) { | 729 visitBinary(Binary binary) { |
725 Expression left = binary.left; | 730 Expression left = binary.left; |
726 Expression right = binary.right; | 731 Expression right = binary.right; |
727 String op = binary.op; | 732 String op = binary.op; |
728 int leftPrecedenceRequirement; | 733 int leftPrecedenceRequirement; |
729 int rightPrecedenceRequirement; | 734 int rightPrecedenceRequirement; |
730 bool leftSpace = true; // left<HERE>op right | 735 bool leftSpace = true; // left<HERE>op right |
731 switch (op) { | 736 switch (op) { |
732 case ',': | 737 case ',': |
733 // x, (y, z) <=> (x, y), z. | 738 // x, (y, z) <=> (x, y), z. |
734 leftPrecedenceRequirement = EXPRESSION; | 739 leftPrecedenceRequirement = EXPRESSION; |
735 rightPrecedenceRequirement = EXPRESSION; | 740 rightPrecedenceRequirement = EXPRESSION; |
736 leftSpace = false; | 741 leftSpace = false; |
737 break; | 742 break; |
738 case "||": | 743 case "||": |
739 leftPrecedenceRequirement = LOGICAL_OR; | 744 leftPrecedenceRequirement = LOGICAL_OR; |
740 // x || (y || z) <=> (x || y) || z. | 745 // x || (y || z) <=> (x || y) || z. |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
797 case "%": | 802 case "%": |
798 leftPrecedenceRequirement = MULTIPLICATIVE; | 803 leftPrecedenceRequirement = MULTIPLICATIVE; |
799 // We cannot remove parenthesis for "*" because of precision issues. | 804 // We cannot remove parenthesis for "*" because of precision issues. |
800 rightPrecedenceRequirement = UNARY; | 805 rightPrecedenceRequirement = UNARY; |
801 break; | 806 break; |
802 default: | 807 default: |
803 context.error("Forgot operator: $op"); | 808 context.error("Forgot operator: $op"); |
804 } | 809 } |
805 | 810 |
806 visitNestedExpression(left, leftPrecedenceRequirement, | 811 visitNestedExpression(left, leftPrecedenceRequirement, |
807 newInForInit: inForInit, | 812 newInForInit: inForInit, newAtStatementBegin: atStatementBegin); |
808 newAtStatementBegin: atStatementBegin); | |
809 | 813 |
810 if (op == "in" || op == "instanceof") { | 814 if (op == "in" || op == "instanceof") { |
811 // There are cases where the space is not required but without further | 815 // There are cases where the space is not required but without further |
812 // analysis we cannot know. | 816 // analysis we cannot know. |
813 out(" "); | 817 out(" "); |
814 out(op); | 818 out(op); |
815 out(" "); | 819 out(" "); |
816 } else { | 820 } else { |
817 if (leftSpace) spaceOut(); | 821 if (leftSpace) spaceOut(); |
818 out(op); | 822 out(op); |
819 spaceOut(); | 823 spaceOut(); |
820 } | 824 } |
821 visitNestedExpression(right, rightPrecedenceRequirement, | 825 visitNestedExpression(right, rightPrecedenceRequirement, |
822 newInForInit: inForInit, | 826 newInForInit: inForInit, newAtStatementBegin: false); |
823 newAtStatementBegin: false); | |
824 } | 827 } |
825 | 828 |
826 visitPrefix(Prefix unary) { | 829 visitPrefix(Prefix unary) { |
827 String op = unary.op; | 830 String op = unary.op; |
828 switch (op) { | 831 switch (op) { |
829 case "delete": | 832 case "delete": |
830 case "void": | 833 case "void": |
831 case "typeof": | 834 case "typeof": |
832 // There are cases where the space is not required but without further | 835 // There are cases where the space is not required but without further |
833 // analysis we cannot know. | 836 // analysis we cannot know. |
834 out(op); | 837 out(op); |
835 out(" "); | 838 out(" "); |
836 break; | 839 break; |
837 case "+": | 840 case "+": |
838 case "++": | 841 case "++": |
839 if (lastCharCode == charCodes.$PLUS) out(" "); | 842 if (lastCharCode == charCodes.$PLUS) out(" "); |
840 out(op); | 843 out(op); |
841 break; | 844 break; |
842 case "-": | 845 case "-": |
843 case "--": | 846 case "--": |
844 if (lastCharCode == charCodes.$MINUS) out(" "); | 847 if (lastCharCode == charCodes.$MINUS) out(" "); |
845 out(op); | 848 out(op); |
846 break; | 849 break; |
847 default: | 850 default: |
848 out(op); | 851 out(op); |
849 } | 852 } |
850 visitNestedExpression(unary.argument, unary.precedenceLevel, | 853 visitNestedExpression(unary.argument, unary.precedenceLevel, |
851 newInForInit: inForInit, newAtStatementBegin: false); | 854 newInForInit: inForInit, newAtStatementBegin: false); |
852 } | 855 } |
853 | 856 |
854 visitSpread(Spread unary) => visitPrefix(unary); | 857 visitSpread(Spread unary) => visitPrefix(unary); |
855 | 858 |
856 visitYield(Yield yield) { | 859 visitYield(Yield yield) { |
857 out(yield.star ? "yield*" : "yield"); | 860 out(yield.star ? "yield*" : "yield"); |
858 if (yield.value == null) return; | 861 if (yield.value == null) return; |
859 out(" "); | 862 out(" "); |
860 visitNestedExpression(yield.value, yield.precedenceLevel, | 863 visitNestedExpression(yield.value, yield.precedenceLevel, |
861 newInForInit: inForInit, newAtStatementBegin: false); | 864 newInForInit: inForInit, newAtStatementBegin: false); |
862 } | 865 } |
863 | 866 |
864 visitPostfix(Postfix postfix) { | 867 visitPostfix(Postfix postfix) { |
865 visitNestedExpression(postfix.argument, LEFT_HAND_SIDE, | 868 visitNestedExpression(postfix.argument, LEFT_HAND_SIDE, |
866 newInForInit: inForInit, | 869 newInForInit: inForInit, newAtStatementBegin: atStatementBegin); |
867 newAtStatementBegin: atStatementBegin); | |
868 out(postfix.op); | 870 out(postfix.op); |
869 } | 871 } |
870 | 872 |
871 visitThis(This node) { | 873 visitThis(This node) { |
872 out("this"); | 874 out("this"); |
873 } | 875 } |
874 | 876 |
875 visitSuper(Super node) { | 877 visitSuper(Super node) { |
876 out("super"); | 878 out("super"); |
877 } | 879 } |
(...skipping 12 matching lines...) Expand all Loading... |
890 return charCodes.$0 <= charCode && charCode <= charCodes.$9; | 892 return charCodes.$0 <= charCode && charCode <= charCodes.$9; |
891 } | 893 } |
892 | 894 |
893 bool isValidJavaScriptId(String field) { | 895 bool isValidJavaScriptId(String field) { |
894 if (field.length < 3) return false; | 896 if (field.length < 3) return false; |
895 // Ignore the leading and trailing string-delimiter. | 897 // Ignore the leading and trailing string-delimiter. |
896 for (int i = 1; i < field.length - 1; i++) { | 898 for (int i = 1; i < field.length - 1; i++) { |
897 // TODO(floitsch): allow more characters. | 899 // TODO(floitsch): allow more characters. |
898 int charCode = field.codeUnitAt(i); | 900 int charCode = field.codeUnitAt(i); |
899 if (!(charCodes.$a <= charCode && charCode <= charCodes.$z || | 901 if (!(charCodes.$a <= charCode && charCode <= charCodes.$z || |
900 charCodes.$A <= charCode && charCode <= charCodes.$Z || | 902 charCodes.$A <= charCode && charCode <= charCodes.$Z || |
901 charCode == charCodes.$$ || | 903 charCode == charCodes.$$ || |
902 charCode == charCodes.$_ || | 904 charCode == charCodes.$_ || |
903 i != 1 && isDigit(charCode))) { | 905 i != 1 && isDigit(charCode))) { |
904 return false; | 906 return false; |
905 } | 907 } |
906 } | 908 } |
907 | 909 |
908 // TODO(floitsch): normally we should also check that the field is not a | 910 // TODO(floitsch): normally we should also check that the field is not a |
909 // reserved word. We don't generate fields with reserved word names except | 911 // reserved word. We don't generate fields with reserved word names except |
910 // for 'super'. | 912 // for 'super'. |
911 return options.allowKeywordsInProperties || field != '"super"'; | 913 return options.allowKeywordsInProperties || field != '"super"'; |
912 } | 914 } |
913 | 915 |
914 visitAccess(PropertyAccess access) { | 916 visitAccess(PropertyAccess access) { |
915 // Normally we can omit parens on the receiver if it is a Call, even though | 917 // Normally we can omit parens on the receiver if it is a Call, even though |
916 // Call expressions have lower precedence. However this optimization doesn't | 918 // Call expressions have lower precedence. However this optimization doesn't |
917 // work inside New expressions: | 919 // work inside New expressions: |
918 // | 920 // |
919 // new obj.foo().bar() | 921 // new obj.foo().bar() |
920 // | 922 // |
921 // This will be parsed as: | 923 // This will be parsed as: |
922 // | 924 // |
923 // (new obj.foo()).bar() | 925 // (new obj.foo()).bar() |
924 // | 926 // |
925 // Which is incorrect. So we must have parenthesis in this case: | 927 // Which is incorrect. So we must have parenthesis in this case: |
926 // | 928 // |
927 // new (obj.foo()).bar() | 929 // new (obj.foo()).bar() |
928 // | 930 // |
929 int precedence = inNewTarget ? ACCESS : CALL; | 931 int precedence = inNewTarget ? ACCESS : CALL; |
930 | 932 |
931 visitNestedExpression(access.receiver, precedence, | 933 visitNestedExpression(access.receiver, precedence, |
932 newInForInit: inForInit, | 934 newInForInit: inForInit, newAtStatementBegin: atStatementBegin); |
933 newAtStatementBegin: atStatementBegin); | |
934 propertyNameOut(access.selector, inAccess: true); | 935 propertyNameOut(access.selector, inAccess: true); |
935 } | 936 } |
936 | 937 |
937 visitNamedFunction(NamedFunction namedFunction) { | 938 visitNamedFunction(NamedFunction namedFunction) { |
938 functionOut(namedFunction.function, namedFunction.name); | 939 functionOut(namedFunction.function, namedFunction.name); |
939 } | 940 } |
940 | 941 |
941 visitFun(Fun fun) { | 942 visitFun(Fun fun) { |
942 functionOut(fun, null); | 943 functionOut(fun, null); |
943 } | 944 } |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1008 // and [1,,] (array with 1 and a hole) would become [1,] = [1]. | 1009 // and [1,,] (array with 1 and a hole) would become [1,] = [1]. |
1009 out(","); | 1010 out(","); |
1010 continue; | 1011 continue; |
1011 } | 1012 } |
1012 if (i != 0 && !multiline) spaceOut(); | 1013 if (i != 0 && !multiline) spaceOut(); |
1013 if (multiline) { | 1014 if (multiline) { |
1014 forceLine(); | 1015 forceLine(); |
1015 indent(); | 1016 indent(); |
1016 } | 1017 } |
1017 visitNestedExpression(element, ASSIGNMENT, | 1018 visitNestedExpression(element, ASSIGNMENT, |
1018 newInForInit: false, newAtStatementBegin: false); | 1019 newInForInit: false, newAtStatementBegin: false); |
1019 // We can skip the trailing "," for the last element (since it's not | 1020 // We can skip the trailing "," for the last element (since it's not |
1020 // an array hole). | 1021 // an array hole). |
1021 if (i != elements.length - 1) out(","); | 1022 if (i != elements.length - 1) out(","); |
1022 } | 1023 } |
1023 indentLess(); | 1024 indentLess(); |
1024 if (multiline) { | 1025 if (multiline) { |
1025 lineOut(); | 1026 lineOut(); |
1026 indent(); | 1027 indent(); |
1027 } | 1028 } |
1028 out("]"); | 1029 out("]"); |
(...skipping 26 matching lines...) Expand all Loading... |
1055 indent(); | 1056 indent(); |
1056 } | 1057 } |
1057 out("}"); | 1058 out("}"); |
1058 } | 1059 } |
1059 | 1060 |
1060 visitProperty(Property node) { | 1061 visitProperty(Property node) { |
1061 propertyNameOut(node.name); | 1062 propertyNameOut(node.name); |
1062 out(":"); | 1063 out(":"); |
1063 spaceOut(); | 1064 spaceOut(); |
1064 visitNestedExpression(node.value, ASSIGNMENT, | 1065 visitNestedExpression(node.value, ASSIGNMENT, |
1065 newInForInit: false, newAtStatementBegin: false); | 1066 newInForInit: false, newAtStatementBegin: false); |
1066 } | 1067 } |
1067 | 1068 |
1068 visitRegExpLiteral(RegExpLiteral node) { | 1069 visitRegExpLiteral(RegExpLiteral node) { |
1069 out(node.pattern); | 1070 out(node.pattern); |
1070 } | 1071 } |
1071 | 1072 |
1072 visitTemplateString(TemplateString node) { | 1073 visitTemplateString(TemplateString node) { |
1073 out('`'); | 1074 out('`'); |
1074 for (var element in node.elements) { | 1075 for (var element in node.elements) { |
1075 if (element is String) { | 1076 if (element is String) { |
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1177 if (node != null && node.closureAnnotation != null) { | 1178 if (node != null && node.closureAnnotation != null) { |
1178 String comment = node.closureAnnotation.toString(indentation); | 1179 String comment = node.closureAnnotation.toString(indentation); |
1179 if (comment.isNotEmpty) { | 1180 if (comment.isNotEmpty) { |
1180 out(comment); | 1181 out(comment); |
1181 lineOut(); | 1182 lineOut(); |
1182 indent(); | 1183 indent(); |
1183 } | 1184 } |
1184 } | 1185 } |
1185 } | 1186 } |
1186 | 1187 |
1187 void propertyNameOut(Expression node, {bool inMethod: false, | 1188 void propertyNameOut(Expression node, |
1188 bool inAccess: false}) { | 1189 {bool inMethod: false, bool inAccess: false}) { |
1189 | |
1190 if (node is LiteralNumber) { | 1190 if (node is LiteralNumber) { |
1191 LiteralNumber nameNumber = node; | 1191 LiteralNumber nameNumber = node; |
1192 if (inAccess) out('['); | 1192 if (inAccess) out('['); |
1193 out(nameNumber.value); | 1193 out(nameNumber.value); |
1194 if (inAccess) out(']'); | 1194 if (inAccess) out(']'); |
1195 } else { | 1195 } else { |
1196 if (node is LiteralString) { | 1196 if (node is LiteralString) { |
1197 if (isValidJavaScriptId(node.value)) { | 1197 if (isValidJavaScriptId(node.value)) { |
1198 if (inAccess) out('.'); | 1198 if (inAccess) out('.'); |
1199 out(node.valueWithoutQuotes); | 1199 out(node.valueWithoutQuotes); |
1200 } else { | 1200 } else { |
1201 if (inMethod || inAccess) out("["); | 1201 if (inMethod || inAccess) out("["); |
1202 out(node.value); | 1202 out(node.value); |
1203 if (inMethod || inAccess) out("]"); | 1203 if (inMethod || inAccess) out("]"); |
1204 } | 1204 } |
1205 } else { | 1205 } else { |
1206 // ComputedPropertyName | 1206 // ComputedPropertyName |
1207 out("["); | 1207 out("["); |
1208 visitNestedExpression(node, EXPRESSION, | 1208 visitNestedExpression(node, EXPRESSION, |
1209 newInForInit: false, newAtStatementBegin: false); | 1209 newInForInit: false, newAtStatementBegin: false); |
1210 out("]"); | 1210 out("]"); |
1211 } | 1211 } |
1212 } | 1212 } |
1213 } | 1213 } |
1214 | 1214 |
1215 visitImportDeclaration(ImportDeclaration node) { | 1215 visitImportDeclaration(ImportDeclaration node) { |
1216 indent(); | 1216 indent(); |
1217 out('import '); | 1217 out('import '); |
1218 if (node.defaultBinding != null) { | 1218 if (node.defaultBinding != null) { |
1219 visit(node.defaultBinding); | 1219 visit(node.defaultBinding); |
(...skipping 168 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1388 } | 1388 } |
1389 } | 1389 } |
1390 | 1390 |
1391 // Collects all the var declarations in the function. We need to do this in a | 1391 // Collects all the var declarations in the function. We need to do this in a |
1392 // separate pass because JS vars are lifted to the top of the function. | 1392 // separate pass because JS vars are lifted to the top of the function. |
1393 class VarCollector extends BaseVisitor { | 1393 class VarCollector extends BaseVisitor { |
1394 bool nested; | 1394 bool nested; |
1395 final Set<String> vars; | 1395 final Set<String> vars; |
1396 final Set<String> params; | 1396 final Set<String> params; |
1397 | 1397 |
1398 VarCollector() : nested = false, | 1398 VarCollector() |
1399 vars = new Set<String>(), | 1399 : nested = false, |
1400 params = new Set<String>(); | 1400 vars = new Set<String>(), |
| 1401 params = new Set<String>(); |
1401 | 1402 |
1402 void forEachVar(void fn(String v)) => vars.forEach(fn); | 1403 void forEachVar(void fn(String v)) => vars.forEach(fn); |
1403 void forEachParam(void fn(String p)) => params.forEach(fn); | 1404 void forEachParam(void fn(String p)) => params.forEach(fn); |
1404 | 1405 |
1405 void collectVarsInFunction(FunctionExpression fun) { | 1406 void collectVarsInFunction(FunctionExpression fun) { |
1406 if (!nested) { | 1407 if (!nested) { |
1407 nested = true; | 1408 nested = true; |
1408 if (fun.params != null) { | 1409 if (fun.params != null) { |
1409 for (var param in fun.params) { | 1410 for (var param in fun.params) { |
1410 // TODO(jmesserly): add ES6 support. Currently not needed because | 1411 // TODO(jmesserly): add ES6 support. Currently not needed because |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1453 void visitVariableInitialization(VariableInitialization node) { | 1454 void visitVariableInitialization(VariableInitialization node) { |
1454 declareVariable(node.declaration); | 1455 declareVariable(node.declaration); |
1455 if (node.value != null) node.value.accept(this); | 1456 if (node.value != null) node.value.accept(this); |
1456 } | 1457 } |
1457 | 1458 |
1458 void declareVariable(Identifier decl) { | 1459 void declareVariable(Identifier decl) { |
1459 if (decl.allowRename) vars.add(decl.name); | 1460 if (decl.allowRename) vars.add(decl.name); |
1460 } | 1461 } |
1461 } | 1462 } |
1462 | 1463 |
1463 | |
1464 /** | 1464 /** |
1465 * Returns true, if the given node must be wrapped into braces when used | 1465 * Returns true, if the given node must be wrapped into braces when used |
1466 * as then-statement in an [If] that has an else branch. | 1466 * as then-statement in an [If] that has an else branch. |
1467 */ | 1467 */ |
1468 class DanglingElseVisitor extends BaseVisitor<bool> { | 1468 class DanglingElseVisitor extends BaseVisitor<bool> { |
1469 JavaScriptPrintingContext context; | 1469 JavaScriptPrintingContext context; |
1470 | 1470 |
1471 DanglingElseVisitor(this.context); | 1471 DanglingElseVisitor(this.context); |
1472 | 1472 |
1473 bool visitProgram(Program node) => false; | 1473 bool visitProgram(Program node) => false; |
1474 | 1474 |
1475 bool visitNode(Node node) { | 1475 bool visitNode(Node node) { |
1476 context.error("Forgot node: $node"); | 1476 context.error("Forgot node: $node"); |
1477 return null; | 1477 return null; |
1478 } | 1478 } |
1479 | 1479 |
1480 bool visitBlock(Block node) => false; | 1480 bool visitBlock(Block node) => false; |
1481 bool visitExpressionStatement(ExpressionStatement node) => false; | 1481 bool visitExpressionStatement(ExpressionStatement node) => false; |
1482 bool visitEmptyStatement(EmptyStatement node) => false; | 1482 bool visitEmptyStatement(EmptyStatement node) => false; |
1483 bool visitIf(If node) { | 1483 bool visitIf(If node) { |
1484 if (!node.hasElse) return true; | 1484 if (!node.hasElse) return true; |
1485 return node.otherwise.accept(this); | 1485 return node.otherwise.accept(this); |
1486 } | 1486 } |
| 1487 |
1487 bool visitFor(For node) => node.body.accept(this); | 1488 bool visitFor(For node) => node.body.accept(this); |
1488 bool visitForIn(ForIn node) => node.body.accept(this); | 1489 bool visitForIn(ForIn node) => node.body.accept(this); |
1489 bool visitForOf(ForOf node) => node.body.accept(this); | 1490 bool visitForOf(ForOf node) => node.body.accept(this); |
1490 bool visitWhile(While node) => node.body.accept(this); | 1491 bool visitWhile(While node) => node.body.accept(this); |
1491 bool visitDo(Do node) => false; | 1492 bool visitDo(Do node) => false; |
1492 bool visitContinue(Continue node) => false; | 1493 bool visitContinue(Continue node) => false; |
1493 bool visitBreak(Break node) => false; | 1494 bool visitBreak(Break node) => false; |
1494 bool visitReturn(Return node) => false; | 1495 bool visitReturn(Return node) => false; |
1495 bool visitThrow(Throw node) => false; | 1496 bool visitThrow(Throw node) => false; |
1496 bool visitTry(Try node) { | 1497 bool visitTry(Try node) { |
1497 if (node.finallyPart != null) { | 1498 if (node.finallyPart != null) { |
1498 return node.finallyPart.accept(this); | 1499 return node.finallyPart.accept(this); |
1499 } else { | 1500 } else { |
1500 return node.catchPart.accept(this); | 1501 return node.catchPart.accept(this); |
1501 } | 1502 } |
1502 } | 1503 } |
| 1504 |
1503 bool visitCatch(Catch node) => node.body.accept(this); | 1505 bool visitCatch(Catch node) => node.body.accept(this); |
1504 bool visitSwitch(Switch node) => false; | 1506 bool visitSwitch(Switch node) => false; |
1505 bool visitCase(Case node) => false; | 1507 bool visitCase(Case node) => false; |
1506 bool visitDefault(Default node) => false; | 1508 bool visitDefault(Default node) => false; |
1507 bool visitFunctionDeclaration(FunctionDeclaration node) => false; | 1509 bool visitFunctionDeclaration(FunctionDeclaration node) => false; |
1508 bool visitLabeledStatement(LabeledStatement node) | 1510 bool visitLabeledStatement(LabeledStatement node) => node.body.accept(this); |
1509 => node.body.accept(this); | |
1510 bool visitLiteralStatement(LiteralStatement node) => true; | 1511 bool visitLiteralStatement(LiteralStatement node) => true; |
1511 bool visitClassDeclaration(ClassDeclaration node) => false; | 1512 bool visitClassDeclaration(ClassDeclaration node) => false; |
1512 | 1513 |
1513 bool visitExpression(Expression node) => false; | 1514 bool visitExpression(Expression node) => false; |
1514 } | 1515 } |
1515 | 1516 |
1516 | |
1517 abstract class LocalNamer { | 1517 abstract class LocalNamer { |
1518 String getName(Identifier node); | 1518 String getName(Identifier node); |
1519 void enterScope(FunctionExpression node); | 1519 void enterScope(FunctionExpression node); |
1520 void leaveScope(); | 1520 void leaveScope(); |
1521 } | 1521 } |
1522 | 1522 |
1523 | |
1524 class IdentityNamer implements LocalNamer { | 1523 class IdentityNamer implements LocalNamer { |
1525 String getName(Identifier node) => node.name; | 1524 String getName(Identifier node) => node.name; |
1526 void enterScope(FunctionExpression node) {} | 1525 void enterScope(FunctionExpression node) {} |
1527 void leaveScope() {} | 1526 void leaveScope() {} |
1528 } | 1527 } |
1529 | 1528 |
1530 | |
1531 class MinifyRenamer implements LocalNamer { | 1529 class MinifyRenamer implements LocalNamer { |
1532 final List<Map<String, String>> maps = []; | 1530 final List<Map<String, String>> maps = []; |
1533 final List<int> parameterNumberStack = []; | 1531 final List<int> parameterNumberStack = []; |
1534 final List<int> variableNumberStack = []; | 1532 final List<int> variableNumberStack = []; |
1535 int parameterNumber = 0; | 1533 int parameterNumber = 0; |
1536 int variableNumber = 0; | 1534 int variableNumber = 0; |
1537 | 1535 |
1538 void enterScope(FunctionExpression node) { | 1536 void enterScope(FunctionExpression node) { |
1539 var vars = new VarCollector(); | 1537 var vars = new VarCollector(); |
1540 node.accept(vars); | 1538 node.accept(vars); |
(...skipping 19 matching lines...) Expand all Loading... |
1560 if (replacement != null) return replacement; | 1558 if (replacement != null) return replacement; |
1561 } | 1559 } |
1562 return oldName; | 1560 return oldName; |
1563 } | 1561 } |
1564 | 1562 |
1565 static const LOWER_CASE_LETTERS = 26; | 1563 static const LOWER_CASE_LETTERS = 26; |
1566 static const LETTERS = LOWER_CASE_LETTERS; | 1564 static const LETTERS = LOWER_CASE_LETTERS; |
1567 static const DIGITS = 10; | 1565 static const DIGITS = 10; |
1568 | 1566 |
1569 static int nthLetter(int n) { | 1567 static int nthLetter(int n) { |
1570 return (n < LOWER_CASE_LETTERS) ? | 1568 return (n < LOWER_CASE_LETTERS) |
1571 charCodes.$a + n : | 1569 ? charCodes.$a + n |
1572 charCodes.$A + n - LOWER_CASE_LETTERS; | 1570 : charCodes.$A + n - LOWER_CASE_LETTERS; |
1573 } | 1571 } |
1574 | 1572 |
1575 // Parameters go from a to z and variables go from z to a. This makes each | 1573 // Parameters go from a to z and variables go from z to a. This makes each |
1576 // argument list and each top-of-function var declaration look similar and | 1574 // argument list and each top-of-function var declaration look similar and |
1577 // helps gzip compress the file. If we have more than 26 arguments and | 1575 // helps gzip compress the file. If we have more than 26 arguments and |
1578 // variables then we meet somewhere in the middle of the alphabet. After | 1576 // variables then we meet somewhere in the middle of the alphabet. After |
1579 // that we give up trying to be nice to the compression algorithm and just | 1577 // that we give up trying to be nice to the compression algorithm and just |
1580 // use the same namespace for arguments and variables, starting with A, and | 1578 // use the same namespace for arguments and variables, starting with A, and |
1581 // moving on to a0, a1, etc. | 1579 // moving on to a0, a1, etc. |
1582 String declareVariable(String oldName) { | 1580 String declareVariable(String oldName) { |
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1654 /// [visitIdentifier] otherwise. | 1652 /// [visitIdentifier] otherwise. |
1655 abstract class VariableDeclarationVisitor<T> extends BaseVisitor<T> { | 1653 abstract class VariableDeclarationVisitor<T> extends BaseVisitor<T> { |
1656 declare(Identifier node); | 1654 declare(Identifier node); |
1657 | 1655 |
1658 visitFunctionExpression(FunctionExpression node) { | 1656 visitFunctionExpression(FunctionExpression node) { |
1659 node.params.forEach(_scanVariableBinding); | 1657 node.params.forEach(_scanVariableBinding); |
1660 node.body.accept(this); | 1658 node.body.accept(this); |
1661 } | 1659 } |
1662 | 1660 |
1663 _scanVariableBinding(VariableBinding d) { | 1661 _scanVariableBinding(VariableBinding d) { |
1664 if (d is Identifier) declare(d); | 1662 if (d is Identifier) |
1665 else d.accept(this); | 1663 declare(d); |
| 1664 else |
| 1665 d.accept(this); |
1666 } | 1666 } |
1667 | 1667 |
1668 visitRestParameter(RestParameter node) { | 1668 visitRestParameter(RestParameter node) { |
1669 _scanVariableBinding(node.parameter); | 1669 _scanVariableBinding(node.parameter); |
1670 super.visitRestParameter(node); | 1670 super.visitRestParameter(node); |
1671 } | 1671 } |
1672 | 1672 |
1673 visitDestructuredVariable(DestructuredVariable node) { | 1673 visitDestructuredVariable(DestructuredVariable node) { |
1674 var name = node.name; | 1674 var name = node.name; |
1675 if (name is Identifier) _scanVariableBinding(name); | 1675 if (name is Identifier) _scanVariableBinding(name); |
(...skipping 24 matching lines...) Expand all Loading... |
1700 declare(node.name); | 1700 declare(node.name); |
1701 node.function.accept(this); | 1701 node.function.accept(this); |
1702 } | 1702 } |
1703 | 1703 |
1704 visitClassExpression(ClassExpression node) { | 1704 visitClassExpression(ClassExpression node) { |
1705 declare(node.name); | 1705 declare(node.name); |
1706 if (node.heritage != null) node.heritage.accept(this); | 1706 if (node.heritage != null) node.heritage.accept(this); |
1707 for (Method element in node.methods) element.accept(this); | 1707 for (Method element in node.methods) element.accept(this); |
1708 } | 1708 } |
1709 } | 1709 } |
OLD | NEW |