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 | 7 |
8 class JavaScriptPrintingOptions { | 8 class JavaScriptPrintingOptions { |
9 final bool shouldCompressOutput; | 9 final bool shouldCompressOutput; |
10 final bool minifyLocalVariables; | 10 final bool minifyLocalVariables; |
11 final bool preferSemicolonToNewlineInMinifiedOutput; | 11 final bool preferSemicolonToNewlineInMinifiedOutput; |
12 | 12 |
13 JavaScriptPrintingOptions( | 13 JavaScriptPrintingOptions( |
14 {this.shouldCompressOutput: false, | 14 {this.shouldCompressOutput: false, |
15 this.minifyLocalVariables: false, | 15 this.minifyLocalVariables: false, |
16 this.preferSemicolonToNewlineInMinifiedOutput: false}); | 16 this.preferSemicolonToNewlineInMinifiedOutput: false}); |
17 } | 17 } |
18 | 18 |
19 | 19 |
20 /// An environment in which JavaScript printing is done. Provides emitting of | 20 /// An environment in which JavaScript printing is done. Provides emitting of |
21 /// text and pre- and post-visit callbacks. | 21 /// text and pre- and post-visit callbacks. |
22 abstract class JavaScriptPrintingContext { | 22 abstract class JavaScriptPrintingContext { |
23 /// Signals an error. This should happen only for serious internal errors. | 23 /// Signals an error. This should happen only for serious internal errors. |
24 void error(String message) { throw message; } | 24 void error(String message) { throw message; } |
25 | 25 |
26 /// Adds [string] to the output. | 26 /// Adds [string] to the output. |
27 void emit(String string); | 27 void emit(String string); |
28 | 28 |
29 /// Callback immediately before printing [node]. Whitespace may be printed | 29 /// Callback for the start of printing of [node]. [startPosition] is the |
30 /// after this callback before the first non-whitespace character for [node]. | 30 /// position of the first non-whitespace character of [node]. |
31 void enterNode(Node node) {} | 31 /// |
32 /// Callback after printing the last character representing [node]. | 32 /// [enterNode] is called in pre-traversal order. |
33 void exitNode(Node node) {} | 33 void enterNode(Node node, int startPosition) {} |
| 34 |
| 35 /// Callback for the end of printing of [node]. [startPosition] is the |
| 36 /// position of the first non-whitespace character of [node] (also provided |
| 37 /// in the [enterNode] callback), [endPosition] is the position immediately |
| 38 /// following the last character of [node]. [closingPosition] is the |
| 39 /// position of the ending delimiter of [node]. This is only provided for |
| 40 /// [Fun] nodes and is `null` otherwise. |
| 41 /// |
| 42 /// [enterNode] is called in post-traversal order. |
| 43 void exitNode(Node node, |
| 44 int startPosition, |
| 45 int endPosition, |
| 46 int closingPosition) {} |
34 } | 47 } |
35 | 48 |
36 /// A simple implementation of [JavaScriptPrintingContext] suitable for tests. | 49 /// A simple implementation of [JavaScriptPrintingContext] suitable for tests. |
37 class SimpleJavaScriptPrintingContext extends JavaScriptPrintingContext { | 50 class SimpleJavaScriptPrintingContext extends JavaScriptPrintingContext { |
38 final StringBuffer buffer = new StringBuffer(); | 51 final StringBuffer buffer = new StringBuffer(); |
39 | 52 |
40 void emit(String string) { | 53 void emit(String string) { |
41 buffer.write(string); | 54 buffer.write(string); |
42 } | 55 } |
43 | 56 |
44 String getText() => buffer.toString(); | 57 String getText() => buffer.toString(); |
45 } | 58 } |
46 | 59 |
47 | 60 |
48 class Printer implements NodeVisitor { | 61 class Printer implements NodeVisitor { |
49 final JavaScriptPrintingOptions options; | 62 final JavaScriptPrintingOptions options; |
50 final JavaScriptPrintingContext context; | 63 final JavaScriptPrintingContext context; |
51 final bool shouldCompressOutput; | 64 final bool shouldCompressOutput; |
52 final DanglingElseVisitor danglingElseVisitor; | 65 final DanglingElseVisitor danglingElseVisitor; |
53 final LocalNamer localNamer; | 66 final LocalNamer localNamer; |
54 | 67 |
| 68 int _charCount = 0; |
55 bool inForInit = false; | 69 bool inForInit = false; |
56 bool atStatementBegin = false; | 70 bool atStatementBegin = false; |
57 bool pendingSemicolon = false; | 71 bool pendingSemicolon = false; |
58 bool pendingSpace = false; | 72 bool pendingSpace = false; |
59 | 73 |
60 // The current indentation level. | 74 // The current indentation level. |
61 int _indentLevel = 0; | 75 int _indentLevel = 0; |
62 // A cache of all indentation strings used so far. | 76 // A cache of all indentation strings used so far. |
63 List<String> _indentList = <String>[""]; | 77 List<String> _indentList = <String>[""]; |
64 | 78 |
(...skipping 26 matching lines...) Expand all Loading... |
91 } | 105 } |
92 | 106 |
93 void indentMore() { | 107 void indentMore() { |
94 _indentLevel++; | 108 _indentLevel++; |
95 } | 109 } |
96 | 110 |
97 void indentLess() { | 111 void indentLess() { |
98 _indentLevel--; | 112 _indentLevel--; |
99 } | 113 } |
100 | 114 |
101 | |
102 /// Always emit a newline, even under `enableMinification`. | 115 /// Always emit a newline, even under `enableMinification`. |
103 void forceLine() { | 116 void forceLine() { |
104 out("\n"); | 117 out("\n", isWhitespace: true); |
105 } | 118 } |
| 119 |
106 /// Emits a newline for readability. | 120 /// Emits a newline for readability. |
107 void lineOut() { | 121 void lineOut() { |
108 if (!shouldCompressOutput) forceLine(); | 122 if (!shouldCompressOutput) forceLine(); |
109 } | 123 } |
| 124 |
110 void spaceOut() { | 125 void spaceOut() { |
111 if (!shouldCompressOutput) out(" "); | 126 if (!shouldCompressOutput) out(" ", isWhitespace: true); |
112 } | 127 } |
113 | 128 |
114 String lastAddedString = null; | 129 String lastAddedString = null; |
| 130 |
115 int get lastCharCode { | 131 int get lastCharCode { |
116 if (lastAddedString == null) return 0; | 132 if (lastAddedString == null) return 0; |
117 assert(lastAddedString.length != ""); | 133 assert(lastAddedString.length != ""); |
118 return lastAddedString.codeUnitAt(lastAddedString.length - 1); | 134 return lastAddedString.codeUnitAt(lastAddedString.length - 1); |
119 } | 135 } |
120 | 136 |
121 void out(String str) { | 137 void out(String str, {bool isWhitespace: false}) { |
122 if (str != "") { | 138 if (str != "") { |
123 if (pendingSemicolon) { | 139 if (pendingSemicolon) { |
124 if (!shouldCompressOutput) { | 140 if (!shouldCompressOutput) { |
125 context.emit(";"); | 141 _emit(";"); |
126 } else if (str != "}") { | 142 } else if (str != "}") { |
127 // We want to output newline instead of semicolon because it makes | 143 // We want to output newline instead of semicolon because it makes |
128 // the raw stack traces much easier to read and it also makes line- | 144 // the raw stack traces much easier to read and it also makes line- |
129 // based tools like diff work much better. JavaScript will | 145 // based tools like diff work much better. JavaScript will |
130 // automatically insert the semicolon at the newline if it means a | 146 // automatically insert the semicolon at the newline if it means a |
131 // parsing error is avoided, so we can only do this trick if the | 147 // parsing error is avoided, so we can only do this trick if the |
132 // next line is not something that can be glued onto a valid | 148 // next line is not something that can be glued onto a valid |
133 // expression to make a new valid expression. | 149 // expression to make a new valid expression. |
134 | 150 |
135 // If we're using the new emitter where most pretty printed code | 151 // If we're using the new emitter where most pretty printed code |
136 // is escaped in strings, it is a lot easier to deal with semicolons | 152 // is escaped in strings, it is a lot easier to deal with semicolons |
137 // than newlines because the former doesn't need escaping. | 153 // than newlines because the former doesn't need escaping. |
138 if (options.preferSemicolonToNewlineInMinifiedOutput || | 154 if (options.preferSemicolonToNewlineInMinifiedOutput || |
139 expressionContinuationRegExp.hasMatch(str)) { | 155 expressionContinuationRegExp.hasMatch(str)) { |
140 context.emit(";"); | 156 _emit(";"); |
141 } else { | 157 } else { |
142 context.emit("\n"); | 158 _emit("\n"); |
143 } | 159 } |
144 } | 160 } |
145 } | 161 } |
146 if (pendingSpace && | 162 if (pendingSpace && |
147 (!shouldCompressOutput || identifierCharacterRegExp.hasMatch(str))) { | 163 (!shouldCompressOutput || identifierCharacterRegExp.hasMatch(str))) { |
148 context.emit(" "); | 164 _emit(" "); |
149 } | 165 } |
150 pendingSpace = false; | 166 pendingSpace = false; |
151 pendingSemicolon = false; | 167 pendingSemicolon = false; |
152 context.emit(str); | 168 if (!isWhitespace) { |
| 169 enterNode(); |
| 170 } |
| 171 _emit(str); |
153 lastAddedString = str; | 172 lastAddedString = str; |
154 } | 173 } |
155 } | 174 } |
156 | 175 |
157 void outLn(String str) { | 176 void outLn(String str) { |
158 out(str); | 177 out(str); |
159 lineOut(); | 178 lineOut(); |
160 } | 179 } |
161 | 180 |
162 void outSemicolonLn() { | 181 void outSemicolonLn() { |
163 if (shouldCompressOutput) { | 182 if (shouldCompressOutput) { |
164 pendingSemicolon = true; | 183 pendingSemicolon = true; |
165 } else { | 184 } else { |
166 out(";"); | 185 out(";"); |
167 forceLine(); | 186 forceLine(); |
168 } | 187 } |
169 } | 188 } |
170 | 189 |
171 void outIndent(String str) { indent(); out(str); } | 190 void outIndent(String str) { |
172 void outIndentLn(String str) { indent(); outLn(str); } | 191 indent(); |
| 192 out(str); |
| 193 } |
| 194 |
| 195 void outIndentLn(String str) { |
| 196 indent(); |
| 197 outLn(str); |
| 198 } |
| 199 |
173 void indent() { | 200 void indent() { |
174 if (!shouldCompressOutput) { | 201 if (!shouldCompressOutput) { |
175 out(indentation); | 202 out(indentation, isWhitespace: true); |
176 } | 203 } |
177 } | 204 } |
178 | 205 |
179 visit(Node node) { | 206 EnterExitNode currentNode; |
180 context.enterNode(node); | 207 |
181 node.accept(this); | 208 void _emit(String text) { |
182 context.exitNode(node); | 209 context.emit(text); |
| 210 _charCount += text.length; |
183 } | 211 } |
184 | 212 |
185 visitCommaSeparated(List<Node> nodes, int hasRequiredType, | 213 void startNode(Node node) { |
186 {bool newInForInit, bool newAtStatementBegin}) { | 214 currentNode = new EnterExitNode(currentNode, node); |
| 215 } |
| 216 |
| 217 void enterNode() { |
| 218 currentNode.addToNode(context, _charCount); |
| 219 } |
| 220 |
| 221 void endNode(Node node) { |
| 222 assert(currentNode.node == node); |
| 223 currentNode = currentNode.exitNode(context, _charCount); |
| 224 } |
| 225 |
| 226 void visit(Node node) { |
| 227 startNode(node); |
| 228 node.accept(this); |
| 229 endNode(node); |
| 230 } |
| 231 |
| 232 void visitCommaSeparated(List<Node> nodes, int hasRequiredType, |
| 233 {bool newInForInit, bool newAtStatementBegin}) { |
187 for (int i = 0; i < nodes.length; i++) { | 234 for (int i = 0; i < nodes.length; i++) { |
188 if (i != 0) { | 235 if (i != 0) { |
189 atStatementBegin = false; | 236 atStatementBegin = false; |
190 out(","); | 237 out(","); |
191 spaceOut(); | 238 spaceOut(); |
192 } | 239 } |
193 visitNestedExpression(nodes[i], hasRequiredType, | 240 visitNestedExpression(nodes[i], hasRequiredType, |
194 newInForInit: newInForInit, | 241 newInForInit: newInForInit, |
195 newAtStatementBegin: newAtStatementBegin); | 242 newAtStatementBegin: newAtStatementBegin); |
196 } | 243 } |
197 } | 244 } |
198 | 245 |
199 visitAll(List<Node> nodes) { | 246 void visitAll(List<Node> nodes) { |
200 nodes.forEach(visit); | 247 nodes.forEach(visit); |
201 } | 248 } |
202 | 249 |
203 visitProgram(Program program) { | 250 @override |
204 visitAll(program.body); | 251 void visitProgram(Program program) { |
| 252 if (program.body.isNotEmpty) { |
| 253 visitAll(program.body); |
| 254 } |
205 } | 255 } |
206 | 256 |
207 Statement unwrapBlockIfSingleStatement(Statement body) { | 257 Statement unwrapBlockIfSingleStatement(Statement body) { |
208 Statement result = body; | 258 Statement result = body; |
209 while (result is Block) { | 259 while (result is Block) { |
210 Block block = result; | 260 Block block = result; |
211 if (block.statements.length != 1) break; | 261 if (block.statements.length != 1) break; |
212 result = block.statements.single; | 262 result = block.statements.single; |
213 } | 263 } |
214 return result; | 264 return result; |
215 } | 265 } |
216 | 266 |
217 bool blockBody(Statement body, {bool needsSeparation, bool needsNewline}) { | 267 bool blockBody(Statement body, {bool needsSeparation, bool needsNewline}) { |
218 if (body is Block) { | 268 if (body is Block) { |
219 spaceOut(); | 269 spaceOut(); |
220 blockOut(body, shouldIndent: false, needsNewline: needsNewline); | 270 blockOut(body, shouldIndent: false, needsNewline: needsNewline); |
221 return true; | 271 return true; |
222 } | 272 } |
223 if (shouldCompressOutput && needsSeparation) { | 273 if (shouldCompressOutput && needsSeparation) { |
224 // If [shouldCompressOutput] is false, then the 'lineOut' will insert | 274 // If [shouldCompressOutput] is false, then the 'lineOut' will insert |
225 // the separation. | 275 // the separation. |
226 out(" "); | 276 out(" ", isWhitespace: true); |
227 } else { | 277 } else { |
228 lineOut(); | 278 lineOut(); |
229 } | 279 } |
230 indentMore(); | 280 indentMore(); |
231 visit(body); | 281 visit(body); |
232 indentLess(); | 282 indentLess(); |
233 return false; | 283 return false; |
234 } | 284 } |
235 | 285 |
236 void blockOutWithoutBraces(Node node) { | 286 void blockOutWithoutBraces(Node node) { |
237 if (node is Block) { | 287 if (node is Block) { |
238 context.enterNode(node); | 288 startNode(node); |
239 Block block = node; | 289 Block block = node; |
240 block.statements.forEach(blockOutWithoutBraces); | 290 block.statements.forEach(blockOutWithoutBraces); |
241 context.exitNode(node); | 291 endNode(node); |
242 } else { | 292 } else { |
243 visit(node); | 293 visit(node); |
244 } | 294 } |
245 } | 295 } |
246 | 296 |
247 void blockOut(Block node, {bool shouldIndent, bool needsNewline}) { | 297 int blockOut(Block node, {bool shouldIndent, bool needsNewline}) { |
248 if (shouldIndent) indent(); | 298 if (shouldIndent) indent(); |
249 context.enterNode(node); | 299 startNode(node); |
250 out("{"); | 300 out("{"); |
251 lineOut(); | 301 lineOut(); |
252 indentMore(); | 302 indentMore(); |
253 node.statements.forEach(blockOutWithoutBraces); | 303 node.statements.forEach(blockOutWithoutBraces); |
254 indentLess(); | 304 indentLess(); |
255 indent(); | 305 indent(); |
256 out("}"); | 306 out("}"); |
257 context.exitNode(node); | 307 int closingPosition = _charCount - 1; |
| 308 endNode(node); |
258 if (needsNewline) lineOut(); | 309 if (needsNewline) lineOut(); |
| 310 return closingPosition; |
259 } | 311 } |
260 | 312 |
261 visitBlock(Block block) { | 313 @override |
| 314 void visitBlock(Block block) { |
262 blockOut(block, shouldIndent: true, needsNewline: true); | 315 blockOut(block, shouldIndent: true, needsNewline: true); |
263 } | 316 } |
264 | 317 |
265 visitExpressionStatement(ExpressionStatement expressionStatement) { | 318 @override |
| 319 void visitExpressionStatement(ExpressionStatement node) { |
266 indent(); | 320 indent(); |
267 visitNestedExpression(expressionStatement.expression, EXPRESSION, | 321 visitNestedExpression(node.expression, EXPRESSION, |
268 newInForInit: false, newAtStatementBegin: true); | 322 newInForInit: false, newAtStatementBegin: true); |
269 outSemicolonLn(); | 323 outSemicolonLn(); |
270 } | 324 } |
271 | 325 |
272 visitEmptyStatement(EmptyStatement nop) { | 326 @override |
| 327 void visitEmptyStatement(EmptyStatement node) { |
273 outIndentLn(";"); | 328 outIndentLn(";"); |
274 } | 329 } |
275 | 330 |
276 void ifOut(If node, bool shouldIndent) { | 331 void ifOut(If node, bool shouldIndent) { |
277 Statement then = unwrapBlockIfSingleStatement(node.then); | 332 Statement then = unwrapBlockIfSingleStatement(node.then); |
278 Statement elsePart = node.otherwise; | 333 Statement elsePart = node.otherwise; |
279 bool hasElse = node.hasElse; | 334 bool hasElse = node.hasElse; |
280 | 335 |
281 // Handle dangling elses and a work-around for Android 4.0 stock browser. | 336 // Handle dangling elses and a work-around for Android 4.0 stock browser. |
282 // Android 4.0 requires braces for a single do-while in the `then` branch. | 337 // Android 4.0 requires braces for a single do-while in the `then` branch. |
(...skipping 23 matching lines...) Expand all Loading... |
306 if (elsePart is If) { | 361 if (elsePart is If) { |
307 pendingSpace = true; | 362 pendingSpace = true; |
308 ifOut(elsePart, false); | 363 ifOut(elsePart, false); |
309 } else { | 364 } else { |
310 blockBody(unwrapBlockIfSingleStatement(elsePart), | 365 blockBody(unwrapBlockIfSingleStatement(elsePart), |
311 needsSeparation: true, needsNewline: true); | 366 needsSeparation: true, needsNewline: true); |
312 } | 367 } |
313 } | 368 } |
314 } | 369 } |
315 | 370 |
316 visitIf(If node) { | 371 @override |
| 372 void visitIf(If node) { |
317 ifOut(node, true); | 373 ifOut(node, true); |
318 } | 374 } |
319 | 375 |
320 visitFor(For loop) { | 376 @override |
| 377 void visitFor(For loop) { |
321 outIndent("for"); | 378 outIndent("for"); |
322 spaceOut(); | 379 spaceOut(); |
323 out("("); | 380 out("("); |
324 if (loop.init != null) { | 381 if (loop.init != null) { |
325 visitNestedExpression(loop.init, EXPRESSION, | 382 visitNestedExpression(loop.init, EXPRESSION, |
326 newInForInit: true, newAtStatementBegin: false); | 383 newInForInit: true, newAtStatementBegin: false); |
327 } | 384 } |
328 out(";"); | 385 out(";"); |
329 if (loop.condition != null) { | 386 if (loop.condition != null) { |
330 spaceOut(); | 387 spaceOut(); |
331 visitNestedExpression(loop.condition, EXPRESSION, | 388 visitNestedExpression(loop.condition, EXPRESSION, |
332 newInForInit: false, newAtStatementBegin: false); | 389 newInForInit: false, newAtStatementBegin: false); |
333 } | 390 } |
334 out(";"); | 391 out(";"); |
335 if (loop.update != null) { | 392 if (loop.update != null) { |
336 spaceOut(); | 393 spaceOut(); |
337 visitNestedExpression(loop.update, EXPRESSION, | 394 visitNestedExpression(loop.update, EXPRESSION, |
338 newInForInit: false, newAtStatementBegin: false); | 395 newInForInit: false, newAtStatementBegin: false); |
339 } | 396 } |
340 out(")"); | 397 out(")"); |
341 blockBody(unwrapBlockIfSingleStatement(loop.body), | 398 blockBody(unwrapBlockIfSingleStatement(loop.body), |
342 needsSeparation: false, needsNewline: true); | 399 needsSeparation: false, needsNewline: true); |
343 } | 400 } |
344 | 401 |
345 visitForIn(ForIn loop) { | 402 @override |
| 403 void visitForIn(ForIn loop) { |
346 outIndent("for"); | 404 outIndent("for"); |
347 spaceOut(); | 405 spaceOut(); |
348 out("("); | 406 out("("); |
349 visitNestedExpression(loop.leftHandSide, EXPRESSION, | 407 visitNestedExpression(loop.leftHandSide, EXPRESSION, |
350 newInForInit: true, newAtStatementBegin: false); | 408 newInForInit: true, newAtStatementBegin: false); |
351 out(" in"); | 409 out(" in"); |
352 pendingSpace = true; | 410 pendingSpace = true; |
353 visitNestedExpression(loop.object, EXPRESSION, | 411 visitNestedExpression(loop.object, EXPRESSION, |
354 newInForInit: false, newAtStatementBegin: false); | 412 newInForInit: false, newAtStatementBegin: false); |
355 out(")"); | 413 out(")"); |
356 blockBody(unwrapBlockIfSingleStatement(loop.body), | 414 blockBody(unwrapBlockIfSingleStatement(loop.body), |
357 needsSeparation: false, needsNewline: true); | 415 needsSeparation: false, needsNewline: true); |
358 } | 416 } |
359 | 417 |
360 visitWhile(While loop) { | 418 @override |
| 419 void visitWhile(While loop) { |
361 outIndent("while"); | 420 outIndent("while"); |
362 spaceOut(); | 421 spaceOut(); |
363 out("("); | 422 out("("); |
364 visitNestedExpression(loop.condition, EXPRESSION, | 423 visitNestedExpression(loop.condition, EXPRESSION, |
365 newInForInit: false, newAtStatementBegin: false); | 424 newInForInit: false, newAtStatementBegin: false); |
366 out(")"); | 425 out(")"); |
367 blockBody(unwrapBlockIfSingleStatement(loop.body), | 426 blockBody(unwrapBlockIfSingleStatement(loop.body), |
368 needsSeparation: false, needsNewline: true); | 427 needsSeparation: false, needsNewline: true); |
369 } | 428 } |
370 | 429 |
371 visitDo(Do loop) { | 430 @override |
| 431 void visitDo(Do loop) { |
372 outIndent("do"); | 432 outIndent("do"); |
373 if (blockBody(unwrapBlockIfSingleStatement(loop.body), | 433 if (blockBody(unwrapBlockIfSingleStatement(loop.body), |
374 needsSeparation: true, needsNewline: false)) { | 434 needsSeparation: true, needsNewline: false)) { |
375 spaceOut(); | 435 spaceOut(); |
376 } else { | 436 } else { |
377 indent(); | 437 indent(); |
378 } | 438 } |
379 out("while"); | 439 out("while"); |
380 spaceOut(); | 440 spaceOut(); |
381 out("("); | 441 out("("); |
382 visitNestedExpression(loop.condition, EXPRESSION, | 442 visitNestedExpression(loop.condition, EXPRESSION, |
383 newInForInit: false, newAtStatementBegin: false); | 443 newInForInit: false, newAtStatementBegin: false); |
384 out(")"); | 444 out(")"); |
385 outSemicolonLn(); | 445 outSemicolonLn(); |
386 } | 446 } |
387 | 447 |
388 visitContinue(Continue node) { | 448 @override |
| 449 void visitContinue(Continue node) { |
389 if (node.targetLabel == null) { | 450 if (node.targetLabel == null) { |
390 outIndent("continue"); | 451 outIndent("continue"); |
391 } else { | 452 } else { |
392 outIndent("continue ${node.targetLabel}"); | 453 outIndent("continue ${node.targetLabel}"); |
393 } | 454 } |
394 outSemicolonLn(); | 455 outSemicolonLn(); |
395 } | 456 } |
396 | 457 |
397 visitBreak(Break node) { | 458 @override |
| 459 void visitBreak(Break node) { |
398 if (node.targetLabel == null) { | 460 if (node.targetLabel == null) { |
399 outIndent("break"); | 461 outIndent("break"); |
400 } else { | 462 } else { |
401 outIndent("break ${node.targetLabel}"); | 463 outIndent("break ${node.targetLabel}"); |
402 } | 464 } |
403 outSemicolonLn(); | 465 outSemicolonLn(); |
404 } | 466 } |
405 | 467 |
406 visitReturn(Return node) { | 468 @override |
| 469 void visitReturn(Return node) { |
407 if (node.value == null) { | 470 if (node.value == null) { |
408 outIndent("return"); | 471 outIndent("return"); |
409 } else { | 472 } else { |
410 outIndent("return"); | 473 outIndent("return"); |
411 pendingSpace = true; | 474 pendingSpace = true; |
412 visitNestedExpression(node.value, EXPRESSION, | 475 visitNestedExpression(node.value, EXPRESSION, |
413 newInForInit: false, newAtStatementBegin: false); | 476 newInForInit: false, newAtStatementBegin: false); |
414 } | 477 } |
415 outSemicolonLn(); | 478 outSemicolonLn(); |
416 } | 479 } |
417 | 480 |
418 visitDartYield(DartYield node) { | 481 @override |
| 482 void visitDartYield(DartYield node) { |
419 if (node.hasStar) { | 483 if (node.hasStar) { |
420 outIndent("yield*"); | 484 outIndent("yield*"); |
421 } else { | 485 } else { |
422 outIndent("yield"); | 486 outIndent("yield"); |
423 } | 487 } |
424 pendingSpace = true; | 488 pendingSpace = true; |
425 visitNestedExpression(node.expression, EXPRESSION, | 489 visitNestedExpression(node.expression, EXPRESSION, |
426 newInForInit: false, newAtStatementBegin: false); | 490 newInForInit: false, newAtStatementBegin: false); |
427 outSemicolonLn(); | 491 outSemicolonLn(); |
428 } | 492 } |
429 | 493 |
430 | 494 @override |
431 visitThrow(Throw node) { | 495 void visitThrow(Throw node) { |
432 outIndent("throw"); | 496 outIndent("throw"); |
433 pendingSpace = true; | 497 pendingSpace = true; |
434 visitNestedExpression(node.expression, EXPRESSION, | 498 visitNestedExpression(node.expression, EXPRESSION, |
435 newInForInit: false, newAtStatementBegin: false); | 499 newInForInit: false, newAtStatementBegin: false); |
436 outSemicolonLn(); | 500 outSemicolonLn(); |
437 } | 501 } |
438 | 502 |
439 visitTry(Try node) { | 503 @override |
| 504 void visitTry(Try node) { |
440 outIndent("try"); | 505 outIndent("try"); |
441 blockBody(node.body, needsSeparation: true, needsNewline: false); | 506 blockBody(node.body, needsSeparation: true, needsNewline: false); |
442 if (node.catchPart != null) { | 507 if (node.catchPart != null) { |
443 visit(node.catchPart); | 508 visit(node.catchPart); |
444 } | 509 } |
445 if (node.finallyPart != null) { | 510 if (node.finallyPart != null) { |
446 spaceOut(); | 511 spaceOut(); |
447 out("finally"); | 512 out("finally"); |
448 blockBody(node.finallyPart, needsSeparation: true, needsNewline: true); | 513 blockBody(node.finallyPart, needsSeparation: true, needsNewline: true); |
449 } else { | 514 } else { |
450 lineOut(); | 515 lineOut(); |
451 } | 516 } |
452 } | 517 } |
453 | 518 |
454 visitCatch(Catch node) { | 519 @override |
| 520 void visitCatch(Catch node) { |
455 spaceOut(); | 521 spaceOut(); |
456 out("catch"); | 522 out("catch"); |
457 spaceOut(); | 523 spaceOut(); |
458 out("("); | 524 out("("); |
459 visitNestedExpression(node.declaration, EXPRESSION, | 525 visitNestedExpression(node.declaration, EXPRESSION, |
460 newInForInit: false, newAtStatementBegin: false); | 526 newInForInit: false, newAtStatementBegin: false); |
461 out(")"); | 527 out(")"); |
462 blockBody(node.body, needsSeparation: false, needsNewline: false); | 528 blockBody(node.body, needsSeparation: false, needsNewline: false); |
463 } | 529 } |
464 | 530 |
465 visitSwitch(Switch node) { | 531 @override |
| 532 void visitSwitch(Switch node) { |
466 outIndent("switch"); | 533 outIndent("switch"); |
467 spaceOut(); | 534 spaceOut(); |
468 out("("); | 535 out("("); |
469 visitNestedExpression(node.key, EXPRESSION, | 536 visitNestedExpression(node.key, EXPRESSION, |
470 newInForInit: false, newAtStatementBegin: false); | 537 newInForInit: false, newAtStatementBegin: false); |
471 out(")"); | 538 out(")"); |
472 spaceOut(); | 539 spaceOut(); |
473 outLn("{"); | 540 outLn("{"); |
474 indentMore(); | 541 indentMore(); |
475 visitAll(node.cases); | 542 visitAll(node.cases); |
476 indentLess(); | 543 indentLess(); |
477 outIndentLn("}"); | 544 outIndentLn("}"); |
478 } | 545 } |
479 | 546 |
480 visitCase(Case node) { | 547 @override |
| 548 void visitCase(Case node) { |
481 outIndent("case"); | 549 outIndent("case"); |
482 pendingSpace = true; | 550 pendingSpace = true; |
483 visitNestedExpression(node.expression, EXPRESSION, | 551 visitNestedExpression(node.expression, EXPRESSION, |
484 newInForInit: false, newAtStatementBegin: false); | 552 newInForInit: false, newAtStatementBegin: false); |
485 outLn(":"); | 553 outLn(":"); |
486 if (!node.body.statements.isEmpty) { | 554 if (!node.body.statements.isEmpty) { |
487 indentMore(); | 555 indentMore(); |
488 blockOutWithoutBraces(node.body); | 556 blockOutWithoutBraces(node.body); |
489 indentLess(); | 557 indentLess(); |
490 } | 558 } |
491 } | 559 } |
492 | 560 |
493 visitDefault(Default node) { | 561 @override |
| 562 void visitDefault(Default node) { |
494 outIndentLn("default:"); | 563 outIndentLn("default:"); |
495 if (!node.body.statements.isEmpty) { | 564 if (!node.body.statements.isEmpty) { |
496 indentMore(); | 565 indentMore(); |
497 blockOutWithoutBraces(node.body); | 566 blockOutWithoutBraces(node.body); |
498 indentLess(); | 567 indentLess(); |
499 } | 568 } |
500 } | 569 } |
501 | 570 |
502 visitLabeledStatement(LabeledStatement node) { | 571 @override |
| 572 void visitLabeledStatement(LabeledStatement node) { |
503 Statement body = unwrapBlockIfSingleStatement(node.body); | 573 Statement body = unwrapBlockIfSingleStatement(node.body); |
504 // `label: break label;` | 574 // `label: break label;` |
505 // Does not work on IE. The statement is a nop, so replace it by an empty | 575 // Does not work on IE. The statement is a nop, so replace it by an empty |
506 // statement. | 576 // statement. |
507 // See: | 577 // See: |
508 // https://connect.microsoft.com/IE/feedback/details/891889/parser-bugs | 578 // https://connect.microsoft.com/IE/feedback/details/891889/parser-bugs |
509 if (body is Break && body.targetLabel == node.label) { | 579 if (body is Break && body.targetLabel == node.label) { |
510 visit(new EmptyStatement()); | 580 visit(new EmptyStatement()); |
511 return; | 581 return; |
512 } | 582 } |
513 outIndent("${node.label}:"); | 583 outIndent("${node.label}:"); |
514 blockBody(body, needsSeparation: false, needsNewline: true); | 584 blockBody(body, needsSeparation: false, needsNewline: true); |
515 } | 585 } |
516 | 586 |
517 void functionOut(Fun fun, Node name, VarCollector vars) { | 587 int functionOut(Fun fun, Node name, VarCollector vars) { |
518 out("function"); | 588 out("function"); |
519 if (name != null) { | 589 if (name != null) { |
520 out(" "); | 590 out(" "); |
521 // Name must be a [Decl]. Therefore only test for primary expressions. | 591 // Name must be a [Decl]. Therefore only test for primary expressions. |
522 visitNestedExpression(name, PRIMARY, | 592 visitNestedExpression(name, PRIMARY, |
523 newInForInit: false, newAtStatementBegin: false); | 593 newInForInit: false, newAtStatementBegin: false); |
524 } | 594 } |
525 localNamer.enterScope(vars); | 595 localNamer.enterScope(vars); |
526 out("("); | 596 out("("); |
527 if (fun.params != null) { | 597 if (fun.params != null) { |
528 visitCommaSeparated(fun.params, PRIMARY, | 598 visitCommaSeparated(fun.params, PRIMARY, |
529 newInForInit: false, newAtStatementBegin: false); | 599 newInForInit: false, newAtStatementBegin: false); |
530 } | 600 } |
531 out(")"); | 601 out(")"); |
532 switch (fun.asyncModifier) { | 602 switch (fun.asyncModifier) { |
533 case const AsyncModifier.sync(): | 603 case const AsyncModifier.sync(): |
534 break; | 604 break; |
535 case const AsyncModifier.async(): | 605 case const AsyncModifier.async(): |
536 out(' async'); | 606 out(' ', isWhitespace: true); |
| 607 out('async'); |
537 break; | 608 break; |
538 case const AsyncModifier.syncStar(): | 609 case const AsyncModifier.syncStar(): |
539 out(' sync*'); | 610 out(' ', isWhitespace: true); |
| 611 out('sync*'); |
540 break; | 612 break; |
541 case const AsyncModifier.asyncStar(): | 613 case const AsyncModifier.asyncStar(): |
542 out(' async*'); | 614 out(' ', isWhitespace: true); |
| 615 out('async*'); |
543 break; | 616 break; |
544 } | 617 } |
545 blockBody(fun.body, needsSeparation: false, needsNewline: false); | 618 spaceOut(); |
| 619 int closingPosition = |
| 620 blockOut(fun.body, shouldIndent: false, needsNewline: false); |
546 localNamer.leaveScope(); | 621 localNamer.leaveScope(); |
| 622 return closingPosition; |
| 623 |
547 } | 624 } |
548 | 625 |
| 626 @override |
549 visitFunctionDeclaration(FunctionDeclaration declaration) { | 627 visitFunctionDeclaration(FunctionDeclaration declaration) { |
550 VarCollector vars = new VarCollector(); | 628 VarCollector vars = new VarCollector(); |
551 vars.visitFunctionDeclaration(declaration); | 629 vars.visitFunctionDeclaration(declaration); |
552 indent(); | 630 indent(); |
553 functionOut(declaration.function, declaration.name, vars); | 631 functionOut(declaration.function, declaration.name, vars); |
554 lineOut(); | 632 lineOut(); |
555 } | 633 } |
556 | 634 |
557 visitNestedExpression(Expression node, int requiredPrecedence, | 635 visitNestedExpression(Expression node, int requiredPrecedence, |
558 {bool newInForInit, bool newAtStatementBegin}) { | 636 {bool newInForInit, bool newAtStatementBegin}) { |
(...skipping 14 matching lines...) Expand all Loading... |
573 out("("); | 651 out("("); |
574 visit(node); | 652 visit(node); |
575 out(")"); | 653 out(")"); |
576 } else { | 654 } else { |
577 inForInit = newInForInit; | 655 inForInit = newInForInit; |
578 atStatementBegin = newAtStatementBegin; | 656 atStatementBegin = newAtStatementBegin; |
579 visit(node); | 657 visit(node); |
580 } | 658 } |
581 } | 659 } |
582 | 660 |
| 661 @override |
583 visitVariableDeclarationList(VariableDeclarationList list) { | 662 visitVariableDeclarationList(VariableDeclarationList list) { |
584 out("var "); | 663 out("var "); |
585 visitCommaSeparated(list.declarations, ASSIGNMENT, | 664 visitCommaSeparated(list.declarations, ASSIGNMENT, |
586 newInForInit: inForInit, newAtStatementBegin: false); | 665 newInForInit: inForInit, newAtStatementBegin: false); |
587 } | 666 } |
588 | 667 |
| 668 @override |
589 visitAssignment(Assignment assignment) { | 669 visitAssignment(Assignment assignment) { |
590 visitNestedExpression(assignment.leftHandSide, LEFT_HAND_SIDE, | 670 visitNestedExpression(assignment.leftHandSide, LEFT_HAND_SIDE, |
591 newInForInit: inForInit, | 671 newInForInit: inForInit, |
592 newAtStatementBegin: atStatementBegin); | 672 newAtStatementBegin: atStatementBegin); |
593 if (assignment.value != null) { | 673 if (assignment.value != null) { |
594 spaceOut(); | 674 spaceOut(); |
595 String op = assignment.op; | 675 String op = assignment.op; |
596 if (op != null) out(op); | 676 if (op != null) out(op); |
597 out("="); | 677 out("="); |
598 spaceOut(); | 678 spaceOut(); |
599 visitNestedExpression(assignment.value, ASSIGNMENT, | 679 visitNestedExpression(assignment.value, ASSIGNMENT, |
600 newInForInit: inForInit, | 680 newInForInit: inForInit, |
601 newAtStatementBegin: false); | 681 newAtStatementBegin: false); |
602 } | 682 } |
603 } | 683 } |
604 | 684 |
| 685 @override |
605 visitVariableInitialization(VariableInitialization initialization) { | 686 visitVariableInitialization(VariableInitialization initialization) { |
606 visitAssignment(initialization); | 687 visitAssignment(initialization); |
607 } | 688 } |
608 | 689 |
| 690 @override |
609 visitConditional(Conditional cond) { | 691 visitConditional(Conditional cond) { |
610 visitNestedExpression(cond.condition, LOGICAL_OR, | 692 visitNestedExpression(cond.condition, LOGICAL_OR, |
611 newInForInit: inForInit, | 693 newInForInit: inForInit, |
612 newAtStatementBegin: atStatementBegin); | 694 newAtStatementBegin: atStatementBegin); |
613 spaceOut(); | 695 spaceOut(); |
614 out("?"); | 696 out("?"); |
615 spaceOut(); | 697 spaceOut(); |
616 // The then part is allowed to have an 'in'. | 698 // The then part is allowed to have an 'in'. |
617 visitNestedExpression(cond.then, ASSIGNMENT, | 699 visitNestedExpression(cond.then, ASSIGNMENT, |
618 newInForInit: false, newAtStatementBegin: false); | 700 newInForInit: false, newAtStatementBegin: false); |
619 spaceOut(); | 701 spaceOut(); |
620 out(":"); | 702 out(":"); |
621 spaceOut(); | 703 spaceOut(); |
622 visitNestedExpression(cond.otherwise, ASSIGNMENT, | 704 visitNestedExpression(cond.otherwise, ASSIGNMENT, |
623 newInForInit: inForInit, newAtStatementBegin: false); | 705 newInForInit: inForInit, newAtStatementBegin: false); |
624 } | 706 } |
625 | 707 |
| 708 @override |
626 visitNew(New node) { | 709 visitNew(New node) { |
627 out("new "); | 710 out("new "); |
628 visitNestedExpression(node.target, CALL, | 711 visitNestedExpression(node.target, CALL, |
629 newInForInit: inForInit, newAtStatementBegin: false); | 712 newInForInit: inForInit, newAtStatementBegin: false); |
630 out("("); | 713 out("("); |
631 visitCommaSeparated(node.arguments, ASSIGNMENT, | 714 visitCommaSeparated(node.arguments, ASSIGNMENT, |
632 newInForInit: false, newAtStatementBegin: false); | 715 newInForInit: false, newAtStatementBegin: false); |
633 out(")"); | 716 out(")"); |
634 } | 717 } |
635 | 718 |
| 719 @override |
636 visitCall(Call call) { | 720 visitCall(Call call) { |
637 visitNestedExpression(call.target, LEFT_HAND_SIDE, | 721 visitNestedExpression(call.target, LEFT_HAND_SIDE, |
638 newInForInit: inForInit, | 722 newInForInit: inForInit, |
639 newAtStatementBegin: atStatementBegin); | 723 newAtStatementBegin: atStatementBegin); |
640 out("("); | 724 out("("); |
641 visitCommaSeparated(call.arguments, ASSIGNMENT, | 725 visitCommaSeparated(call.arguments, ASSIGNMENT, |
642 newInForInit: false, newAtStatementBegin: false); | 726 newInForInit: false, newAtStatementBegin: false); |
643 out(")"); | 727 out(")"); |
644 } | 728 } |
645 | 729 |
646 visitBinary(Binary binary) { | 730 @override |
| 731 void visitBinary(Binary binary) { |
647 Expression left = binary.left; | 732 Expression left = binary.left; |
648 Expression right = binary.right; | 733 Expression right = binary.right; |
649 String op = binary.op; | 734 String op = binary.op; |
650 int leftPrecedenceRequirement; | 735 int leftPrecedenceRequirement; |
651 int rightPrecedenceRequirement; | 736 int rightPrecedenceRequirement; |
652 bool leftSpace = true; // left<HERE>op right | 737 bool leftSpace = true; // left<HERE>op right |
653 switch (op) { | 738 switch (op) { |
654 case ',': | 739 case ',': |
655 // x, (y, z) <=> (x, y), z. | 740 // x, (y, z) <=> (x, y), z. |
656 leftPrecedenceRequirement = EXPRESSION; | 741 leftPrecedenceRequirement = EXPRESSION; |
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
725 context.error("Forgot operator: $op"); | 810 context.error("Forgot operator: $op"); |
726 } | 811 } |
727 | 812 |
728 visitNestedExpression(left, leftPrecedenceRequirement, | 813 visitNestedExpression(left, leftPrecedenceRequirement, |
729 newInForInit: inForInit, | 814 newInForInit: inForInit, |
730 newAtStatementBegin: atStatementBegin); | 815 newAtStatementBegin: atStatementBegin); |
731 | 816 |
732 if (op == "in" || op == "instanceof") { | 817 if (op == "in" || op == "instanceof") { |
733 // There are cases where the space is not required but without further | 818 // There are cases where the space is not required but without further |
734 // analysis we cannot know. | 819 // analysis we cannot know. |
735 out(" "); | 820 out(" ", isWhitespace: true); |
736 out(op); | 821 out(op); |
737 out(" "); | 822 out(" ", isWhitespace: true); |
738 } else { | 823 } else { |
739 if (leftSpace) spaceOut(); | 824 if (leftSpace) spaceOut(); |
740 out(op); | 825 out(op); |
741 spaceOut(); | 826 spaceOut(); |
742 } | 827 } |
743 visitNestedExpression(right, rightPrecedenceRequirement, | 828 visitNestedExpression(right, rightPrecedenceRequirement, |
744 newInForInit: inForInit, | 829 newInForInit: inForInit, |
745 newAtStatementBegin: false); | 830 newAtStatementBegin: false); |
746 } | 831 } |
747 | 832 |
748 visitPrefix(Prefix unary) { | 833 @override |
| 834 void visitPrefix(Prefix unary) { |
749 String op = unary.op; | 835 String op = unary.op; |
750 switch (op) { | 836 switch (op) { |
751 case "delete": | 837 case "delete": |
752 case "void": | 838 case "void": |
753 case "typeof": | 839 case "typeof": |
754 // 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 |
755 // analysis we cannot know. | 841 // analysis we cannot know. |
756 out(op); | 842 out(op); |
757 out(" "); | 843 out(" ", isWhitespace: true); |
758 break; | 844 break; |
759 case "+": | 845 case "+": |
760 case "++": | 846 case "++": |
761 if (lastCharCode == charCodes.$PLUS) out(" "); | 847 if (lastCharCode == charCodes.$PLUS) out(" ", isWhitespace: true); |
762 out(op); | 848 out(op); |
763 break; | 849 break; |
764 case "-": | 850 case "-": |
765 case "--": | 851 case "--": |
766 if (lastCharCode == charCodes.$MINUS) out(" "); | 852 if (lastCharCode == charCodes.$MINUS) out(" ", isWhitespace: true); |
767 out(op); | 853 out(op); |
768 break; | 854 break; |
769 default: | 855 default: |
770 out(op); | 856 out(op); |
771 } | 857 } |
772 visitNestedExpression(unary.argument, UNARY, | 858 visitNestedExpression(unary.argument, UNARY, |
773 newInForInit: inForInit, newAtStatementBegin: false); | 859 newInForInit: inForInit, newAtStatementBegin: false); |
774 } | 860 } |
775 | 861 |
776 visitPostfix(Postfix postfix) { | 862 @override |
| 863 void visitPostfix(Postfix postfix) { |
777 visitNestedExpression(postfix.argument, LEFT_HAND_SIDE, | 864 visitNestedExpression(postfix.argument, LEFT_HAND_SIDE, |
778 newInForInit: inForInit, | 865 newInForInit: inForInit, |
779 newAtStatementBegin: atStatementBegin); | 866 newAtStatementBegin: atStatementBegin); |
780 out(postfix.op); | 867 out(postfix.op); |
781 } | 868 } |
782 | 869 |
783 visitVariableUse(VariableUse ref) { | 870 @override |
| 871 void visitVariableUse(VariableUse ref) { |
784 out(localNamer.getName(ref.name)); | 872 out(localNamer.getName(ref.name)); |
785 } | 873 } |
786 | 874 |
787 visitThis(This node) { | 875 @override |
| 876 void visitThis(This node) { |
788 out("this"); | 877 out("this"); |
789 } | 878 } |
790 | 879 |
791 visitVariableDeclaration(VariableDeclaration decl) { | 880 @override |
| 881 void visitVariableDeclaration(VariableDeclaration decl) { |
792 out(localNamer.getName(decl.name)); | 882 out(localNamer.getName(decl.name)); |
793 } | 883 } |
794 | 884 |
795 visitParameter(Parameter param) { | 885 @override |
| 886 void visitParameter(Parameter param) { |
796 out(localNamer.getName(param.name)); | 887 out(localNamer.getName(param.name)); |
797 } | 888 } |
798 | 889 |
799 bool isDigit(int charCode) { | 890 bool isDigit(int charCode) { |
800 return charCodes.$0 <= charCode && charCode <= charCodes.$9; | 891 return charCodes.$0 <= charCode && charCode <= charCodes.$9; |
801 } | 892 } |
802 | 893 |
803 bool isValidJavaScriptId(String field) { | 894 bool isValidJavaScriptId(String field) { |
804 if (field.length < 3) return false; | 895 if (field.length < 3) return false; |
805 // Ignore the leading and trailing string-delimiter. | 896 // Ignore the leading and trailing string-delimiter. |
806 for (int i = 1; i < field.length - 1; i++) { | 897 for (int i = 1; i < field.length - 1; i++) { |
807 // TODO(floitsch): allow more characters. | 898 // TODO(floitsch): allow more characters. |
808 int charCode = field.codeUnitAt(i); | 899 int charCode = field.codeUnitAt(i); |
809 if (!(charCodes.$a <= charCode && charCode <= charCodes.$z || | 900 if (!(charCodes.$a <= charCode && charCode <= charCodes.$z || |
810 charCodes.$A <= charCode && charCode <= charCodes.$Z || | 901 charCodes.$A <= charCode && charCode <= charCodes.$Z || |
811 charCode == charCodes.$$ || | 902 charCode == charCodes.$$ || |
812 charCode == charCodes.$_ || | 903 charCode == charCodes.$_ || |
813 i != 1 && isDigit(charCode))) { | 904 i != 1 && isDigit(charCode))) { |
814 return false; | 905 return false; |
815 } | 906 } |
816 } | 907 } |
817 // TODO(floitsch): normally we should also check that the field is not a | 908 // TODO(floitsch): normally we should also check that the field is not a |
818 // reserved word. We don't generate fields with reserved word names except | 909 // reserved word. We don't generate fields with reserved word names except |
819 // for 'super'. | 910 // for 'super'. |
820 if (field == '"super"') return false; | 911 if (field == '"super"') return false; |
821 return true; | 912 return true; |
822 } | 913 } |
823 | 914 |
824 visitAccess(PropertyAccess access) { | 915 @override |
| 916 void visitAccess(PropertyAccess access) { |
825 visitNestedExpression(access.receiver, CALL, | 917 visitNestedExpression(access.receiver, CALL, |
826 newInForInit: inForInit, | 918 newInForInit: inForInit, |
827 newAtStatementBegin: atStatementBegin); | 919 newAtStatementBegin: atStatementBegin); |
828 Node selector = access.selector; | 920 Node selector = access.selector; |
829 if (selector is LiteralString) { | 921 if (selector is LiteralString) { |
830 LiteralString selectorString = selector; | 922 LiteralString selectorString = selector; |
831 String fieldWithQuotes = selectorString.value; | 923 String fieldWithQuotes = selectorString.value; |
832 if (isValidJavaScriptId(fieldWithQuotes)) { | 924 if (isValidJavaScriptId(fieldWithQuotes)) { |
833 if (access.receiver is LiteralNumber) out(" "); | 925 if (access.receiver is LiteralNumber) out(" ", isWhitespace: true); |
834 out("."); | 926 out("."); |
835 out(fieldWithQuotes.substring(1, fieldWithQuotes.length - 1)); | 927 out(fieldWithQuotes.substring(1, fieldWithQuotes.length - 1)); |
836 return; | 928 return; |
837 } | 929 } |
838 } | 930 } |
839 out("["); | 931 out("["); |
840 visitNestedExpression(selector, EXPRESSION, | 932 visitNestedExpression(selector, EXPRESSION, |
841 newInForInit: false, newAtStatementBegin: false); | 933 newInForInit: false, newAtStatementBegin: false); |
842 out("]"); | 934 out("]"); |
843 } | 935 } |
844 | 936 |
845 visitNamedFunction(NamedFunction namedFunction) { | 937 @override |
| 938 void visitNamedFunction(NamedFunction namedFunction) { |
846 VarCollector vars = new VarCollector(); | 939 VarCollector vars = new VarCollector(); |
847 vars.visitNamedFunction(namedFunction); | 940 vars.visitNamedFunction(namedFunction); |
848 functionOut(namedFunction.function, namedFunction.name, vars); | 941 startNode(namedFunction.function); |
| 942 currentNode.closingPosition = |
| 943 functionOut(namedFunction.function, namedFunction.name, vars); |
| 944 endNode(namedFunction.function); |
849 } | 945 } |
850 | 946 |
851 visitFun(Fun fun) { | 947 @override |
| 948 void visitFun(Fun fun) { |
852 VarCollector vars = new VarCollector(); | 949 VarCollector vars = new VarCollector(); |
853 vars.visitFun(fun); | 950 vars.visitFun(fun); |
854 functionOut(fun, null, vars); | 951 currentNode.closingPosition = functionOut(fun, null, vars); |
855 } | 952 } |
856 | 953 |
857 visitLiteralBool(LiteralBool node) { | 954 @override |
| 955 void visitLiteralBool(LiteralBool node) { |
858 out(node.value ? "true" : "false"); | 956 out(node.value ? "true" : "false"); |
859 } | 957 } |
860 | 958 |
861 visitLiteralString(LiteralString node) { | 959 @override |
| 960 void visitLiteralString(LiteralString node) { |
862 out(node.value); | 961 out(node.value); |
863 } | 962 } |
864 | 963 |
865 visitLiteralNumber(LiteralNumber node) { | 964 @override |
| 965 void visitLiteralNumber(LiteralNumber node) { |
866 int charCode = node.value.codeUnitAt(0); | 966 int charCode = node.value.codeUnitAt(0); |
867 if (charCode == charCodes.$MINUS && lastCharCode == charCodes.$MINUS) { | 967 if (charCode == charCodes.$MINUS && lastCharCode == charCodes.$MINUS) { |
868 out(" "); | 968 out(" ", isWhitespace: true); |
869 } | 969 } |
870 out(node.value); | 970 out(node.value); |
871 } | 971 } |
872 | 972 |
873 visitLiteralNull(LiteralNull node) { | 973 @override |
| 974 void visitLiteralNull(LiteralNull node) { |
874 out("null"); | 975 out("null"); |
875 } | 976 } |
876 | 977 |
877 visitArrayInitializer(ArrayInitializer node) { | 978 @override |
| 979 void visitArrayInitializer(ArrayInitializer node) { |
878 out("["); | 980 out("["); |
879 List<Expression> elements = node.elements; | 981 List<Expression> elements = node.elements; |
880 for (int i = 0; i < elements.length; i++) { | 982 for (int i = 0; i < elements.length; i++) { |
881 Expression element = elements[i]; | 983 Expression element = elements[i]; |
882 if (element is ArrayHole) { | 984 if (element is ArrayHole) { |
883 // Note that array holes must have a trailing "," even if they are | 985 // Note that array holes must have a trailing "," even if they are |
884 // in last position. Otherwise `[,]` (having length 1) would become | 986 // in last position. Otherwise `[,]` (having length 1) would become |
885 // equal to `[]` (the empty array) | 987 // equal to `[]` (the empty array) |
886 // and [1,,] (array with 1 and a hole) would become [1,] = [1]. | 988 // and [1,,] (array with 1 and a hole) would become [1,] = [1]. |
887 out(","); | 989 out(","); |
888 continue; | 990 continue; |
889 } | 991 } |
890 if (i != 0) spaceOut(); | 992 if (i != 0) spaceOut(); |
891 visitNestedExpression(element, ASSIGNMENT, | 993 visitNestedExpression(element, ASSIGNMENT, |
892 newInForInit: false, newAtStatementBegin: false); | 994 newInForInit: false, newAtStatementBegin: false); |
893 // We can skip the trailing "," for the last element (since it's not | 995 // We can skip the trailing "," for the last element (since it's not |
894 // an array hole). | 996 // an array hole). |
895 if (i != elements.length - 1) out(","); | 997 if (i != elements.length - 1) out(","); |
896 } | 998 } |
897 out("]"); | 999 out("]"); |
898 } | 1000 } |
899 | 1001 |
900 visitArrayHole(ArrayHole node) { | 1002 @override |
| 1003 void visitArrayHole(ArrayHole node) { |
901 throw "Unreachable"; | 1004 throw "Unreachable"; |
902 } | 1005 } |
903 | 1006 |
904 visitObjectInitializer(ObjectInitializer node) { | 1007 @override |
| 1008 void visitObjectInitializer(ObjectInitializer node) { |
905 // Print all the properties on one line until we see a function-valued | 1009 // Print all the properties on one line until we see a function-valued |
906 // property. Ideally, we would use a proper pretty-printer to make the | 1010 // property. Ideally, we would use a proper pretty-printer to make the |
907 // decision based on layout. | 1011 // decision based on layout. |
908 List<Property> properties = node.properties; | 1012 List<Property> properties = node.properties; |
909 out("{"); | 1013 out("{"); |
910 indentMore(); | 1014 indentMore(); |
911 for (int i = 0; i < properties.length; i++) { | 1015 for (int i = 0; i < properties.length; i++) { |
912 Expression value = properties[i].value; | 1016 Expression value = properties[i].value; |
913 if (i != 0) { | 1017 if (i != 0) { |
914 out(","); | 1018 out(","); |
915 if (node.isOneLiner) spaceOut(); | 1019 if (node.isOneLiner) spaceOut(); |
916 } | 1020 } |
917 if (!node.isOneLiner) { | 1021 if (!node.isOneLiner) { |
918 forceLine(); | 1022 forceLine(); |
919 indent(); | 1023 indent(); |
920 } | 1024 } |
921 visit(properties[i]); | 1025 visit(properties[i]); |
922 } | 1026 } |
923 indentLess(); | 1027 indentLess(); |
924 if (!node.isOneLiner && !properties.isEmpty) { | 1028 if (!node.isOneLiner && !properties.isEmpty) { |
925 lineOut(); | 1029 lineOut(); |
926 indent(); | 1030 indent(); |
927 } | 1031 } |
928 out("}"); | 1032 out("}"); |
929 } | 1033 } |
930 | 1034 |
931 visitProperty(Property node) { | 1035 @override |
| 1036 void visitProperty(Property node) { |
932 if (node.name is LiteralString) { | 1037 if (node.name is LiteralString) { |
933 LiteralString nameString = node.name; | 1038 LiteralString nameString = node.name; |
934 String name = nameString.value; | 1039 String name = nameString.value; |
935 if (isValidJavaScriptId(name)) { | 1040 if (isValidJavaScriptId(name)) { |
936 out(name.substring(1, name.length - 1)); | 1041 out(name.substring(1, name.length - 1)); |
937 } else { | 1042 } else { |
938 out(name); | 1043 out(name); |
939 } | 1044 } |
940 } else { | 1045 } else { |
941 assert(node.name is LiteralNumber); | 1046 assert(node.name is LiteralNumber); |
942 LiteralNumber nameNumber = node.name; | 1047 LiteralNumber nameNumber = node.name; |
943 out(nameNumber.value); | 1048 out(nameNumber.value); |
944 } | 1049 } |
945 out(":"); | 1050 out(":"); |
946 spaceOut(); | 1051 spaceOut(); |
947 visitNestedExpression(node.value, ASSIGNMENT, | 1052 visitNestedExpression(node.value, ASSIGNMENT, |
948 newInForInit: false, newAtStatementBegin: false); | 1053 newInForInit: false, newAtStatementBegin: false); |
949 } | 1054 } |
950 | 1055 |
951 visitRegExpLiteral(RegExpLiteral node) { | 1056 @override |
| 1057 void visitRegExpLiteral(RegExpLiteral node) { |
952 out(node.pattern); | 1058 out(node.pattern); |
953 } | 1059 } |
954 | 1060 |
955 visitLiteralExpression(LiteralExpression node) { | 1061 @override |
| 1062 void visitLiteralExpression(LiteralExpression node) { |
956 String template = node.template; | 1063 String template = node.template; |
957 List<Expression> inputs = node.inputs; | 1064 List<Expression> inputs = node.inputs; |
958 | 1065 |
959 List<String> parts = template.split('#'); | 1066 List<String> parts = template.split('#'); |
960 int inputsLength = inputs == null ? 0 : inputs.length; | 1067 int inputsLength = inputs == null ? 0 : inputs.length; |
961 if (parts.length != inputsLength + 1) { | 1068 if (parts.length != inputsLength + 1) { |
962 context.error('Wrong number of arguments for JS: $template'); | 1069 context.error('Wrong number of arguments for JS: $template'); |
963 } | 1070 } |
964 // Code that uses JS must take care of operator precedences, and | 1071 // Code that uses JS must take care of operator precedences, and |
965 // put parenthesis if needed. | 1072 // put parenthesis if needed. |
966 out(parts[0]); | 1073 out(parts[0]); |
967 for (int i = 0; i < inputsLength; i++) { | 1074 for (int i = 0; i < inputsLength; i++) { |
968 visit(inputs[i]); | 1075 visit(inputs[i]); |
969 out(parts[i + 1]); | 1076 out(parts[i + 1]); |
970 } | 1077 } |
971 } | 1078 } |
972 | 1079 |
973 visitLiteralStatement(LiteralStatement node) { | 1080 @override |
| 1081 void visitLiteralStatement(LiteralStatement node) { |
974 outLn(node.code); | 1082 outLn(node.code); |
975 } | 1083 } |
976 | 1084 |
977 visitInterpolatedNode(InterpolatedNode node) { | 1085 void visitInterpolatedNode(InterpolatedNode node) { |
978 out('#${node.nameOrPosition}'); | 1086 out('#${node.nameOrPosition}'); |
979 } | 1087 } |
980 | 1088 |
981 visitInterpolatedExpression(InterpolatedExpression node) => | 1089 @override |
| 1090 void visitInterpolatedExpression(InterpolatedExpression node) => |
982 visitInterpolatedNode(node); | 1091 visitInterpolatedNode(node); |
983 | 1092 |
984 visitInterpolatedLiteral(InterpolatedLiteral node) => | 1093 @override |
| 1094 void visitInterpolatedLiteral(InterpolatedLiteral node) => |
985 visitInterpolatedNode(node); | 1095 visitInterpolatedNode(node); |
986 | 1096 |
987 visitInterpolatedParameter(InterpolatedParameter node) => | 1097 @override |
| 1098 void visitInterpolatedParameter(InterpolatedParameter node) => |
988 visitInterpolatedNode(node); | 1099 visitInterpolatedNode(node); |
989 | 1100 |
990 visitInterpolatedSelector(InterpolatedSelector node) => | 1101 @override |
| 1102 void visitInterpolatedSelector(InterpolatedSelector node) => |
991 visitInterpolatedNode(node); | 1103 visitInterpolatedNode(node); |
992 | 1104 |
993 visitInterpolatedStatement(InterpolatedStatement node) { | 1105 @override |
| 1106 void visitInterpolatedStatement(InterpolatedStatement node) { |
994 outLn('#${node.nameOrPosition}'); | 1107 outLn('#${node.nameOrPosition}'); |
995 } | 1108 } |
996 | 1109 |
997 visitInterpolatedDeclaration(InterpolatedDeclaration node) { | 1110 @override |
| 1111 void visitInterpolatedDeclaration(InterpolatedDeclaration node) { |
998 visitInterpolatedNode(node); | 1112 visitInterpolatedNode(node); |
999 } | 1113 } |
1000 | 1114 |
| 1115 @override |
1001 void visitComment(Comment node) { | 1116 void visitComment(Comment node) { |
1002 if (shouldCompressOutput) return; | 1117 if (shouldCompressOutput) return; |
1003 String comment = node.comment.trim(); | 1118 String comment = node.comment.trim(); |
1004 if (comment.isEmpty) return; | 1119 if (comment.isEmpty) return; |
1005 for (var line in comment.split('\n')) { | 1120 for (var line in comment.split('\n')) { |
1006 if (comment.startsWith('//')) { | 1121 if (comment.startsWith('//')) { |
1007 outIndentLn(line.trim()); | 1122 outIndentLn(line.trim()); |
1008 } else { | 1123 } else { |
1009 outIndentLn('// ${line.trim()}'); | 1124 outIndentLn('// ${line.trim()}'); |
1010 } | 1125 } |
1011 } | 1126 } |
1012 } | 1127 } |
1013 | 1128 |
| 1129 @override |
1014 void visitAwait(Await node) { | 1130 void visitAwait(Await node) { |
1015 out("await "); | 1131 out("await "); |
1016 visit(node.expression); | 1132 visit(node.expression); |
1017 } | 1133 } |
1018 } | 1134 } |
1019 | 1135 |
1020 | 1136 |
1021 class OrderedSet<T> { | 1137 class OrderedSet<T> { |
1022 final Set<T> set; | 1138 final Set<T> set; |
1023 final List<T> list; | 1139 final List<T> list; |
1024 | 1140 |
1025 OrderedSet() : set = new Set<T>(), list = <T>[]; | 1141 OrderedSet() : set = new Set<T>(), list = <T>[]; |
1026 | 1142 |
1027 void add(T x) { | 1143 void add(T x) { |
1028 if (!set.contains(x)) { | 1144 if (set.add(x)) { |
1029 set.add(x); | 1145 // [Set.add] returns `true` if 'x' was added. |
1030 list.add(x); | 1146 list.add(x); |
1031 } | 1147 } |
1032 } | 1148 } |
1033 | 1149 |
1034 void forEach(void fun(T x)) { | 1150 void forEach(void fun(T x)) { |
1035 list.forEach(fun); | 1151 list.forEach(fun); |
1036 } | 1152 } |
1037 } | 1153 } |
1038 | 1154 |
1039 // Collects all the var declarations in the function. We need to do this in a | 1155 // Collects all the var declarations in the function. We need to do this in a |
(...skipping 227 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1267 codes.add(nthLetter((n ~/ nameSpaceSize) % LETTERS)); | 1383 codes.add(nthLetter((n ~/ nameSpaceSize) % LETTERS)); |
1268 } | 1384 } |
1269 codes.add(charCodes.$0 + digit); | 1385 codes.add(charCodes.$0 + digit); |
1270 newName = new String.fromCharCodes(codes); | 1386 newName = new String.fromCharCodes(codes); |
1271 } | 1387 } |
1272 assert(new RegExp(r'[a-zA-Z][a-zA-Z0-9]*').hasMatch(newName)); | 1388 assert(new RegExp(r'[a-zA-Z][a-zA-Z0-9]*').hasMatch(newName)); |
1273 maps.last[oldName] = newName; | 1389 maps.last[oldName] = newName; |
1274 return newName; | 1390 return newName; |
1275 } | 1391 } |
1276 } | 1392 } |
| 1393 |
| 1394 /// Information pertaining the enter and exit callbacks for [node]. |
| 1395 class EnterExitNode { |
| 1396 final EnterExitNode parent; |
| 1397 final Node node; |
| 1398 |
| 1399 int startPosition; |
| 1400 int closingPosition; |
| 1401 |
| 1402 EnterExitNode(this.parent, this.node); |
| 1403 |
| 1404 void addToNode(JavaScriptPrintingContext context, int position) { |
| 1405 if (startPosition == null) { |
| 1406 // [position] is the start position of [node]. |
| 1407 if (parent != null) { |
| 1408 // This might be the start position of the parent as well. |
| 1409 parent.addToNode(context, position); |
| 1410 } |
| 1411 startPosition = position; |
| 1412 context.enterNode(node, position); |
| 1413 } |
| 1414 } |
| 1415 |
| 1416 EnterExitNode exitNode(JavaScriptPrintingContext context, int position) { |
| 1417 // Enter must happen before exit. |
| 1418 addToNode(context, position); |
| 1419 context.exitNode(node, startPosition, position, closingPosition); |
| 1420 return parent; |
| 1421 } |
| 1422 } |
OLD | NEW |