diff --git a/src/me/nov/threadtear/decompiler/CFR.java b/src/me/nov/threadtear/decompiler/CFR.java index d93f3fe..7c26bc3 100644 --- a/src/me/nov/threadtear/decompiler/CFR.java +++ b/src/me/nov/threadtear/decompiler/CFR.java @@ -14,70 +14,18 @@ public class CFR { public static final HashMap options = new HashMap<>(); static { - options.put("aexagg", "false"); - options.put("allowcorrecting", "true"); - options.put("arrayiter", "true"); - options.put("caseinsensitivefs", "false"); - options.put("clobber", "false"); - options.put("collectioniter", "true"); - options.put("commentmonitors", "false"); - options.put("decodeenumswitch", "true"); - options.put("decodefinally", "true"); - options.put("decodelambdas", "true"); - options.put("decodestringswitch", "true"); - options.put("dumpclasspath", "false"); - options.put("eclipse", "true"); - options.put("elidescala", "false"); - options.put("forcecondpropagate", "false"); - options.put("forceexceptionprune", "false"); - options.put("forcereturningifs", "false"); - options.put("forcetopsort", "true"); - options.put("forcetopsortaggress", "true"); - options.put("forloopaggcapture", "false"); - options.put("hidebridgemethods", "true"); - options.put("hidelangimports", "true"); + options.put("showversion", "false"); options.put("hidelongstrings", "true"); options.put("hideutf", "false"); - options.put("ignoreexceptionsalways", "true"); - options.put("innerclasses", "true"); - options.put("j14classobj", "false"); - options.put("labelledblocks", "true"); - options.put("lenient", "false"); - options.put("liftconstructorinit", "true"); - options.put("override", "true"); - options.put("pullcodecase", "false"); - options.put("recover", "true"); - options.put("recovertypeclash", "false"); - options.put("recovertypehints", "false"); - options.put("relinkconststring", "true"); - options.put("removebadgenerics", "true"); - options.put("removeboilerplate", "true"); - options.put("removedeadmethods", "true"); - options.put("removeinnerclasssynthetics", "false"); - options.put("rename", "false"); - options.put("renamedupmembers", "false"); - options.put("renameenumidents", "false"); - options.put("renameillegalidents", "false"); - options.put("showinferrable", "false"); - options.put("showversion", "false"); - options.put("silent", "false"); - options.put("stringbuffer", "true"); - options.put("stringbuilder", "true"); - options.put("sugarasserts", "true"); - options.put("sugarboxing", "true"); - options.put("sugarenums", "true"); - options.put("tidymonitors", "true"); - options.put("commentmonitors", "false"); - options.put("tryresources", "true"); - options.put("usenametable", "true"); - options.put("analyseas", "CLASS"); + options.put("innerclasses", "false"); + options.put("forcetopsort", "true"); + options.put("forcetopsortaggress", "true"); } private static String decompiled; - public static String decompile(ClassNode cn) { + public static String decompile(String name, byte[] bytes) { try { - byte[] bytes = Conversion.toBytecode0(cn); decompiled = null; OutputSinkFactory mySink = new OutputSinkFactory() { @Override @@ -113,7 +61,7 @@ public String getPossiblyRenamedPath(String path) { @Override public Pair getClassFileContent(String path) throws IOException { String name = path.substring(0, path.length() - 6); - if (name.equals(cn.name)) { + if (name.equals(name)) { return Pair.make(bytes, name); } // cfr loads unnecessary classes. normally you should throw a FNF exception here, but this way, no long comment at the top of the code is created @@ -129,7 +77,7 @@ public Collection addJar(String arg0) { } }; CfrDriver cfrDriver = new CfrDriver.Builder().withClassFileSource(source).withOutputSink(mySink).withOptions(options).build(); - cfrDriver.analyse(Arrays.asList(cn.name)); + cfrDriver.analyse(Arrays.asList(name)); } catch (Exception e) { e.printStackTrace(); StringWriter sw = new StringWriter(); diff --git a/src/me/nov/threadtear/io/Clazz.java b/src/me/nov/threadtear/io/Clazz.java index 5dd4f2e..ac8f66c 100644 --- a/src/me/nov/threadtear/io/Clazz.java +++ b/src/me/nov/threadtear/io/Clazz.java @@ -1,18 +1,26 @@ package me.nov.threadtear.io; -import java.util.jar.JarEntry; +import java.io.*; +import java.util.jar.*; import org.objectweb.asm.tree.ClassNode; public class Clazz { public boolean transform = true; - public ClassNode node; - public JarEntry oldEntry; + public final ClassNode node; + public final JarEntry oldEntry; + public final JarFile jar; - public Clazz(ClassNode node, JarEntry oldEntry) { + public Clazz(ClassNode node, JarEntry oldEntry, JarFile jar) { super(); this.node = node; this.oldEntry = oldEntry; + this.jar = jar; + } + + public InputStream streamOriginal() throws IOException { + JarFile jf = new JarFile(jar.getName()); + return jf.getInputStream(jf.getEntry(oldEntry.getName())); } } diff --git a/src/me/nov/threadtear/io/JarIO.java b/src/me/nov/threadtear/io/JarIO.java index 42712f8..6d94da2 100644 --- a/src/me/nov/threadtear/io/JarIO.java +++ b/src/me/nov/threadtear/io/JarIO.java @@ -29,7 +29,7 @@ private static ArrayList readEntry(JarFile jar, JarEntry en, ArrayList conversionMethod; + private JCheckBox ignoreTCB; + private JCheckBox ignoreMon; + + public DecompilerPanel(Clazz cn) { + this.clazz = cn; this.setLayout(new BorderLayout(4, 4)); - JPanel actionPanel = new JPanel(); - actionPanel.setLayout(new GridBagLayout()); + + JPanel leftActionPanel = new JPanel(); + leftActionPanel.setLayout(new GridBagLayout()); + leftActionPanel.add(new JLabel("CFR 0.149 ")); + conversionMethod = new JComboBox<>(new String[] { "Use source", "Pass through ASM" }); + conversionMethod.addActionListener(this); + conversionMethod.setEnabled(false); + leftActionPanel.add(conversionMethod); + leftActionPanel.add(ignoreTCB = new JCheckBox("Ingore try catch blocks")); + leftActionPanel.add(ignoreMon = new JCheckBox("Ignore synchronized")); + ignoreTCB.setEnabled(false); + ignoreTCB.addActionListener(this); + ignoreMon.setEnabled(false); + ignoreMon.addActionListener(this); + JPanel rightActionPanel = new JPanel(); + rightActionPanel.setLayout(new GridBagLayout()); JButton reload = new JButton(IconLoader.get().loadSVGIcon("res/refresh.svg", false)); - reload.addActionListener(l -> { - textArea.setText(CFR.decompile(cn)); - }); + reload.addActionListener(this); JTextField search = new JTextField(); - search.putClientProperty(DarkTextUI.KEY_DEFAULT_TEXT, "Search..."); + search.putClientProperty(DarkTextUI.KEY_DEFAULT_TEXT, "Search for text or regex"); search.setPreferredSize(new Dimension(200, reload.getPreferredSize().height)); search.addActionListener(l -> { try { @@ -54,7 +82,7 @@ public DecompilerPanel(ClassNode cn) { Label: { for (int i = 0; i < split.length; i++) { String line = split[i]; - if (line.toLowerCase().contains(searchText)) { + if (Strings.containsRegex(line, searchText)) { if (i > searchIndex) { textArea.setCaretPosition(textArea.getDocument().getDefaultRootElement().getElement(i).getStartOffset()); searchIndex = i; @@ -65,6 +93,7 @@ public DecompilerPanel(ClassNode cn) { } } } + Toolkit.getDefaultToolkit().beep(); if (first) { // go back to first line textArea.setCaretPosition(textArea.getDocument().getDefaultRootElement().getElement(firstIndex).getStartOffset()); @@ -77,15 +106,15 @@ public DecompilerPanel(ClassNode cn) { } }); - actionPanel.add(search); + rightActionPanel.add(search); GridBagConstraints gbc = new GridBagConstraints(); gbc.anchor = GridBagConstraints.EAST; - actionPanel.add(reload, gbc); + rightActionPanel.add(reload, gbc); JPanel topPanel = new JPanel(); topPanel.setBorder(new EmptyBorder(1, 5, 0, 1)); topPanel.setLayout(new BorderLayout()); - topPanel.add(new JLabel("CFR Decompiler 0.149 (www.benf.org/other/cfr)"), BorderLayout.WEST); - topPanel.add(actionPanel, BorderLayout.EAST); + topPanel.add(leftActionPanel, BorderLayout.WEST); + topPanel.add(rightActionPanel, BorderLayout.EAST); this.add(topPanel, BorderLayout.NORTH); LoadingIndicator loadingLabel = new LoadingIndicator("Decompiling class file... ", JLabel.CENTER); loadingLabel.setRunning(true); @@ -93,18 +122,78 @@ public DecompilerPanel(ClassNode cn) { SwingUtilities.invokeLater(() -> { new Thread(() -> { this.textArea = new DecompilerTextArea(); - this.textArea.setText(CFR.decompile(cn)); - JScrollPane scp = new RTextScrollPane(textArea); + this.update(); + scp = new RTextScrollPane(textArea); scp.getVerticalScrollBar().setUnitIncrement(16); scp.setBorder(BorderFactory.createLoweredSoftBevelBorder()); this.remove(loadingLabel); this.add(scp, BorderLayout.CENTER); + conversionMethod.setEnabled(true); invalidate(); validate(); + repaint(); }, "decompile-thread").start(); }); } + @Override + public void actionPerformed(ActionEvent e) { + reload(); + } + + private void reload() { + LoadingIndicator loadingLabel = new LoadingIndicator("Decompiling class file... ", JLabel.CENTER); + loadingLabel.setRunning(true); + this.add(loadingLabel, BorderLayout.CENTER); + this.remove(scp); + invalidate(); + validate(); + SwingUtilities.invokeLater(() -> { + new Thread(() -> { + update(); + this.remove(loadingLabel); + this.add(scp, BorderLayout.CENTER); + invalidate(); + validate(); + repaint(); + }).start(); + }); + } + + public void update() { + try { + byte[] bytes; + if (conversionMethod.getSelectedIndex() == 0) { + // use the original jar file to retrieve bytes + bytes = IOUtils.toByteArray(clazz.streamOriginal()); + ignoreTCB.setEnabled(false); + ignoreMon.setEnabled(false); + } else { + System.out.println("decompile ASM " + conversionMethod.getSelectedIndex()); + ignoreTCB.setEnabled(true); + ignoreMon.setEnabled(true); + ClassNode copy = Conversion.toNode(Conversion.toBytecode0(clazz.node)); + // do some asm action here + if (ignoreTCB.isSelected()) { + copy.methods.forEach(m -> m.tryCatchBlocks = null); + } + if (ignoreMon.isSelected()) { + copy.methods.forEach(m -> StreamSupport.stream(m.instructions.spliterator(), false).filter(i -> i.getOpcode() == MONITORENTER || i.getOpcode() == MONITOREXIT) + .forEach(i -> m.instructions.set(i, new InsnNode(POP)))); + } + bytes = Conversion.toBytecode0(copy); + } + String decompiled = CFR.decompile(clazz.node.name, bytes); + this.textArea.setText(decompiled); + } catch (IOException e) { + e.printStackTrace(); + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + e.printStackTrace(pw); + this.textArea.setText(sw.toString()); + } + } + private void hightlightText(String searchText) throws BadLocationException { Highlighter highlighter = textArea.getHighlighter(); highlighter.removeAllHighlights(); diff --git a/src/me/nov/threadtear/swing/tree/ClassTreePanel.java b/src/me/nov/threadtear/swing/tree/ClassTreePanel.java index 3cf5644..266c36d 100644 --- a/src/me/nov/threadtear/swing/tree/ClassTreePanel.java +++ b/src/me/nov/threadtear/swing/tree/ClassTreePanel.java @@ -62,7 +62,7 @@ private JPanel createButtons() { decompile.addActionListener(l -> { SortedTreeClassNode tn = (SortedTreeClassNode) tree.getLastSelectedPathComponent(); if (tn != null && tn.member != null) { - new DecompilerFrame(tn.member.node).setVisible(true); + new DecompilerFrame(tn.member).setVisible(true); } }); panel.add(decompile); @@ -142,7 +142,7 @@ public void mouseClicked(MouseEvent e) { if (e.getClickCount() == 2) { SortedTreeClassNode tn = (SortedTreeClassNode) getLastSelectedPathComponent(); if (tn != null && tn.member != null) { - new DecompilerFrame(tn.member.node).setVisible(true); + new DecompilerFrame(tn.member).setVisible(true); } } } diff --git a/src/me/nov/threadtear/util/Strings.java b/src/me/nov/threadtear/util/Strings.java index 2f4abe6..3019ce0 100644 --- a/src/me/nov/threadtear/util/Strings.java +++ b/src/me/nov/threadtear/util/Strings.java @@ -2,6 +2,7 @@ import java.io.*; import java.util.*; +import java.util.regex.*; import javax.lang.model.SourceVersion; @@ -22,6 +23,14 @@ public static boolean isHighSDev(String cst) { return cst.length() >= 2 ? (calcSdev(cst) > 30) : false; } + public static boolean containsRegex(String input, String search) { + try { + return Pattern.compile(search).matcher(input).find(); + } catch (PatternSyntaxException e) { + return input.toLowerCase().contains(search.toLowerCase()); + } + } + public static double calcSdev(String cst) { double sum = 0; char[] ccst = cst.toCharArray(); diff --git a/src/res/rsta-theme.xml b/src/res/rsta-theme.xml index ffa44d0..07c82e4 100644 --- a/src/res/rsta-theme.xml +++ b/src/res/rsta-theme.xml @@ -9,10 +9,10 @@ - + - +