diff --git a/src/main/java/jd/core/model/instruction/fast/instruction/FastTry.java b/src/main/java/jd/core/model/instruction/fast/instruction/FastTry.java index 5b6b7360..46044371 100644 --- a/src/main/java/jd/core/model/instruction/fast/instruction/FastTry.java +++ b/src/main/java/jd/core/model/instruction/fast/instruction/FastTry.java @@ -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; @@ -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 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) { @@ -231,35 +235,9 @@ public List getResources() { return resources; } - public boolean processTryResources(LocalVariables localVariables, ConstantPool cp) { + public boolean processTryResources() { boolean processed = false; List 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 iterator = instructions.iterator(); Instruction instruction = iterator.next(); diff --git a/src/main/java/jd/core/process/analyzer/instruction/fast/FastInstructionListBuilder.java b/src/main/java/jd/core/process/analyzer/instruction/fast/FastInstructionListBuilder.java index b767e8f9..3fcee295 100644 --- a/src/main/java/jd/core/process/analyzer/instruction/fast/FastInstructionListBuilder.java +++ b/src/main/java/jd/core/process/analyzer/instruction/fast/FastInstructionListBuilder.java @@ -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); @@ -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); + } } } } diff --git a/src/test/java/jd/core/test/TryResourcesBCEL.java b/src/test/java/jd/core/test/TryResourcesBCEL.java new file mode 100644 index 00000000..2a07a552 --- /dev/null +++ b/src/test/java/jd/core/test/TryResourcesBCEL.java @@ -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 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; + } +} diff --git a/src/test/java/jd/core/test/TryResourcesBCELTest.java b/src/test/java/jd/core/test/TryResourcesBCELTest.java new file mode 100644 index 00000000..ef2fe1a3 --- /dev/null +++ b/src/test/java/jd/core/test/TryResourcesBCELTest.java @@ -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); + } + } +} diff --git a/src/test/resources/jd/core/test/TryResourcesBCEL.txt b/src/test/resources/jd/core/test/TryResourcesBCEL.txt new file mode 100644 index 00000000..0c2bfce7 --- /dev/null +++ b/src/test/resources/jd/core/test/TryResourcesBCEL.txt @@ -0,0 +1,101 @@ +/* */ package jd.core.test; +/* */ +/* */ 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; +/* */ 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; +/* */ +/* */ public class TryResourcesBCEL +/* */ { +/* */ public byte[] getBytes(ClassPath cp, String name, String suffix) throws IOException +/* */ { +/* 22 */ DataInputStream dis = null; +/* 23 */ try (InputStream inputStream = cp.getInputStream(name, suffix)) { +/* 24 */ if (inputStream == null) { +/* 25 */ throw new IOException("Couldn't find: " + name + suffix); +/* */ } +/* 27 */ dis = new DataInputStream(inputStream); +/* 28 */ byte[] bytes = new byte[inputStream.available()]; +/* 29 */ dis.readFully(bytes); +/* 30 */ return bytes; +/* */ } finally { +/* 32 */ if (dis != null) { +/* 33 */ dis.close(); +/* */ } +/* */ } +/* */ } +/* */ +/* */ static Attribute[] getAnnotationAttributes(ConstantPoolGen cp, AnnotationEntryGen[] annotationEntryGens) { +/* 39 */ if (annotationEntryGens.length == 0) { +/* 40 */ return Attribute.EMPTY_ARRAY; +/* */ } +/* */ try +/* */ { +/* 44 */ int countVisible = 0; +/* 45 */ int countInvisible = 0; +/* */ +/* 47 */ for (AnnotationEntryGen a : annotationEntryGens) { +/* 48 */ if (a.isRuntimeVisible()) { +/* 49 */ countVisible++; +/* */ } else { +/* 51 */ countInvisible++; +/* */ } +/* */ } +/* */ +/* 55 */ ByteArrayOutputStream rvaBytes = new ByteArrayOutputStream(); +/* 56 */ ByteArrayOutputStream riaBytes = new ByteArrayOutputStream(); +/* 57 */ try (DataOutputStream rvaDos = new DataOutputStream(rvaBytes);DataOutputStream riaDos = new DataOutputStream(riaBytes);) +/* */ { +/* 59 */ rvaDos.writeShort(countVisible); +/* 60 */ riaDos.writeShort(countInvisible); +/* */ +/* 62 */ for (AnnotationEntryGen a : annotationEntryGens) { +/* 63 */ if (a.isRuntimeVisible()) { +/* 64 */ a.dump(rvaDos); +/* */ } else { +/* 66 */ a.dump(riaDos); +/* */ } +/* */ } +/* */ } +/* */ +/* 71 */ byte[] rvaData = rvaBytes.toByteArray(); +/* 72 */ byte[] riaData = riaBytes.toByteArray(); +/* */ +/* 74 */ int rvaIndex = -1; +/* 75 */ int riaIndex = -1; +/* */ +/* 77 */ if (rvaData.length > 2) { +/* 78 */ rvaIndex = cp.addUtf8("RuntimeVisibleAnnotations"); +/* */ } +/* 80 */ if (riaData.length > 2) { +/* 81 */ riaIndex = cp.addUtf8("RuntimeInvisibleAnnotations"); +/* */ } +/* */ +/* 84 */ List newAttributes = new ArrayList<>(); +/* 85 */ if (rvaData.length > 2) { +/* 86 */ newAttributes +/* 87 */ .add(new RuntimeVisibleAnnotations(rvaIndex, rvaData.length, new DataInputStream(new ByteArrayInputStream(rvaData)), cp.getConstantPool())); +/* */ } +/* 89 */ if (riaData.length > 2) { +/* 90 */ newAttributes.add(new RuntimeInvisibleAnnotations(riaIndex, riaData.length, new DataInputStream(new ByteArrayInputStream(riaData)), cp +/* 91 */ .getConstantPool())); +/* */ } +/* */ +/* 94 */ return (Attribute[])newAttributes.toArray(Attribute.EMPTY_ARRAY); +/* */ } catch (IOException e) { +/* 96 */ System.err.println("IOException whilst processing annotations"); +/* 97 */ e.printStackTrace(); +/* */ } +/* 99 */ return null; +/* */ } +/* */ } diff --git a/src/test/resources/try-resources-bcel-jdk1.8.0_331.jar b/src/test/resources/try-resources-bcel-jdk1.8.0_331.jar new file mode 100644 index 00000000..398c2e16 Binary files /dev/null and b/src/test/resources/try-resources-bcel-jdk1.8.0_331.jar differ