| OLD | NEW |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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 dart2js.ir_builder; | 5 part of dart2js.ir_builder; |
| 6 | 6 |
| 7 /** | 7 /** |
| 8 * This task iterates through all resolved elements and builds [ir.Node]s. The | 8 * This task iterates through all resolved elements and builds [ir.Node]s. The |
| 9 * nodes are stored in the [nodes] map and accessible through [hasIr] and | 9 * nodes are stored in the [nodes] map and accessible through [hasIr] and |
| 10 * [getIr]. | 10 * [getIr]. |
| (...skipping 435 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 446 // where (C', x) = Build(e, C) | 446 // where (C', x) = Build(e, C) |
| 447 // | 447 // |
| 448 // Return without a subexpression is translated as if it were return null. | 448 // Return without a subexpression is translated as if it were return null. |
| 449 ir.Primitive visitReturn(ast.Return node) { | 449 ir.Primitive visitReturn(ast.Return node) { |
| 450 assert(irBuilder.isOpen); | 450 assert(irBuilder.isOpen); |
| 451 assert(invariant(node, node.beginToken.value != 'native')); | 451 assert(invariant(node, node.beginToken.value != 'native')); |
| 452 irBuilder.buildReturn(build(node.expression)); | 452 irBuilder.buildReturn(build(node.expression)); |
| 453 return null; | 453 return null; |
| 454 } | 454 } |
| 455 | 455 |
| 456 ir.Primitive visitTryStatement(ast.TryStatement node) { | 456 visitTryStatement(ast.TryStatement node) { |
| 457 assert(this.irBuilder.isOpen); | |
| 458 // Try/catch is not yet implemented in the JS backend. | 457 // Try/catch is not yet implemented in the JS backend. |
| 459 if (this.irBuilder.tryStatements == null) { | 458 if (this.irBuilder.tryStatements == null) { |
| 460 return giveup(node, 'try/catch in the JS backend'); | 459 return giveup(node, 'try/catch in the JS backend'); |
| 461 } | 460 } |
| 462 // Multiple catch blocks are not yet implemented. | 461 // Multiple catch blocks are not yet implemented. |
| 463 if (node.catchBlocks.isEmpty || | 462 if (node.catchBlocks.isEmpty || |
| 464 node.catchBlocks.nodes.tail == null) { | 463 node.catchBlocks.nodes.tail == null) { |
| 465 return giveup(node, 'not exactly one catch block'); | 464 return giveup(node, 'not exactly one catch block'); |
| 466 } | 465 } |
| 467 // 'on T' catch blocks are not yet implemented. | 466 // 'on T' catch blocks are not yet implemented. |
| 468 if ((node.catchBlocks.nodes.head as ast.CatchBlock).onKeyword != null) { | 467 if ((node.catchBlocks.nodes.head as ast.CatchBlock).onKeyword != null) { |
| 469 return giveup(node, '"on T" catch block'); | 468 return giveup(node, '"on T" catch block'); |
| 470 } | 469 } |
| 471 // Finally blocks are not yet implemented. | 470 // Finally blocks are not yet implemented. |
| 472 if (node.finallyBlock != null) { | 471 if (node.finallyBlock != null) { |
| 473 return giveup(node, 'try/finally'); | 472 return giveup(node, 'try/finally'); |
| 474 } | 473 } |
| 475 | 474 |
| 476 // Catch handlers are in scope for their body. The CPS translation of | 475 List<CatchClauseInfo> catchClauseInfos = <CatchClauseInfo>[]; |
| 477 // [[try tryBlock catch (e) catchBlock; successor]] is: | 476 for (ast.CatchBlock catchClause in node.catchBlocks.nodes) { |
| 478 // | 477 assert(catchClause.exception != null); |
| 479 // let cont join(v0, v1, ...) = [[successor]] in | 478 LocalVariableElement exceptionVariable = elements[catchClause.exception]; |
| 480 // let mutable m0 = x0 in | 479 LocalVariableElement stackTraceVariable; |
| 481 // let mutable m1 = x1 in | 480 if (catchClause.trace != null) { |
| 482 // ... | 481 stackTraceVariable = elements[catchClause.trace]; |
| 483 // let handler catch_(e) = | 482 } |
| 484 // let prim p0 = GetMutable(m0) in | 483 catchClauseInfos.add(new CatchClauseInfo( |
| 485 // let prim p1 = GetMutable(m1) in | 484 exceptionVariable: exceptionVariable, |
| 486 // ... | 485 stackTraceVariable: stackTraceVariable, |
| 487 // [[catchBlock]] | 486 buildCatchBlock: subbuild(catchClause.block))); |
| 488 // join(p0, p1, ...) | |
| 489 // in | |
| 490 // [[tryBlock]] | |
| 491 // let prim p0' = GetMutable(m0) in | |
| 492 // let prim p1' = GetMutable(m1) in | |
| 493 // ... | |
| 494 // join(p0', p1', ...) | |
| 495 // | |
| 496 // In other words, both the try and catch block are in the scope of the | |
| 497 // join-point continuation, and they are both in the scope of a sequence | |
| 498 // of mutable bindings for the variables assigned in the try. The join- | |
| 499 // point continuation is not in the scope of these mutable bindings. | |
| 500 // The tryBlock is in the scope of a binding for the catch handler. Each | |
| 501 // instruction (specifically, each call) in the tryBlock is in the dynamic | |
| 502 // scope of the handler. The mutable bindings are dereferenced at the end | |
| 503 // of the try block and at the beginning of the catch block, so the | |
| 504 // variables are unboxed in the catch block and at the join point. | |
| 505 | |
| 506 IrBuilder tryCatchBuilder = irBuilder.makeDelimitedBuilder(); | |
| 507 TryStatementInfo tryInfo = tryCatchBuilder.tryStatements[node]; | |
| 508 // Variables that are boxed due to being captured in a closure are boxed | |
| 509 // for their entire lifetime, and so they do not need to be boxed on | |
| 510 // entry to any try block. We check for them here because we can not | |
| 511 // identify all of them in the same pass where we identify the variables | |
| 512 // assigned in the try (the may be captured by a closure after the try | |
| 513 // statement). | |
| 514 Iterable<LocalVariableElement> boxedOnEntry = | |
| 515 tryInfo.boxedOnEntry.where((LocalVariableElement variable) { | |
| 516 return !tryCatchBuilder.mutableCapturedVariables.contains(variable); | |
| 517 }); | |
| 518 for (LocalVariableElement variable in boxedOnEntry) { | |
| 519 assert(!tryCatchBuilder.isInMutableVariable(variable)); | |
| 520 ir.Primitive value = tryCatchBuilder.buildLocalGet(variable); | |
| 521 tryCatchBuilder.makeMutableVariable(variable); | |
| 522 tryCatchBuilder.declareLocalVariable(variable, initialValue: value); | |
| 523 } | 487 } |
| 524 | 488 |
| 525 IrBuilder catchBuilder = tryCatchBuilder.makeDelimitedBuilder(); | 489 irBuilder.buildTry( |
| 526 IrBuilder tryBuilder = tryCatchBuilder.makeDelimitedBuilder(); | 490 tryStatementInfo: irBuilder.tryStatements[node], |
| 527 List<ir.Parameter> joinParameters = | 491 buildTryBlock: subbuild(node.tryBlock), |
| 528 new List<ir.Parameter>.generate(irBuilder.environment.length, (i) { | 492 catchClauseInfos: catchClauseInfos); |
| 529 return new ir.Parameter(irBuilder.environment.index2variable[i]); | |
| 530 }); | |
| 531 ir.Continuation joinContinuation = new ir.Continuation(joinParameters); | |
| 532 | |
| 533 void interceptJumps(JumpCollector collector) { | |
| 534 collector.enterTry(boxedOnEntry); | |
| 535 } | |
| 536 void restoreJumps(JumpCollector collector) { | |
| 537 collector.leaveTry(); | |
| 538 } | |
| 539 tryBuilder.state.breakCollectors.forEach(interceptJumps); | |
| 540 tryBuilder.state.continueCollectors.forEach(interceptJumps); | |
| 541 withBuilder(tryBuilder, () { | |
| 542 visit(node.tryBlock); | |
| 543 }); | |
| 544 tryBuilder.state.breakCollectors.forEach(restoreJumps); | |
| 545 tryBuilder.state.continueCollectors.forEach(restoreJumps); | |
| 546 if (tryBuilder.isOpen) { | |
| 547 for (LocalVariableElement variable in boxedOnEntry) { | |
| 548 assert(tryBuilder.isInMutableVariable(variable)); | |
| 549 ir.Primitive value = tryBuilder.buildLocalGet(variable); | |
| 550 tryBuilder.environment.update(variable, value); | |
| 551 } | |
| 552 tryBuilder.jumpTo(joinContinuation); | |
| 553 } | |
| 554 | |
| 555 for (LocalVariableElement variable in boxedOnEntry) { | |
| 556 assert(catchBuilder.isInMutableVariable(variable)); | |
| 557 ir.Primitive value = catchBuilder.buildLocalGet(variable); | |
| 558 // Note that we remove the variable from the set of mutable variables | |
| 559 // here (and not above for the try body). This is because the set of | |
| 560 // mutable variables is global for the whole function and not local to | |
| 561 // a delimited builder. | |
| 562 catchBuilder.removeMutableVariable(variable); | |
| 563 catchBuilder.environment.update(variable, value); | |
| 564 } | |
| 565 ast.CatchBlock catchClause = node.catchBlocks.nodes.head; | |
| 566 assert(catchClause.exception != null); | |
| 567 LocalVariableElement exceptionElement = elements[catchClause.exception]; | |
| 568 ir.Parameter exceptionParameter = new ir.Parameter(exceptionElement); | |
| 569 catchBuilder.environment.extend(exceptionElement, exceptionParameter); | |
| 570 ir.Parameter traceParameter; | |
| 571 if (catchClause.trace != null) { | |
| 572 LocalVariableElement traceElement = elements[catchClause.trace]; | |
| 573 traceParameter = new ir.Parameter(traceElement); | |
| 574 catchBuilder.environment.extend(traceElement, traceParameter); | |
| 575 } else { | |
| 576 // Use a dummy continuation parameter for the stack trace parameter. | |
| 577 // This will ensure that all handlers have two parameters and so they | |
| 578 // can be treated uniformly. | |
| 579 traceParameter = new ir.Parameter(null); | |
| 580 } | |
| 581 withBuilder(catchBuilder, () { | |
| 582 visit(catchClause.block); | |
| 583 }); | |
| 584 if (catchBuilder.isOpen) { | |
| 585 catchBuilder.jumpTo(joinContinuation); | |
| 586 } | |
| 587 List<ir.Parameter> catchParameters = | |
| 588 <ir.Parameter>[exceptionParameter, traceParameter]; | |
| 589 ir.Continuation catchContinuation = new ir.Continuation(catchParameters); | |
| 590 catchContinuation.body = catchBuilder._root; | |
| 591 | |
| 592 tryCatchBuilder.add(new ir.LetHandler(catchContinuation, tryBuilder._root)); | |
| 593 tryCatchBuilder._current = null; | |
| 594 | |
| 595 irBuilder.add(new ir.LetCont(joinContinuation, tryCatchBuilder._root)); | |
| 596 for (int i = 0; i < irBuilder.environment.length; ++i) { | |
| 597 irBuilder.environment.index2value[i] = joinParameters[i]; | |
| 598 } | |
| 599 return null; | |
| 600 } | 493 } |
| 601 | 494 |
| 602 // ==== Expressions ==== | 495 // ==== Expressions ==== |
| 603 ir.Primitive visitConditional(ast.Conditional node) { | 496 ir.Primitive visitConditional(ast.Conditional node) { |
| 604 return irBuilder.buildConditional( | 497 return irBuilder.buildConditional( |
| 605 build(node.condition), | 498 build(node.condition), |
| 606 subbuild(node.thenExpression), | 499 subbuild(node.thenExpression), |
| 607 subbuild(node.elseExpression)); | 500 subbuild(node.elseExpression)); |
| 608 } | 501 } |
| 609 | 502 |
| (...skipping 1331 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1941 SourceInformation buildCall(ast.Node node) { | 1834 SourceInformation buildCall(ast.Node node) { |
| 1942 return new PositionSourceInformation( | 1835 return new PositionSourceInformation( |
| 1943 new TokenSourceLocation(sourceFile, node.getBeginToken(), name)); | 1836 new TokenSourceLocation(sourceFile, node.getBeginToken(), name)); |
| 1944 } | 1837 } |
| 1945 | 1838 |
| 1946 @override | 1839 @override |
| 1947 SourceInformationBuilder forContext(AstElement element) { | 1840 SourceInformationBuilder forContext(AstElement element) { |
| 1948 return new PositionSourceInformationBuilder(element); | 1841 return new PositionSourceInformationBuilder(element); |
| 1949 } | 1842 } |
| 1950 } | 1843 } |
| OLD | NEW |