diff --git a/pom.xml b/pom.xml
index 75de908e..39bf5f93 100644
--- a/pom.xml
+++ b/pom.xml
@@ -31,8 +31,8 @@
2.7.3
0.3.4
5.2.1.Final
- v45
- b803ad9
+ v46
+ 5a2b2cc
2.9.0
31.0.1-jre
4.2
@@ -44,7 +44,7 @@
3.2
0.2.0
0.6.0
- 3.1.6
+ 3.2.0
2.1.1
1.7.36
2.5.2
diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/decompilers/bytecode/InstructionPrinter.java b/src/main/java/the/bytecode/club/bytecodeviewer/decompilers/bytecode/InstructionPrinter.java
index 737fdd4b..7fbc44b8 100644
--- a/src/main/java/the/bytecode/club/bytecodeviewer/decompilers/bytecode/InstructionPrinter.java
+++ b/src/main/java/the/bytecode/club/bytecodeviewer/decompilers/bytecode/InstructionPrinter.java
@@ -117,7 +117,7 @@ public class InstructionPrinter {
info.add("}");
return info;
}
-
+
public String printInstruction(AbstractInsnNode ain)
{
String line = "";
@@ -136,15 +136,15 @@ public class InstructionPrinter {
} else if (ain instanceof JumpInsnNode) {
line = printJumpInsnNode((JumpInsnNode) ain);
} else if (ain instanceof LineNumberNode) {
- line = printLineNumberNode();
+ line = printLineNumberNode((LineNumberNode) ain);
} else if (ain instanceof LabelNode) {
if (firstLabel
&& BytecodeViewer.viewer.appendBracketsToLabels
.isSelected())
info.add("}");
-
+
line = printLabelnode((LabelNode) ain);
-
+
if (BytecodeViewer.viewer.appendBracketsToLabels.isSelected()) {
if (!firstLabel)
firstLabel = true;
@@ -168,7 +168,7 @@ public class InstructionPrinter {
line += "UNADDED OPCODE: " + nameOpcode(ain.getOpcode()) + " "
+ ain;
}
-
+
return line;
}
@@ -245,8 +245,8 @@ public class InstructionPrinter {
+ resolveLabel(jin.label);
}
- protected String printLineNumberNode() {
- return "";
+ protected String printLineNumberNode(LineNumberNode lnn) {
+ return "// line " + lnn.line;
}
protected String printLabelnode(LabelNode label) {
diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/decompilers/impl/CFRDecompiler.java b/src/main/java/the/bytecode/club/bytecodeviewer/decompilers/impl/CFRDecompiler.java
index f447a554..86883766 100644
--- a/src/main/java/the/bytecode/club/bytecodeviewer/decompilers/impl/CFRDecompiler.java
+++ b/src/main/java/the/bytecode/club/bytecodeviewer/decompilers/impl/CFRDecompiler.java
@@ -1,29 +1,43 @@
package the.bytecode.club.bytecodeviewer.decompilers.impl;
+import java.io.BufferedOutputStream;
import java.io.File;
-import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
-import java.util.Deque;
-import java.util.LinkedList;
-import java.util.Random;
-import java.util.zip.ZipEntry;
+import java.nio.charset.StandardCharsets;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.zip.ZipException;
import java.util.zip.ZipOutputStream;
-import me.konloch.kontainer.io.DiskReader;
+import org.apache.commons.io.IOUtils;
+import org.benf.cfr.reader.api.CfrDriver;
+import org.benf.cfr.reader.api.ClassFileSource;
+import org.benf.cfr.reader.api.OutputSinkFactory;
+import org.benf.cfr.reader.api.SinkReturns;
+import org.benf.cfr.reader.bytecode.analysis.parse.utils.Pair;
+import org.benf.cfr.reader.state.ClassFileSourceImpl;
+import org.benf.cfr.reader.util.getopt.Options;
+import org.benf.cfr.reader.util.getopt.OptionsImpl;
import org.objectweb.asm.tree.ClassNode;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.api.ExceptionUI;
import the.bytecode.club.bytecodeviewer.decompilers.InternalDecompiler;
+import the.bytecode.club.bytecodeviewer.resources.ResourceContainer;
import the.bytecode.club.bytecodeviewer.translation.TranslatedStrings;
-import the.bytecode.club.bytecodeviewer.util.MiscUtils;
-import static the.bytecode.club.bytecodeviewer.Constants.fs;
import static the.bytecode.club.bytecodeviewer.Constants.nl;
-import static the.bytecode.club.bytecodeviewer.Constants.tempDirectory;
import static the.bytecode.club.bytecodeviewer.translation.TranslatedStrings.CFR;
import static the.bytecode.club.bytecodeviewer.translation.TranslatedStrings.ERROR;
@@ -48,313 +62,193 @@ import static the.bytecode.club.bytecodeviewer.translation.TranslatedStrings.ERR
/**
* CFR Java Wrapper
*
- * @author Konloch
+ * @author GraxCode
+ * Taken mostly out of Threadtear.
*/
+public class CFRDecompiler extends InternalDecompiler {
-public class CFRDecompiler extends InternalDecompiler
-{
+ private static final String CLASS_SUFFIX = ".class";
- private static final String[] WINDOWS_IS_GREAT = new String[]
- {
- "CON",
- "PRN",
- "AUX",
- "NUL",
- "COM1",
- "COM2",
- "COM3",
- "COM4",
- "COM5",
- "COM6",
- "COM7",
- "COM8",
- "COM9",
- "LPT1",
- "LPT2",
- "LPT3",
- "LPT4",
- "LPT5",
- "LPT6",
- "LPT7",
- "LPT8",
- "LPT9"
- };
+ @Override
+ public String decompileClassNode(ClassNode cn, byte[] content) {
+ return decompile(cn, cn.name, content);
+ }
- public static String windowsFun(String base) {
- for (String s : WINDOWS_IS_GREAT) {
- if (base.contains(s.toLowerCase())) {
- base = base.replace(s.toLowerCase(), "BCV");
- }
+ private String decompile(ClassNode cn, String name, byte[] content) {
+ try {
+ String classPath = name + (name.endsWith(CLASS_SUFFIX) ? "" : CLASS_SUFFIX);
+
+ StringBuilder builder = new StringBuilder();
+ Consumer dumpDecompiled = d -> builder.append(d.getJava());
+
+ Options options = generateOptions();
+ ClassFileSource source = new BCVDataSource(options, cn, classPath, content);
+ CfrDriver driver = new CfrDriver.Builder()
+ .withClassFileSource(source)
+ .withBuiltOptions(options)
+ .withOutputSink(new BCVOutputSinkFactory(dumpDecompiled))
+ .build();
+ driver.analyse(Collections.singletonList(name));
+
+ return builder.toString();
+ } catch (Throwable t) {
+ t.printStackTrace();
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ t.printStackTrace(pw);
+ return CFR + " " + ERROR + "! " + ExceptionUI.SEND_STACKTRACE_TO +
+ nl + nl + TranslatedStrings.SUGGESTED_FIX_DECOMPILER_ERROR +
+ nl + nl + sw;
}
-
- return base;
}
@Override
- public String decompileClassNode(ClassNode cn, byte[] b) {
- String fileStart = tempDirectory + fs.toLowerCase();
+ public void decompileToZip(String sourceJar, String outJar) {
+ try (JarFile jfile = new JarFile(new File(sourceJar));
+ FileOutputStream dest = new FileOutputStream(outJar);
+ BufferedOutputStream buffDest = new BufferedOutputStream(dest);
+ ZipOutputStream out = new ZipOutputStream(buffDest)) {
+ byte[] data = new byte[1024];
- String exception = "";
- //final File tempClass = new File(windowsFun(MiscUtils.getUniqueName(fileStart, ".class") + ".class"));
- final File tempClass = new File(MiscUtils.getUniqueName(fileStart, ".class") + ".class");
-
- try (FileOutputStream fos = new FileOutputStream(tempClass)) {
- fos.write(b);
- } catch (final IOException e) {
- BytecodeViewer.handleException(e);
- }
-
- String fuckery = fuckery(fileStart);
-
- /*if (!BytecodeViewer.fatJar) {
- try {
- ProcessBuilder pb = new ProcessBuilder(ArrayUtils.addAll(
- new String[]{BytecodeViewer.getJavaCommand(), "-jar", Resources.findLibrary("cfr")},
- generateMainMethod(tempClass.getAbsolutePath(), fuckery)
- ));
- BytecodeViewer.sm.stopBlocking();
- Process p = pb.start();
- BytecodeViewer.createdProcesses.add(p);
- p.waitFor();
- } catch (Exception e) {
- BytecodeViewer.handleException(e);
- } finally {
- BytecodeViewer.sm.setBlocking();
- }
- } else {
- org.benf.cfr.reader.Main.main(generateMainMethod(tempClass.getAbsolutePath(), fuckery));
- }*/
-
- try {
- org.benf.cfr.reader.Main.main(generateMainMethod(tempClass.getAbsolutePath(), fuckery));
- } catch (StackOverflowError | Exception e) {
- StringWriter exceptionWriter = new StringWriter();
- e.printStackTrace(new PrintWriter(exceptionWriter));
- e.printStackTrace();
- exception = exceptionWriter.toString();
- }
-
- tempClass.delete();
- File file = new File(fuckery);
-
- if (file.exists())
- return findFile(MiscUtils.listFiles(file));
-
- return CFR + " " + ERROR + "! " + ExceptionUI.SEND_STACKTRACE_TO +
- nl + nl + TranslatedStrings.SUGGESTED_FIX_DECOMPILER_ERROR +
- nl + nl + exception;
- }
-
- Random r = new Random();
- File f;
-
- public String fuckery(String start) {
- while (true) {
- f = new File(start + r.nextInt(Integer.MAX_VALUE));
- if (!f.exists())
- return f.toString();
- }
- }
-
- public String findFile(File[] fA) {
- for (File f : fA) {
- if (f.isDirectory())
- return findFile(MiscUtils.listFiles(f));
- else {
- String s;
- try {
- s = DiskReader.loadAsString(f.getAbsolutePath());
- } catch (Exception e) {
- StringWriter exceptionWriter = new StringWriter();
- e.printStackTrace(new PrintWriter(exceptionWriter));
- e.printStackTrace();
-
- return CFR + " " + ERROR + "! " + ExceptionUI.SEND_STACKTRACE_TO +
- nl + nl + TranslatedStrings.SUGGESTED_FIX_DECOMPILER_ERROR +
- nl + nl + exceptionWriter;
- }
-
- return s;
- }
- }
-
- return "CFR error!" +
- nl + nl + TranslatedStrings.SUGGESTED_FIX_DECOMPILER_ERROR;
- }
-
- public String[] generateMainMethod(String filePath, String outputPath) {
- return new String[]{
- filePath,
- "--outputdir",
- outputPath,
- "--decodeenumswitch",
- String.valueOf(BytecodeViewer.viewer.decodeEnumSwitch
- .isSelected()),
- "--sugarenums",
- String.valueOf(BytecodeViewer.viewer.sugarEnums.isSelected()),
- "--decodestringswitch",
- String.valueOf(BytecodeViewer.viewer.decodeStringSwitch
- .isSelected()),
- "--arrayiter",
- String.valueOf(BytecodeViewer.viewer.arrayiter.isSelected()),
- "--collectioniter",
- String.valueOf(BytecodeViewer.viewer.collectioniter
- .isSelected()),
- "--innerclasses",
- String.valueOf(BytecodeViewer.viewer.innerClasses.isSelected()),
- "--removeboilerplate",
- String.valueOf(BytecodeViewer.viewer.removeBoilerPlate
- .isSelected()),
- "--removeinnerclasssynthetics",
- String.valueOf(BytecodeViewer.viewer.removeInnerClassSynthetics
- .isSelected()),
- "--decodelambdas",
- String.valueOf(BytecodeViewer.viewer.decodeLambdas.isSelected()),
- "--hidebridgemethods",
- String.valueOf(BytecodeViewer.viewer.hideBridgeMethods
- .isSelected()),
- "--liftconstructorinit",
- String.valueOf(BytecodeViewer.viewer.liftConstructorInit
- .isSelected()),
- "--removedeadmethods",
- String.valueOf(BytecodeViewer.viewer.removeDeadMethods
- .isSelected()),
- "--removebadgenerics",
- String.valueOf(BytecodeViewer.viewer.removeBadGenerics
- .isSelected()),
- "--sugarasserts",
- String.valueOf(BytecodeViewer.viewer.sugarAsserts.isSelected()),
- "--sugarboxing",
- String.valueOf(BytecodeViewer.viewer.sugarBoxing.isSelected()),
- "--showversion",
- String.valueOf(BytecodeViewer.viewer.showVersion.isSelected()),
- "--decodefinally",
- String.valueOf(BytecodeViewer.viewer.decodeFinally.isSelected()),
- "--tidymonitors",
- String.valueOf(BytecodeViewer.viewer.tidyMonitors.isSelected()),
- "--lenient",
- String.valueOf(BytecodeViewer.viewer.lenient.isSelected()),
- "--dumpclasspath",
- String.valueOf(BytecodeViewer.viewer.dumpClassPath.isSelected()),
- "--comments",
- String.valueOf(BytecodeViewer.viewer.comments.isSelected()),
- "--forcetopsort",
- String.valueOf(BytecodeViewer.viewer.forceTopSort.isSelected()),
- "--forcetopsortaggress",
- String.valueOf(BytecodeViewer.viewer.forceTopSortAggress
- .isSelected()),
- "--stringbuffer",
- String.valueOf(BytecodeViewer.viewer.stringBuffer.isSelected()),
- "--stringbuilder",
- String.valueOf(BytecodeViewer.viewer.stringBuilder.isSelected()),
- "--silent",
- String.valueOf(BytecodeViewer.viewer.silent.isSelected()),
- "--recover",
- String.valueOf(BytecodeViewer.viewer.recover.isSelected()),
- "--eclipse",
- String.valueOf(BytecodeViewer.viewer.eclipse.isSelected()),
- "--override",
- String.valueOf(BytecodeViewer.viewer.override.isSelected()),
- "--showinferrable",
- String.valueOf(BytecodeViewer.viewer.showInferrable
- .isSelected()),
- "--aexagg",
- String.valueOf(BytecodeViewer.viewer.aexagg.isSelected()),
- "--forcecondpropagate",
- String.valueOf(BytecodeViewer.viewer.forceCondPropagate
- .isSelected()),
- "--hideutf",
- String.valueOf(BytecodeViewer.viewer.hideUTF.isSelected()),
- "--hidelongstrings",
- String.valueOf(BytecodeViewer.viewer.hideLongStrings
- .isSelected()),
- "--commentmonitors",
- String.valueOf(BytecodeViewer.viewer.commentMonitor
- .isSelected()),
- "--allowcorrecting",
- String.valueOf(BytecodeViewer.viewer.allowCorrecting
- .isSelected()),
- "--labelledblocks",
- String.valueOf(BytecodeViewer.viewer.labelledBlocks
- .isSelected()),
- "--j14classobj",
- String.valueOf(BytecodeViewer.viewer.j14ClassOBJ.isSelected()),
- "--hidelangimports",
- String.valueOf(BytecodeViewer.viewer.hideLangImports
- .isSelected()),
- "--recovertypeclash",
- String.valueOf(BytecodeViewer.viewer.recoveryTypeClash
- .isSelected()),
- "--recovertypehints",
- String.valueOf(BytecodeViewer.viewer.recoveryTypehInts
- .isSelected()),
- "--forcereturningifs",
- String.valueOf(BytecodeViewer.viewer.forceTurningIFs
- .isSelected()),
- "--forloopaggcapture",
- String.valueOf(BytecodeViewer.viewer.forLoopAGGCapture
- .isSelected()),};
- }
-
- @Override
- public void decompileToZip(String sourceJar, String zipName) {
- File tempZip = new File(sourceJar);
-
- String fileStart = tempDirectory + fs;
- String fuckery = fuckery(fileStart);
-
- org.benf.cfr.reader.Main.main(generateMainMethod(tempZip.getAbsolutePath(), fuckery));
-
- File fuck = new File(fuckery);
-
- try {
- zip(fuck, new File(zipName));
- } catch (IOException e) {
- BytecodeViewer.handleException(e);
- }
-
- fuck.delete();
- }
-
- public void zip(File directory, File zipFile) throws IOException {
- java.net.URI base = directory.toURI();
- Deque queue = new LinkedList<>();
- queue.push(directory);
- try (OutputStream out = new FileOutputStream(zipFile);
- ZipOutputStream zout = new ZipOutputStream(out)) {
- while (!queue.isEmpty()) {
- directory = queue.pop();
- for (File kid : MiscUtils.listFiles(directory)) {
- String name = base.relativize(kid.toURI()).getPath();
- if (kid.isDirectory()) {
- queue.push(kid);
- name = name.endsWith("/") ? name : name + "/";
- zout.putNextEntry(new ZipEntry(name));
- } else {
- zout.putNextEntry(new ZipEntry(name));
- copy(kid, zout);
- zout.closeEntry();
+ Enumeration ent = jfile.entries();
+ Set history = new HashSet<>();
+ while (ent.hasMoreElements()) {
+ JarEntry entry = ent.nextElement();
+ if (entry.getName().endsWith(CLASS_SUFFIX)) {
+ JarEntry etn = new JarEntry(entry.getName().replace(CLASS_SUFFIX, ".java"));
+ if (history.add(etn)) {
+ out.putNextEntry(etn);
+ try {
+ IOUtils.write(decompile(null, entry.getName(),
+ IOUtils.toByteArray(jfile.getInputStream(entry))),
+ out, StandardCharsets.UTF_8);
+ } finally {
+ out.closeEntry();
+ }
+ }
+ } else {
+ try {
+ JarEntry etn = new JarEntry(entry.getName());
+ if (history.add(etn)) continue;
+ history.add(etn);
+ out.putNextEntry(etn);
+ try (InputStream in = jfile.getInputStream(entry)) {
+ if (in != null) {
+ int count;
+ while ((count = in.read(data, 0, 1024)) != -1) {
+ out.write(data, 0, count);
+ }
+ }
+ } finally {
+ out.closeEntry();
+ }
+ } catch (ZipException ze) {
+ // some jars contain duplicate pom.xml entries: ignore it
+ if (!ze.getMessage().contains("duplicate")) {
+ throw ze;
+ }
}
}
}
+ } catch (StackOverflowError | Exception e) {
+ BytecodeViewer.handleException(e);
}
}
- private static void copy(InputStream in, OutputStream out)
- throws IOException {
- byte[] buffer = new byte[1024];
- while (true) {
- int readCount = in.read(buffer);
- if (readCount < 0) {
- break;
+ public Options generateOptions() {
+ Map options = new HashMap<>();
+ options.put("decodeenumswitch", String.valueOf(BytecodeViewer.viewer.decodeEnumSwitch.isSelected()));
+ options.put("sugarenums", String.valueOf(BytecodeViewer.viewer.sugarEnums.isSelected()));
+ options.put("decodestringswitch", String.valueOf(BytecodeViewer.viewer.decodeStringSwitch.isSelected()));
+ options.put("arrayiter", String.valueOf(BytecodeViewer.viewer.arrayiter.isSelected()));
+ options.put("collectioniter", String.valueOf(BytecodeViewer.viewer.collectioniter.isSelected()));
+ options.put("innerclasses", String.valueOf(BytecodeViewer.viewer.innerClasses.isSelected()));
+ options.put("removeboilerplate", String.valueOf(BytecodeViewer.viewer.removeBoilerPlate.isSelected()));
+ options.put("removeinnerclasssynthetics",
+ String.valueOf(BytecodeViewer.viewer.removeInnerClassSynthetics.isSelected()));
+ options.put("decodelambdas", String.valueOf(BytecodeViewer.viewer.decodeLambdas.isSelected()));
+ options.put("hidebridgemethods", String.valueOf(BytecodeViewer.viewer.hideBridgeMethods.isSelected()));
+ options.put("liftconstructorinit", String.valueOf(BytecodeViewer.viewer.liftConstructorInit.isSelected()));
+ options.put("removebadgenerics", String.valueOf(BytecodeViewer.viewer.removeBadGenerics.isSelected()));
+ options.put("sugarasserts", String.valueOf(BytecodeViewer.viewer.sugarAsserts.isSelected()));
+ options.put("sugarboxing", String.valueOf(BytecodeViewer.viewer.sugarBoxing.isSelected()));
+ options.put("showversion", String.valueOf(BytecodeViewer.viewer.showVersion.isSelected()));
+ options.put("decodefinally", String.valueOf(BytecodeViewer.viewer.decodeFinally.isSelected()));
+ options.put("tidymonitors", String.valueOf(BytecodeViewer.viewer.tidyMonitors.isSelected()));
+ options.put("lenient", String.valueOf(BytecodeViewer.viewer.lenient.isSelected()));
+ options.put("dumpclasspath", String.valueOf(BytecodeViewer.viewer.dumpClassPath.isSelected()));
+ options.put("comments", String.valueOf(BytecodeViewer.viewer.comments.isSelected()));
+ options.put("forcetopsort", String.valueOf(BytecodeViewer.viewer.forceTopSort.isSelected()));
+ options.put("forcetopsortaggress", String.valueOf(BytecodeViewer.viewer.forceTopSortAggress.isSelected()));
+ options.put("stringbuffer", String.valueOf(BytecodeViewer.viewer.stringBuffer.isSelected()));
+ options.put("stringbuilder", String.valueOf(BytecodeViewer.viewer.stringBuilder.isSelected()));
+ options.put("silent", String.valueOf(BytecodeViewer.viewer.silent.isSelected()));
+ options.put("recover", String.valueOf(BytecodeViewer.viewer.recover.isSelected()));
+ options.put("eclipse", String.valueOf(BytecodeViewer.viewer.eclipse.isSelected()));
+ options.put("override", String.valueOf(BytecodeViewer.viewer.override.isSelected()));
+ options.put("showinferrable", String.valueOf(BytecodeViewer.viewer.showInferrable.isSelected()));
+ options.put("aexagg", String.valueOf(BytecodeViewer.viewer.aexagg.isSelected()));
+ options.put("hideutf", String.valueOf(BytecodeViewer.viewer.hideUTF.isSelected()));
+ options.put("hidelongstrings", String.valueOf(BytecodeViewer.viewer.hideLongStrings.isSelected()));
+ options.put("commentmonitors", String.valueOf(BytecodeViewer.viewer.commentMonitor.isSelected()));
+ options.put("allowcorrecting", String.valueOf(BytecodeViewer.viewer.allowCorrecting.isSelected()));
+ options.put("labelledblocks", String.valueOf(BytecodeViewer.viewer.labelledBlocks.isSelected()));
+ options.put("j14classobj", String.valueOf(BytecodeViewer.viewer.j14ClassOBJ.isSelected()));
+ options.put("hidelangimports", String.valueOf(BytecodeViewer.viewer.hideLangImports.isSelected()));
+ options.put("recovertypehints", String.valueOf(BytecodeViewer.viewer.recoveryTypehInts.isSelected()));
+ options.put("forcereturningifs", String.valueOf(BytecodeViewer.viewer.forceTurningIFs.isSelected()));
+ options.put("forloopaggcapture", String.valueOf(BytecodeViewer.viewer.forLoopAGGCapture.isSelected()));
+ return new OptionsImpl(options);
+ }
+
+ private static class BCVDataSource extends ClassFileSourceImpl {
+
+ private final ResourceContainer container;
+ private final String classFilePath;
+ private final byte[] content;
+
+ private BCVDataSource(Options options, ClassNode cn, String classFilePath, byte[] content) {
+ super(options);
+ this.container = BytecodeViewer.getResourceContainers().stream()
+ .filter(rc -> rc.resourceClasses.containsValue(cn))
+ .findFirst().orElse(null);
+ this.classFilePath = classFilePath;
+ this.content = content;
+ }
+
+ @Override
+ public Pair getClassFileContent(String classFilePath) throws IOException {
+ if (classFilePath.equals(this.classFilePath) && content != null) return Pair.make(content, classFilePath);
+ if (container == null) return super.getClassFileContent(classFilePath);
+ byte[] data = container.resourceClassBytes.get(classFilePath);
+ if (data == null) return super.getClassFileContent(classFilePath);
+ return Pair.make(data, classFilePath);
+ }
+
+ }
+
+ private static class BCVOutputSinkFactory implements OutputSinkFactory {
+
+ private final Consumer dumpDecompiled;
+
+ private BCVOutputSinkFactory(Consumer dumpDecompiled) {
+ this.dumpDecompiled = dumpDecompiled;
+ }
+
+ @Override
+ public List getSupportedSinks(SinkType sinkType, Collection available) {
+ return Collections.singletonList(SinkClass.DECOMPILED);
+ }
+
+ @Override
+ public Sink getSink(SinkType sinkType, SinkClass sinkClass) {
+ if (sinkType == SinkType.JAVA && sinkClass == SinkClass.DECOMPILED) {
+ return x -> dumpDecompiled.accept((SinkReturns.Decompiled) x);
}
- out.write(buffer, 0, readCount);
+ return ignore -> {
+ };
}
+
}
- private static void copy(File file, OutputStream out) throws IOException {
- try (InputStream in = new FileInputStream(file)) {
- copy(in, out);
- }
- }
}
diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/gui/components/SearchableJTextArea.java b/src/main/java/the/bytecode/club/bytecodeviewer/gui/components/SearchableJTextArea.java
index e26d042b..3acce26e 100644
--- a/src/main/java/the/bytecode/club/bytecodeviewer/gui/components/SearchableJTextArea.java
+++ b/src/main/java/the/bytecode/club/bytecodeviewer/gui/components/SearchableJTextArea.java
@@ -50,7 +50,7 @@ public class SearchableJTextArea extends JTextArea
private final JScrollPane scrollPane = new JScrollPane();
private final JPanel searchPanel = new JPanel(new BorderLayout());
private final JTextField searchInput = new JTextField();
- private final JCheckBox caseSensitiveSearch = new TranslatedJCheckBox("Exact", TranslatedComponents.EXACT);
+ private final JCheckBox caseSensitiveSearch = new TranslatedJCheckBox("Match case", TranslatedComponents.MATCH_CASE);
public SearchableJTextArea()
{
diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/gui/components/SearchableRSyntaxTextArea.java b/src/main/java/the/bytecode/club/bytecodeviewer/gui/components/SearchableRSyntaxTextArea.java
index ae88d89b..5c47163b 100644
--- a/src/main/java/the/bytecode/club/bytecodeviewer/gui/components/SearchableRSyntaxTextArea.java
+++ b/src/main/java/the/bytecode/club/bytecodeviewer/gui/components/SearchableRSyntaxTextArea.java
@@ -55,7 +55,7 @@ public class SearchableRSyntaxTextArea extends RSyntaxTextArea
private final RTextScrollPane scrollPane = new RTextScrollPane(this);
private final JPanel searchPanel = new JPanel(new BorderLayout());
private final JTextField searchInput = new JTextField();
- private final JCheckBox caseSensitiveSearch = new TranslatedJCheckBox("Exact", TranslatedComponents.EXACT);
+ private final JCheckBox caseSensitiveSearch = new TranslatedJCheckBox("Match case", TranslatedComponents.MATCH_CASE);
private final JLabel titleHeader = new JLabel("");
private final Color darkScrollBackground = new Color(0x3c3f41);
private final Color darkScrollForeground = new Color(0x575859);
diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/gui/resourcelist/ResourceListPane.java b/src/main/java/the/bytecode/club/bytecodeviewer/gui/resourcelist/ResourceListPane.java
index 9e5c02a6..682ca3f7 100644
--- a/src/main/java/the/bytecode/club/bytecodeviewer/gui/resourcelist/ResourceListPane.java
+++ b/src/main/java/the/bytecode/club/bytecodeviewer/gui/resourcelist/ResourceListPane.java
@@ -1,7 +1,6 @@
package the.bytecode.club.bytecodeviewer.gui.resourcelist;
-import java.awt.BorderLayout;
-import java.awt.Color;
+import java.awt.*;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyAdapter;
@@ -71,7 +70,8 @@ import static the.bytecode.club.bytecodeviewer.Constants.tempDirectory;
public class ResourceListPane extends TranslatedVisibleComponent implements FileDrop.Listener
{
public final JPopupMenu rightClickMenu = new JPopupMenu();
- public final JCheckBox exact = new TranslatedJCheckBox("Exact", TranslatedComponents.EXACT);
+ public final JCheckBox exact = new TranslatedJCheckBox("Exact path", TranslatedComponents.EXACT_PATH);
+ public final JCheckBox caseSensitive = new TranslatedJCheckBox("Match case", TranslatedComponents.MATCH_CASE);
public final JButton open = new JButton("+");
public final JButton close = new JButton("-");
public final ResourceTreeNode treeRoot = new ResourceTreeNode("Loaded Files:");
@@ -104,7 +104,7 @@ public class ResourceListPane extends TranslatedVisibleComponent implements File
tree.setRootVisible(false);
tree.setShowsRootHandles(true);
- quickSearch.setForeground(Color.gray);
+ quickSearch.setForeground(quickSearch.getDisabledTextColor());
attachTreeListeners();
attachQuickSearchListeners();
@@ -118,7 +118,12 @@ public class ResourceListPane extends TranslatedVisibleComponent implements File
quickSearchPanel.setLayout(new BorderLayout());
quickSearchPanel.add(quickSearch, BorderLayout.NORTH);
- exactPanel.add(exact, BorderLayout.WEST);
+
+ JPanel btns = new JPanel(new FlowLayout());
+ btns.add(exact);
+ btns.add(caseSensitive);
+ exactPanel.add(btns, BorderLayout.WEST);
+
buttonPanel.add(open, BorderLayout.EAST);
buttonPanel.add(close, BorderLayout.WEST);
exactPanel.add(buttonPanel, BorderLayout.EAST);
@@ -519,7 +524,7 @@ public class ResourceListPane extends TranslatedVisibleComponent implements File
if (quickSearch.getText().equals(TranslatedStrings.QUICK_FILE_SEARCH_NO_FILE_EXTENSION.toString()))
{
quickSearch.setText("");
- quickSearch.setForeground(Color.black);
+ quickSearch.setForeground(quickSearch.getSelectedTextColor());
}
}
@@ -529,7 +534,7 @@ public class ResourceListPane extends TranslatedVisibleComponent implements File
if (quickSearch.getText().isEmpty())
{
quickSearch.setText(TranslatedStrings.QUICK_FILE_SEARCH_NO_FILE_EXTENSION.toString());
- quickSearch.setForeground(Color.gray);
+ quickSearch.setForeground(quickSearch.getDisabledTextColor());
}
}
});
diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/gui/resourcelist/SearchKeyAdapter.java b/src/main/java/the/bytecode/club/bytecodeviewer/gui/resourcelist/SearchKeyAdapter.java
index 114261b2..4f464dee 100644
--- a/src/main/java/the/bytecode/club/bytecodeviewer/gui/resourcelist/SearchKeyAdapter.java
+++ b/src/main/java/the/bytecode/club/bytecodeviewer/gui/resourcelist/SearchKeyAdapter.java
@@ -1,11 +1,11 @@
package the.bytecode.club.bytecodeviewer.gui.resourcelist;
+import javax.swing.tree.TreeNode;
+import javax.swing.tree.TreePath;
+import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.Enumeration;
-import javax.swing.tree.TreeNode;
-import javax.swing.tree.TreePath;
-import the.bytecode.club.bytecodeviewer.BytecodeViewer;
/***************************************************************************
* Bytecode Viewer (BCV) - Java & Android Reverse Engineering Suite *
@@ -29,118 +29,103 @@ import the.bytecode.club.bytecodeviewer.BytecodeViewer;
* @author Konloch
* @since 6/22/2021
*/
-public class SearchKeyAdapter extends KeyAdapter
-{
- private final ResourceListPane resourceListPane;
-
- public SearchKeyAdapter(ResourceListPane resourceListPane) {this.resourceListPane = resourceListPane;}
-
- @Override
- public void keyPressed(final KeyEvent ke)
- {
- //only trigger on enter
- if (ke.getKeyCode() != KeyEvent.VK_ENTER)
- return;
-
- final String qt = resourceListPane.quickSearch.getText();
- resourceListPane.quickSearch.setText("");
-
- if (qt.isEmpty()) //NOPE
- return;
-
- String[] path;
- int found = 0;
-
- if (qt.contains("."))
- {
- path = qt.split("\\.");
- }
- else
- {
- path = new String[]{qt};
- }
-
- ResourceTreeNode curNode = resourceListPane.treeRoot;
- if (resourceListPane.exact.isSelected())
- {
- pathLoop:
- for (int i = 0; i < path.length; i++)
- {
- final String pathName = path[i];
- final boolean isLast = i == path.length - 1;
-
- for (int c = 0; c < curNode.getChildCount(); c++)
- {
- final ResourceTreeNode child = (ResourceTreeNode) curNode.getChildAt(c);
- System.out.println(pathName + ":" + child.getUserObject());
-
- if (child.getUserObject().equals(pathName))
- {
- curNode = child;
- if (isLast)
- {
- System.out.println("Found! " + curNode);
- found++;
- final TreePath pathn = new TreePath(curNode.getPath());
- resourceListPane.tree.setSelectionPath(pathn);
- resourceListPane.tree.makeVisible(pathn);
- resourceListPane.tree.scrollPathToVisible(pathn);
- resourceListPane.openPath(pathn); //auto open
- break pathLoop;
- }
- continue pathLoop;
- }
- }
-
- System.out.println("Could not find " + pathName);
- break;
- }
- }
- else
- {
- @SuppressWarnings("unchecked")
- Enumeration enums = curNode.depthFirstEnumeration();
- while (enums != null && enums.hasMoreElements())
- {
- ResourceTreeNode node = (ResourceTreeNode) enums.nextElement();
- if (node.isLeaf())
- {
- if (((String) (node.getUserObject())).toLowerCase().contains(path[path.length - 1].toLowerCase()))
- {
- TreeNode[] pathArray = node.getPath();
- int k = 0;
- StringBuilder fullPath = new StringBuilder();
- while (pathArray != null
- && k < pathArray.length)
- {
- ResourceTreeNode n = (ResourceTreeNode) pathArray[k];
- String s = (String) (n.getUserObject());
- fullPath.append(s);
- if (k++ != pathArray.length - 1)
- {
- fullPath.append(".");
- }
- }
- String fullPathString = fullPath.toString();
- if (fullPathString.toLowerCase().contains(qt.toLowerCase()))
- {
- System.out.println("Found! " + node);
- found++;
- if (found >= 30)
- { //TODO probably make this a setting, no real reason it's 30
- BytecodeViewer.showMessage("Uh oh, there could be more results but you've"
- + " triggered the 30 classes at once limit. Try refining your search.");
- return;
- }
- final TreePath pathn = new TreePath(node.getPath());
- resourceListPane.tree.setSelectionPath(pathn.getParentPath());
- resourceListPane.tree.setSelectionPath(pathn);
- resourceListPane.tree.makeVisible(pathn);
- resourceListPane.tree.scrollPathToVisible(pathn);
- }
- }
- }
- }
- }
- }
+public class SearchKeyAdapter extends KeyAdapter {
+ private final ResourceListPane resourceListPane;
+
+ public SearchKeyAdapter(ResourceListPane resourceListPane) {
+ this.resourceListPane = resourceListPane;
+ }
+
+ @Override
+ public void keyPressed(final KeyEvent ke) {
+ //only trigger on enter
+ if (ke.getKeyCode() != KeyEvent.VK_ENTER)
+ return;
+
+ final String qt = resourceListPane.quickSearch.getText();
+
+ if (qt.trim().isEmpty()) //NOPE
+ return;
+
+ String[] path;
+ if (qt.contains(".")) {
+ path = qt.split("\\.");
+ } else {
+ path = new String[]{qt};
+ }
+
+ ResourceTreeNode curNode = resourceListPane.treeRoot;
+ boolean caseSensitive = resourceListPane.caseSensitive.isSelected();
+
+ boolean success = false;
+ if (resourceListPane.exact.isSelected()) {
+ pathLoop:
+ for (int i = 0; i < path.length; i++) {
+ final String pathName = path[i];
+ final boolean isLast = i == path.length - 1;
+
+ for (int c = 0; c < curNode.getChildCount(); c++) {
+ final ResourceTreeNode child = (ResourceTreeNode) curNode.getChildAt(c);
+ Object userObject = child.getUserObject();
+ if (caseSensitive ? userObject.equals(pathName) : userObject.toString().equalsIgnoreCase(pathName)) {
+ curNode = child;
+ if (isLast) {
+ final TreePath pathn = new TreePath(curNode.getPath());
+ resourceListPane.tree.setSelectionPath(pathn);
+ resourceListPane.tree.makeVisible(pathn);
+ resourceListPane.tree.scrollPathToVisible(pathn);
+ resourceListPane.openPath(pathn); //auto open
+ success = true;
+ break pathLoop;
+ }
+ continue pathLoop;
+ }
+ }
+
+ System.out.println("Could not find " + pathName);
+ break;
+ }
+ } else {
+ @SuppressWarnings("unchecked")
+ Enumeration enums = curNode.depthFirstEnumeration();
+ while (enums != null && enums.hasMoreElements()) {
+ ResourceTreeNode node = (ResourceTreeNode) enums.nextElement();
+ if (node.isLeaf()) {
+ String userObject = (String) (node.getUserObject());
+ String lastElem = path[path.length - 1];
+
+ if (caseSensitive ? userObject.contains(lastElem) : userObject.toLowerCase().contains(lastElem.toLowerCase())) {
+ TreeNode[] pathArray = node.getPath();
+ int k = 0;
+ StringBuilder fullPath = new StringBuilder();
+ while (pathArray != null
+ && k < pathArray.length) {
+ ResourceTreeNode n = (ResourceTreeNode) pathArray[k];
+ String s = (String) (n.getUserObject());
+ fullPath.append(s);
+ if (k++ != pathArray.length - 1) {
+ fullPath.append(".");
+ }
+ }
+ String fullPathString = fullPath.toString();
+
+ if (caseSensitive ? fullPathString.contains(qt) : fullPathString.toLowerCase().contains(qt.toLowerCase())) {
+ final TreePath pathn = new TreePath(node.getPath());
+ resourceListPane.tree.setSelectionPath(pathn.getParentPath());
+ resourceListPane.tree.setSelectionPath(pathn);
+ resourceListPane.tree.makeVisible(pathn);
+ resourceListPane.tree.scrollPathToVisible(pathn);
+ success = true;
+ break;
+ }
+ }
+ }
+ }
+
+ }
+
+ if (!success) {
+ Toolkit.getDefaultToolkit().beep();
+ }
+ }
}
diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/gui/resourcesearch/SearchType.java b/src/main/java/the/bytecode/club/bytecodeviewer/gui/resourcesearch/SearchType.java
index 4ed49f29..ec3cd451 100644
--- a/src/main/java/the/bytecode/club/bytecodeviewer/gui/resourcesearch/SearchType.java
+++ b/src/main/java/the/bytecode/club/bytecodeviewer/gui/resourcesearch/SearchType.java
@@ -1,10 +1,7 @@
package the.bytecode.club.bytecodeviewer.gui.resourcesearch;
import the.bytecode.club.bytecodeviewer.searching.SearchPanel;
-import the.bytecode.club.bytecodeviewer.searching.impl.FieldCallSearch;
-import the.bytecode.club.bytecodeviewer.searching.impl.LDCSearch;
-import the.bytecode.club.bytecodeviewer.searching.impl.MethodCallSearch;
-import the.bytecode.club.bytecodeviewer.searching.impl.RegexSearch;
+import the.bytecode.club.bytecodeviewer.searching.impl.*;
/***************************************************************************
* Bytecode Viewer (BCV) - Java & Android Reverse Engineering Suite *
@@ -34,6 +31,7 @@ public enum SearchType
Regex(new RegexSearch()),
MethodCall(new MethodCallSearch()),
FieldCall(new FieldCallSearch()),
+ MemberWithAnnotation(new MemberWithAnnotationSearch())
;
public final SearchPanel panel;
diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/searching/impl/MemberWithAnnotationSearch.java b/src/main/java/the/bytecode/club/bytecodeviewer/searching/impl/MemberWithAnnotationSearch.java
new file mode 100644
index 00000000..2b1b19c7
--- /dev/null
+++ b/src/main/java/the/bytecode/club/bytecodeviewer/searching/impl/MemberWithAnnotationSearch.java
@@ -0,0 +1,83 @@
+package the.bytecode.club.bytecodeviewer.searching.impl;
+
+import org.objectweb.asm.Type;
+import org.objectweb.asm.tree.AnnotationNode;
+import org.objectweb.asm.tree.ClassNode;
+import the.bytecode.club.bytecodeviewer.BytecodeViewer;
+import the.bytecode.club.bytecodeviewer.resources.ResourceContainer;
+import the.bytecode.club.bytecodeviewer.searching.EnterKeyEvent;
+import the.bytecode.club.bytecodeviewer.searching.LDCSearchTreeNodeResult;
+import the.bytecode.club.bytecodeviewer.searching.SearchPanel;
+import the.bytecode.club.bytecodeviewer.translation.TranslatedComponents;
+import the.bytecode.club.bytecodeviewer.translation.components.TranslatedJLabel;
+
+import javax.swing.*;
+import java.awt.*;
+import java.util.List;
+
+/***************************************************************************
+ * Bytecode Viewer (BCV) - Java & Android Reverse Engineering Suite *
+ * Copyright (C) 2014 Kalen 'Konloch' Kinloch - http://bytecodeviewer.com *
+ * *
+ * This program is free software: you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation, either version 3 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program. If not, see . *
+ ***************************************************************************/
+
+/**
+ * Annotation Searching
+ *
+ * @author GraxCode
+ */
+
+public class MemberWithAnnotationSearch implements SearchPanel {
+ JTextField annotation;
+ JPanel myPanel = null;
+
+ public MemberWithAnnotationSearch() {
+ annotation = new JTextField("");
+ annotation.addKeyListener(EnterKeyEvent.SINGLETON);
+ }
+
+ @Override
+ public JPanel getPanel() {
+ if (myPanel == null) {
+ myPanel = new JPanel(new GridLayout(1, 2));
+ myPanel.add(new TranslatedJLabel("Annotation name: ", TranslatedComponents.ANNOTATION_NAME));
+ myPanel.add(annotation);
+ }
+
+ return myPanel;
+ }
+
+ public void search(final ResourceContainer container, final String resourceWorkingName, final ClassNode node, boolean caseSensitive) {
+ final String srchText = annotation.getText().trim();
+
+ if (srchText.isEmpty()) return;
+
+ node.fields.stream().filter(fn -> hasAnnotation(srchText, fn.invisibleAnnotations, fn.visibleAnnotations)).forEach(fn -> BytecodeViewer.viewer.searchBoxPane.treeRoot.add(new LDCSearchTreeNodeResult(container, resourceWorkingName, node, null, fn, fn.name + " " + fn.desc, "")));
+ node.methods.stream().filter(mn -> hasAnnotation(srchText, mn.invisibleAnnotations, mn.visibleAnnotations)).forEach(mn -> BytecodeViewer.viewer.searchBoxPane.treeRoot.add(new LDCSearchTreeNodeResult(container, resourceWorkingName, node, mn, null, mn.name + mn.desc, "")));
+ }
+
+ public static boolean hasAnnotation(String annotation, List... annoLists) {
+ if (annoLists == null) return false;
+ for (List annos : annoLists) {
+ if (annos == null) continue;
+ if (annos.stream().anyMatch(ant -> {
+ String internalName = Type.getType(ant.desc).getInternalName();
+ return internalName.equals(annotation) || internalName.endsWith('/' + annotation) || ant.desc.endsWith('/' + annotation.replace('.', '$'));
+ // in case dot is used for inner class annotations
+ })) return true;
+ }
+ return false;
+ }
+}
diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/translation/TranslatedComponents.java b/src/main/java/the/bytecode/club/bytecodeviewer/translation/TranslatedComponents.java
index 6912e611..c8005695 100644
--- a/src/main/java/the/bytecode/club/bytecodeviewer/translation/TranslatedComponents.java
+++ b/src/main/java/the/bytecode/club/bytecodeviewer/translation/TranslatedComponents.java
@@ -251,8 +251,9 @@ public enum TranslatedComponents
RESULTS,
REFRESH,
MIN_SDK_VERSION,
-
- ;
+ ANNOTATION_NAME,
+ MATCH_CASE,
+ EXACT_PATH;
private final TranslatedComponentReference componentReference;