From 1c887abb7368a8060a4f4246b40e7c36cb8fed26 Mon Sep 17 00:00:00 2001 From: SuspiciousActivity <38253929+SuspiciousActivity@users.noreply.github.com> Date: Sat, 24 Nov 2018 14:08:54 +0100 Subject: [PATCH] Added renaming (CTRL + R) and field goto (CTRL + G) --- README.md | 4 +- src/me/ByteEdit/boxes/OptionBox.java | 25 +-- src/me/ByteEdit/boxes/RenameBox.java | 232 +++++++++++++++++++++++++ src/me/ByteEdit/boxes/SearchBox.java | 25 ++- src/me/ByteEdit/boxes/TypeOpenBox.java | 29 ++-- src/me/ByteEdit/boxes/UnicodeBox.java | 21 +-- src/me/ByteEdit/edit/Disassembler.java | 7 +- src/me/ByteEdit/main/Main.java | 114 +++++++++++- src/me/ByteEdit/utils/ClassUtil.java | 70 ++++++-- 9 files changed, 436 insertions(+), 91 deletions(-) create mode 100644 src/me/ByteEdit/boxes/RenameBox.java diff --git a/README.md b/README.md index 2603ea9..23d49b2 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,8 @@ To open a class, press CTRL + T. To change settings like auto-computation of stack frames and max locals/stack press CTRL + O. -To go to the method definition of the selected line's invoke-method press CTRL + G. +To go to the field/method definition in the selected line press CTRL + G. To (un-)escape unicode characters, press CTRL + U. + +To rename a field/method with attention to references in the selected line, press CTRL + R. diff --git a/src/me/ByteEdit/boxes/OptionBox.java b/src/me/ByteEdit/boxes/OptionBox.java index df2c4e3..e42028a 100644 --- a/src/me/ByteEdit/boxes/OptionBox.java +++ b/src/me/ByteEdit/boxes/OptionBox.java @@ -1,6 +1,5 @@ package me.ByteEdit.boxes; -import java.awt.EventQueue; import java.awt.Toolkit; import javax.swing.JCheckBox; @@ -10,31 +9,11 @@ import javax.swing.UIManager; import javax.swing.border.EmptyBorder; public class OptionBox extends JFrame { - + private JPanel contentPane; public JCheckBox chckbxComputeFrames; public JCheckBox chckbxComputeMax; - - /** - * Launch the application. - */ - public static void main(String[] args) { - EventQueue.invokeLater(new Runnable() { - - public void run() { - try { - OptionBox frame = new OptionBox(); - frame.setVisible(true); - } catch (Exception e) { - e.printStackTrace(); - } - } - }); - } - - /** - * Create the frame. - */ + public OptionBox() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); diff --git a/src/me/ByteEdit/boxes/RenameBox.java b/src/me/ByteEdit/boxes/RenameBox.java new file mode 100644 index 0000000..df6453a --- /dev/null +++ b/src/me/ByteEdit/boxes/RenameBox.java @@ -0,0 +1,232 @@ +package me.ByteEdit.boxes; + +import java.awt.Toolkit; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.LinkedList; + +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JTextField; +import javax.swing.border.EmptyBorder; + +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.FieldInsnNode; +import org.objectweb.asm.tree.FieldNode; +import org.objectweb.asm.tree.MethodInsnNode; +import org.objectweb.asm.tree.MethodNode; + +import me.ByteEdit.edit.Disassembler; +import me.ByteEdit.main.Main; +import me.ByteEdit.utils.ClassUtil; +import me.ByteEdit.utils.UnicodeUtils; + +public class RenameBox extends JFrame { + + private JPanel contentPane; + public JTextField txtName; + public JTextField txtDesc; + + public String className; + public String name; + public String desc; + + public RenameBox() { + setResizable(false); + setTitle("Rename"); + setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE); + setBounds((int) (Toolkit.getDefaultToolkit().getScreenSize().getWidth() / 2 - 225), + (int) (Toolkit.getDefaultToolkit().getScreenSize().getHeight() / 2 - 150), 450, 300); + contentPane = new JPanel(); + contentPane.setBorder(new EmptyBorder(5, 5, 5, 5)); + setContentPane(contentPane); + contentPane.setLayout(null); + + JLabel lblName = new JLabel("Name"); + lblName.setBounds(10, 11, 33, 14); + contentPane.add(lblName); + + JLabel lblDesc = new JLabel("Desc"); + lblDesc.setBounds(10, 36, 33, 14); + contentPane.add(lblDesc); + + txtName = new JTextField(); + txtName.setBounds(53, 8, 381, 20); + contentPane.add(txtName); + txtName.setColumns(10); + + txtDesc = new JTextField(); + txtDesc.setColumns(10); + txtDesc.setBounds(53, 36, 381, 20); + contentPane.add(txtDesc); + + JButton btnRename = new JButton("Rename"); + btnRename.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + if (className == null) { + JOptionPane.showMessageDialog(null, "ClassName not set!", "Error!", JOptionPane.ERROR_MESSAGE); + return; + } + if (name == null) { + JOptionPane.showMessageDialog(null, "Name not set!", "Error!", JOptionPane.ERROR_MESSAGE); + return; + } + if (desc == null) { + JOptionPane.showMessageDialog(null, "Desc not set!", "Error!", JOptionPane.ERROR_MESSAGE); + return; + } + ClassNode clazz = Main.classNodes.get(className.replace("/", ".") + ".class"); + if (clazz == null) { + JOptionPane.showMessageDialog(null, "ClassNode not found!", "Error!", JOptionPane.ERROR_MESSAGE); + return; + } + String newName = UnicodeUtils.unescape(txtName.getText()); + String newDesc = UnicodeUtils.unescape(txtDesc.getText()); + if (ClassUtil.isObjectClassMethod(newName, newDesc, true)) { + JOptionPane.showMessageDialog(null, "Invalid name!", "Error!", JOptionPane.ERROR_MESSAGE); + return; + } + if (desc.startsWith("(")) { + if (newDesc.startsWith("(")) { // method + ClassNode lastHit = clazz; + while (clazz != null && clazz.superName != null) { + ClassNode tmp = Main.classNodes.get(clazz.superName.replace("/", ".") + ".class"); + if (tmp != null) { + boolean found = false; + for (MethodNode mn : clazz.methods) { + if (mn.name.equals(name) && mn.desc.equals(desc)) { + lastHit = clazz; + clazz = tmp; + found = true; + break; + } + } + if (!found) { + clazz = tmp; + lastHit = null; + } + } else { + break; + } + } + if (lastHit != null) { + clazz = lastHit; + } + LinkedList methodsToRename = new LinkedList<>(); + LinkedList minsToRename = new LinkedList<>(); + for (ClassNode cn : Main.classNodes.values()) { + boolean extendsMyClass = cn == clazz; + if (!extendsMyClass) { + ClassNode checkCN = cn; + while (checkCN != null && checkCN.superName != null) { + if (clazz.name.equals(checkCN.superName)) { + extendsMyClass = true; + break; + } + ClassNode tmp = Main.classNodes.get(checkCN.superName.replace("/", ".") + ".class"); + if (tmp != null) { + checkCN = tmp; + } else { + break; + } + } + } + for (MethodNode mn : cn.methods) { + if (extendsMyClass) { + if (mn.name.equals(newName) && mn.desc.equals(newDesc)) { + JOptionPane.showMessageDialog(null, "Method already exists in " + cn.name + "!", "Error!", + JOptionPane.ERROR_MESSAGE); + return; + } + if (mn.name.equals(name) && mn.desc.equals(desc)) { + methodsToRename.add(mn); + } + } + for (AbstractInsnNode ain : mn.instructions.toArray()) { + if (ain instanceof MethodInsnNode) { + MethodInsnNode min = (MethodInsnNode) ain; + if (min.name.equals(name) && min.desc.equals(desc) && min.owner.equals(clazz.name)) { + minsToRename.add(min); + } + } + } + } + } + for (MethodNode mn : methodsToRename) { + mn.name = newName; + mn.desc = newDesc; + } + for (MethodInsnNode min : minsToRename) { + min.name = newName; + min.desc = newDesc; + } + } else { + JOptionPane.showMessageDialog(null, "New Desc is not a method desc!", "Error!", JOptionPane.ERROR_MESSAGE); + return; + } + } else { + if (!newDesc.startsWith("(")) { // field + FieldNode currentFN = null; + for (FieldNode fn : clazz.fields) { + if (fn.name.equals(newName) && fn.desc.equals(newDesc)) { + JOptionPane.showMessageDialog(null, "Field already exists!", "Error!", JOptionPane.ERROR_MESSAGE); + return; + } + if (fn.name.equals(name) && fn.desc.equals(desc)) { + currentFN = fn; + } + } + if (currentFN == null) { + JOptionPane.showMessageDialog(null, "Field not found?!", "Error!", JOptionPane.ERROR_MESSAGE); + return; + } + currentFN.name = newName; + currentFN.desc = newDesc; + for (ClassNode cn : Main.classNodes.values()) { + for (MethodNode mn : cn.methods) { + for (AbstractInsnNode ain : mn.instructions.toArray()) { + if (ain instanceof FieldInsnNode) { + FieldInsnNode fin = (FieldInsnNode) ain; + if (fin.name.equals(name) && fin.desc.equals(desc) && fin.owner.equals(className)) { + fin.name = newName; + fin.desc = newDesc; + } + } + } + } + } + } else { + JOptionPane.showMessageDialog(null, "New Desc is not a field desc!", "Error!", JOptionPane.ERROR_MESSAGE); + return; + } + } + try { // refresh + ClassNode classNode = Main.classNodes.get(Main.currentNodeName); + int prev = Main.txtByteEditView.getCaretPosition(); + String dis = Disassembler.disassemble(classNode); + String substr = Main.currentNodeName.substring(0, Main.currentNodeName.length() - 6); + for (String key : Main.classNodes.keySet()) { + if (key.contains("$")) { + String[] split = key.split("\\$"); + if (split[0].equals(substr)) { + dis += "\n" + Disassembler.disassemble(Main.classNodes.get(key)); + } + } + } + Main.txtByteEditView.setText(dis); + Main.txtByteEditView.setCaretPosition(prev); + } catch (Exception e2) { + e2.printStackTrace(); + } + name = newName; + desc = newDesc; + } + }); + btnRename.setBounds(345, 237, 89, 23); + contentPane.add(btnRename); + } +} diff --git a/src/me/ByteEdit/boxes/SearchBox.java b/src/me/ByteEdit/boxes/SearchBox.java index 19fbc39..eaffe57 100644 --- a/src/me/ByteEdit/boxes/SearchBox.java +++ b/src/me/ByteEdit/boxes/SearchBox.java @@ -20,15 +20,12 @@ import javax.swing.border.EmptyBorder; import me.ByteEdit.main.Main; public class SearchBox extends JFrame { - + public JPanel contentPane; public JTextField txtFind; public JTextField txtReplace; public JCheckBox chckbxCaseSensitive; - - /** - * Create the frame. - */ + public SearchBox() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); @@ -50,7 +47,7 @@ public class SearchBox extends JFrame { txtFind = new JTextField(); KeyStroke enter = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0); txtFind.registerKeyboardAction(new ActionListener() { - + @Override public void actionPerformed(ActionEvent e) { find(); @@ -71,7 +68,7 @@ public class SearchBox extends JFrame { contentPane.add(chckbxCaseSensitive); JButton btnFind = new JButton("Find"); btnFind.addActionListener(new ActionListener() { - + public void actionPerformed(ActionEvent e) { find(); } @@ -80,7 +77,7 @@ public class SearchBox extends JFrame { contentPane.add(btnFind); JButton btnReplacefind = new JButton("Replace/Find"); btnReplacefind.addActionListener(new ActionListener() { - + public void actionPerformed(ActionEvent e) { replaceFind(); } @@ -89,7 +86,7 @@ public class SearchBox extends JFrame { contentPane.add(btnReplacefind); JButton btnReplace = new JButton("Replace"); btnReplace.addActionListener(new ActionListener() { - + public void actionPerformed(ActionEvent e) { replace(); } @@ -98,7 +95,7 @@ public class SearchBox extends JFrame { contentPane.add(btnReplace); JButton btnReplaceAll = new JButton("Replace All"); btnReplaceAll.addActionListener(new ActionListener() { - + public void actionPerformed(ActionEvent e) { replaceAll(); } @@ -106,7 +103,7 @@ public class SearchBox extends JFrame { btnReplaceAll.setBounds(130, 184, 110, 23); contentPane.add(btnReplaceAll); } - + public void find() { int startPos = Main.txtByteEditView.getCaretPosition(); String toFind = txtFind.getText(); @@ -123,19 +120,19 @@ public class SearchBox extends JFrame { } Main.txtByteEditView.select(startPos + index, startPos + index + toFind.length()); } - + public void replaceFind() { replace(); find(); } - + public void replace() { String txt = Main.txtByteEditView.getText(); int startPos = Main.txtByteEditView.getSelectionStart(); Main.txtByteEditView.replaceSelection(txtReplace.getText()); Main.txtByteEditView.select(startPos, startPos + txtReplace.getText().length()); } - + public void replaceAll() { String toFind = txtFind.getText(); String toReplace = txtReplace.getText(); diff --git a/src/me/ByteEdit/boxes/TypeOpenBox.java b/src/me/ByteEdit/boxes/TypeOpenBox.java index a58c06d..fea86f6 100644 --- a/src/me/ByteEdit/boxes/TypeOpenBox.java +++ b/src/me/ByteEdit/boxes/TypeOpenBox.java @@ -21,13 +21,10 @@ import javax.swing.event.ListSelectionListener; import me.ByteEdit.main.Main; public class TypeOpenBox extends JFrame { - + public JPanel contentPane; public JTextField txtSearch; - - /** - * Create the frame. - */ + public TypeOpenBox() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); @@ -43,11 +40,11 @@ public class TypeOpenBox extends JFrame { contentPane.setBorder(new EmptyBorder(5, 5, 5, 5)); setContentPane(contentPane); contentPane.setLayout(null); - + DefaultListModel model = new DefaultListModel<>(); JList list = new JList(model); list.addListSelectionListener(new ListSelectionListener() { - + public void valueChanged(ListSelectionEvent e) { String val = list.getSelectedValue(); if (val != null && !val.equals(Main.currentNodeName)) { @@ -55,10 +52,10 @@ public class TypeOpenBox extends JFrame { } } }); - + txtSearch = new JTextField(); txtSearch.addActionListener(new ActionListener() { - + public void actionPerformed(ActionEvent e) { if (model.size() == 1) { list.setSelectedIndex(0); @@ -66,19 +63,19 @@ public class TypeOpenBox extends JFrame { } }); txtSearch.getDocument().addDocumentListener(new DocumentListener() { - + public void changedUpdate(DocumentEvent e) { updateList(); } - + public void removeUpdate(DocumentEvent e) { updateList(); } - + public void insertUpdate(DocumentEvent e) { updateList(); } - + public void updateList() { model.clear(); if (txtSearch.getText().isEmpty()) { @@ -107,13 +104,13 @@ public class TypeOpenBox extends JFrame { txtSearch.setBounds(10, 11, 374, 20); contentPane.add(txtSearch); txtSearch.setColumns(10); - + JScrollPane scrollPane = new JScrollPane(); scrollPane.setBounds(10, 46, 374, 194); contentPane.add(scrollPane); - + list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); scrollPane.setViewportView(list); } - + } diff --git a/src/me/ByteEdit/boxes/UnicodeBox.java b/src/me/ByteEdit/boxes/UnicodeBox.java index d57c645..218c9c8 100644 --- a/src/me/ByteEdit/boxes/UnicodeBox.java +++ b/src/me/ByteEdit/boxes/UnicodeBox.java @@ -16,12 +16,9 @@ import me.ByteEdit.main.Main; import me.ByteEdit.utils.UnicodeUtils; public class UnicodeBox extends JFrame { - + private JPanel contentPane; - - /** - * Create the frame. - */ + public UnicodeBox() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); @@ -37,12 +34,12 @@ public class UnicodeBox extends JFrame { contentPane.setBorder(new EmptyBorder(5, 5, 5, 5)); setContentPane(contentPane); contentPane.setLayout(null); - + JTextArea textArea = new JTextArea(); - + JButton btnEscape = new JButton("Escape"); btnEscape.addActionListener(new ActionListener() { - + public void actionPerformed(ActionEvent e) { try { textArea.setText(UnicodeUtils.escape(textArea.getText())); @@ -53,10 +50,10 @@ public class UnicodeBox extends JFrame { }); btnEscape.setBounds(10, 137, 89, 23); contentPane.add(btnEscape); - + JButton btnUnescape = new JButton("Unescape"); btnUnescape.addActionListener(new ActionListener() { - + public void actionPerformed(ActionEvent e) { try { textArea.setText(UnicodeUtils.unescape(textArea.getText())); @@ -67,11 +64,11 @@ public class UnicodeBox extends JFrame { }); btnUnescape.setBounds(195, 137, 89, 23); contentPane.add(btnUnescape); - + JScrollPane scrollPane = new JScrollPane(); scrollPane.setBounds(10, 11, 274, 115); contentPane.add(scrollPane); - + scrollPane.setViewportView(textArea); } } diff --git a/src/me/ByteEdit/edit/Disassembler.java b/src/me/ByteEdit/edit/Disassembler.java index 936a6a0..4646f4a 100644 --- a/src/me/ByteEdit/edit/Disassembler.java +++ b/src/me/ByteEdit/edit/Disassembler.java @@ -70,7 +70,7 @@ public class Disassembler { } - public static DisassembleTuple disassemble(ClassNode classNode, MethodNode methodToFind) { + public static DisassembleTuple disassemble(ClassNode classNode, Object nodeToFind) { if (classNode == null) { return new DisassembleTuple("ClassNode is null! This is not a valid java class file!"); } @@ -228,6 +228,9 @@ public class Disassembler { s += "\n"; } } + if (fn.equals(nodeToFind)) { + lineFound = s.split("\\n").length; + } s += "\t" + ClassUtil.getAccessFlagsFull(fn.access).replace("varargs", "transient") + UnicodeUtils.escapeWithSpaces(fn.desc) + " " + UnicodeUtils.escapeWithSpaces(fn.name); if (fn.value != null) { @@ -303,7 +306,7 @@ public class Disassembler { s += "\n"; } } - if (mn.equals(methodToFind)) { + if (mn.equals(nodeToFind)) { lineFound = s.split("\\n").length; } s += "\t" + ClassUtil.getAccessFlagsFull(mn.access) + UnicodeUtils.escapeWithSpaces(mn.name) + " " diff --git a/src/me/ByteEdit/main/Main.java b/src/me/ByteEdit/main/Main.java index a810219..94fdd2c 100644 --- a/src/me/ByteEdit/main/Main.java +++ b/src/me/ByteEdit/main/Main.java @@ -30,6 +30,7 @@ import java.util.Map.Entry; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.jar.JarOutputStream; +import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.swing.JComponent; @@ -67,9 +68,11 @@ import org.fife.ui.rtextarea.RTextScrollPane; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.FieldNode; import org.objectweb.asm.tree.MethodNode; import me.ByteEdit.boxes.OptionBox; +import me.ByteEdit.boxes.RenameBox; import me.ByteEdit.boxes.SearchBox; import me.ByteEdit.boxes.TypeOpenBox; import me.ByteEdit.boxes.UnicodeBox; @@ -94,6 +97,7 @@ public class Main extends JFrame { public static TypeOpenBox typeOpenBox; public static OptionBox optionBox; public static UnicodeBox unicodeBox; + public static RenameBox renameBox; public static JTree tree; public static RTextScrollPane scrollPane_ByteEdit; public static Theme theme; @@ -169,6 +173,7 @@ public class Main extends JFrame { typeOpenBox = new TypeOpenBox(); optionBox = new OptionBox(); unicodeBox = new UnicodeBox(); + renameBox = new RenameBox(); setTitle("ByteEdit"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setBounds((int) (Toolkit.getDefaultToolkit().getScreenSize().getWidth() / 2 - 407), @@ -267,6 +272,13 @@ public class Main extends JFrame { } catch (IOException e1) { e1.printStackTrace(); } + txtByteEditView.setText(""); + currentNodeName = null; + searchBox.setVisible(false); + typeOpenBox.setVisible(false); + optionBox.setVisible(false); + unicodeBox.setVisible(false); + renameBox.setVisible(false); isChangingFile = false; } dtde.dropComplete(true); @@ -300,7 +312,7 @@ public class Main extends JFrame { @Override public void keyReleased(KeyEvent e) { - if (e.getKeyCode() == 116 && currentNodeName != null) { + if (e.getKeyCode() == 116 && currentNodeName != null) { // F5 try { ClassNode classNode = classNodes.get(currentNodeName); int prev = txtByteEditView.getCaretPosition(); @@ -316,7 +328,9 @@ public class Main extends JFrame { } txtByteEditView.setText(dis); txtByteEditView.setCaretPosition(prev); - } catch (Exception e2) {} + } catch (Exception e2) { + e2.printStackTrace(); + } } } }); @@ -326,6 +340,7 @@ public class Main extends JFrame { KeyStroke ctrlG = KeyStroke.getKeyStroke(KeyEvent.VK_G, KeyEvent.CTRL_DOWN_MASK); KeyStroke ctrlO = KeyStroke.getKeyStroke(KeyEvent.VK_O, KeyEvent.CTRL_DOWN_MASK); KeyStroke ctrlU = KeyStroke.getKeyStroke(KeyEvent.VK_U, KeyEvent.CTRL_DOWN_MASK); + KeyStroke ctrlR = KeyStroke.getKeyStroke(KeyEvent.VK_R, KeyEvent.CTRL_DOWN_MASK); // global txtByteEditView.registerKeyboardAction(new ActionListener() { @@ -385,6 +400,17 @@ public class Main extends JFrame { } } }, ctrlG, JComponent.WHEN_FOCUSED); + txtByteEditView.registerKeyboardAction(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + try { + renameSelected(); + } catch (BadLocationException e1) { + e1.printStackTrace(); + } + } + }, ctrlR, JComponent.WHEN_FOCUSED); txtByteEditView.setEditable(true); txtByteEditView.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVA_DISASSEMBLE); txtByteEditView.setCodeFoldingEnabled(true); @@ -445,6 +471,84 @@ public class Main extends JFrame { return; } selectFile(className + ".class"); + } else if (line.startsWith("getstatic ") || line.startsWith("putstatic ") || line.startsWith("getfield ") || line.startsWith("putfield ")) { + String[] split = line.split(" "); + String desc = UnicodeUtils.unescape(split[1]); + String className = UnicodeUtils.unescape(split[2]); + String[] nameSplit = className.split("/"); + String fieldName = nameSplit[nameSplit.length - 1]; + className = className.substring(0, className.length() - fieldName.length() - 1); + ClassNode classNode = classNodes.get(className + ".class"); + if (classNode == null) { + return; + } + for (FieldNode fn : classNode.fields) { + if (fn.name.equals(fieldName) && fn.desc.equals(desc)) { + while (classNode.outerClass != null) { + classNode = classNodes.get(classNode.outerClass + ".class"); + } + int lineFound = selectFileWithSearch(classNode.name + ".class", fn); + if (lineFound != -1) { + Main.txtByteEditView.setCaretPosition(Main.txtByteEditView.getLineStartOffset(lineFound)); + } + break; + } + } + } + } + + private final Pattern renameableFieldPattern = Pattern.compile("^\t(?:[a-z]+ |0x[0-9a-fA-F]+ )*?(\\[*(?:V|Z|C|B|S|I|F|J|D|L.+?;)) ([^ ]+) ?.*"); + private final Pattern renameableMethodPattern = Pattern + .compile("^\t(?:[a-z]+ |0x[0-9a-fA-F]+ )*?([^ ]+) (\\((?:\\[*(?:V|Z|C|B|S|I|F|J|D|L.+?;))*\\)[^ ]+) .*\\{"); + + public void renameSelected() throws BadLocationException { + int lineStart = txtByteEditView.getLineStartOffsetOfCurrentLine(); + int lineEnd = txtByteEditView.getLineEndOffsetOfCurrentLine() - 1; + String line = txtByteEditView.getText(lineStart, lineEnd - lineStart); + Matcher m = renameableFieldPattern.matcher(line); + if (m.find()) { + setRenameInfo(m, lineEnd); + } else { + m = renameableMethodPattern.matcher(line); + if (m.find()) { + if (ClassUtil.isObjectClassMethod(m.group(1), m.group(2), false)) { + JOptionPane.showMessageDialog(null, "This method can not be renamed!", "Error!", JOptionPane.ERROR_MESSAGE); + return; + } + setRenameInfo(m, lineEnd); + } + } + } + + public void setRenameInfo(Matcher m, int lineEnd) throws BadLocationException { + String[] arr = txtByteEditView.getText(0, lineEnd).split("\n"); + int lastClassLine = 0; + for (int i = 0; i < arr.length; i++) { + if (arr[i].startsWith("// #Class")) { + lastClassLine = i; + } + } + while (lastClassLine < arr.length && arr[lastClassLine].startsWith("// ")) { + lastClassLine++; + } + if (lastClassLine < arr.length) { + String s = arr[lastClassLine]; + s = s.substring(0, s.length() - 2); + if (s.contains(" implements ")) + s = s.substring(0, s.lastIndexOf(" implements ")); + if (s.contains(" extends ")) + s = s.substring(0, s.lastIndexOf(" extends ")); + String[] split = s.split(" "); + String className = UnicodeUtils.unescape(split[split.length - 1]); + renameBox.className = className; + String name = m.pattern() == renameableFieldPattern ? m.group(2) : m.group(1); + String desc = m.pattern() == renameableFieldPattern ? m.group(1) : m.group(2); + renameBox.name = name; + renameBox.desc = desc; + renameBox.txtName.setText(name); + renameBox.txtDesc.setText(desc); + renameBox.setVisible(true); + renameBox.txtName.requestFocusInWindow(); } } @@ -463,13 +567,13 @@ public class Main extends JFrame { } } - public static int selectFileWithSearch(String s, MethodNode methodToFind) { + public static int selectFileWithSearch(String s, Object nodeToFind) { if (s.endsWith(".class")) { currentNodeName = s; int lineFound = -1; ClassNode classNode = classNodes.get(s); String dis = ""; - DisassembleTuple tuple = Disassembler.disassemble(classNode, methodToFind); + DisassembleTuple tuple = Disassembler.disassemble(classNode, nodeToFind); if (tuple.getLine() != -1) { lineFound = dis.split("\\n").length + tuple.getLine() - 1; } @@ -479,7 +583,7 @@ public class Main extends JFrame { if (key.contains("$")) { String[] split = key.split("\\$"); if (split[0].equals(substr)) { - tuple = Disassembler.disassemble(classNodes.get(key), methodToFind); + tuple = Disassembler.disassemble(classNodes.get(key), nodeToFind); if (tuple.getLine() != -1) { lineFound = dis.split("\\n").length + tuple.getLine() + 1; } diff --git a/src/me/ByteEdit/utils/ClassUtil.java b/src/me/ByteEdit/utils/ClassUtil.java index c7227d5..220052a 100644 --- a/src/me/ByteEdit/utils/ClassUtil.java +++ b/src/me/ByteEdit/utils/ClassUtil.java @@ -7,7 +7,7 @@ import java.util.HashSet; import org.objectweb.asm.Type; public class ClassUtil { - + public static final int ACC_PUBLIC = 0x0001; public static final int ACC_PRIVATE = 0x0002; public static final int ACC_PROTECTED = 0x0004; @@ -25,7 +25,41 @@ public class ClassUtil { public static final int ACC_ANNOTATION = 0x2000; public static final int ACC_ENUM = 0x4000; public static final int ACC_MANDATED = 0x8000; - + + public static boolean isObjectClassMethod(String name, String desc, boolean forbidInits) { + switch (name) { + case "": + case "": + if (forbidInits) { + return true; + } + case "notifyAll": + case "notify": + if (desc.equals("()V")) { + return true; + } + break; + case "wait": + if (desc.equals("()V") || desc.equals("(J)V") || desc.equals("(JI)V")) { + return true; + } + break; + case "equals": + if (desc.equals("(Ljava/lang/Object;)Z")) { + return true; + } + break; + case "hashCode": + if (desc.equals("()I")) { + return true; + } + break; + default: + break; + } + return false; + } + public static int getIDFromClassNameForType(String s) { s = s.toUpperCase(); switch (s) { @@ -64,7 +98,7 @@ public class ClassUtil { } } } - + public static String getClassNameFromType(Type type) { switch (type.getSort()) { case 0: { @@ -103,7 +137,7 @@ public class ClassUtil { } return Integer.toString(type.getSort()); } - + public static String getArrayTypeByID(int id) { switch (id) { case 4: @@ -126,7 +160,7 @@ public class ClassUtil { return Integer.toString(id); } } - + public static int getArrayIDByType(String type) { switch (type) { case "Z": @@ -149,7 +183,7 @@ public class ClassUtil { return Integer.parseInt(type); } } - + public static String getAccessFlagsClass(int access) { String s = ""; if ((ACC_PUBLIC & access) != 0) { @@ -175,7 +209,7 @@ public class ClassUtil { } return s; } - + public static String getAccessFlagsFull(int access) { int acc = 0; String s = ""; @@ -249,7 +283,7 @@ public class ClassUtil { return "0x" + Integer.toHexString(access) + " "; } } - + /** * @return { JavaType, Import } */ @@ -308,7 +342,7 @@ public class ClassUtil { } return new String[] { type, importName }; } - + /** * @return { ReturnValue, ParameterValues, Imports } getrennt durch ";" */ @@ -411,7 +445,7 @@ public class ClassUtil { } return new String[] { returnType[0], parameterStr, importStr }; } - + public static int countMethodParams(String desc) { int count = 0; String parameterType = desc.split("\\)")[0].substring(1); @@ -446,7 +480,7 @@ public class ClassUtil { } return count; } - + public static String getDecompiledValue(Object o, String desc, boolean escapeSpaces) { switch (o.getClass().getName()) { case "java.lang.String": { @@ -468,11 +502,11 @@ public class ClassUtil { return o.toString(); } } - + public static String getDecompiledValue(Object o, String desc) { return getDecompiledValue(o, desc, false); } - + public static Object getCastedValue(String o, String className) { switch (className) { case "java/lang/String": { @@ -503,7 +537,7 @@ public class ClassUtil { return o.toString(); } } - + public static String getSimpleDesc(String type) { String desc = ""; for (int i = 0; i < countArraySize(type); i++) { @@ -517,7 +551,7 @@ public class ClassUtil { } return desc; } - + private static int countArraySize(String type) { int size = 0; for (byte b : type.getBytes()) { @@ -527,7 +561,7 @@ public class ClassUtil { } return size; } - + public static final HashMap VARS = new HashMap<>(); static { VARS.put("byte", "B"); @@ -540,7 +574,7 @@ public class ClassUtil { VARS.put("boolean", "Z"); VARS.put("void", "V"); } - + public static int externalArrayTypeDimensionCount(String s) { int i = 0; int j = "[]".length(); @@ -551,7 +585,7 @@ public class ClassUtil { } return i; } - + public static String internalMethodDescriptor(String returnType, String methodArgs) { returnType = returnType.replace(".", "/"); methodArgs = methodArgs.replace(".", "/");