Skip to content

Commit

Permalink
Improve pattern matching with reference types
Browse files Browse the repository at this point in the history
  • Loading branch information
jaskarth committed Aug 16, 2024
1 parent 6284227 commit 084581c
Show file tree
Hide file tree
Showing 13 changed files with 1,110 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,12 @@ public static boolean matchInstanceof(RootStatement root) {
if (res) {
ValidationHelper.validateStatement(root);

SequenceHelper.condenseSequences(root);

// IfHelper already called SequenceHelper.condenseSequences if it returned true
if (!IfHelper.mergeAllIfs(root)) {
if (IfHelper.mergeAllIfs(root)) {
improvePatternTypes(root);
} else {
SequenceHelper.condenseSequences(root);
}
}
Expand Down Expand Up @@ -349,6 +353,23 @@ private static boolean identifyRecordPatternMatch(IfStatement stat, Statement br
}
}
}
} else {
// Is the next statement an if with an instanceof inside? It might be a type-improving if. Search inside it too.
if (next instanceof IfStatement ifSt && ifSt.iftype == IfStatement.IFTYPE_IF
&& ifSt.getHeadexprent().getCondition() instanceof FunctionExprent func && func.getFuncType() == FunctionType.INSTANCEOF) {

// "<stackVar> = <originalVar>;" idiom
// Ensure this is the right idiom be fore we mark it for destruction.
if (branch.getBasichead().getExprents().size() == 1) {
if (branch.getBasichead().getExprents().get(0) instanceof AssignmentExprent assign
&& assign.getLeft() instanceof VarExprent && assign.getRight() instanceof VarExprent) {
toDestroy.add(branch.getBasichead());
}
}

branch = ifSt.getIfstat();
stIdx = 0;
}
}

// If we found a "realVar = exVar;" then we can skip over this statement and move on.
Expand All @@ -364,8 +385,6 @@ private static boolean identifyRecordPatternMatch(IfStatement stat, Statement br
}
}

toDestroy.add(branch.getBasichead());

PatternExprent pattern = new PatternExprent(PatternExprent.recordData(cl), type, new ArrayList<>(vars.values()));

instOf.getLstOperands().add(2, pattern);
Expand Down Expand Up @@ -482,4 +501,104 @@ public static Exprent getLastExprentWhen(Exprent base, boolean ifTrue, boolean o
// otherwise, return ourselves
return base;
}

private static boolean improvePatternTypes(Statement stat) {
boolean res = false;
for (Statement st : stat.getStats()) {
res |= improvePatternTypes(st);
}

if (stat instanceof IfStatement ifSt) {
Exprent cond = ifSt.getHeadexprent().getCondition();

if (improvePatternType(ifSt.getHeadexprent(), cond, ifSt.getIfstat())) {
res = true;
}
}

return res;
}

