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 30 matching lines...) Expand all Loading... |
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 final tree_ir.FallthroughStack fallthrough = new tree_ir.FallthroughStack(); | 49 final tree_ir.FallthroughStack fallthrough = new tree_ir.FallthroughStack(); |
50 | 50 |
| 51 /// Stacks whose top element is the current target of an unlabeled break |
| 52 /// or continue. For continues, this is the loop node itself. |
| 53 final tree_ir.FallthroughStack shortBreak = new tree_ir.FallthroughStack(); |
| 54 final tree_ir.FallthroughStack shortContinue = |
| 55 new tree_ir.FallthroughStack(); |
| 56 |
51 Set<tree_ir.Label> usedLabels = new Set<tree_ir.Label>(); | 57 Set<tree_ir.Label> usedLabels = new Set<tree_ir.Label>(); |
52 | 58 |
53 List<js.Statement> accumulator = new List<js.Statement>(); | 59 List<js.Statement> accumulator = new List<js.Statement>(); |
54 | 60 |
55 CodeGenerator(this.glue, this.registry); | 61 CodeGenerator(this.glue, this.registry); |
56 | 62 |
57 /// Generates JavaScript code for the body of [function]. | 63 /// Generates JavaScript code for the body of [function]. |
58 js.Fun buildFunction(tree_ir.FunctionDefinition function) { | 64 js.Fun buildFunction(tree_ir.FunctionDefinition function) { |
59 currentFunction = function.element; | 65 currentFunction = function.element; |
60 visitStatement(function.body); | 66 visitStatement(function.body); |
(...skipping 322 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
383 @override | 389 @override |
384 js.Expression visitAssign(tree_ir.Assign node) { | 390 js.Expression visitAssign(tree_ir.Assign node) { |
385 return new js.Assignment( | 391 return new js.Assignment( |
386 buildVariableAccess(node.variable), | 392 buildVariableAccess(node.variable), |
387 visitExpression(node.value)); | 393 visitExpression(node.value)); |
388 } | 394 } |
389 | 395 |
390 @override | 396 @override |
391 void visitContinue(tree_ir.Continue node) { | 397 void visitContinue(tree_ir.Continue node) { |
392 tree_ir.Statement next = fallthrough.target; | 398 tree_ir.Statement next = fallthrough.target; |
393 if (node.target.binding == next) { | 399 if (node.target.binding == next || |
394 // Fall through to continue target | 400 next is tree_ir.Continue && node.target == next.target) { |
| 401 // Fall through to continue target or to equivalent continue. |
395 fallthrough.use(); | 402 fallthrough.use(); |
396 } else if (next is tree_ir.Continue && next.target == node.target) { | 403 } else if (node.target.binding == shortContinue.target) { |
397 // Fall through to equivalent continue | 404 // The target is the immediately enclosing loop. |
398 fallthrough.use(); | 405 shortContinue.use(); |
| 406 accumulator.add(new js.Continue(null)); |
399 } else { | 407 } else { |
400 usedLabels.add(node.target); | 408 usedLabels.add(node.target); |
401 accumulator.add(new js.Continue(node.target.name)); | 409 accumulator.add(new js.Continue(node.target.name)); |
402 } | 410 } |
403 } | 411 } |
404 | 412 |
| 413 /// True if [other] is the target of [node] or is a [Break] with the same |
| 414 /// target. This means jumping to [other] is equivalent to executing [node]. |
| 415 bool isEffectiveBreakTarget(tree_ir.Break node, tree_ir.Statement other) { |
| 416 return node.target.binding.next == other || |
| 417 other is tree_ir.Break && node.target == other.target; |
| 418 } |
| 419 |
405 @override | 420 @override |
406 void visitBreak(tree_ir.Break node) { | 421 void visitBreak(tree_ir.Break node) { |
407 tree_ir.Statement next = fallthrough.target; | 422 if (isEffectiveBreakTarget(node, fallthrough.target)) { |
408 if (node.target.binding.next == next) { | 423 // Fall through to break target or to equivalent break. |
409 // Fall through to break target | |
410 fallthrough.use(); | 424 fallthrough.use(); |
411 } else if (next is tree_ir.Break && next.target == node.target) { | 425 } else if (isEffectiveBreakTarget(node, shortBreak.target)) { |
412 // Fall through to equivalent break | 426 // Unlabeled break to the break target or to an equivalent break. |
413 fallthrough.use(); | 427 shortBreak.use(); |
| 428 accumulator.add(new js.Break(null)); |
414 } else { | 429 } else { |
415 usedLabels.add(node.target); | 430 usedLabels.add(node.target); |
416 accumulator.add(new js.Break(node.target.name)); | 431 accumulator.add(new js.Break(node.target.name)); |
417 } | 432 } |
418 } | 433 } |
419 | 434 |
420 @override | 435 @override |
421 void visitExpressionStatement(tree_ir.ExpressionStatement node) { | 436 void visitExpressionStatement(tree_ir.ExpressionStatement node) { |
422 accumulator.add(new js.ExpressionStatement( | 437 accumulator.add(new js.ExpressionStatement( |
423 visitExpression(node.expression))); | 438 visitExpression(node.expression))); |
(...skipping 12 matching lines...) Expand all Loading... |
436 } else { | 451 } else { |
437 // The 'then' body cannot complete normally, so emit a short 'if' | 452 // The 'then' body cannot complete normally, so emit a short 'if' |
438 // and put the 'else' body after it. | 453 // and put the 'else' body after it. |
439 accumulator.add(new js.If.noElse(condition, thenBody)); | 454 accumulator.add(new js.If.noElse(condition, thenBody)); |
440 visitStatement(node.elseStatement); | 455 visitStatement(node.elseStatement); |
441 } | 456 } |
442 } | 457 } |
443 | 458 |
444 @override | 459 @override |
445 void visitLabeledStatement(tree_ir.LabeledStatement node) { | 460 void visitLabeledStatement(tree_ir.LabeledStatement node) { |
446 accumulator.add(buildLabeled(() => buildBodyStatement(node.body), | 461 fallthrough.push(node.next); |
447 node.label, | 462 js.Statement body = buildBodyStatement(node.body); |
448 node.next)); | 463 fallthrough.pop(); |
| 464 accumulator.add(insertLabel(node.label, body)); |
449 visitStatement(node.next); | 465 visitStatement(node.next); |
450 } | 466 } |
451 | 467 |
452 js.Statement buildLabeled(js.Statement buildBody(), | 468 /// Wraps a node in a labeled statement unless the label is unused. |
453 tree_ir.Label label, | 469 js.Statement insertLabel(tree_ir.Label label, js.Statement node) { |
454 tree_ir.Statement fallthroughStatement) { | |
455 fallthrough.push(fallthroughStatement); | |
456 js.Statement result = buildBody(); | |
457 if (usedLabels.remove(label)) { | 470 if (usedLabels.remove(label)) { |
458 result = new js.LabeledStatement(label.name, result); | 471 return new js.LabeledStatement(label.name, node); |
| 472 } else { |
| 473 return node; |
459 } | 474 } |
460 fallthrough.pop(); | |
461 return result; | |
462 } | 475 } |
463 | 476 |
464 /// Returns the current [accumulator] wrapped in a block if neccessary. | 477 /// Returns the current [accumulator] wrapped in a block if neccessary. |
465 js.Statement _bodyAsStatement() { | 478 js.Statement _bodyAsStatement() { |
466 if (accumulator.length == 0) { | 479 if (accumulator.length == 0) { |
467 return new js.EmptyStatement(); | 480 return new js.EmptyStatement(); |
468 } | 481 } |
469 if (accumulator.length == 1) { | 482 if (accumulator.length == 1) { |
470 return accumulator.single; | 483 return accumulator.single; |
471 } | 484 } |
(...skipping 12 matching lines...) Expand all Loading... |
484 | 497 |
485 js.Block buildBodyBlock(tree_ir.Statement statement) { | 498 js.Block buildBodyBlock(tree_ir.Statement statement) { |
486 List<js.Statement> savedAccumulator = accumulator; | 499 List<js.Statement> savedAccumulator = accumulator; |
487 accumulator = <js.Statement>[]; | 500 accumulator = <js.Statement>[]; |
488 visitStatement(statement); | 501 visitStatement(statement); |
489 js.Statement result = new js.Block(accumulator); | 502 js.Statement result = new js.Block(accumulator); |
490 accumulator = savedAccumulator; | 503 accumulator = savedAccumulator; |
491 return result; | 504 return result; |
492 } | 505 } |
493 | 506 |
494 js.Statement buildWhile(js.Expression condition, | |
495 tree_ir.Statement body, | |
496 tree_ir.Label label, | |
497 tree_ir.Statement fallthroughStatement) { | |
498 return buildLabeled(() => new js.While(condition, buildBodyStatement(body)), | |
499 label, | |
500 fallthroughStatement); | |
501 } | |
502 | |
503 @override | 507 @override |
504 void visitWhileCondition(tree_ir.WhileCondition node) { | 508 void visitWhileCondition(tree_ir.WhileCondition node) { |
505 accumulator.add( | 509 js.Expression condition = visitExpression(node.condition); |
506 buildWhile(visitExpression(node.condition), | 510 shortBreak.push(node.next); |
507 node.body, | 511 shortContinue.push(node); |
508 node.label, | 512 fallthrough.push(node); |
509 node)); | 513 js.Statement jsBody = buildBodyStatement(node.body); |
| 514 fallthrough.pop(); |
| 515 shortContinue.pop(); |
| 516 shortBreak.pop(); |
| 517 accumulator.add(insertLabel(node.label, new js.While(condition, jsBody))); |
510 visitStatement(node.next); | 518 visitStatement(node.next); |
511 } | 519 } |
512 | 520 |
513 @override | 521 @override |
514 void visitWhileTrue(tree_ir.WhileTrue node) { | 522 void visitWhileTrue(tree_ir.WhileTrue node) { |
515 accumulator.add( | 523 js.Expression condition = new js.LiteralBool(true); |
516 buildWhile(new js.LiteralBool(true), node.body, node.label, node)); | 524 // A short break in the while will jump to the current fallthrough target. |
| 525 shortBreak.push(fallthrough.target); |
| 526 shortContinue.push(node); |
| 527 fallthrough.push(node); |
| 528 js.Statement jsBody = buildBodyStatement(node.body); |
| 529 fallthrough.pop(); |
| 530 shortContinue.pop(); |
| 531 if (shortBreak.useCount > 0) { |
| 532 // Short breaks use the current fallthrough target. |
| 533 fallthrough.use(); |
| 534 } |
| 535 shortBreak.pop(); |
| 536 accumulator.add(insertLabel(node.label, new js.While(condition, jsBody))); |
517 } | 537 } |
518 | 538 |
519 bool isNull(tree_ir.Expression node) { | 539 bool isNull(tree_ir.Expression node) { |
520 return node is tree_ir.Constant && node.value.isNull; | 540 return node is tree_ir.Constant && node.value.isNull; |
521 } | 541 } |
522 | 542 |
523 @override | 543 @override |
524 void visitReturn(tree_ir.Return node) { | 544 void visitReturn(tree_ir.Return node) { |
525 if (isNull(node.value) && fallthrough.target == null) { | 545 if (isNull(node.value) && fallthrough.target == null) { |
526 // Do nothing. Implicitly return JS undefined by falling over the end. | 546 // Do nothing. Implicitly return JS undefined by falling over the end. |
(...skipping 259 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
786 return js.js("typeof # === 'number' && Math.floor(#) === #", args); | 806 return js.js("typeof # === 'number' && Math.floor(#) === #", args); |
787 } | 807 } |
788 } | 808 } |
789 | 809 |
790 visitFunctionExpression(tree_ir.FunctionExpression node) { | 810 visitFunctionExpression(tree_ir.FunctionExpression node) { |
791 // FunctionExpressions are currently unused. | 811 // FunctionExpressions are currently unused. |
792 // We might need them if we want to emit raw JS nested functions. | 812 // We might need them if we want to emit raw JS nested functions. |
793 throw 'FunctionExpressions should not be used'; | 813 throw 'FunctionExpressions should not be used'; |
794 } | 814 } |
795 } | 815 } |
OLD | NEW |