Skip to content

Commit

Permalink
fix for try resources special cases (#227)
Browse files Browse the repository at this point in the history
* fix for try resources special cases

* remove unused code

---------

Co-authored-by: nbauma109 <nbauma109@github.com>
  • Loading branch information
nbauma109 and nbauma109 authored Apr 10, 2023
1 parent 4312b71 commit 802e10a
Show file tree
Hide file tree
Showing 6 changed files with 322 additions and 112 deletions.
198 changes: 88 additions & 110 deletions src/main/java/jd/core/model/instruction/fast/instruction/FastTry.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,7 @@
import jd.core.model.classfile.ConstantPool;
import jd.core.model.classfile.LocalVariable;
import jd.core.model.classfile.LocalVariables;
import jd.core.model.instruction.bytecode.instruction.AConstNull;
import jd.core.model.instruction.bytecode.instruction.ALoad;
import jd.core.model.instruction.bytecode.instruction.AStore;
import jd.core.model.instruction.bytecode.instruction.IfInstruction;
import jd.core.model.instruction.bytecode.instruction.Instruction;
import jd.core.model.instruction.bytecode.instruction.InvokeNoStaticInstruction;
Expand Down Expand Up @@ -136,90 +134,96 @@ private void cleanUpCatches(int firstLineNumber) {
}
}

