84 private final Target target; |
84 private final Target target; |
85 private final Source source; |
85 private final Source source; |
86 private final TypeEnvs typeEnvs; |
86 private final TypeEnvs typeEnvs; |
87 private final Name dollarAssertionsDisabled; |
87 private final Name dollarAssertionsDisabled; |
88 private final Name classDollar; |
88 private final Name classDollar; |
|
89 private final Name dollarCloseResource; |
89 private final Types types; |
90 private final Types types; |
90 private final boolean debugLower; |
91 private final boolean debugLower; |
91 private final PkgInfo pkginfoOpt; |
92 private final PkgInfo pkginfoOpt; |
92 |
93 |
93 protected Lower(Context context) { |
94 protected Lower(Context context) { |
107 typeEnvs = TypeEnvs.instance(context); |
108 typeEnvs = TypeEnvs.instance(context); |
108 dollarAssertionsDisabled = names. |
109 dollarAssertionsDisabled = names. |
109 fromString(target.syntheticNameChar() + "assertionsDisabled"); |
110 fromString(target.syntheticNameChar() + "assertionsDisabled"); |
110 classDollar = names. |
111 classDollar = names. |
111 fromString("class" + target.syntheticNameChar()); |
112 fromString("class" + target.syntheticNameChar()); |
|
113 dollarCloseResource = names. |
|
114 fromString(target.syntheticNameChar() + "closeResource"); |
112 |
115 |
113 types = Types.instance(context); |
116 types = Types.instance(context); |
114 Options options = Options.instance(context); |
117 Options options = Options.instance(context); |
115 debugLower = options.isSet("debuglower"); |
118 debugLower = options.isSet("debuglower"); |
116 pkginfoOpt = PkgInfo.get(options); |
119 pkginfoOpt = PkgInfo.get(options); |
1646 |
1649 |
1647 // Add resource declaration or expression to block statements |
1650 // Add resource declaration or expression to block statements |
1648 ListBuffer<JCStatement> stats = new ListBuffer<>(); |
1651 ListBuffer<JCStatement> stats = new ListBuffer<>(); |
1649 JCTree resource = resources.head; |
1652 JCTree resource = resources.head; |
1650 JCExpression expr = null; |
1653 JCExpression expr = null; |
|
1654 boolean resourceNonNull; |
1651 if (resource instanceof JCVariableDecl) { |
1655 if (resource instanceof JCVariableDecl) { |
1652 JCVariableDecl var = (JCVariableDecl) resource; |
1656 JCVariableDecl var = (JCVariableDecl) resource; |
1653 expr = make.Ident(var.sym).setType(resource.type); |
1657 expr = make.Ident(var.sym).setType(resource.type); |
|
1658 resourceNonNull = var.init != null && TreeInfo.skipParens(var.init).hasTag(NEWCLASS); |
1654 stats.add(var); |
1659 stats.add(var); |
1655 } else { |
1660 } else { |
1656 Assert.check(resource instanceof JCExpression); |
1661 Assert.check(resource instanceof JCExpression); |
1657 VarSymbol syntheticTwrVar = |
1662 VarSymbol syntheticTwrVar = |
1658 new VarSymbol(SYNTHETIC | FINAL, |
1663 new VarSymbol(SYNTHETIC | FINAL, |
1663 currentMethodSym); |
1668 currentMethodSym); |
1664 twrVars.enter(syntheticTwrVar); |
1669 twrVars.enter(syntheticTwrVar); |
1665 JCVariableDecl syntheticTwrVarDecl = |
1670 JCVariableDecl syntheticTwrVarDecl = |
1666 make.VarDef(syntheticTwrVar, (JCExpression)resource); |
1671 make.VarDef(syntheticTwrVar, (JCExpression)resource); |
1667 expr = (JCExpression)make.Ident(syntheticTwrVar); |
1672 expr = (JCExpression)make.Ident(syntheticTwrVar); |
|
1673 resourceNonNull = TreeInfo.skipParens(resource).hasTag(NEWCLASS); |
1668 stats.add(syntheticTwrVarDecl); |
1674 stats.add(syntheticTwrVarDecl); |
1669 } |
1675 } |
1670 |
1676 |
1671 // Add primaryException declaration |
1677 // Add primaryException declaration |
1672 VarSymbol primaryException = |
1678 VarSymbol primaryException = |
1692 JCBlock catchBlock = make.Block(0L, List.<JCStatement>of(assign, rethrowStat)); |
1698 JCBlock catchBlock = make.Block(0L, List.<JCStatement>of(assign, rethrowStat)); |
1693 JCCatch catchClause = make.Catch(paramTree, catchBlock); |
1699 JCCatch catchClause = make.Catch(paramTree, catchBlock); |
1694 |
1700 |
1695 int oldPos = make.pos; |
1701 int oldPos = make.pos; |
1696 make.at(TreeInfo.endPos(block)); |
1702 make.at(TreeInfo.endPos(block)); |
1697 JCBlock finallyClause = makeTwrFinallyClause(primaryException, expr); |
1703 JCBlock finallyClause = makeTwrFinallyClause(primaryException, expr, resourceNonNull); |
1698 make.at(oldPos); |
1704 make.at(oldPos); |
1699 JCTry outerTry = make.Try(makeTwrBlock(resources.tail, block, |
1705 JCTry outerTry = make.Try(makeTwrBlock(resources.tail, block, |
1700 finallyCanCompleteNormally, depth + 1), |
1706 finallyCanCompleteNormally, depth + 1), |
1701 List.<JCCatch>of(catchClause), |
1707 List.<JCCatch>of(catchClause), |
1702 finallyClause); |
1708 finallyClause); |
1704 stats.add(outerTry); |
1710 stats.add(outerTry); |
1705 JCBlock newBlock = make.Block(0L, stats.toList()); |
1711 JCBlock newBlock = make.Block(0L, stats.toList()); |
1706 return newBlock; |
1712 return newBlock; |
1707 } |
1713 } |
1708 |
1714 |
1709 private JCBlock makeTwrFinallyClause(Symbol primaryException, JCExpression resource) { |
1715 /**If the estimated number of copies the close resource code in a single class is above this |
|
1716 * threshold, generate and use a method for the close resource code, leading to smaller code. |
|
1717 * As generating a method has overhead on its own, generating the method for cases below the |
|
1718 * threshold could lead to an increase in code size. |
|
1719 */ |
|
1720 public static final int USE_CLOSE_RESOURCE_METHOD_THRESHOLD = 4; |
|
1721 |
|
1722 private JCBlock makeTwrFinallyClause(Symbol primaryException, JCExpression resource, |
|
1723 boolean resourceNonNull) { |
|
1724 MethodSymbol closeResource = (MethodSymbol)lookupSynthetic(dollarCloseResource, |
|
1725 currentClass.members()); |
|
1726 |
|
1727 if (closeResource == null && shouldUseCloseResourceMethod()) { |
|
1728 closeResource = new MethodSymbol( |
|
1729 PRIVATE | STATIC | SYNTHETIC, |
|
1730 dollarCloseResource, |
|
1731 new MethodType( |
|
1732 List.of(syms.throwableType, syms.autoCloseableType), |
|
1733 syms.voidType, |
|
1734 List.<Type>nil(), |
|
1735 syms.methodClass), |
|
1736 currentClass); |
|
1737 enterSynthetic(resource.pos(), closeResource, currentClass.members()); |
|
1738 |
|
1739 JCMethodDecl md = make.MethodDef(closeResource, null); |
|
1740 List<JCVariableDecl> params = md.getParameters(); |
|
1741 md.body = make.Block(0, List.<JCStatement>of(makeTwrCloseStatement(params.get(0).sym, |
|
1742 make.Ident(params.get(1))))); |
|
1743 |
|
1744 JCClassDecl currentClassDecl = classDef(currentClass); |
|
1745 currentClassDecl.defs = currentClassDecl.defs.prepend(md); |
|
1746 } |
|
1747 |
|
1748 JCStatement closeStatement; |
|
1749 |
|
1750 if (closeResource != null) { |
|
1751 //$closeResource(#primaryException, #resource) |
|
1752 closeStatement = make.Exec(make.Apply(List.<JCExpression>nil(), |
|
1753 make.Ident(closeResource), |
|
1754 List.of(make.Ident(primaryException), |
|
1755 resource) |
|
1756 ).setType(syms.voidType)); |
|
1757 } else { |
|
1758 closeStatement = makeTwrCloseStatement(primaryException, resource); |
|
1759 } |
|
1760 |
|
1761 JCStatement finallyStatement; |
|
1762 |
|
1763 if (resourceNonNull) { |
|
1764 finallyStatement = closeStatement; |
|
1765 } else { |
|
1766 // if (#resource != null) { $closeResource(...); } |
|
1767 finallyStatement = make.If(makeNonNullCheck(resource), |
|
1768 closeStatement, |
|
1769 null); |
|
1770 } |
|
1771 |
|
1772 return make.Block(0L, |
|
1773 List.<JCStatement>of(finallyStatement)); |
|
1774 } |
|
1775 //where: |
|
1776 private boolean shouldUseCloseResourceMethod() { |
|
1777 class TryFinder extends TreeScanner { |
|
1778 int closeCount; |
|
1779 @Override |
|
1780 public void visitTry(JCTry tree) { |
|
1781 boolean empty = tree.body.stats.isEmpty(); |
|
1782 |
|
1783 for (JCTree r : tree.resources) { |
|
1784 closeCount += empty ? 1 : 2; |
|
1785 empty = false; //with multiple resources, only the innermost try can be empty. |
|
1786 } |
|
1787 super.visitTry(tree); |
|
1788 } |
|
1789 @Override |
|
1790 public void scan(JCTree tree) { |
|
1791 if (useCloseResourceMethod()) |
|
1792 return; |
|
1793 super.scan(tree); |
|
1794 } |
|
1795 boolean useCloseResourceMethod() { |
|
1796 return closeCount >= USE_CLOSE_RESOURCE_METHOD_THRESHOLD; |
|
1797 } |
|
1798 } |
|
1799 TryFinder tryFinder = new TryFinder(); |
|
1800 tryFinder.scan(classDef(currentClass)); |
|
1801 return tryFinder.useCloseResourceMethod(); |
|
1802 } |
|
1803 |
|
1804 private JCStatement makeTwrCloseStatement(Symbol primaryException, JCExpression resource) { |
1710 // primaryException.addSuppressed(catchException); |
1805 // primaryException.addSuppressed(catchException); |
1711 VarSymbol catchException = |
1806 VarSymbol catchException = |
1712 new VarSymbol(SYNTHETIC, make.paramName(2), |
1807 new VarSymbol(SYNTHETIC, make.paramName(2), |
1713 syms.throwableType, |
1808 syms.throwableType, |
1714 currentMethodSym); |
1809 currentMethodSym); |
1729 // if (primaryException != null) {try...} else resourceClose; |
1824 // if (primaryException != null) {try...} else resourceClose; |
1730 JCIf closeIfStatement = make.If(makeNonNullCheck(make.Ident(primaryException)), |
1825 JCIf closeIfStatement = make.If(makeNonNullCheck(make.Ident(primaryException)), |
1731 tryTree, |
1826 tryTree, |
1732 makeResourceCloseInvocation(resource)); |
1827 makeResourceCloseInvocation(resource)); |
1733 |
1828 |
1734 // if (#resource != null) { if (primaryException ... } |
1829 return closeIfStatement; |
1735 return make.Block(0L, |
|
1736 List.<JCStatement>of(make.If(makeNonNullCheck(resource), |
|
1737 closeIfStatement, |
|
1738 null))); |
|
1739 } |
1830 } |
1740 |
1831 |
1741 private JCStatement makeResourceCloseInvocation(JCExpression resource) { |
1832 private JCStatement makeResourceCloseInvocation(JCExpression resource) { |
1742 // convert to AutoCloseable if needed |
1833 // convert to AutoCloseable if needed |
1743 if (types.asSuper(resource.type, syms.autoCloseableType.tsym) == null) { |
1834 if (types.asSuper(resource.type, syms.autoCloseableType.tsym) == null) { |