88import org .jetbrains .java .decompiler .main .collectors .VarNamesCollector ;
99import org .jetbrains .java .decompiler .main .extern .IFernflowerPreferences ;
1010import org .jetbrains .java .decompiler .main .rels .MethodWrapper ;
11- import org .jetbrains .java .decompiler .modules .decompiler .ExprProcessor ;
12- import org .jetbrains .java .decompiler .modules .decompiler .StackVarsProcessor ;
13- import org .jetbrains .java .decompiler .modules .decompiler .ValidationHelper ;
11+ import org .jetbrains .java .decompiler .modules .decompiler .*;
1412import org .jetbrains .java .decompiler .modules .decompiler .exps .*;
1513import org .jetbrains .java .decompiler .modules .decompiler .flow .DirectGraph ;
1614import org .jetbrains .java .decompiler .modules .decompiler .flow .FlattenStatementsHelper ;
@@ -1593,7 +1591,7 @@ private void iterateClashingNames(Statement stat, StructMethod mt, Map<Statement
15931591 // Process var definitions as owned by the parent- they come before the statement, and so their scope extends past the actual statement.
15941592 for (Exprent exprent : stat .getVarDefinitions ()) {
15951593 Set <VarInMethod > upDefs = new HashSet <>();
1596- iterateClashingExprent (stat , mt , varDefinitions , exprent , liveVarDefs , upDefs , nameMap , seenMethods );
1594+ iterateClashingExprent (stat , mt , varDefinitions , exprent , liveVarDefs , upDefs , nameMap , seenMethods , new HashSet <>(), new HashSet <>() );
15971595 liveVarDefs .addAll (upDefs );
15981596 varDefinitions .computeIfAbsent (stat .getParent (), parent -> new HashSet <>()).addAll (upDefs );
15991597 }
@@ -1603,44 +1601,109 @@ private void iterateClashingNames(Statement stat, StructMethod mt, Map<Statement
16031601 Set <VarInMethod > upDefs = new HashSet <>();
16041602 BasicBlockStatement basic = stat .getBasichead ();
16051603 for (Exprent exprent : basic .getExprents ()) {
1606- for (Exprent ex : exprent .getAllExprents (true , true )) {
1607- iterateClashingExprent (basic , mt , varDefinitions , ex , liveVarDefs , upDefs , nameMap , seenMethods );
1608- }
1604+ iterateClashingExprent (basic , mt , varDefinitions , exprent , liveVarDefs , upDefs , nameMap , seenMethods , new HashSet <>(), new HashSet <>());
16091605 }
16101606
16111607 liveVarDefs .addAll (upDefs );
16121608 varDefinitions .computeIfAbsent (stat .getParent (), parent -> new HashSet <>()).addAll (upDefs );
16131609 }
16141610
16151611 // If this is a basic block, iterate all exprents
1612+ Map <Statement , Set <VarInMethod >> statementVars = new HashMap <>();
1613+ Set <VarInMethod > post = new HashSet <>();
16161614 if (stat .getExprents () != null ) {
16171615 for (Exprent exprent : stat .getExprents ()) {
1618- for (Exprent ex : exprent .getAllExprents (true , true )) {
1619- // Sort order from getAllExprents here is crucial!
1620- // Say, for example, "MyType t = method(t -> ....);"
1621- // It is imperative that the lhs of the assign comes first, so that any defs in the rhs can be properly seen.
1622- iterateClashingExprent (stat , mt , varDefinitions , ex , liveVarDefs , curVarDefs , nameMap , seenMethods );
1623- }
1616+ // Sort order here is crucial!
1617+ // Say, for example, "MyType t = method(t -> ....);"
1618+ // It is imperative that the lhs of the assign comes first, so that any defs in the rhs can be properly seen.
1619+ iterateClashingExprent (stat , mt , varDefinitions , exprent , liveVarDefs , curVarDefs , nameMap , seenMethods , new HashSet <>(), new HashSet <>());
16241620 }
16251621 } else {
1626- if (stat instanceof CatchStatement ) {
1622+ if (stat instanceof CatchStatement catchStat ) {
16271623 // Resources and catch vars are only in context in certain statements
1624+ for (int i = 0 ; i < catchStat .getStats ().size (); i ++) {
1625+ Statement st = catchStat .getStats ().get (i );
1626+ Set <VarInMethod > stVars = new HashSet <>();
1627+ if (i == 0 ) {
1628+ // Process var definitions in resources
1629+ for (Exprent exp : catchStat .getResources ()) {
1630+ iterateClashingExprent (st , mt , varDefinitions , exp , liveVarDefs , stVars , nameMap , seenMethods , new HashSet <>(), new HashSet <>());
1631+ }
1632+ } else {
1633+ // Process var definitions in catch
1634+ VarExprent exp = catchStat .getVars ().get (i - 1 );
1635+ iterateClashingExprent (st , mt , varDefinitions , exp , liveVarDefs , stVars , nameMap , seenMethods , new HashSet <>(), new HashSet <>());
1636+ }
1637+ liveVarDefs .removeAll (stVars );
1638+ statementVars .put (st , stVars );
1639+ }
16281640 } else if (stat instanceof SwitchStatement switchStat ) {
16291641 // Variables defined in case values and case guards are only in context in their respective statements
1630- Exprent exp = switchStat .getHeadexprent ();
1631- List <Exprent > exprents = exp .getAllExprents (true , true );
1642+ Exprent headExp = switchStat .getHeadexprent ();
1643+ iterateClashingExprent (stat , mt , varDefinitions , headExp , liveVarDefs , curVarDefs , nameMap , seenMethods , new HashSet <>(), new HashSet <>());
1644+ for (int i = 0 ; i < switchStat .getCaseStatements ().size (); i ++) {
1645+ Statement st = switchStat .getCaseStatements ().get (i );
1646+ Set <VarInMethod > stVars = new HashSet <>();
1647+ // Process var definitions in case values
1648+ for (Exprent exp : switchStat .getCaseValues ().get (i )) {
1649+ if (exp != null ) {
1650+ iterateClashingExprent (st , mt , varDefinitions , exp , liveVarDefs , stVars , nameMap , seenMethods , new HashSet <>(), new HashSet <>());
1651+ }
1652+ }
1653+ // Process var definitions in case guard
1654+ if (switchStat .getCaseGuards ().size () > i ) {
1655+ Exprent exp = switchStat .getCaseGuards ().get (i );
1656+ if (exp != null ) {
1657+ iterateClashingExprent (st , mt , varDefinitions , exp , liveVarDefs , stVars , nameMap , seenMethods , new HashSet <>(), new HashSet <>());
1658+ }
1659+ }
1660+ liveVarDefs .removeAll (stVars );
1661+ statementVars .put (st , stVars );
1662+ }
1663+ } else if (stat instanceof IfStatement ifStat ) {
1664+ Set <VarInMethod > whenTrue = new HashSet <>();
1665+ Set <VarInMethod > whenFalse = new HashSet <>();
1666+ iterateClashingExprent (stat , mt , varDefinitions , ifStat .getHeadexprent ().getCondition (), liveVarDefs , curVarDefs , nameMap , seenMethods , whenTrue , whenFalse );
1667+ boolean ifCompleteNormally = ifStat .getIfstat () == null || !ifStat .getIfstat ().getSuccessorEdges (StatEdge .TYPE_REGULAR ).isEmpty ();
1668+ boolean elseCompleteNormally = ifStat .getElsestat () == null || !ifStat .getElsestat ().getSuccessorEdges (StatEdge .TYPE_REGULAR ).isEmpty ();
1669+ if (ifStat .getIfstat () != null ) {
1670+ statementVars .put (ifStat .getIfstat (), whenTrue );
1671+ }
1672+
1673+ if (ifStat .getElsestat () != null ) {
1674+ statementVars .put (ifStat .getElsestat (), whenFalse );
1675+ }
16321676
1633- for (Exprent exprent : exprents ) {
1634- iterateClashingExprent (stat , mt , varDefinitions , exprent , liveVarDefs , curVarDefs , nameMap , seenMethods );
1677+ if (!ifCompleteNormally && elseCompleteNormally ) {
1678+ post = whenFalse ;
1679+ } else if (ifCompleteNormally && !elseCompleteNormally ) {
1680+ post = whenTrue ;
1681+ }
1682+ } else if (stat instanceof DoStatement doStat ) {
1683+ Set <VarInMethod > whenTrue = new HashSet <>();
1684+ Set <VarInMethod > whenFalse = new HashSet <>();
1685+ if (doStat .getInitExprent () != null ) {
1686+ iterateClashingExprent (stat , mt , varDefinitions , doStat .getInitExprent (), liveVarDefs , curVarDefs , nameMap , seenMethods , new HashSet <>(), new HashSet <>());
1687+ }
1688+ if (doStat .getConditionExprent () != null ) {
1689+ iterateClashingExprent (stat , mt , varDefinitions , doStat .getConditionExprent (), liveVarDefs , curVarDefs , nameMap , seenMethods , whenTrue , whenFalse );
1690+ }
1691+ if (doStat .getIncExprent () != null ) {
1692+ liveVarDefs .addAll (whenTrue );
1693+ iterateClashingExprent (doStat , mt , varDefinitions , doStat .getIncExprent (), liveVarDefs , curVarDefs , nameMap , seenMethods , new HashSet <>(), new HashSet <>());
1694+ liveVarDefs .removeAll (whenTrue );
1695+ }
1696+
1697+ if (doStat .getLooptype () == DoStatement .Type .WHILE || doStat .getLooptype () == DoStatement .Type .FOR ) {
1698+ statementVars .put (doStat .getFirst (), whenTrue );
1699+ }
1700+ if (doStat .getSuccessorEdges (StatEdge .TYPE_BREAK ).isEmpty ()) {
1701+ post = whenFalse ;
16351702 }
16361703 } else {
16371704 // Process var definitions in statement head
16381705 for (Exprent exp : stat .getStatExprents ()) {
1639- List <Exprent > exprents = exp .getAllExprents (true , true );
1640-
1641- for (Exprent exprent : exprents ) {
1642- iterateClashingExprent (stat , mt , varDefinitions , exprent , liveVarDefs , curVarDefs , nameMap , seenMethods );
1643- }
1706+ iterateClashingExprent (stat , mt , varDefinitions , exp , liveVarDefs , curVarDefs , nameMap , seenMethods , new HashSet <>(), new HashSet <>());
16441707 }
16451708 }
16461709
@@ -1661,80 +1724,24 @@ private void iterateClashingNames(Statement stat, StructMethod mt, Map<Statement
16611724 }
16621725
16631726 List <Statement > deferred = new ArrayList <>();
1727+ boolean seperate = stat instanceof IfStatement || stat instanceof CatchStatement ;
16641728 if (iterate ) {
16651729 for (Statement st : stat .getStats ()) {
16661730 Set <VarInMethod > stVars = new HashSet <>();
1667- Set <VarInMethod > tmpVars = new HashSet <>();
1668- boolean remove = false ;
1731+ Set <VarInMethod > tmpVars = statementVars .getOrDefault (st , new HashSet <>());
16691732 if (stat instanceof IfStatement ) {
1670- IfStatement ifstat = (IfStatement )stat ;
1671-
1672- if (ifstat .getElsestat () == st ) {
1673- // Defer else blocks of if statements, as they are independent from the context of the if block
1674- deferred .add (st );
1675- continue ;
1676- }
1677-
16781733 // We've already looked at the head- don't look again!
16791734 if (st == stat .getBasichead ()) {
16801735 continue ;
16811736 }
1682- } else if (stat instanceof CatchStatement catchStat ) {
1683- if (catchStat .getFirst () == st ) {
1684- // Process var definitions in resources
1685- for (Exprent exp : catchStat .getResources ()) {
1686- List <Exprent > exprents = exp .getAllExprents (true , true );
1687-
1688- for (Exprent exprent : exprents ) {
1689- iterateClashingExprent (st , mt , varDefinitions , exprent , liveVarDefs , stVars , nameMap , seenMethods );
1690- }
1691- }
1692- remove = true ;
1693- } else if (catchStat .getStats ().indexOf (st ) > 0 ) {
1694- // Process var definitions in catch
1695- int i = catchStat .getStats ().indexOf (st );
1696- VarExprent exp = catchStat .getVars ().get (i - 1 );
1697- List <Exprent > exprents = exp .getAllExprents (true , true );
1698-
1699- for (Exprent exprent : exprents ) {
1700- iterateClashingExprent (st , mt , varDefinitions , exprent , liveVarDefs , stVars , nameMap , seenMethods );
1701- }
1702- remove = true ;
1703- }
1704- } else if (stat instanceof SwitchStatement switchStat ) {
1705- int i = switchStat .getCaseStatements ().indexOf (st );
1706- if (i >= 0 ) {
1707- // Process var definitions in case values
1708- for (Exprent exp : switchStat .getCaseValues ().get (i )) {
1709- if (exp != null ) {
1710- List <Exprent > exprents = exp .getAllExprents (true , true );
1711-
1712- for (Exprent exprent : exprents ) {
1713- iterateClashingExprent (st , mt , varDefinitions , exprent , liveVarDefs , tmpVars , nameMap , seenMethods );
1714- }
1715- }
1716- }
1717- // Process var definitions in case guard
1718- if (switchStat .getCaseGuards ().size () > i ) {
1719- Exprent exp = switchStat .getCaseGuards ().get (i );
1720- if (exp != null ) {
1721- List <Exprent > exprents = exp .getAllExprents (true , true );
1722-
1723- for (Exprent exprent : exprents ) {
1724- iterateClashingExprent (st , mt , varDefinitions , exprent , liveVarDefs , tmpVars , nameMap , seenMethods );
1725- }
1726- }
1727- }
1728- }
17291737 }
17301738 liveVarDefs .addAll (tmpVars );
1731-
17321739 iterateClashingNames (st , mt , varDefinitions , liveVarDefs , stVars , nameMap , seenMethods );
1733- if (remove ) {
1740+ liveVarDefs .removeAll (tmpVars );
1741+ if (seperate ) {
17341742 clearStatement (varDefinitions , liveVarDefs , nameMap , st );
1743+ clearStatement (varDefinitions , liveVarDefs , nameMap , stat );
17351744 }
1736- liveVarDefs .removeAll (tmpVars );
1737- tmpVars .forEach (nameMap ::remove );
17381745 }
17391746 }
17401747
@@ -1751,7 +1758,10 @@ private void iterateClashingNames(Statement stat, StructMethod mt, Map<Statement
17511758 // Process deferred statements
17521759 if (iterate ) {
17531760 for (Statement st : deferred ) {
1761+ Set <VarInMethod > tmpVars = statementVars .getOrDefault (st , new HashSet <>());
1762+ liveVarDefs .addAll (tmpVars );
17541763 iterateClashingNames (st , mt , varDefinitions , liveVarDefs , new HashSet <>(), nameMap , seenMethods );
1764+ liveVarDefs .removeAll (tmpVars );
17551765 }
17561766 }
17571767
@@ -1760,6 +1770,11 @@ private void iterateClashingNames(Statement stat, StructMethod mt, Map<Statement
17601770 clearStatement (varDefinitions , liveVarDefs , nameMap , st );
17611771 }
17621772 }
1773+
1774+ if (!(stat instanceof RootStatement )) {
1775+ varDefinitions .computeIfAbsent (stat .getParent (), st -> new HashSet <>()).addAll (post );
1776+ liveVarDefs .addAll (post );
1777+ }
17631778 }
17641779
17651780 private void clearStatement (Map <Statement , Set <VarInMethod >> varDefinitions , Set <VarInMethod > liveVarDefs , Map <VarInMethod , String > nameMap , Statement st ) {
@@ -1774,7 +1789,8 @@ private void clearStatement(Map<Statement, Set<VarInMethod>> varDefinitions, Set
17741789 }
17751790
17761791 private void iterateClashingExprent (Statement stat , StructMethod mt , Map <Statement , Set <VarInMethod >> varDefinitions , Exprent exprent ,
1777- Set <VarInMethod > liveVarDefs , Set <VarInMethod > curVarDefs , Map <VarInMethod , String > nameMap , Set <String > seenMethods ) {
1792+ Set <VarInMethod > liveVarDefs , Set <VarInMethod > curVarDefs , Map <VarInMethod , String > nameMap , Set <String > seenMethods , Set <VarInMethod > whenTrue , Set <VarInMethod > whenFalse ) {
1793+ boolean iterate = true ;
17781794 if (exprent instanceof NewExprent ) {
17791795 NewExprent newExprent = (NewExprent ) exprent ;
17801796 // Check if this is a lambda with a body
@@ -1803,7 +1819,7 @@ private void iterateClashingExprent(Statement stat, StructMethod mt, Map<Stateme
18031819 // Try to perform a rename
18041820 String name = mw .varproc .getVarName (vvp );
18051821 String original = name ;
1806- name = rename (nameMap , name );
1822+ name = rename (nameMap , name , liveVarDefs );
18071823
18081824 // Did we rename? If so, we should add it to the name map and set as clashing
18091825 if (original != null && !original .equals (name )) {
@@ -1857,7 +1873,7 @@ private void iterateClashingExprent(Statement stat, StructMethod mt, Map<Stateme
18571873 // Try to perform a rename
18581874 String name = mw .varproc .getVarName (vvp );
18591875 String original = name ;
1860- name = rename (nameMap , name );
1876+ name = rename (nameMap , name , liveVarDefs );
18611877
18621878 // Did we rename? If so, we should add it to the name map and set as clashing
18631879 if (original != null && !original .equals (name )) {
@@ -1892,14 +1908,16 @@ private void iterateClashingExprent(Statement stat, StructMethod mt, Map<Stateme
18921908 VarExprent var = (VarExprent ) exprent ;
18931909
18941910 if (var .isDefinition ()) {
1895- curVarDefs .add (new VarInMethod (var .getVarVersionPair (), mt ));
1911+ VarInMethod def = new VarInMethod (var .getVarVersionPair (), mt );
1912+ curVarDefs .add (def );
1913+ liveVarDefs .add (def );
18961914
18971915 // Only process vars that have lvt as the default var<index>_<version> names can never conflict
18981916 if (var .getLVT () != null || this .varproc .getVarName (var .getVarVersionPair ()) != null ) {
18991917 String name = var .getLVT () == null ? this .varproc .getVarName (var .getVarVersionPair ()) : var .getLVT ().getName ();
19001918
19011919 String originalName = name ;
1902- name = rename (nameMap , name );
1920+ name = rename (nameMap , name , liveVarDefs );
19031921
19041922 boolean scopedSwitch = false ;
19051923 if (!originalName .equals (name )) {
@@ -1958,10 +1976,61 @@ private void iterateClashingExprent(Statement stat, StructMethod mt, Map<Stateme
19581976 }
19591977 }
19601978 }
1979+
1980+ if (exprent instanceof FunctionExprent function ) {
1981+ iterate = false ;
1982+ // https://docs.oracle.com/javase/specs/jls/se25/html/jls-6.html#jls-6.3.1
1983+ switch (function .getFuncType ()) {
1984+ case BOOLEAN_AND -> {
1985+ for (Exprent exp : function .getLstOperands ()) {
1986+ liveVarDefs .addAll (whenTrue );
1987+ iterateClashingExprent (stat , mt , varDefinitions , exp , liveVarDefs , curVarDefs , nameMap , seenMethods , whenTrue , new HashSet <>());
1988+ }
1989+ liveVarDefs .removeAll (whenTrue );
1990+ }
1991+ case BOOLEAN_OR -> {
1992+ for (Exprent exp : function .getLstOperands ()) {
1993+ liveVarDefs .addAll (whenFalse );
1994+ iterateClashingExprent (stat , mt , varDefinitions , exp , liveVarDefs , curVarDefs , nameMap , seenMethods , new HashSet <>(), whenFalse );
1995+ }
1996+ liveVarDefs .removeAll (whenFalse );
1997+ }
1998+ case BOOL_NOT -> {
1999+ iterateClashingExprent (stat , mt , varDefinitions , function .getLstOperands ().get (0 ), liveVarDefs , curVarDefs , nameMap , seenMethods , whenFalse , whenTrue );
2000+ }
2001+ case TERNARY -> {
2002+ Set <VarInMethod > subWhenTrue = new HashSet <>();
2003+ Set <VarInMethod > subWhenFalse = new HashSet <>();
2004+ iterateClashingExprent (stat , mt , varDefinitions , function .getLstOperands ().get (0 ), liveVarDefs , curVarDefs , nameMap , seenMethods , subWhenTrue , subWhenFalse );
2005+ liveVarDefs .addAll (subWhenTrue );
2006+ iterateClashingExprent (stat , mt , varDefinitions , function .getLstOperands ().get (1 ), liveVarDefs , curVarDefs , nameMap , seenMethods , new HashSet <>(), new HashSet <>());
2007+ liveVarDefs .removeAll (subWhenTrue );
2008+ liveVarDefs .addAll (subWhenFalse );
2009+ iterateClashingExprent (stat , mt , varDefinitions , function .getLstOperands ().get (2 ), liveVarDefs , curVarDefs , nameMap , seenMethods , new HashSet <>(), new HashSet <>());
2010+ liveVarDefs .removeAll (subWhenFalse );
2011+ }
2012+ case INSTANCEOF -> {
2013+ if (function .getLstOperands ().size () > 2 ) {
2014+ iterateClashingExprent (stat , mt , varDefinitions , function .getLstOperands ().get (0 ), liveVarDefs , curVarDefs , nameMap , seenMethods , new HashSet <>(), new HashSet <>());
2015+ iterateClashingExprent (stat , mt , varDefinitions , function .getLstOperands ().get (2 ), liveVarDefs , whenTrue , nameMap , seenMethods , new HashSet <>(), new HashSet <>());
2016+ liveVarDefs .removeAll (whenTrue );
2017+ } else {
2018+ iterate = true ;
2019+ }
2020+ }
2021+ default -> iterate = true ;
2022+ }
2023+ }
2024+
2025+ if (iterate ) {
2026+ for (Exprent exp : exprent .getAllExprents ()) {
2027+ iterateClashingExprent (stat , mt , varDefinitions , exp , liveVarDefs , curVarDefs , nameMap , seenMethods , new HashSet <>(), new HashSet <>());
2028+ }
2029+ }
19612030 }
19622031
1963- private static @ NotNull String rename (Map <VarInMethod , String > nameMap , String name ) {
1964- while (nameMap . containsValue (name )) {
2032+ private static @ NotNull String rename (Map <VarInMethod , String > nameMap , String name , Set < VarInMethod > liveVarDefs ) {
2033+ while (liveVarDefs . stream (). map ( nameMap :: get ). anyMatch (name :: equals )) {
19652034 name += "x" ;
19662035 }
19672036 return name ;
0 commit comments