Skip to content

Commit cb7f62e

Browse files
authored
Merge pull request #468 from Vineflower/develop/1.11.1
Release 1.11.1
2 parents 971c157 + a1967e8 commit cb7f62e

16 files changed

+1005
-590
lines changed

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ allprojects {
5858
group = 'org.vineflower'
5959
base.archivesName = 'vineflower'
6060

61-
version = '1.11.0'
61+
version = '1.11.1'
6262

6363
def ENV = System.getenv()
6464
version = version + (ENV.GITHUB_ACTIONS ? "" : "+local")

src/org/jetbrains/java/decompiler/modules/decompiler/SwitchPatternMatchProcessor.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ private static boolean processStatement(SwitchStatement stat, Statement root) {
187187
if (assign.getLeft() instanceof VarExprent) {
188188
VarExprent var = (VarExprent) assign.getLeft();
189189

190-
if (assign.getRight() instanceof FunctionExprent && ((FunctionExprent) assign.getRight()).getFuncType() == FunctionExprent.FunctionType.CAST) {
190+
if (isPatternMatchingCastAssignment(head, assign)) {
191191
FunctionExprent cast = (FunctionExprent) assign.getRight();
192192

193193
List<Exprent> operands = new ArrayList<>();
@@ -547,4 +547,19 @@ private static boolean isSwitchPatternMatch(SwitchHeadExprent head) {
547547
public static boolean hasPatternMatch(RootStatement root) {
548548
return root.mt.getBytecodeVersion().hasSwitchPatternMatch() && DecompilerContext.getOption(IFernflowerPreferences.PATTERN_MATCHING);
549549
}
550+
551+
private static boolean isPatternMatchingCastAssignment(final SwitchHeadExprent switchHead, final AssignmentExprent assignment) {
552+
if (!(assignment.getRight() instanceof final FunctionExprent functionExprent)) return false;
553+
if (functionExprent.getFuncType() != FunctionType.CAST) return false;
554+
555+
final List<Exprent> lstOperands = functionExprent.getLstOperands();
556+
// We expect the assignment to be a literal `n = (Type) m`.
557+
// Any other operands are not allowed in simple pattern matching.
558+
if (lstOperands.size() < 2) return false;
559+
if (!(lstOperands.get(0) instanceof final VarExprent switchHeadRef)) return false;
560+
if (!(lstOperands.get(1) instanceof final ConstExprent castTypeRef)) return false;
561+
if (!switchHead.containsVar(switchHeadRef.getVarVersionPair())) return false; // Not the switch head var ref
562+
563+
return true;
564+
}
550565
}

src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@ else if (!bounds.containsKey(from)) {
307307
}
308308
}
309309

310-
if (ok && isMappingInBounds(from, to, named, bounds)) {
310+
if (ok && isMappingInBounds(from, to, named, bounds, new HashSet<>())) {
311311
upperBoundsMap.put(from, to);
312312
}
313313
}
@@ -1421,6 +1421,16 @@ private BitSet getAmbiguousParameters(List<StructMethod> matches) {
14211421
((FunctionExprent) exp).setNeedsCast(true);
14221422
}
14231423

1424+
if (i == md.params.length - 1 && mt.hasModifier(CodeConstants.ACC_VARARGS)) {
1425+
if (exp instanceof NewExprent newE && newE.getExprType().arrayDim > 0) {
1426+
for (Exprent entry : newE.getLstArrayElements()) {
1427+
if (entry instanceof FunctionExprent func && func.getSimpleCastType() != null) {
1428+
func.setNeedsCast(true);
1429+
}
1430+
}
1431+
}
1432+
}
1433+
14241434
// Check if the current parameters and method descriptor are of the same type, or if the descriptor's type is a superset of the parameter's type.
14251435
// This check ensures that parameters that can be safely passed don't have an unneeded cast on them, such as System.out.println((int)5);.
14261436
// TODO: The root cause of the above issue seems to be threading related- When debugging line by line it doesn't cast, but when running normally it does. More digging needs to be done to figure out why this happens.
@@ -1560,12 +1570,12 @@ private boolean processGenericMapping(VarType from, VarType to, Map<VarType, Lis
15601570
}
15611571

15621572
private void putGenericMapping(VarType from, VarType to, Map<VarType, List<VarType>> named, Map<VarType, List<VarType>> bounds) {
1563-
if (isMappingInBounds(from, to, named, bounds)) {
1573+
if (isMappingInBounds(from, to, named, bounds, new HashSet<>())) {
15641574
genericsMap.put(from, to);
15651575
}
15661576
}
15671577

1568-
private boolean isMappingInBounds(VarType from, VarType to, Map<VarType, List<VarType>> named, Map<VarType, List<VarType>> bounds) {
1578+
private boolean isMappingInBounds(VarType from, VarType to, Map<VarType, List<VarType>> named, Map<VarType, List<VarType>> bounds, Set<Pair<VarType, VarType>> recursivelySeen) {
15691579
if (!bounds.containsKey(from)) {
15701580
return false;
15711581
}
@@ -1602,7 +1612,11 @@ private boolean isMappingInBounds(VarType from, VarType to, Map<VarType, List<Va
16021612
}
16031613
}
16041614

1605-
return isMappingInBounds(bound, newTo, named, bounds);
1615+
Pair<VarType, VarType> pair = Pair.of(bound, newTo);
1616+
if (!recursivelySeen.contains(pair)) {
1617+
recursivelySeen.add(pair);
1618+
return isMappingInBounds(bound, newTo, named, bounds, recursivelySeen);
1619+
}
16061620
}
16071621

16081622
if (newTo.type.ordinal() < CodeType.OBJECT.ordinal()) {
@@ -1644,9 +1658,15 @@ private boolean isMappingInBounds(VarType from, VarType to, Map<VarType, List<Va
16441658
}
16451659

16461660
// T extends Comparable<S>, S extends Object
1647-
if (bounds.containsKey(boundArg) && isMappingInBounds(boundArg, newArg, named, bounds)) {
1648-
toAdd.put(boundArg, newArg);
1649-
continue;
1661+
if (bounds.containsKey(boundArg)) {
1662+
Pair<VarType, VarType> pair = Pair.of(bound, newTo);
1663+
if (!recursivelySeen.contains(pair)) {
1664+
recursivelySeen.add(pair);
1665+
if (isMappingInBounds(boundArg, newArg, named, bounds, recursivelySeen)) {
1666+
toAdd.put(boundArg, newArg);
1667+
continue;
1668+
}
1669+
}
16501670
}
16511671
return false;
16521672
}

src/org/jetbrains/java/decompiler/modules/decompiler/exps/SwitchExprent.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
public class SwitchExprent extends Exprent {
1717
private final SwitchStatement backing;
18-
private final VarType type;
18+
private VarType type;
1919
// TODO: is needed?
2020
private final boolean fallthrough;
2121
// Whether the switch expression returns a value, for case type coercion
@@ -222,6 +222,10 @@ private static boolean isSyntheticThrowEdge(StatEdge edge){
222222
return false;
223223
}
224224

225+
public void setType(VarType type) {
226+
this.type = type;
227+
}
228+
225229
@Override
226230
public int getPrecedence() {
227231
return 1; // Should enclose in case of invocation

src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1452,18 +1452,22 @@ public void remapClashingNames(Statement root, StructMethod mt) {
14521452
start += (md.params[i].stackSize - 1);
14531453
}
14541454

1455-
iterateClashingNames(root, mt, varDefinitions, liveVarDefs, nameMap);
1455+
Set<String> seenMethods = new HashSet<>();
1456+
seenMethods.add(InterpreterUtil.makeUniqueKey(mt.getName(), mt.getDescriptor()));
1457+
1458+
iterateClashingNames(root, mt, varDefinitions, liveVarDefs, nameMap, seenMethods);
14561459
}
14571460

1458-
private void iterateClashingNames(Statement stat, StructMethod mt, Map<Statement, Set<VarInMethod>> varDefinitions, Set<VarInMethod> liveVarDefs, Map<VarInMethod, String> nameMap) {
1461+
private void iterateClashingNames(Statement stat, StructMethod mt, Map<Statement, Set<VarInMethod>> varDefinitions,
1462+
Set<VarInMethod> liveVarDefs, Map<VarInMethod, String> nameMap, Set<String> seenMethods) {
14591463
Set<VarInMethod> curVarDefs = new HashSet<>();
14601464

14611465
boolean shouldRemoveAtEnd = false;
14621466

14631467
// Process var definitions as owned by the parent- they come before the statement, and so their scope extends past the actual statement.
14641468
for (Exprent exprent : stat.getVarDefinitions()) {
14651469
Set<VarInMethod> upDefs = new HashSet<>();
1466-
iterateClashingExprent(stat, mt, varDefinitions, exprent, liveVarDefs, upDefs, nameMap);
1470+
iterateClashingExprent(stat, mt, varDefinitions, exprent, liveVarDefs, upDefs, nameMap, seenMethods);
14671471
liveVarDefs.addAll(upDefs);
14681472
varDefinitions.put(stat.getParent(), upDefs);
14691473
}
@@ -1474,7 +1478,7 @@ private void iterateClashingNames(Statement stat, StructMethod mt, Map<Statement
14741478
BasicBlockStatement basic = stat.getBasichead();
14751479
for (Exprent exprent : basic.getExprents()) {
14761480
for (Exprent ex : exprent.getAllExprents(true, true)) {
1477-
iterateClashingExprent(basic, mt, varDefinitions, ex, liveVarDefs, upDefs, nameMap);
1481+
iterateClashingExprent(basic, mt, varDefinitions, ex, liveVarDefs, upDefs, nameMap, seenMethods);
14781482
}
14791483
}
14801484

@@ -1489,7 +1493,7 @@ private void iterateClashingNames(Statement stat, StructMethod mt, Map<Statement
14891493
// Sort order from getAllExprents here is crucial!
14901494
// Say, for example, "MyType t = method(t -> ....);"
14911495
// It is imperative that the lhs of the assign comes first, so that any defs in the rhs can be properly seen.
1492-
iterateClashingExprent(stat, mt, varDefinitions, ex, liveVarDefs, curVarDefs, nameMap);
1496+
iterateClashingExprent(stat, mt, varDefinitions, ex, liveVarDefs, curVarDefs, nameMap, seenMethods);
14931497
}
14941498
}
14951499
} else {
@@ -1499,7 +1503,7 @@ private void iterateClashingNames(Statement stat, StructMethod mt, Map<Statement
14991503
List<Exprent> exprents = ((Exprent) obj).getAllExprents(true, true);
15001504

15011505
for (Exprent exprent : exprents) {
1502-
iterateClashingExprent(stat, mt, varDefinitions, exprent, liveVarDefs, curVarDefs, nameMap);
1506+
iterateClashingExprent(stat, mt, varDefinitions, exprent, liveVarDefs, curVarDefs, nameMap, seenMethods);
15031507
}
15041508
}
15051509
}
@@ -1538,7 +1542,7 @@ private void iterateClashingNames(Statement stat, StructMethod mt, Map<Statement
15381542
}
15391543
}
15401544

1541-
iterateClashingNames(st, mt, varDefinitions, liveVarDefs, nameMap);
1545+
iterateClashingNames(st, mt, varDefinitions, liveVarDefs, nameMap, seenMethods);
15421546
}
15431547
}
15441548

@@ -1555,7 +1559,7 @@ private void iterateClashingNames(Statement stat, StructMethod mt, Map<Statement
15551559
// Process deferred statements
15561560
if (iterate) {
15571561
for (Statement st : deferred) {
1558-
iterateClashingNames(st, mt, varDefinitions, liveVarDefs, nameMap);
1562+
iterateClashingNames(st, mt, varDefinitions, liveVarDefs, nameMap, seenMethods);
15591563
}
15601564
}
15611565

@@ -1575,7 +1579,8 @@ private void clearStatement(Map<Statement, Set<VarInMethod>> varDefinitions, Set
15751579
}
15761580
}
15771581

1578-
private void iterateClashingExprent(Statement stat, StructMethod mt, Map<Statement, Set<VarInMethod>> varDefinitions, Exprent exprent, Set<VarInMethod> liveVarDefs, Set<VarInMethod> curVarDefs, Map<VarInMethod, String> nameMap) {
1582+
private void iterateClashingExprent(Statement stat, StructMethod mt, Map<Statement, Set<VarInMethod>> varDefinitions, Exprent exprent,
1583+
Set<VarInMethod> liveVarDefs, Set<VarInMethod> curVarDefs, Map<VarInMethod, String> nameMap, Set<String> seenMethods) {
15791584
if (exprent instanceof NewExprent) {
15801585
NewExprent newExprent = (NewExprent) exprent;
15811586
// Check if this is a lambda with a body
@@ -1584,7 +1589,8 @@ private void iterateClashingExprent(Statement stat, StructMethod mt, Map<Stateme
15841589
if (node != null && node.getWrapper() != null) {
15851590
MethodWrapper mw = node.getWrapper().getMethods().getWithKey(node.lambdaInformation.content_method_key);
15861591
StructMethod mt2 = node.getWrapper().getClassStruct().getMethod(node.lambdaInformation.content_method_key);
1587-
if (mt2 != null && mw != null) {
1592+
if (mt2 != null && mw != null && !seenMethods.contains(InterpreterUtil.makeUniqueKey(mt2.getName(), mt2.getDescriptor()))) {
1593+
seenMethods.add(InterpreterUtil.makeUniqueKey(mt2.getName(), mt2.getDescriptor()));
15881594
// Propagate current data through to lambda
15891595
VarDefinitionHelper vardef = new VarDefinitionHelper(mw.root, mt2, mw.varproc, false);
15901596

@@ -1616,7 +1622,7 @@ private void iterateClashingExprent(Statement stat, StructMethod mt, Map<Stateme
16161622
}
16171623

16181624
// Iterate clashing names with the lambda's body, with the context of the outer method
1619-
vardef.iterateClashingNames(mw.root, mt2, varDefinitions, liveVarDefs, nameMap);
1625+
vardef.iterateClashingNames(mw.root, mt2, varDefinitions, liveVarDefs, nameMap, seenMethods);
16201626

16211627
for (Entry<VarVersionPair, String> e : vardef.getClashingNames().entrySet()) {
16221628
mw.varproc.setClashingName(e.getKey(), e.getValue());
@@ -1636,7 +1642,8 @@ private void iterateClashingExprent(Statement stat, StructMethod mt, Map<Stateme
16361642
for (String mthKey : node.getWrapper().getMethods().getLstKeys()) {
16371643
MethodWrapper mw = node.getWrapper().getMethods().getWithKey(mthKey);
16381644
StructMethod mt2 = node.getWrapper().getClassStruct().getMethod(mthKey);
1639-
if (mt2 != null && mw != null && !mt2.hasModifier(CodeConstants.ACC_SYNTHETIC)) {
1645+
if (mt2 != null && mw != null && !mt2.hasModifier(CodeConstants.ACC_SYNTHETIC) && !seenMethods.contains(InterpreterUtil.makeUniqueKey(mt2.getName(), mt2.getDescriptor()))) {
1646+
seenMethods.add(InterpreterUtil.makeUniqueKey(mt2.getName(), mt2.getDescriptor()));
16401647
// Propagate current data through to method
16411648
VarDefinitionHelper vardef = new VarDefinitionHelper(mw.root, mt2, mw.varproc, false);
16421649

@@ -1669,7 +1676,7 @@ private void iterateClashingExprent(Statement stat, StructMethod mt, Map<Stateme
16691676
}
16701677

16711678
// Iterate clashing names with the lambda's body, with the context of the outer method
1672-
vardef.iterateClashingNames(mw.root, mt2, varDefinitions, liveVarDefs, nameMap);
1679+
vardef.iterateClashingNames(mw.root, mt2, varDefinitions, liveVarDefs, nameMap, seenMethods);
16731680

16741681
for (Entry<VarVersionPair, String> e : vardef.getClashingNames().entrySet()) {
16751682
mw.varproc.setClashingName(e.getKey(), e.getValue());

src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarTypeProcessor.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,8 @@ else if (newType.typeFamily == TypeFamily.INTEGER) {
182182

183183
case FUNCTION:
184184
return changeFunctionExprentType(newType, minMax, (FunctionExprent)exprent);
185+
case SWITCH:
186+
return changeSwitchExprentType(newType, minMax, (SwitchExprent) exprent);
185187
}
186188

187189
return true;
@@ -237,6 +239,16 @@ private boolean changeFunctionExprentType(VarType newType, int minMax, FunctionE
237239
return true;
238240
}
239241

242+
private boolean changeSwitchExprentType(VarType newType, int minMax, SwitchExprent switchExpr) {
243+
if (minMax == 1) { // max
244+
VarType type = switchExpr.getExprType();
245+
if (newType.typeFamily == TypeFamily.INTEGER && type.typeFamily == newType.typeFamily) {
246+
switchExpr.setType(newType);
247+
}
248+
}
249+
return true;
250+
}
251+
240252
public Map<VarVersionPair, VarType> getMapExprentMaxTypes() {
241253
return mapExprentMaxTypes;
242254
}

src/org/jetbrains/java/decompiler/struct/StructClass.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ public ConstantPool getPool() {
213213
if (recordAttr == null) {
214214
// If our class extends j.l.Record but also has no components, it's probably malformed.
215215
// Force processing as a record anyway, in the hopes that we can come to a better result.
216-
if (this.superClass.getString().equals("java/lang/Record")) {
216+
if (this.superClass != null && this.superClass.getString().equals("java/lang/Record")) {
217217
return new ArrayList<>();
218218
}
219219

test/org/jetbrains/java/decompiler/SingleClassesTest.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -715,6 +715,9 @@ private void registerDefault() {
715715
register(JAVA_8, "TestCatchVariable");
716716
register(JAVA_8, "TestExtraneousImports");
717717
register(JAVA_17, "TestSwitchOnEnumFake");
718+
register(JAVA_16, "TestSwitchExpressionReturnType");
719+
register(JAVA_8, "TestGenericMapping");
720+
718721
}
719722

720723
private void registerEntireClassPath() {
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package pkg;
2+
3+
public class TestGenericMapping {
4+
class EntityA<Q extends TestGenericMapping.EntityA<Q, R>, R extends TestGenericMapping.EntityB<Q, R>> {
5+
public void doSomething(Q q) {
6+
}// 6
7+
}
8+
9+
public class EntityB<S extends TestGenericMapping.EntityA<S, T>, T extends TestGenericMapping.EntityB<S, T>> {
10+
public void doSomething(S t) {
11+
t.doSomething(t);// 11
12+
}// 12
13+
}
14+
}
15+
16+
class 'pkg/TestGenericMapping$EntityA' {
17+
method 'doSomething (Lpkg/TestGenericMapping$EntityA;)V' {
18+
0 5
19+
}
20+
}
21+
22+
class 'pkg/TestGenericMapping$EntityB' {
23+
method 'doSomething (Lpkg/TestGenericMapping$EntityA;)V' {
24+
0 10
25+
1 10
26+
2 10
27+
3 10
28+
4 10
29+
5 11
30+
}
31+
}
32+
33+
Lines mapping:
34+
6 <-> 6
35+
11 <-> 11
36+
12 <-> 12

0 commit comments

Comments
 (0)