public boolean removeTryResourcesPattern(LocalVariables localVariables, ConstantPool cp) {
if (finallyInstructions == null || finallyInstructions.size() != 4) {
public boolean removeTryResourcesPattern(LocalVariables localVariables, ConstantPool cp, List<Instruction> instructions) {
if (instructions == null || instructions.size() < 4) {
return false;
}
Instruction firstInstruction = finallyInstructions.get(0);
int firstLineNumber = firstInstruction.getLineNumber();
if (finallyInstructions.stream().anyMatch(instr -> instr.getLineNumber() != firstLineNumber)) {
return false;
}
if (!(firstInstruction instanceof IfInstruction)) {
return false;
}
if (!(finallyInstructions.get(1) instanceof IfInstruction)) {
return false;
}
if (!(finallyInstructions.get(2) instanceof FastTry)) {
return false;
}
if (!(finallyInstructions.get(3) instanceof InvokeNoStaticInstruction)) {
return false;
}
IfInstruction firstIf = (IfInstruction) firstInstruction;
IfInstruction secondIf = (IfInstruction) finallyInstructions.get(1);
FastTry fastTry = (FastTry) finallyInstructions.get(2);
InvokeNoStaticInstruction invokeNoStaticInstruction = (InvokeNoStaticInstruction) finallyInstructions.get(3);
if (!(firstIf.getValue() instanceof ALoad)) {
return false;
}
if (!(invokeNoStaticInstruction.getObjectref() instanceof ALoad)) {
return false;
}
if (((ALoad) firstIf.getValue()).getIndex() != ((ALoad) invokeNoStaticInstruction.getObjectref()).getIndex()) {
return false;
}
if (fastTry.getInstructions() == null || fastTry.getInstructions().size() != 1) {
return false;
}
if (fastTry.getCatches() == null || fastTry.getCatches().size() != 1) {
return false;
}
if (fastTry.getCatches().get(0).instructions() == null || fastTry.getCatches().get(0).instructions().size() != 1) {
return false;
}
if (!(fastTry.getCatches().get(0).instructions().get(0) instanceof Invokevirtual)) {
return false;
}
Invokevirtual catchInstruction = (Invokevirtual) fastTry.getCatches().get(0).instructions().get(0);
if (!(catchInstruction.getObjectref() instanceof ALoad)) {
return false;
}
ALoad aLoad2 = (ALoad) catchInstruction.getObjectref();
if (((ALoad) secondIf.getValue()).getIndex() != aLoad2.getIndex()) {
return false;
}
if (!(fastTry.getInstructions().get(0) instanceof InvokeNoStaticInstruction)) {
return false;
}
InvokeNoStaticInstruction tryInstruction = (InvokeNoStaticInstruction) fastTry.getInstructions().get(0);
if (!(tryInstruction.getObjectref() instanceof ALoad)) {
return false;
}
ALoad aLoad1 = (ALoad) tryInstruction.getObjectref();
if (((ALoad) firstIf.getValue()).getIndex() != aLoad1.getIndex()) {
return false;
}
LocalVariable lv1 = localVariables.getLocalVariableWithIndexAndOffset(aLoad1.getIndex(), aLoad1.getOffset());
LocalVariable lv2 = localVariables.getLocalVariableWithIndexAndOffset(aLoad2.getIndex(), aLoad2.getOffset());
lv1.setTryResources(this);
ConstantCP catchInstructionMethodref = cp.getConstantMethodref(catchInstruction.getIndex());
ConstantClass catchInstructionClass = cp.getConstantClass(catchInstructionMethodref.getClassIndex());
ConstantNameAndType catchInstructionMethod = cp.getConstantNameAndType(catchInstructionMethodref.getNameAndTypeIndex());
String catchInstructionMethodName = cp.getConstantUtf8(catchInstructionMethod.getNameIndex());
String catchInstructionMethodDesc = cp.getConstantUtf8(catchInstructionMethod.getSignatureIndex());
String catchInstructionClassName = cp.getConstantUtf8(catchInstructionClass.getNameIndex());
if ("java/lang/Throwable".equals(catchInstructionClassName)
&& "addSuppressed".equals(catchInstructionMethodName)
&& "(Ljava/lang/Throwable;)V".equals(catchInstructionMethodDesc)) {
lv2.setThrowableFromTryResources(true);
if (lv2.isExceptionOrThrowable(cp)) {
lv2.setToBeRemoved(true);
for (int i = instructions.size() - 1; i >= 3; i--) {
Instruction firstInstruction = instructions.get(i-3);
int firstLineNumber = firstInstruction.getLineNumber();
if (instructions.subList(i-3, i).stream().anyMatch(instr -> instr.getLineNumber() != firstLineNumber)) {
continue;
}
if (!(firstInstruction instanceof IfInstruction)) {
continue;
}
if (!(instructions.get(i-2) instanceof IfInstruction)) {
continue;
}
if (!(instructions.get(i-1) instanceof FastTry)) {
continue;
}
if (!(instructions.get(i) instanceof InvokeNoStaticInstruction)) {
continue;
}
IfInstruction firstIf = (IfInstruction) firstInstruction;
IfInstruction secondIf = (IfInstruction) instructions.get(i-2);
FastTry fastTry = (FastTry) instructions.get(i-1);
InvokeNoStaticInstruction invokeNoStaticInstruction = (InvokeNoStaticInstruction) instructions.get(i);
if (!(firstIf.getValue() instanceof ALoad)) {
continue;
}
if (!(invokeNoStaticInstruction.getObjectref() instanceof ALoad)) {
continue;
}
if (((ALoad) firstIf.getValue()).getIndex() != ((ALoad) invokeNoStaticInstruction.getObjectref()).getIndex()) {
continue;
}
if (fastTry.getInstructions() == null || fastTry.getInstructions().size() != 1) {
continue;
}
}
finallyInstructions.clear();
return true;
if (fastTry.getCatches() == null || fastTry.getCatches().size() != 1) {
continue;
}
if (fastTry.getCatches().get(0).instructions() == null || fastTry.getCatches().get(0).instructions().size() != 1) {
continue;
}
if (!(fastTry.getCatches().get(0).instructions().get(0) instanceof Invokevirtual)) {
continue;
}
Invokevirtual catchInstruction = (Invokevirtual) fastTry.getCatches().get(0).instructions().get(0);
if (!(catchInstruction.getObjectref() instanceof ALoad)) {
continue;
}
ALoad aLoad2 = (ALoad) catchInstruction.getObjectref();
if (((ALoad) secondIf.getValue()).getIndex() != aLoad2.getIndex()) {
continue;
}
if (!(fastTry.getInstructions().get(0) instanceof InvokeNoStaticInstruction)) {
continue;
}
InvokeNoStaticInstruction tryInstruction = (InvokeNoStaticInstruction) fastTry.getInstructions().get(0);
if (!(tryInstruction.getObjectref() instanceof ALoad)) {
continue;
}
ALoad aLoad1 = (ALoad) tryInstruction.getObjectref();
if (((ALoad) firstIf.getValue()).getIndex() != aLoad1.getIndex()) {
continue;
}
LocalVariable lv1 = localVariables.getLocalVariableWithIndexAndOffset(aLoad1.getIndex(), aLoad1.getOffset());
LocalVariable lv2 = localVariables.getLocalVariableWithIndexAndOffset(aLoad2.getIndex(), aLoad2.getOffset());
lv1.setTryResources(this);
ConstantCP catchInstructionMethodref = cp.getConstantMethodref(catchInstruction.getIndex());
ConstantClass catchInstructionClass = cp.getConstantClass(catchInstructionMethodref.getClassIndex());
ConstantNameAndType catchInstructionMethod = cp.getConstantNameAndType(catchInstructionMethodref.getNameAndTypeIndex());
String catchInstructionMethodName = cp.getConstantUtf8(catchInstructionMethod.getNameIndex());
String catchInstructionMethodDesc = cp.getConstantUtf8(catchInstructionMethod.getSignatureIndex());
String catchInstructionClassName = cp.getConstantUtf8(catchInstructionClass.getNameIndex());
if ("java/lang/Throwable".equals(catchInstructionClassName)
&& "addSuppressed".equals(catchInstructionMethodName)
&& "(Ljava/lang/Throwable;)V".equals(catchInstructionMethodDesc)) {
lv2.setThrowableFromTryResources(true);
if (lv2.isExceptionOrThrowable(cp)) {
lv2.setToBeRemoved(true);
}
}
instructions.remove(i);
instructions.remove(i-1);
instructions.remove(i-2);
instructions.remove(i-3);
return true;
}
return false;
}

public void addResource(StoreInstruction si, LocalVariable lv) {
Expand All @@ -231,35 +235,9 @@ public List<Instruction> getResources() {
return resources;
}

public boolean processTryResources(LocalVariables localVariables, ConstantPool cp) {
public boolean processTryResources() {
boolean processed = false;
List<Instruction> instructions = getInstructions();
if (instructions.size() > 2) {
for (int i = 0; i < instructions.size() - 2; i++) {
Instruction instr1 = instructions.get(i);
Instruction instr2 = instructions.get(i + 1);
Instruction instr3 = instructions.get(i + 2);
if (instr1 instanceof AStore && instr2 instanceof AStore && instr3 instanceof FastTry) {
AStore aStore1 = (AStore) instr1;
AStore aStore2 = (AStore) instr2;
FastTry nestedTry = (FastTry)instr3;
LocalVariable lv1 = localVariables.getLocalVariableWithIndexAndOffset(aStore1.getIndex(), aStore1.getOffset());
LocalVariable lv2 = localVariables.getLocalVariableWithIndexAndOffset(aStore2.getIndex(), aStore2.getOffset());
if (lv1 != null && lv2 != null) {
FastTry tryResources = lv1.getTryResources();
if (aStore2.getValueref() instanceof AConstNull && lv2.isExceptionOrThrowable(cp) && tryResources != null) {
lv2.setToBeRemoved(true);
instructions.remove(i);
instructions.remove(i);
nestedTry.processTryResources(localVariables, cp);
tryResources.addResource(aStore1, lv1);
processed = true;
i--;
}
}
}
}
}
if (instructions.size() == 1) {
Iterator<Instruction> iterator = instructions.iterator();
Instruction instruction = iterator.next();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1107,8 +1107,9 @@ private static void createFastTry(ReferenceMap referenceMap, ClassFile classFile
executeReconstructors(referenceMap, classFile, finallyInstructions, localVariables);
}
ConstantPool cp = classFile.getConstantPool();
boolean removedTryResourcesPattern = fastTry.removeTryResourcesPattern(localVariables, cp);
boolean processTryResources = fastTry.processTryResources(localVariables, cp);
boolean removedTryResourcesPattern = fastTry.removeTryResourcesPattern(localVariables, cp, finallyInstructions);
removedTryResourcesPattern |= fastTry.removeTryResourcesPattern(localVariables, cp, tryInstructions);
boolean processTryResources = fastTry.processTryResources();

if (index >= 1 && removedTryResourcesPattern && !processTryResources && !fastTry.hasCatch() && !fastTry.hasFinally()) {
Instruction beforeTry1 = list.get(index - 1);
Expand Down Expand Up @@ -1156,10 +1157,18 @@ private static void createFastTry(ReferenceMap referenceMap, ClassFile classFile
Instruction instruction = list.get(index);
if (instruction instanceof AStore) {
AStore astore = (AStore) instruction;
boolean removeNull = false;
if (astore.getValueref() instanceof AConstNull && index > 0 && list.get(index-1) instanceof AStore) {
astore = (AStore) list.get(index-1);
removeNull = true;
}
LocalVariable lv = localVariables.getLocalVariableWithIndexAndOffset(astore.getIndex(), astore.getOffset());
if (lv != null && lv.getTryResources() == fastTry) {
fastTry.addResource(astore, lv);
list.remove(index);
if (removeNull) {
list.remove(index-1);
}
}
}
}
Expand Down
101 changes: 101 additions & 0 deletions src/test/java/jd/core/test/TryResourcesBCEL.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package jd.core.test;

import org.apache.bcel.classfile.Attribute;
import org.apache.bcel.classfile.RuntimeInvisibleAnnotations;
import org.apache.bcel.classfile.RuntimeVisibleAnnotations;
import org.apache.bcel.generic.AnnotationEntryGen;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.util.ClassPath;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

public class TryResourcesBCEL {

public byte[] getBytes(ClassPath cp, String name, String suffix) throws IOException {
DataInputStream dis = null;
try (InputStream inputStream = cp.getInputStream(name, suffix)) {
if (inputStream == null) {
throw new IOException("Couldn't find: " + name + suffix);
}
dis = new DataInputStream(inputStream);
byte[] bytes = new byte[inputStream.available()];
dis.readFully(bytes);
return bytes;
} finally {
if (dis != null) {
dis.close();
}
}
}

static Attribute[] getAnnotationAttributes(ConstantPoolGen cp, AnnotationEntryGen[] annotationEntryGens) {
if (annotationEntryGens.length == 0) {
return Attribute.EMPTY_ARRAY;
}

try {
int countVisible = 0;
int countInvisible = 0;

for (AnnotationEntryGen a : annotationEntryGens) {
if (a.isRuntimeVisible()) {
countVisible++;
} else {
countInvisible++;
}
}

ByteArrayOutputStream rvaBytes = new ByteArrayOutputStream();
ByteArrayOutputStream riaBytes = new ByteArrayOutputStream();
try (DataOutputStream rvaDos = new DataOutputStream(rvaBytes); DataOutputStream riaDos = new DataOutputStream(riaBytes)) {

rvaDos.writeShort(countVisible);
riaDos.writeShort(countInvisible);

for (AnnotationEntryGen a : annotationEntryGens) {
if (a.isRuntimeVisible()) {
a.dump(rvaDos);
} else {
a.dump(riaDos);
}
}
}

byte[] rvaData = rvaBytes.toByteArray();
byte[] riaData = riaBytes.toByteArray();

int rvaIndex = -1;
int riaIndex = -1;

if (rvaData.length > 2) {
rvaIndex = cp.addUtf8("RuntimeVisibleAnnotations");
}
if (riaData.length > 2) {
riaIndex = cp.addUtf8("RuntimeInvisibleAnnotations");
}

List<Attribute> newAttributes = new ArrayList<>();
if (rvaData.length > 2) {
newAttributes
.add(new RuntimeVisibleAnnotations(rvaIndex, rvaData.length, new DataInputStream(new ByteArrayInputStream(rvaData)), cp.getConstantPool()));
}
if (riaData.length > 2) {
newAttributes.add(
new RuntimeInvisibleAnnotations(riaIndex, riaData.length, new DataInputStream(new ByteArrayInputStream(riaData)), cp.getConstantPool()));
}

return newAttributes.toArray(Attribute.EMPTY_ARRAY);
} catch (IOException e) {
System.err.println("IOException whilst processing annotations");
e.printStackTrace();
}
return null;
}
}
21 changes: 21 additions & 0 deletions src/test/java/jd/core/test/TryResourcesBCELTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package jd.core.test;

import org.apache.commons.io.IOUtils;
import org.jd.core.v1.util.ZipLoader;
import org.junit.Test;

import java.io.InputStream;
import java.nio.charset.StandardCharsets;

import static org.junit.Assert.assertEquals;

public class TryResourcesBCELTest extends AbstractTestCase {
@Test
public void test() throws Exception {
try (InputStream in = getClass().getResourceAsStream("/try-resources-bcel-jdk1.8.0_331.jar")) {
ZipLoader loader = new ZipLoader(in);
String output = decompile("jd/core/test/TryResourcesBCEL", loader);
assertEquals(IOUtils.toString(getClass().getResource("TryResourcesBCEL.txt"), StandardCharsets.UTF_8), output);
}
}
}
Loading

0 comments on commit 802e10a

Please sign in to comment.