private static boolean improvePatternType(Exprent parent, Exprent ex, Statement st) {
boolean res = false;
for (Exprent e : ex.getAllExprents(false, true)) {
// don't recurse on self
if (e != ex) {
res |= improvePatternType(ex, e, st);
}

if (e instanceof FunctionExprent fn && fn.getFuncType() == FunctionType.BOOLEAN_AND) {
Exprent base = fn.getLstOperands().get(0);

// Check for record pattern instanceof
if (base instanceof FunctionExprent baseFn && baseFn.getFuncType() == FunctionType.INSTANCEOF
&& baseFn.getLstOperands().size() > 2 && baseFn.getLstOperands().get(2) instanceof PatternExprent pattern
&& pattern.getData() instanceof PatternExprent.PatternData.RecordPatternData) {
// Found one? now find type-enhancing instanceofs in the other arm

// Map a list of vars 1:1 with the exprents in the pattern
List<VarExprent> vars = new ArrayList<>();

for (Exprent patternEx : pattern.getExprents()) {
if (patternEx instanceof VarExprent var) {
vars.add(var);
} else {
vars.add(null);
}

// TODO: recursively look?
}

for (int j = 0; j < vars.size(); j++) {
VarExprent var = vars.get(j);
if (var == null) {
continue;
}

// Now go through the following ordeal to improve pattern types.
// Look for cases that look like 'Rec(Object synth) && synth instanceof Type t' where 'synth' is a synthetic
// variable that is only used in the pattern.

// Is the 'synth' variable used outside the pattern? All hope is lost.
if (var.isVarReferenced(st)) {
continue;
}

// Go through all of the exprents one by one to see if we can find redundant instanceofs
// We need to start at the parent, as in the case where there is only one instanceof, such as
// 'o instanceof Rec(Object x) && x instanceof String s', we need to replace the whole expression with the
// left hand side.
out:
for (Exprent exp : parent.getAllExprents(true, true)) {
for (Exprent inst : exp.getAllExprents()) {
if (inst instanceof FunctionExprent instFun && instFun.getFuncType() == FunctionType.BOOLEAN_AND) {
// Search each arm of the boolean and
for (int i = 0; i < 2; i++) {
Exprent inner = instFun.getLstOperands().get(i);
if (inner instanceof FunctionExprent innerFun && innerFun.getFuncType() == FunctionType.INSTANCEOF && innerFun.getLstOperands().size() > 2) {
if (innerFun.getLstOperands().get(0).equals(var)) {

// Replace the var
pattern.getExprents().set(j, innerFun.getLstOperands().get(2));
pattern.getVarTypes().set(j, innerFun.getLstOperands().get(1).getExprType());

// replace 'A && B' where A is the redundant instanceof with simply 'B'
exp.replaceExprent(inst, instFun.getLstOperands().get(i ^ 1));

break out;
}
}
}
}
}
}

// Iterate again, to try to replace all components
}
}
}
}

return res;
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.jetbrains.java.decompiler.modules.decompiler.exps;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.java.decompiler.modules.decompiler.DecHelper;
import org.jetbrains.java.decompiler.modules.decompiler.ValidationHelper;
import org.jetbrains.java.decompiler.modules.decompiler.vars.CheckTypesResult;
Expand All @@ -17,17 +18,20 @@ public class PatternExprent extends Exprent implements Pattern {
private final PatternData data;
private final VarType varType;
private final List<Exprent> exprents;
private final List<@Nullable VarType> varTypes;

public PatternExprent(PatternData data, VarType type, List<Exprent> exprents) {
super(Type.PATTERN);
this.data = data;
varType = type;
this.exprents = exprents;
this.varTypes = new ArrayList<>();

for (Exprent exprent : exprents) {
if (!(exprent instanceof Pattern)) {
ValidationHelper.assertTrue(false, "Illegal input for PatternExprent");
}
varTypes.add(null);
}
}

Expand Down Expand Up @@ -76,7 +80,12 @@ public CheckTypesResult checkExprTypeBounds() {

// The type lower bound must be
for (int i = 0; i < exprents.size(); i++) {
res.addMinTypeExprent(exprents.get(i), new VarType(record.cl.getRecordComponents().get(i).getDescriptor()));
VarType type = varTypes.get(i);
if (type != null) {
res.addMinTypeExprent(exprents.get(i), type);
} else {
res.addMinTypeExprent(exprents.get(i), new VarType(record.cl.getRecordComponents().get(i).getDescriptor()));
}
}

return res;
Expand Down Expand Up @@ -107,6 +116,18 @@ public List<VarExprent> getPatternVars() {
return vars;
}

public PatternData getData() {
return data;
}

public List<Exprent> getExprents() {
return exprents;
}

public List<@Nullable VarType> getVarTypes() {
return varTypes;
}

public static PatternData recordData(StructClass cl) {
return new PatternData.RecordPatternData(cl);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -968,6 +968,10 @@ public void setPhantom(boolean phantom) {
this.phantom = phantom;
}

public String toDebug() {
return toJava().convertToStringAndAllowDataDiscard();
}

// helper methods
public String toString() {
return "{" + type.prettyId + "}:" + id;
Expand Down
4 changes: 4 additions & 0 deletions test/org/jetbrains/java/decompiler/SingleClassesTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -685,6 +685,10 @@ private void registerDefault() {
register(JAVA_8, "TestWhileForeach");
register(JAVA_21, "TestRecordPatterns1");
register(JAVA_21, "TestRecordPatterns2");
register(JAVA_21, "TestRecordPatterns3");
register(JAVA_21, "TestRecordPatterns4");
register(JAVA_21, "TestRecordPatterns5");
register(JAVA_21, "TestRecordPatterns6");
register(JAVA_21_PREVIEW, "TestStrProcessor");
register(JAVA_21_PREVIEW, "TestRawProcessor");
register(JAVA_21_PREVIEW, "TestFmtProcessor");
Expand Down
10 changes: 2 additions & 8 deletions testData/results/pkg/TestRecordPatterns1.dec
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ public class TestRecordPatterns1 {
}// 11

public void test2(TestRecordPatterns1.R r) {
if (r instanceof TestRecordPatterns1.R(int var5, Object var11) && var11 instanceof String s) {// 14
if (r instanceof TestRecordPatterns1.R(int var5, String s)) {// 14
System.out.println(var5);// 15
System.out.println(s);// 16
}
}// 18

public void test3(TestRecordPatterns1.R r) {
if (r instanceof TestRecordPatterns1.R(int var5, Object var11) && var11 instanceof String s && s.length() > 10) {// 21
if (r instanceof TestRecordPatterns1.R(int var5, String s) && s.length() > 10) {// 21
System.out.println(var5);// 22
System.out.println(s);// 23
}
Expand Down Expand Up @@ -80,9 +80,6 @@ class 'pkg/TestRecordPatterns1' {
e 11
f 12
10 12
1a 11
1b 11
1c 11
1d 11
1e 11
1f 11
Expand Down Expand Up @@ -118,9 +115,6 @@ class 'pkg/TestRecordPatterns1' {
e 18
f 19
10 19
1a 18
1b 18
1c 18
1d 18
1e 18
1f 18
Expand Down
Loading

0 comments on commit 084581c

Please sign in to comment.