From afeab95f0bb50746a8d87a9c7bc040fd030bd1a1 Mon Sep 17 00:00:00 2001 From: Cody <6558800+Bl3nd@users.noreply.github.com> Date: Thu, 12 Sep 2024 18:42:35 -0600 Subject: [PATCH] Add ability to open an accessed fields parent class. --- .../gui/components/actions/GoToAction.java | 43 +- .../gui/util/BytecodeViewPanelUpdater.java | 1017 +++++++++-------- .../classcontainer/ClassFileContainer.java | 24 +- .../classcontainer/parser/MyVoidVisitor.java | 37 +- 4 files changed, 595 insertions(+), 526 deletions(-) diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/gui/components/actions/GoToAction.java b/src/main/java/the/bytecode/club/bytecodeviewer/gui/components/actions/GoToAction.java index 41a49b8c..07cb9e33 100644 --- a/src/main/java/the/bytecode/club/bytecodeviewer/gui/components/actions/GoToAction.java +++ b/src/main/java/the/bytecode/club/bytecodeviewer/gui/components/actions/GoToAction.java @@ -2,6 +2,9 @@ package the.bytecode.club.bytecodeviewer.gui.components.actions; import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; import org.fife.ui.rsyntaxtextarea.Token; +import org.objectweb.asm.tree.ClassNode; +import the.bytecode.club.bytecodeviewer.BytecodeViewer; +import the.bytecode.club.bytecodeviewer.resources.ResourceContainer; import the.bytecode.club.bytecodeviewer.resources.classcontainer.ClassFileContainer; import the.bytecode.club.bytecodeviewer.resources.classcontainer.locations.ClassFieldLocation; import the.bytecode.club.bytecodeviewer.resources.classcontainer.parser.TokenUtil; @@ -31,7 +34,21 @@ public class GoToAction extends AbstractAction int column = textArea.getCaretOffsetFromLineStart(); container.fieldMembers.values().forEach(fields -> fields.forEach(field -> { - if (field.line == line && field.columnStart - 1 <= column && field.columnEnd >= column) { + if (field.line == line && field.columnStart - 1 <= column && field.columnEnd >= column) + { + // Open the class that is associated with the field's owner. + if (!field.owner.equals(container.getName())) + { + ResourceContainer resourceContainer = BytecodeViewer.getFileContainer(container.getParentContainer()); + if (resourceContainer != null) + { + String s = container.getImport(field.owner); + BytecodeViewer.viewer.workPane.addClassResource(resourceContainer, s + ".class"); + } + + return; + } + Element root = textArea.getDocument().getDefaultRootElement(); ClassFieldLocation first = fields.get(0); int startOffset = root.getElement(first.line - 1).getStartOffset() + (first.columnStart - 1); @@ -40,15 +57,19 @@ public class GoToAction extends AbstractAction })); container.methodParameterMembers.values().forEach(parameters -> parameters.forEach(parameter -> { - if (parameter.line == line && parameter.columnStart - 1 <= column && parameter.columnEnd >= column) { + if (parameter.line == line && parameter.columnStart - 1 <= column && parameter.columnEnd >= column) + { Element root = textArea.getDocument().getDefaultRootElement(); - if (parameter.decRef.equalsIgnoreCase("declaration")) { + if (parameter.decRef.equalsIgnoreCase("declaration")) + { int startOffset = root.getElement(parameter.line - 1).getStartOffset() + (parameter.columnStart - 1); textArea.setCaretPosition(startOffset); - } else { + } else + { String method = parameter.method; parameters.stream().filter(classParameterLocation -> classParameterLocation.method.equals(method)).forEach(classParameterLocation -> { - if (classParameterLocation.decRef.equalsIgnoreCase("declaration")) { + if (classParameterLocation.decRef.equalsIgnoreCase("declaration")) + { int startOffset = root.getElement(classParameterLocation.line - 1).getStartOffset() + (classParameterLocation.columnStart - 1); textArea.setCaretPosition(startOffset); } @@ -58,15 +79,19 @@ public class GoToAction extends AbstractAction })); container.methodLocalMembers.values().forEach(localMembers -> localMembers.forEach(localMember -> { - if (localMember.line == line && localMember.columnStart - 1 <= column && localMember.columnEnd >= column) { + if (localMember.line == line && localMember.columnStart - 1 <= column && localMember.columnEnd >= column) + { Element root = textArea.getDocument().getDefaultRootElement(); - if (localMember.decRef.equals("declaration")) { + if (localMember.decRef.equals("declaration")) + { int startOffset = root.getElement(localMember.line - 1).getStartOffset() + (localMember.columnStart - 1); textArea.setCaretPosition(startOffset); - } else { + } else + { String method = localMember.method; localMembers.stream().filter(classLocalVariableLocation -> classLocalVariableLocation.method.equals(method)).forEach(classLocalVariableLocation -> { - if (classLocalVariableLocation.decRef.equalsIgnoreCase("declaration")) { + if (classLocalVariableLocation.decRef.equalsIgnoreCase("declaration")) + { int startOffset = root.getElement(classLocalVariableLocation.line - 1).getStartOffset() + (classLocalVariableLocation.columnStart - 1); textArea.setCaretPosition(startOffset); } diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/gui/util/BytecodeViewPanelUpdater.java b/src/main/java/the/bytecode/club/bytecodeviewer/gui/util/BytecodeViewPanelUpdater.java index 64c71eb1..b22fd95b 100644 --- a/src/main/java/the/bytecode/club/bytecodeviewer/gui/util/BytecodeViewPanelUpdater.java +++ b/src/main/java/the/bytecode/club/bytecodeviewer/gui/util/BytecodeViewPanelUpdater.java @@ -18,22 +18,8 @@ package the.bytecode.club.bytecodeviewer.gui.util; -import java.awt.*; -import java.awt.event.InputEvent; -import java.awt.event.KeyEvent; -import java.util.Objects; -import java.util.regex.Matcher; -import javax.swing.*; -import javax.swing.event.CaretEvent; -import javax.swing.event.CaretListener; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; -import javax.swing.text.BadLocationException; -import javax.swing.text.Element; - import org.fife.ui.rsyntaxtextarea.*; import org.fife.ui.rtextarea.SmartHighlightPainter; -import org.jetbrains.annotations.NotNull; import org.objectweb.asm.ClassWriter; import the.bytecode.club.bytecodeviewer.BytecodeViewer; import the.bytecode.club.bytecodeviewer.Configuration; @@ -54,6 +40,19 @@ import the.bytecode.club.bytecodeviewer.resources.classcontainer.locations.Class import the.bytecode.club.bytecodeviewer.resources.classcontainer.parser.TokenUtil; import the.bytecode.club.bytecodeviewer.util.MethodParser; +import javax.swing.*; +import javax.swing.event.CaretEvent; +import javax.swing.event.CaretListener; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.text.BadLocationException; +import javax.swing.text.Element; +import java.awt.*; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.util.Objects; +import java.util.regex.Matcher; + import static the.bytecode.club.bytecodeviewer.gui.resourceviewer.TabbedPane.BLANK_COLOR; import static the.bytecode.club.bytecodeviewer.translation.TranslatedStrings.EDITABLE; @@ -68,562 +67,564 @@ import static the.bytecode.club.bytecodeviewer.translation.TranslatedStrings.EDI public class BytecodeViewPanelUpdater implements Runnable { - public final ClassViewer viewer; - public final BytecodeViewPanel bytecodeViewPanel; - private final JButton button; - private final byte[] classBytes; + public final ClassViewer viewer; + public final BytecodeViewPanel bytecodeViewPanel; + private final JButton button; + private final byte[] classBytes; - public SearchableRSyntaxTextArea updateUpdaterTextArea; - public JComboBox methodsList; - public boolean isPanelEditable; - public boolean waitingFor; - private Thread thread; + public SearchableRSyntaxTextArea updateUpdaterTextArea; + public JComboBox methodsList; + public boolean isPanelEditable; + public boolean waitingFor; + private Thread thread; - public BytecodeViewPanelUpdater(BytecodeViewPanel bytecodeViewPanel, ClassViewer cv, byte[] classBytes, boolean isPanelEditable, JButton button) - { - this.viewer = cv; - this.bytecodeViewPanel = bytecodeViewPanel; - this.classBytes = classBytes; - this.isPanelEditable = isPanelEditable; - this.button = button; - waitingFor = true; - } + public BytecodeViewPanelUpdater(BytecodeViewPanel bytecodeViewPanel, ClassViewer cv, byte[] classBytes, boolean isPanelEditable, JButton button) + { + this.viewer = cv; + this.bytecodeViewPanel = bytecodeViewPanel; + this.classBytes = classBytes; + this.isPanelEditable = isPanelEditable; + this.button = button; + waitingFor = true; + } - public void processDisplay() - { - try - { - BytecodeViewer.updateBusyStatus(true); + public void processDisplay() + { + try + { + BytecodeViewer.updateBusyStatus(true); - if (bytecodeViewPanel.decompiler != Decompiler.NONE) - { - //hex viewer - if (bytecodeViewPanel.decompiler == Decompiler.HEXCODE_VIEWER) - { - final ClassWriter cw = new ClassWriter(0); - viewer.resource.getResourceClassNode().accept(cw); + if (bytecodeViewPanel.decompiler != Decompiler.NONE) + { + //hex viewer + if (bytecodeViewPanel.decompiler == Decompiler.HEXCODE_VIEWER) + { + final ClassWriter cw = new ClassWriter(0); + viewer.resource.getResourceClassNode().accept(cw); - SwingUtilities.invokeLater(() -> - { - final HexViewer hex = new HexViewer(cw.toByteArray()); - bytecodeViewPanel.add(hex); - }); - } else - { - final Decompiler decompiler = bytecodeViewPanel.decompiler; + SwingUtilities.invokeLater(() -> + { + final HexViewer hex = new HexViewer(cw.toByteArray()); + bytecodeViewPanel.add(hex); + }); + } else + { + final Decompiler decompiler = bytecodeViewPanel.decompiler; - //perform decompiling inside of this thread - final String decompiledSource = decompiler.getDecompiler().decompileClassNode(viewer.resource.getResourceClassNode(), classBytes); + //perform decompiling inside of this thread + final String decompiledSource = decompiler.getDecompiler().decompileClassNode(viewer.resource.getResourceClassNode(), classBytes); - ClassFileContainer container = new ClassFileContainer(viewer.resource.workingName + "-" + decompiler.getDecompilerName(), decompiledSource); - if (!container.hasBeenParsed) - { - container.parse(); - viewer.classFiles.put(viewer.resource.workingName + "-" + decompiler.getDecompilerName(), container); - } + ClassFileContainer container = new ClassFileContainer(viewer.resource.workingName + "-" + decompiler.getDecompilerName(), decompiledSource, viewer.resource.container.name); + if (!container.hasBeenParsed) + { + container.parse(); + viewer.classFiles.put(viewer.resource.workingName + "-" + decompiler.getDecompilerName(), container); + container.hasBeenParsed = true; + } - //set the swing components on the swing thread - SwingUtilities.invokeLater(() -> - { - buildTextArea(decompiler, decompiledSource); - waitingFor = false; - }); + //set the swing components on the swing thread + SwingUtilities.invokeLater(() -> + { + buildTextArea(decompiler, decompiledSource); + waitingFor = false; + }); - //hold this thread until the swing thread has finished attaching the components - while (waitingFor) - { - try - { - Thread.sleep(1); - } catch (Exception ignored) - { - } - } - } - } - } catch (IndexOutOfBoundsException | NullPointerException e) - { - //ignore - } catch (Exception e) - { - BytecodeViewer.handleException(e); - } finally - { - viewer.resetDivider(); - BytecodeViewer.updateBusyStatus(false); - SwingUtilities.invokeLater(() -> - { - if (button != null) - button.setEnabled(true); - }); - } - } + //hold this thread until the swing thread has finished attaching the components + while (waitingFor) + { + try + { + Thread.sleep(1); + } catch (Exception ignored) + { + } + } + } + } + } catch (IndexOutOfBoundsException | NullPointerException e) + { + //ignore + } catch (Exception e) + { + BytecodeViewer.handleException(e); + } finally + { + viewer.resetDivider(); + BytecodeViewer.updateBusyStatus(false); + SwingUtilities.invokeLater(() -> + { + if (button != null) + button.setEnabled(true); + }); + } + } - public void startNewThread() - { - thread = new Thread(this, "Pane Update"); - thread.start(); - } + public void startNewThread() + { + thread = new Thread(this, "Pane Update"); + thread.start(); + } - @Override - public void run() - { - if (bytecodeViewPanel.decompiler == Decompiler.NONE) - return; + @Override + public void run() + { + if (bytecodeViewPanel.decompiler == Decompiler.NONE) + return; - processDisplay(); + processDisplay(); - if (bytecodeViewPanel.decompiler == Decompiler.HEXCODE_VIEWER) - return; + if (bytecodeViewPanel.decompiler == Decompiler.HEXCODE_VIEWER) + return; - //nullcheck broken pane - if (updateUpdaterTextArea == null || updateUpdaterTextArea.getScrollPane() == null - || updateUpdaterTextArea.getScrollPane().getViewport() == null) - { - //build an error message - SwingUtilities.invokeLater(() -> - buildTextArea(bytecodeViewPanel.decompiler, "Critical BCV Error")); - return; - } + //nullcheck broken pane + if (updateUpdaterTextArea == null || updateUpdaterTextArea.getScrollPane() == null + || updateUpdaterTextArea.getScrollPane().getViewport() == null) + { + //build an error message + SwingUtilities.invokeLater(() -> + buildTextArea(bytecodeViewPanel.decompiler, "Critical BCV Error")); + return; + } - //this still freezes the swing UI - synchronizePane(); - } + //this still freezes the swing UI + synchronizePane(); + } - public final CaretListener caretListener = new CaretListener() - { - @Override - public void caretUpdate(CaretEvent e) - { - MethodParser methods = viewer.methods.get(bytecodeViewPanel.panelIndex); - if (methods != null) - { - int methodLine = methods.findActiveMethod(updateUpdaterTextArea.getCaretLineNumber()); + public final CaretListener caretListener = new CaretListener() + { + @Override + public void caretUpdate(CaretEvent e) + { + MethodParser methods = viewer.methods.get(bytecodeViewPanel.panelIndex); + if (methods != null) + { + int methodLine = methods.findActiveMethod(updateUpdaterTextArea.getCaretLineNumber()); - if (methodLine != -1) - { - if (BytecodeViewer.viewer.showClassMethods.isSelected()) - { - if (methodsList != null) - { - if (methodLine != (int) Objects.requireNonNull(methodsList.getSelectedItem())) - { - methodsList.setSelectedItem(methodLine); - } - } - } - if (BytecodeViewer.viewer.synchronizedViewing.isSelected()) - { - int panes = 2; - if (viewer.bytecodeViewPanel3 != null) - panes = 3; + if (methodLine != -1) + { + if (BytecodeViewer.viewer.showClassMethods.isSelected()) + { + if (methodsList != null) + { + if (methodLine != (int) Objects.requireNonNull(methodsList.getSelectedItem())) + { + methodsList.setSelectedItem(methodLine); + } + } + } + if (BytecodeViewer.viewer.synchronizedViewing.isSelected()) + { + int panes = 2; + if (viewer.bytecodeViewPanel3 != null) + panes = 3; - for (int i = 0; i < panes; i++) - { - if (i != bytecodeViewPanel.panelIndex) - { - ClassViewer.selectMethod(viewer, i, methods.getMethod(methodLine)); - } - } - } - } - } - } - }; + for (int i = 0; i < panes; i++) + { + if (i != bytecodeViewPanel.panelIndex) + { + ClassViewer.selectMethod(viewer, i, methods.getMethod(methodLine)); + } + } + } + } + } + } + }; - public final ChangeListener viewportListener = new ChangeListener() - { - @Override - public void stateChanged(ChangeEvent e) - { - int panes = 2; - if (viewer.bytecodeViewPanel3 != null) - panes = 3; + public final ChangeListener viewportListener = new ChangeListener() + { + @Override + public void stateChanged(ChangeEvent e) + { + int panes = 2; + if (viewer.bytecodeViewPanel3 != null) + panes = 3; - if (BytecodeViewer.viewer.synchronizedViewing.isSelected()) - { - if (updateUpdaterTextArea.isShowing() && (updateUpdaterTextArea.hasFocus() || updateUpdaterTextArea.getMousePosition() != null)) - { - int caretLine = updateUpdaterTextArea.getCaretLineNumber(); - int maxViewLine = ClassViewer.getMaxViewLine(updateUpdaterTextArea); - int activeViewLine = ClassViewer.getViewLine(updateUpdaterTextArea); - int activeLine = (activeViewLine == maxViewLine && caretLine > maxViewLine) ? caretLine : - activeViewLine; - int activeLineDelta = -1; - MethodParser.Method activeMethod = null; - MethodParser activeMethods = viewer.methods.get(bytecodeViewPanel.panelIndex); - if (activeMethods != null) - { - int activeMethodLine = activeMethods.findActiveMethod(activeLine); - if (activeMethodLine != -1) - { - activeLineDelta = activeLine - activeMethodLine; - activeMethod = activeMethods.getMethod(activeMethodLine); - ClassViewer.selectMethod(updateUpdaterTextArea, activeMethodLine); - } - } - for (int i = 0; i < panes; i++) - { - if (i != bytecodeViewPanel.panelIndex) - { - int setLine = -1; + if (BytecodeViewer.viewer.synchronizedViewing.isSelected()) + { + if (updateUpdaterTextArea.isShowing() && (updateUpdaterTextArea.hasFocus() || updateUpdaterTextArea.getMousePosition() != null)) + { + int caretLine = updateUpdaterTextArea.getCaretLineNumber(); + int maxViewLine = ClassViewer.getMaxViewLine(updateUpdaterTextArea); + int activeViewLine = ClassViewer.getViewLine(updateUpdaterTextArea); + int activeLine = (activeViewLine == maxViewLine && caretLine > maxViewLine) ? caretLine : + activeViewLine; + int activeLineDelta = -1; + MethodParser.Method activeMethod = null; + MethodParser activeMethods = viewer.methods.get(bytecodeViewPanel.panelIndex); + if (activeMethods != null) + { + int activeMethodLine = activeMethods.findActiveMethod(activeLine); + if (activeMethodLine != -1) + { + activeLineDelta = activeLine - activeMethodLine; + activeMethod = activeMethods.getMethod(activeMethodLine); + ClassViewer.selectMethod(updateUpdaterTextArea, activeMethodLine); + } + } + for (int i = 0; i < panes; i++) + { + if (i != bytecodeViewPanel.panelIndex) + { + int setLine = -1; - RSyntaxTextArea area = null; - switch (i) - { - case 0: - area = viewer.bytecodeViewPanel1.updateThread.updateUpdaterTextArea; - break; - case 1: - area = viewer.bytecodeViewPanel2.updateThread.updateUpdaterTextArea; - break; - case 2: - area = viewer.bytecodeViewPanel3.updateThread.updateUpdaterTextArea; - break; - } + RSyntaxTextArea area = null; + switch (i) + { + case 0: + area = viewer.bytecodeViewPanel1.updateThread.updateUpdaterTextArea; + break; + case 1: + area = viewer.bytecodeViewPanel2.updateThread.updateUpdaterTextArea; + break; + case 2: + area = viewer.bytecodeViewPanel3.updateThread.updateUpdaterTextArea; + break; + } - if (area != null) - { - if (activeMethod != null && activeLineDelta >= 0) - { - MethodParser methods = viewer.methods.get(i); - if (methods != null) - { - int methodLine = methods.findMethod(activeMethod); - if (methodLine != -1) - { - int viewLine = ClassViewer.getViewLine(area); - if (activeLineDelta != viewLine - methodLine) - { - setLine = methodLine + activeLineDelta; - } - } - } - } else if (activeLine != ClassViewer.getViewLine(area)) - { - setLine = activeLine; - } - if (setLine >= 0) - { - ClassViewer.setViewLine(area, setLine); - } - } - } - } - } - } - } - }; + if (area != null) + { + if (activeMethod != null && activeLineDelta >= 0) + { + MethodParser methods = viewer.methods.get(i); + if (methods != null) + { + int methodLine = methods.findMethod(activeMethod); + if (methodLine != -1) + { + int viewLine = ClassViewer.getViewLine(area); + if (activeLineDelta != viewLine - methodLine) + { + setLine = methodLine + activeLineDelta; + } + } + } + } else if (activeLine != ClassViewer.getViewLine(area)) + { + setLine = activeLine; + } + if (setLine >= 0) + { + ClassViewer.setViewLine(area, setLine); + } + } + } + } + } + } + } + }; - public void synchronizePane() - { - if (bytecodeViewPanel.decompiler == Decompiler.HEXCODE_VIEWER - || bytecodeViewPanel.decompiler == Decompiler.NONE) - return; + public void synchronizePane() + { + if (bytecodeViewPanel.decompiler == Decompiler.HEXCODE_VIEWER + || bytecodeViewPanel.decompiler == Decompiler.NONE) + return; - SwingUtilities.invokeLater(() -> - { - JViewport viewport = updateUpdaterTextArea.getScrollPane().getViewport(); - viewport.addChangeListener(viewportListener); - updateUpdaterTextArea.addCaretListener(caretListener); - }); + SwingUtilities.invokeLater(() -> + { + JViewport viewport = updateUpdaterTextArea.getScrollPane().getViewport(); + viewport.addChangeListener(viewportListener); + updateUpdaterTextArea.addCaretListener(caretListener); + }); - final MethodParser methods = viewer.methods.get(bytecodeViewPanel.panelIndex); - for (int i = 0; i < updateUpdaterTextArea.getLineCount(); i++) - { - String lineText = updateUpdaterTextArea.getLineText(i); - Matcher regexMatcher = MethodParser.regex.matcher(lineText); - if (regexMatcher.find()) - { - String methodName = regexMatcher.group("name"); - String methodParams = regexMatcher.group("params"); - methods.addMethod(i, methodName, methodParams); - } - } + final MethodParser methods = viewer.methods.get(bytecodeViewPanel.panelIndex); + for (int i = 0; i < updateUpdaterTextArea.getLineCount(); i++) + { + String lineText = updateUpdaterTextArea.getLineText(i); + Matcher regexMatcher = MethodParser.regex.matcher(lineText); + if (regexMatcher.find()) + { + String methodName = regexMatcher.group("name"); + String methodParams = regexMatcher.group("params"); + methods.addMethod(i, methodName, methodParams); + } + } - //TODO fix this - if (BytecodeViewer.viewer.showClassMethods.isSelected()) - { - if (!methods.isEmpty()) - { - methodsList = new JComboBox<>(); + //TODO fix this + if (BytecodeViewer.viewer.showClassMethods.isSelected()) + { + if (!methods.isEmpty()) + { + methodsList = new JComboBox<>(); - for (Integer line : methods.getMethodsLines()) - methodsList.addItem(line); + for (Integer line : methods.getMethodsLines()) + methodsList.addItem(line); - methodsList.setRenderer(new MethodsRenderer(this)); - methodsList.addActionListener(e -> - { - int line = (int) Objects.requireNonNull(methodsList.getSelectedItem()); + methodsList.setRenderer(new MethodsRenderer(this)); + methodsList.addActionListener(e -> + { + int line = (int) Objects.requireNonNull(methodsList.getSelectedItem()); - RSyntaxTextArea area = null; - switch (bytecodeViewPanel.panelIndex) - { - case 0: - area = viewer.bytecodeViewPanel1.updateThread.updateUpdaterTextArea; - break; - case 1: - area = viewer.bytecodeViewPanel2.updateThread.updateUpdaterTextArea; - break; - case 2: - area = viewer.bytecodeViewPanel3.updateThread.updateUpdaterTextArea; - break; - } + RSyntaxTextArea area = null; + switch (bytecodeViewPanel.panelIndex) + { + case 0: + area = viewer.bytecodeViewPanel1.updateThread.updateUpdaterTextArea; + break; + case 1: + area = viewer.bytecodeViewPanel2.updateThread.updateUpdaterTextArea; + break; + case 2: + area = viewer.bytecodeViewPanel3.updateThread.updateUpdaterTextArea; + break; + } - if (area != null) - ClassViewer.selectMethod(area, line); - }); + if (area != null) + ClassViewer.selectMethod(area, line); + }); - JPanel panel = new JPanel(new BorderLayout()); - panel.add(updateUpdaterTextArea.getScrollPane().getColumnHeader().getComponent(0), BorderLayout.NORTH); - panel.add(methodsList, BorderLayout.SOUTH); - methodsList.setBackground(BLANK_COLOR); + JPanel panel = new JPanel(new BorderLayout()); + panel.add(updateUpdaterTextArea.getScrollPane().getColumnHeader().getComponent(0), BorderLayout.NORTH); + panel.add(methodsList, BorderLayout.SOUTH); + methodsList.setBackground(BLANK_COLOR); - SwingUtilities.invokeLater(() -> - { - updateUpdaterTextArea.getScrollPane().getColumnHeader().removeAll(); - updateUpdaterTextArea.getScrollPane().getColumnHeader().add(panel); - }); - } - } - } + SwingUtilities.invokeLater(() -> + { + updateUpdaterTextArea.getScrollPane().getColumnHeader().removeAll(); + updateUpdaterTextArea.getScrollPane().getColumnHeader().add(panel); + }); + } + } + } - public void buildTextArea(Decompiler decompiler, String decompiledSource) - { - updateUpdaterTextArea = new SearchableRSyntaxTextArea(); + public void buildTextArea(Decompiler decompiler, String decompiledSource) + { + updateUpdaterTextArea = new SearchableRSyntaxTextArea(); - Configuration.rstaTheme.apply(updateUpdaterTextArea); - bytecodeViewPanel.add(updateUpdaterTextArea.getTextAreaSearchPanel(), BorderLayout.NORTH); - bytecodeViewPanel.add(updateUpdaterTextArea.getScrollPane()); + Configuration.rstaTheme.apply(updateUpdaterTextArea); + bytecodeViewPanel.add(updateUpdaterTextArea.getTextAreaSearchPanel(), BorderLayout.NORTH); + bytecodeViewPanel.add(updateUpdaterTextArea.getScrollPane()); - bytecodeViewPanel.textArea = updateUpdaterTextArea; + bytecodeViewPanel.textArea = updateUpdaterTextArea; - bytecodeViewPanel.textArea.setMarkOccurrencesColor(Color.ORANGE); - bytecodeViewPanel.textArea.setCursor(new Cursor(Cursor.TEXT_CURSOR)); - bytecodeViewPanel.textArea.getCaret().setVisible(true); - bytecodeViewPanel.textArea.getCaret().setBlinkRate(0); - bytecodeViewPanel.textArea.setHighlighter(new RSyntaxTextAreaHighlighterEx()); + bytecodeViewPanel.textArea.setMarkOccurrencesColor(Color.ORANGE); + bytecodeViewPanel.textArea.setCursor(new Cursor(Cursor.TEXT_CURSOR)); + bytecodeViewPanel.textArea.getCaret().setVisible(true); + bytecodeViewPanel.textArea.getCaret().setBlinkRate(0); + bytecodeViewPanel.textArea.setHighlighter(new RSyntaxTextAreaHighlighterEx()); - if (bytecodeViewPanel.decompiler != Decompiler.BYTECODE_DISASSEMBLER) - { - bytecodeViewPanel.textArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVA); - } else - { - AbstractTokenMakerFactory tokenMakerFactory = (AbstractTokenMakerFactory) TokenMakerFactory.getDefaultInstance(); - tokenMakerFactory.putMapping("text/javaBytecode", "the.bytecode.club.bytecodeviewer.decompilers.bytecode.JavaBytecodeTokenMaker"); - bytecodeViewPanel.textArea.setSyntaxEditingStyle("text/javaBytecode"); - } - bytecodeViewPanel.textArea.setCodeFoldingEnabled(true); - bytecodeViewPanel.textArea.setText(decompiledSource); - bytecodeViewPanel.textArea.setCaretPosition(0); - bytecodeViewPanel.textArea.setEditable(isPanelEditable); + if (bytecodeViewPanel.decompiler != Decompiler.BYTECODE_DISASSEMBLER) + { + bytecodeViewPanel.textArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVA); + } else + { + AbstractTokenMakerFactory tokenMakerFactory = (AbstractTokenMakerFactory) TokenMakerFactory.getDefaultInstance(); + tokenMakerFactory.putMapping("text/javaBytecode", "the.bytecode.club.bytecodeviewer.decompilers.bytecode.JavaBytecodeTokenMaker"); + bytecodeViewPanel.textArea.setSyntaxEditingStyle("text/javaBytecode"); + } + bytecodeViewPanel.textArea.setCodeFoldingEnabled(true); + bytecodeViewPanel.textArea.setText(decompiledSource); + bytecodeViewPanel.textArea.setCaretPosition(0); + bytecodeViewPanel.textArea.setEditable(isPanelEditable); - if (isPanelEditable && decompiler == Decompiler.SMALI_DISASSEMBLER) - bytecodeViewPanel.compiler = Compiler.SMALI_ASSEMBLER; - else if (isPanelEditable && decompiler == Decompiler.KRAKATAU_DISASSEMBLER) - bytecodeViewPanel.compiler = Compiler.KRAKATAU_ASSEMBLER; + if (isPanelEditable && decompiler == Decompiler.SMALI_DISASSEMBLER) + bytecodeViewPanel.compiler = Compiler.SMALI_ASSEMBLER; + else if (isPanelEditable && decompiler == Decompiler.KRAKATAU_DISASSEMBLER) + bytecodeViewPanel.compiler = Compiler.KRAKATAU_ASSEMBLER; - String editable = isPanelEditable ? " - " + EDITABLE : ""; - bytecodeViewPanel.textArea.getTextAreaSearchPanel().getTitleHeader().setText(decompiler.getDecompilerName() + editable); + String editable = isPanelEditable ? " - " + EDITABLE : ""; + bytecodeViewPanel.textArea.getTextAreaSearchPanel().getTitleHeader().setText(decompiler.getDecompilerName() + editable); - MyErrorStripe errorStripe = new MyErrorStripe(bytecodeViewPanel.textArea); - bytecodeViewPanel.add(errorStripe, BorderLayout.LINE_END); + MyErrorStripe errorStripe = new MyErrorStripe(bytecodeViewPanel.textArea); + bytecodeViewPanel.add(errorStripe, BorderLayout.LINE_END); - bytecodeViewPanel.revalidate(); - bytecodeViewPanel.repaint(); + bytecodeViewPanel.revalidate(); + bytecodeViewPanel.repaint(); - String classContainerName = viewer.resource.workingName + "-" + decompiler.getDecompilerName(); - ClassFileContainer classFileContainer = viewer.classFiles.get(classContainerName); - bytecodeViewPanel.textArea.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_B, InputEvent.CTRL_DOWN_MASK), "goToAction"); - bytecodeViewPanel.textArea.getActionMap().put("goToAction", new GoToAction(classFileContainer)); + String classContainerName = viewer.resource.workingName + "-" + decompiler.getDecompilerName(); + ClassFileContainer classFileContainer = viewer.classFiles.get(classContainerName); + bytecodeViewPanel.textArea.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_B, InputEvent.CTRL_DOWN_MASK), "goToAction"); + bytecodeViewPanel.textArea.getActionMap().put("goToAction", new GoToAction(classFileContainer)); - bytecodeViewPanel.textArea.addCaretListener(e -> { - if (bytecodeViewPanel.textArea.isFocusOwner()) - { - RSyntaxTextAreaHighlighterEx highlighterEx = (RSyntaxTextAreaHighlighterEx) bytecodeViewPanel.textArea.getHighlighter(); - highlighterEx.clearMarkOccurrencesHighlights(); - RSyntaxTextArea textArea = (RSyntaxTextArea) e.getSource(); - markOccurrences(textArea, viewer.classFiles.get(classContainerName), errorStripe); - } - }); - } + bytecodeViewPanel.textArea.addCaretListener(e -> { + if (bytecodeViewPanel.textArea.isFocusOwner()) + { + RSyntaxTextAreaHighlighterEx highlighterEx = (RSyntaxTextAreaHighlighterEx) bytecodeViewPanel.textArea.getHighlighter(); + highlighterEx.clearMarkOccurrencesHighlights(); + RSyntaxTextArea textArea = (RSyntaxTextArea) e.getSource(); + markOccurrences(textArea, viewer.classFiles.get(classContainerName), errorStripe); + } + }); + } - private void markOccurrences(RSyntaxTextArea textArea, ClassFileContainer classFileContainer, MyErrorStripe errorStripe) - { - RSyntaxTextAreaHighlighterEx highlighterEx = (RSyntaxTextAreaHighlighterEx) textArea.getHighlighter(); - Token token = textArea.modelToToken(textArea.getCaretPosition()); - if (token == null) - { - highlighterEx.clearMarkOccurrencesHighlights(); - errorStripe.refreshMarkers(); - return; - } + private void markOccurrences(RSyntaxTextArea textArea, ClassFileContainer classFileContainer, MyErrorStripe errorStripe) + { + RSyntaxTextAreaHighlighterEx highlighterEx = (RSyntaxTextAreaHighlighterEx) textArea.getHighlighter(); + Token token = textArea.modelToToken(textArea.getCaretPosition()); + if (token == null) + { + highlighterEx.clearMarkOccurrencesHighlights(); + errorStripe.refreshMarkers(); + return; + } - token = TokenUtil.getToken(textArea, token); - if (token == null) - { - highlighterEx.clearMarkOccurrencesHighlights(); - errorStripe.refreshMarkers(); - return; - } + token = TokenUtil.getToken(textArea, token); + if (token == null) + { + highlighterEx.clearMarkOccurrencesHighlights(); + errorStripe.refreshMarkers(); + return; + } - int line = textArea.getCaretLineNumber() + 1; - int column = textArea.getCaretOffsetFromLineStart(); - Token finalToken = token; + int line = textArea.getCaretLineNumber() + 1; + int column = textArea.getCaretOffsetFromLineStart(); + Token finalToken = token; /* Fields */ - markField(textArea, classFileContainer, line, column, finalToken, highlighterEx); + markField(textArea, classFileContainer, line, column, finalToken, highlighterEx); /* Method parameters */ - markMethodParameter(textArea, classFileContainer, line, column, finalToken, highlighterEx); + markMethodParameter(textArea, classFileContainer, line, column, finalToken, highlighterEx); /* Method local variables */ - markMethodLocalVariable(textArea, classFileContainer, line, column, finalToken, highlighterEx); + markMethodLocalVariable(textArea, classFileContainer, line, column, finalToken, highlighterEx); - errorStripe.refreshMarkers(); - } + errorStripe.refreshMarkers(); + } - private void markField(RSyntaxTextArea textArea, ClassFileContainer classFileContainer, int line, int column, Token finalToken, RSyntaxTextAreaHighlighterEx highlighterEx) - { - classFileContainer.fieldMembers.values().forEach(fields -> fields.forEach(field -> { - String owner; - if (field.line == line && field.columnStart - 1 <= column && field.columnEnd >= column) - { - owner = field.owner; - try - { - Element root = textArea.getDocument().getDefaultRootElement(); - for ( - ClassFieldLocation location : - classFileContainer.getFieldLocationsFor(finalToken.getLexeme()) - ) - { - if (Objects.equals(owner, location.owner)) - { - int startOffset = root - .getElement(location.line - 1) - .getStartOffset() + (location.columnStart - 1); - int endOffset = root - .getElement(location.line - 1) - .getStartOffset() + (location.columnEnd - 1); - highlighterEx.addMarkedOccurrenceHighlight( - startOffset, endOffset, new SmartHighlightPainter() - ); - } - } - } catch (BadLocationException ex) - { - throw new RuntimeException(ex); - } - } - })); - } + private void markField(RSyntaxTextArea textArea, ClassFileContainer classFileContainer, int line, int column, Token finalToken, RSyntaxTextAreaHighlighterEx highlighterEx) + { + classFileContainer.fieldMembers.values().forEach(fields -> fields.forEach(field -> { + if (field.line == line && field.columnStart - 1 <= column && field.columnEnd >= column) + { + try + { + Element root = textArea.getDocument().getDefaultRootElement(); + for ( + ClassFieldLocation location : + classFileContainer.getFieldLocationsFor(finalToken.getLexeme()) + ) + { + int startOffset = root + .getElement(location.line - 1) + .getStartOffset() + (location.columnStart - 1); + int endOffset = root + .getElement(location.line - 1) + .getStartOffset() + (location.columnEnd - 1); + highlighterEx.addMarkedOccurrenceHighlight( + startOffset, endOffset, new SmartHighlightPainter() + ); + } + } catch (BadLocationException ex) + { + throw new RuntimeException(ex); + } + } + })); + } - /** - * Search through the text area and mark all occurrences that match the selected token. - * - * @param textArea the text area - * @param classFileContainer the container - * @param line the caret line - * @param column the caret column - * @param finalToken the token - * @param highlighterEx the highlighter - */ - private static void markMethodParameter( - RSyntaxTextArea textArea, - ClassFileContainer classFileContainer, - int line, - int column, - Token finalToken, - RSyntaxTextAreaHighlighterEx highlighterEx - ) - { - classFileContainer.methodParameterMembers.values().forEach(parameters -> parameters.forEach(parameter -> { - String method; - if (parameter.line == line && parameter.columnStart - 1 <= column && parameter.columnEnd >= column) - { - method = parameter.method; - try - { - Element root = textArea.getDocument().getDefaultRootElement(); - for ( - ClassParameterLocation location : - classFileContainer.getParameterLocationsFor(finalToken.getLexeme()) - ) - { - if (Objects.equals(method, location.method)) - { - int startOffset = root - .getElement(location.line - 1) - .getStartOffset() + (location.columnStart - 1); - int endOffset = root - .getElement(location.line - 1) - .getStartOffset() + (location.columnEnd - 1); + /** + * Search through the text area and mark all occurrences that match the selected token. + * + * @param textArea the text area + * @param classFileContainer the container + * @param line the caret line + * @param column the caret column + * @param finalToken the token + * @param highlighterEx the highlighter + */ + private static void markMethodParameter( + RSyntaxTextArea textArea, + ClassFileContainer classFileContainer, + int line, + int column, + Token finalToken, + RSyntaxTextAreaHighlighterEx highlighterEx + ) + { + classFileContainer.methodParameterMembers.values().forEach(parameters -> parameters.forEach(parameter -> { + String method; + if (parameter.line == line && parameter.columnStart - 1 <= column && parameter.columnEnd >= column) + { + method = parameter.method; + try + { + Element root = textArea.getDocument().getDefaultRootElement(); + for ( + ClassParameterLocation location : + classFileContainer.getParameterLocationsFor(finalToken.getLexeme()) + ) + { + if (Objects.equals(method, location.method)) + { + int startOffset = root + .getElement(location.line - 1) + .getStartOffset() + (location.columnStart - 1); + int endOffset = root + .getElement(location.line - 1) + .getStartOffset() + (location.columnEnd - 1); - highlighterEx.addMarkedOccurrenceHighlight( - startOffset, endOffset, new SmartHighlightPainter() - ); - } - } - } catch (BadLocationException ex) - { - throw new RuntimeException(ex); - } - } - })); - } + highlighterEx.addMarkedOccurrenceHighlight( + startOffset, endOffset, new SmartHighlightPainter() + ); + } + } + } catch (BadLocationException ex) + { + throw new RuntimeException(ex); + } + } + })); + } - /** - * Search through the text area and mark all occurrences that match the selected token. - * - * @param textArea the text area - * @param classFileContainer the container - * @param line the caret line - * @param column the caret column - * @param finalToken the token - * @param highlighterEx the highlighter - */ - private static void markMethodLocalVariable( - RSyntaxTextArea textArea, - ClassFileContainer classFileContainer, - int line, - int column, - Token finalToken, - RSyntaxTextAreaHighlighterEx highlighterEx - ) { - classFileContainer.methodLocalMembers.values().forEach(localVariables -> localVariables.forEach(localVariable -> { - String method; - if (localVariable.line == line && localVariable.columnStart - 1 <= column && localVariable.columnEnd >= column) { - method = localVariable.method; - try { - Element root = textArea.getDocument().getDefaultRootElement(); - for ( - ClassLocalVariableLocation location : - classFileContainer.getLocalLocationsFor(finalToken.getLexeme()) - ) { - if (Objects.equals(method, location.method)) { - int startOffset = root - .getElement(location.line - 1) - .getStartOffset() + (location.columnStart - 1); - int endOffset = root - .getElement(location.line - 1) - .getStartOffset() + (location.columnEnd - 1); + /** + * Search through the text area and mark all occurrences that match the selected token. + * + * @param textArea the text area + * @param classFileContainer the container + * @param line the caret line + * @param column the caret column + * @param finalToken the token + * @param highlighterEx the highlighter + */ + private static void markMethodLocalVariable( + RSyntaxTextArea textArea, + ClassFileContainer classFileContainer, + int line, + int column, + Token finalToken, + RSyntaxTextAreaHighlighterEx highlighterEx + ) + { + classFileContainer.methodLocalMembers.values().forEach(localVariables -> localVariables.forEach(localVariable -> { + String method; + if (localVariable.line == line && localVariable.columnStart - 1 <= column && localVariable.columnEnd >= column) + { + method = localVariable.method; + try + { + Element root = textArea.getDocument().getDefaultRootElement(); + for ( + ClassLocalVariableLocation location : + classFileContainer.getLocalLocationsFor(finalToken.getLexeme()) + ) + { + if (Objects.equals(method, location.method)) + { + int startOffset = root + .getElement(location.line - 1) + .getStartOffset() + (location.columnStart - 1); + int endOffset = root + .getElement(location.line - 1) + .getStartOffset() + (location.columnEnd - 1); - highlighterEx.addMarkedOccurrenceHighlight( - startOffset, endOffset, new SmartHighlightPainter() - ); - } - } - } catch (BadLocationException ex) { - throw new RuntimeException(ex); - } - } - })); - } + highlighterEx.addMarkedOccurrenceHighlight( + startOffset, endOffset, new SmartHighlightPainter() + ); + } + } + } catch (BadLocationException ex) + { + throw new RuntimeException(ex); + } + } + })); + } } diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/ClassFileContainer.java b/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/ClassFileContainer.java index 93d7a211..6a9d338b 100644 --- a/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/ClassFileContainer.java +++ b/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/ClassFileContainer.java @@ -26,15 +26,18 @@ public class ClassFileContainer public transient NavigableMap> methodParameterMembers = new TreeMap<>(); public transient NavigableMap> methodLocalMembers = new TreeMap<>(); public transient NavigableMap> methodMembers = new TreeMap<>(); + public transient NavigableMap imports = new TreeMap<>(); public boolean hasBeenParsed = false; public final String className; private final String content; + private final String parentContainer; - public ClassFileContainer(String className, String content) + public ClassFileContainer(String className, String content, String parentContainer) { this.className = className; this.content = content; + this.parentContainer = parentContainer; } public void parse() @@ -50,6 +53,15 @@ public class ClassFileContainer } } + public String getName() { + return this.className.substring(this.className.lastIndexOf('.') + 1); + } + + public String getParentContainer() + { + return this.parentContainer; + } + public void putField(String key, ClassFieldLocation value) { this.fieldMembers.computeIfAbsent(key, v -> new ArrayList<>()).add(value); @@ -89,4 +101,14 @@ public class ClassFileContainer { return methodMembers.getOrDefault(key, new ArrayList<>()); } + + public void putImport(String key, String value) + { + this.imports.put(key, value); + } + + public String getImport(String key) { + String value = this.imports.get(key); + return value + "/" + key; + } } diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/MyVoidVisitor.java b/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/MyVoidVisitor.java index cee420be..2bc3cfee 100644 --- a/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/MyVoidVisitor.java +++ b/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/MyVoidVisitor.java @@ -2,9 +2,11 @@ package the.bytecode.club.bytecodeviewer.resources.classcontainer.parser; import com.github.javaparser.Range; import com.github.javaparser.ast.CompilationUnit; +import com.github.javaparser.ast.ImportDeclaration; import com.github.javaparser.ast.body.*; import com.github.javaparser.ast.expr.*; import com.github.javaparser.ast.stmt.*; +import com.github.javaparser.ast.type.ClassOrInterfaceType; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; import com.github.javaparser.resolution.UnsolvedSymbolException; import com.github.javaparser.resolution.declarations.ResolvedValueDeclaration; @@ -14,6 +16,7 @@ import the.bytecode.club.bytecodeviewer.resources.classcontainer.locations.Class import the.bytecode.club.bytecodeviewer.resources.classcontainer.locations.ClassMethodLocation; import the.bytecode.club.bytecodeviewer.resources.classcontainer.locations.ClassParameterLocation; +import java.util.Objects; import java.util.concurrent.atomic.AtomicInteger; /** @@ -34,7 +37,7 @@ public class MyVoidVisitor extends VoidVisitorAdapter private String getOwner() { - return this.classFileContainer.className.substring(this.classFileContainer.className.lastIndexOf('.') + 1); + return this.classFileContainer.getName(); } private String getMethod(CallableDeclaration method) @@ -66,6 +69,17 @@ public class MyVoidVisitor extends VoidVisitorAdapter }); } + @Override + public void visit(ImportDeclaration n, Object arg) + { + super.visit(n, arg); + Name class_ = n.getName(); + String className = class_.getIdentifier(); + String package_ = Objects.requireNonNull(class_.getQualifier().orElse(null)).asString(); + package_ = package_.replace('.', '/'); + this.classFileContainer.putImport(className, package_); + } + /** * Visit all {@link FieldAccessExpr}s. *

@@ -85,7 +99,6 @@ public class MyVoidVisitor extends VoidVisitorAdapter int line = range.begin.line; int columnStart = range.begin.column; int columnEnd = range.end.column; - this.classFileContainer.putField(fieldName, new ClassFieldLocation(getOwner(), "reference", line, columnStart, columnEnd + 1)); if (n.hasScope()) { @@ -101,15 +114,15 @@ public class MyVoidVisitor extends VoidVisitorAdapter if (scope instanceof NameExpr) { NameExpr nameExpr = (NameExpr) scope; + SimpleName simpleName1 = nameExpr.getName(); + String name1 = simpleName1.getIdentifier(); + Range range1 = nameExpr.getRange().get(); + int line1 = range1.begin.line; + int columnStart1 = range1.begin.column; + int columnEnd1 = range1.end.column; try { ResolvedValueDeclaration vd = nameExpr.resolve(); - SimpleName simpleName1 = nameExpr.getName(); - String name1 = simpleName1.getIdentifier(); - Range range1 = nameExpr.getRange().get(); - int line1 = range1.begin.line; - int columnStart1 = range1.begin.column; - int columnEnd1 = range1.end.column; if (vd.isField()) { this.classFileContainer.putField(name1, new ClassFieldLocation(getOwner(), "reference", line1, columnStart1, columnEnd1 + 1)); @@ -122,12 +135,20 @@ public class MyVoidVisitor extends VoidVisitorAdapter } } catch (UnsolvedSymbolException ignore) { + this.classFileContainer.putField(fieldName, new ClassFieldLocation(name1, "reference", line, columnStart, columnEnd + 1)); } } } } } + @Override + public void visit(ClassOrInterfaceType n, Object arg) + { + super.visit(n, arg); +// System.err.println(n.getName().getIdentifier()); + } + /** * Visit all {@link ConstructorDeclaration}s. *