OLD | NEW |
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, 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 library code_generator; | 5 library code_generator; |
6 | 6 |
7 import 'glue.dart'; | 7 import 'glue.dart'; |
8 | 8 |
9 import '../../tree_ir/tree_ir_nodes.dart' as tree_ir; | 9 import '../../tree_ir/tree_ir_nodes.dart' as tree_ir; |
10 import '../../tree_ir/tree_ir_nodes.dart' show BuiltinOperator; | 10 import '../../tree_ir/tree_ir_nodes.dart' show BuiltinOperator; |
(...skipping 28 matching lines...) Expand all Loading... |
39 /// Maps variables to their name. | 39 /// Maps variables to their name. |
40 Map<tree_ir.Variable, String> variableNames = <tree_ir.Variable, String>{}; | 40 Map<tree_ir.Variable, String> variableNames = <tree_ir.Variable, String>{}; |
41 | 41 |
42 /// Maps local constants to their name. | 42 /// Maps local constants to their name. |
43 Maplet<VariableElement, String> constantNames = | 43 Maplet<VariableElement, String> constantNames = |
44 new Maplet<VariableElement, String>(); | 44 new Maplet<VariableElement, String>(); |
45 | 45 |
46 /// Variable names that have already been used. Used to avoid name clashes. | 46 /// Variable names that have already been used. Used to avoid name clashes. |
47 Set<String> usedVariableNames = new Set<String>(); | 47 Set<String> usedVariableNames = new Set<String>(); |
48 | 48 |
49 /// Input to [visitStatement]. Denotes the statement that will execute next | 49 final tree_ir.FallthroughStack fallthrough = new tree_ir.FallthroughStack(); |
50 /// if the statements produced by [visitStatement] complete normally. | |
51 /// Set to null if control will fall over the end of the method. | |
52 tree_ir.Statement fallthrough = null; | |
53 | 50 |
54 Set<tree_ir.Label> usedLabels = new Set<tree_ir.Label>(); | 51 Set<tree_ir.Label> usedLabels = new Set<tree_ir.Label>(); |
55 | 52 |
56 List<js.Statement> accumulator = new List<js.Statement>(); | 53 List<js.Statement> accumulator = new List<js.Statement>(); |
57 | 54 |
58 CodeGenerator(this.glue, this.registry); | 55 CodeGenerator(this.glue, this.registry); |
59 | 56 |
60 /// Generates JavaScript code for the body of [function]. | 57 /// Generates JavaScript code for the body of [function]. |
61 js.Fun buildFunction(tree_ir.FunctionDefinition function) { | 58 js.Fun buildFunction(tree_ir.FunctionDefinition function) { |
62 currentFunction = function.element; | 59 currentFunction = function.element; |
(...skipping 338 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
401 | 398 |
402 @override | 399 @override |
403 js.Expression visitAssign(tree_ir.Assign node) { | 400 js.Expression visitAssign(tree_ir.Assign node) { |
404 return new js.Assignment( | 401 return new js.Assignment( |
405 buildVariableAccess(node.variable), | 402 buildVariableAccess(node.variable), |
406 visitExpression(node.value)); | 403 visitExpression(node.value)); |
407 } | 404 } |
408 | 405 |
409 @override | 406 @override |
410 void visitContinue(tree_ir.Continue node) { | 407 void visitContinue(tree_ir.Continue node) { |
411 tree_ir.Statement fallthrough = this.fallthrough; | 408 tree_ir.Statement next = fallthrough.target; |
412 if (node.target.binding == fallthrough) { | 409 if (node.target.binding == next) { |
413 // Fall through to continue target | 410 // Fall through to continue target |
414 } else if (fallthrough is tree_ir.Continue && | 411 fallthrough.use(); |
415 fallthrough.target == node.target) { | 412 } else if (next is tree_ir.Continue && next.target == node.target) { |
416 // Fall through to equivalent continue | 413 // Fall through to equivalent continue |
| 414 fallthrough.use(); |
417 } else { | 415 } else { |
418 usedLabels.add(node.target); | 416 usedLabels.add(node.target); |
419 accumulator.add(new js.Continue(node.target.name)); | 417 accumulator.add(new js.Continue(node.target.name)); |
420 } | 418 } |
421 } | 419 } |
422 | 420 |
423 @override | 421 @override |
| 422 void visitBreak(tree_ir.Break node) { |
| 423 tree_ir.Statement next = fallthrough.target; |
| 424 if (node.target.binding.next == next) { |
| 425 // Fall through to break target |
| 426 fallthrough.use(); |
| 427 } else if (next is tree_ir.Break && next.target == node.target) { |
| 428 // Fall through to equivalent break |
| 429 fallthrough.use(); |
| 430 } else { |
| 431 usedLabels.add(node.target); |
| 432 accumulator.add(new js.Break(node.target.name)); |
| 433 } |
| 434 } |
| 435 |
| 436 @override |
424 void visitExpressionStatement(tree_ir.ExpressionStatement node) { | 437 void visitExpressionStatement(tree_ir.ExpressionStatement node) { |
425 accumulator.add(new js.ExpressionStatement( | 438 accumulator.add(new js.ExpressionStatement( |
426 visitExpression(node.expression))); | 439 visitExpression(node.expression))); |
427 visitStatement(node.next); | 440 visitStatement(node.next); |
428 } | 441 } |
429 | 442 |
430 @override | 443 @override |
431 void visitIf(tree_ir.If node) { | 444 void visitIf(tree_ir.If node) { |
432 accumulator.add(new js.If(visitExpression(node.condition), | 445 js.Expression condition = visitExpression(node.condition); |
433 buildBodyStatement(node.thenStatement), | 446 int usesBefore = fallthrough.useCount; |
434 buildBodyStatement(node.elseStatement))); | 447 js.Statement thenBody = buildBodyStatement(node.thenStatement); |
| 448 bool thenHasFallthrough = (fallthrough.useCount > usesBefore); |
| 449 if (thenHasFallthrough) { |
| 450 js.Statement elseBody = buildBodyStatement(node.elseStatement); |
| 451 accumulator.add(new js.If(condition, thenBody, elseBody)); |
| 452 } else { |
| 453 // The 'then' body cannot complete normally, so emit a short 'if' |
| 454 // and put the 'else' body after it. |
| 455 accumulator.add(new js.If.noElse(condition, thenBody)); |
| 456 visitStatement(node.elseStatement); |
| 457 } |
435 } | 458 } |
436 | 459 |
437 @override | 460 @override |
438 void visitLabeledStatement(tree_ir.LabeledStatement node) { | 461 void visitLabeledStatement(tree_ir.LabeledStatement node) { |
439 accumulator.add(buildLabeled(() => buildBodyStatement(node.body), | 462 accumulator.add(buildLabeled(() => buildBodyStatement(node.body), |
440 node.label, | 463 node.label, |
441 node.next)); | 464 node.next)); |
442 visitStatement(node.next); | 465 visitStatement(node.next); |
443 } | 466 } |
444 | 467 |
445 js.Statement buildLabeled(js.Statement buildBody(), | 468 js.Statement buildLabeled(js.Statement buildBody(), |
446 tree_ir.Label label, | 469 tree_ir.Label label, |
447 tree_ir.Statement fallthroughStatement) { | 470 tree_ir.Statement fallthroughStatement) { |
448 tree_ir.Statement savedFallthrough = fallthrough; | 471 fallthrough.push(fallthroughStatement); |
449 fallthrough = fallthroughStatement; | |
450 js.Statement result = buildBody(); | 472 js.Statement result = buildBody(); |
451 if (usedLabels.remove(label)) { | 473 if (usedLabels.remove(label)) { |
452 result = new js.LabeledStatement(label.name, result); | 474 result = new js.LabeledStatement(label.name, result); |
453 } | 475 } |
454 fallthrough = savedFallthrough; | 476 fallthrough.pop(); |
455 return result; | 477 return result; |
456 } | 478 } |
457 | 479 |
458 @override | |
459 void visitBreak(tree_ir.Break node) { | |
460 tree_ir.Statement fallthrough = this.fallthrough; | |
461 if (node.target.binding.next == fallthrough) { | |
462 // Fall through to break target | |
463 } else if (fallthrough is tree_ir.Break && | |
464 fallthrough.target == node.target) { | |
465 // Fall through to equivalent break | |
466 } else { | |
467 usedLabels.add(node.target); | |
468 accumulator.add(new js.Break(node.target.name)); | |
469 } | |
470 } | |
471 | |
472 /// Returns the current [accumulator] wrapped in a block if neccessary. | 480 /// Returns the current [accumulator] wrapped in a block if neccessary. |
473 js.Statement _bodyAsStatement() { | 481 js.Statement _bodyAsStatement() { |
474 if (accumulator.length == 0) { | 482 if (accumulator.length == 0) { |
475 return new js.EmptyStatement(); | 483 return new js.EmptyStatement(); |
476 } | 484 } |
477 if (accumulator.length == 1) { | 485 if (accumulator.length == 1) { |
478 return accumulator.single; | 486 return accumulator.single; |
479 } | 487 } |
480 return new js.Block(accumulator); | 488 return new js.Block(accumulator); |
481 } | 489 } |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
517 node)); | 525 node)); |
518 visitStatement(node.next); | 526 visitStatement(node.next); |
519 } | 527 } |
520 | 528 |
521 @override | 529 @override |
522 void visitWhileTrue(tree_ir.WhileTrue node) { | 530 void visitWhileTrue(tree_ir.WhileTrue node) { |
523 accumulator.add( | 531 accumulator.add( |
524 buildWhile(new js.LiteralBool(true), node.body, node.label, node)); | 532 buildWhile(new js.LiteralBool(true), node.body, node.label, node)); |
525 } | 533 } |
526 | 534 |
| 535 bool isNull(tree_ir.Expression node) { |
| 536 return node is tree_ir.Constant && node.value.isNull; |
| 537 } |
| 538 |
527 @override | 539 @override |
528 void visitReturn(tree_ir.Return node) { | 540 void visitReturn(tree_ir.Return node) { |
529 accumulator.add(new js.Return(visitExpression(node.value))); | 541 if (isNull(node.value) && fallthrough.target == null) { |
| 542 // Do nothing. Implicitly return JS undefined by falling over the end. |
| 543 registry.registerCompileTimeConstant(new NullConstantValue()); |
| 544 fallthrough.use(); |
| 545 } else { |
| 546 accumulator.add(new js.Return(visitExpression(node.value))); |
| 547 } |
530 } | 548 } |
531 | 549 |
532 @override | 550 @override |
533 void visitThrow(tree_ir.Throw node) { | 551 void visitThrow(tree_ir.Throw node) { |
534 accumulator.add(new js.Throw(visitExpression(node.value))); | 552 accumulator.add(new js.Throw(visitExpression(node.value))); |
535 } | 553 } |
536 | 554 |
537 @override | 555 @override |
538 void visitRethrow(tree_ir.Rethrow node) { | 556 void visitRethrow(tree_ir.Rethrow node) { |
539 glue.reportInternalError('rethrow seen in JavaScript output'); | 557 glue.reportInternalError('rethrow seen in JavaScript output'); |
(...skipping 223 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
763 return js.js("typeof # === 'number' && Math.floor(#) === #", args); | 781 return js.js("typeof # === 'number' && Math.floor(#) === #", args); |
764 } | 782 } |
765 } | 783 } |
766 | 784 |
767 visitFunctionExpression(tree_ir.FunctionExpression node) { | 785 visitFunctionExpression(tree_ir.FunctionExpression node) { |
768 // FunctionExpressions are currently unused. | 786 // FunctionExpressions are currently unused. |
769 // We might need them if we want to emit raw JS nested functions. | 787 // We might need them if we want to emit raw JS nested functions. |
770 throw 'FunctionExpressions should not be used'; | 788 throw 'FunctionExpressions should not be used'; |
771 } | 789 } |
772 } | 790 } |
OLD | NEW |