| 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 |