fix(gui): improve code tabs, sync and related code
This commit is contained in:
@@ -899,13 +899,4 @@ public class JadxSettings {
|
|||||||
public void setSaveOption(SaveOptionEnum saveOption) {
|
public void setSaveOption(SaveOptionEnum saveOption) {
|
||||||
settingsData.setSaveOption(saveOption);
|
settingsData.setSaveOption(saveOption);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isSmaliAreaShowBytecode() {
|
|
||||||
return settingsData.isSmaliAreaShowBytecode();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSmaliAreaShowBytecode(boolean smaliAreaShowBytecode) {
|
|
||||||
settingsData.setSmaliAreaShowBytecode(smaliAreaShowBytecode);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,7 +80,6 @@ public class JadxSettingsData extends JadxGUIArgs {
|
|||||||
private int searchResultsPerPage = 50;
|
private int searchResultsPerPage = 50;
|
||||||
private boolean useAutoSearch = true;
|
private boolean useAutoSearch = true;
|
||||||
private boolean keepCommonDialogOpen = false;
|
private boolean keepCommonDialogOpen = false;
|
||||||
private boolean smaliAreaShowBytecode = false;
|
|
||||||
private LineNumbersMode lineNumbersMode = LineNumbersMode.AUTO;
|
private LineNumbersMode lineNumbersMode = LineNumbersMode.AUTO;
|
||||||
|
|
||||||
private int mainWindowVerticalSplitterLoc = 300;
|
private int mainWindowVerticalSplitterLoc = 300;
|
||||||
@@ -398,14 +397,6 @@ public class JadxSettingsData extends JadxGUIArgs {
|
|||||||
this.showHeapUsageBar = showHeapUsageBar;
|
this.showHeapUsageBar = showHeapUsageBar;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isSmaliAreaShowBytecode() {
|
|
||||||
return smaliAreaShowBytecode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSmaliAreaShowBytecode(boolean smaliAreaShowBytecode) {
|
|
||||||
this.smaliAreaShowBytecode = smaliAreaShowBytecode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getUiFontStr() {
|
public String getUiFontStr() {
|
||||||
return uiFontStr;
|
return uiFontStr;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ public abstract class AbstractCodeContentPanel extends ContentPanel {
|
|||||||
|
|
||||||
public abstract Component getChildrenComponent();
|
public abstract Component getChildrenComponent();
|
||||||
|
|
||||||
|
@Override
|
||||||
public void scrollToPos(int pos) {
|
public void scrollToPos(int pos) {
|
||||||
AbstractCodeArea codeArea = getCodeArea();
|
AbstractCodeArea codeArea = getCodeArea();
|
||||||
if (codeArea != null) {
|
if (codeArea != null) {
|
||||||
|
|||||||
@@ -12,20 +12,27 @@ import javax.swing.JTabbedPane;
|
|||||||
import javax.swing.JToolBar;
|
import javax.swing.JToolBar;
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
import javax.swing.border.EmptyBorder;
|
import javax.swing.border.EmptyBorder;
|
||||||
|
import javax.swing.event.CaretListener;
|
||||||
|
import javax.swing.text.JTextComponent;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import jadx.api.DecompilationMode;
|
import jadx.api.DecompilationMode;
|
||||||
|
import jadx.core.utils.Utils;
|
||||||
import jadx.gui.treemodel.JClass;
|
import jadx.gui.treemodel.JClass;
|
||||||
import jadx.gui.ui.codearea.mode.JCodeMode;
|
import jadx.gui.ui.codearea.mode.JCodeMode;
|
||||||
import jadx.gui.ui.codearea.sync.CodePanelSyncee;
|
import jadx.gui.ui.codearea.sync.CodeAreaSyncee;
|
||||||
import jadx.gui.ui.codearea.sync.CodePanelSyncer;
|
import jadx.gui.ui.codearea.sync.CodeAreaSyncer;
|
||||||
import jadx.gui.ui.codearea.sync.CodePanelSyncerAbstractFactory;
|
import jadx.gui.ui.codearea.sync.CodeAreaSyncerAbstractFactory;
|
||||||
import jadx.gui.ui.codearea.sync.fallback.FallbackSyncer;
|
import jadx.gui.ui.codearea.sync.fallback.FallbackSyncer;
|
||||||
import jadx.gui.ui.panel.IViewStateSupport;
|
import jadx.gui.ui.panel.IViewStateSupport;
|
||||||
import jadx.gui.ui.tab.TabbedPane;
|
import jadx.gui.ui.tab.TabbedPane;
|
||||||
import jadx.gui.utils.NLS;
|
import jadx.gui.utils.NLS;
|
||||||
|
import jadx.gui.utils.UiUtils;
|
||||||
|
import jadx.gui.utils.ui.ListenersHelper;
|
||||||
|
|
||||||
import static com.formdev.flatlaf.FlatClientProperties.TABBED_PANE_TRAILING_COMPONENT;
|
import static com.formdev.flatlaf.FlatClientProperties.TABBED_PANE_TRAILING_COMPONENT;
|
||||||
|
|
||||||
@@ -41,152 +48,92 @@ public final class ClassCodeContentPanel extends AbstractCodeContentPanel implem
|
|||||||
private static final Logger LOG = LoggerFactory.getLogger(ClassCodeContentPanel.class);
|
private static final Logger LOG = LoggerFactory.getLogger(ClassCodeContentPanel.class);
|
||||||
private static final long serialVersionUID = -7229931102504634591L;
|
private static final long serialVersionUID = -7229931102504634591L;
|
||||||
|
|
||||||
private final transient CodePanel javaCodePanel;
|
private final JClass jCls;
|
||||||
private final transient CodePanel smaliCodePanel;
|
private final ListenersHelper<JTextComponent, CaretListener> caretListeners = ListenersHelper.buildForCaretListener();
|
||||||
private final transient JTabbedPane areaTabbedPane;
|
|
||||||
private final AtomicBoolean syncInProgress = new AtomicBoolean(false);
|
private final AtomicBoolean syncInProgress = new AtomicBoolean(false);
|
||||||
|
|
||||||
private boolean splitView = false;
|
private final JTabbedPane leftTabbedPane;
|
||||||
private final JCheckBox splitCheckboxNormal;
|
private @Nullable JTabbedPane rightTabbedPane;
|
||||||
|
private CodePanel javaCodePanel;
|
||||||
|
private CodePanel smaliCodePanel;
|
||||||
|
|
||||||
public ClassCodeContentPanel(TabbedPane panel, JClass jCls) {
|
private boolean isSplitViewActivated = false;
|
||||||
super(panel, jCls);
|
|
||||||
|
|
||||||
javaCodePanel = new CodePanel(new CodeArea(this, jCls));
|
public ClassCodeContentPanel(TabbedPane panel, JClass jClass) {
|
||||||
smaliCodePanel = new CodePanel(new SmaliArea(this, jCls, false));
|
super(panel, jClass);
|
||||||
areaTabbedPane = buildTabbedPane(jCls);
|
jCls = jClass;
|
||||||
splitCheckboxNormal = addCustomControls(areaTabbedPane, false);
|
leftTabbedPane = buildTabbedPane(jClass, true);
|
||||||
|
addCustomControls(leftTabbedPane);
|
||||||
javaCodePanel.load();
|
initView();
|
||||||
initView(false);
|
activateCodePanel(javaCodePanel);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initView(boolean splitViewEnabled) {
|
private void initView() {
|
||||||
splitView = splitViewEnabled;
|
|
||||||
removeAll();
|
removeAll();
|
||||||
setLayout(new BorderLayout());
|
setLayout(new BorderLayout());
|
||||||
setBorder(new EmptyBorder(0, 0, 0, 0));
|
setBorder(new EmptyBorder(0, 0, 0, 0));
|
||||||
if (splitViewEnabled) {
|
if (isSplitViewActivated) {
|
||||||
setupSplitPane();
|
rightTabbedPane = buildTabbedPane(jCls, false);
|
||||||
|
rightTabbedPane.setSelectedIndex(1); // default to Smali
|
||||||
|
|
||||||
|
JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leftTabbedPane, rightTabbedPane);
|
||||||
|
splitPane.setResizeWeight(0.5);
|
||||||
|
add(splitPane);
|
||||||
|
revalidate();
|
||||||
|
repaint();
|
||||||
|
|
||||||
|
// set divider location after layout
|
||||||
|
SwingUtilities.invokeLater(() -> splitPane.setDividerLocation(0.5));
|
||||||
} else {
|
} else {
|
||||||
javaCodePanel.load();
|
disposeTabbedPane(rightTabbedPane);
|
||||||
smaliCodePanel.load();
|
rightTabbedPane = null;
|
||||||
attachSyncListeners(javaCodePanel, smaliCodePanel);
|
add(leftTabbedPane);
|
||||||
areaTabbedPane.setSelectedIndex(0); // default to Java
|
revalidate();
|
||||||
splitCheckboxNormal.setSelected(false);
|
repaint();
|
||||||
add(areaTabbedPane);
|
|
||||||
}
|
}
|
||||||
revalidate();
|
|
||||||
repaint();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void attachSyncListeners(CodePanel javaPanel, CodePanel smaliPanel) {
|
private JTabbedPane buildTabbedPane(JClass jCls, boolean leftPanel) {
|
||||||
javaPanel.getCodeArea().addCaretListener(e -> {
|
|
||||||
if (syncInProgress.get()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
syncInProgress.set(true);
|
|
||||||
syncToMethod(javaPanel, smaliPanel);
|
|
||||||
syncInProgress.set(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
smaliPanel.getCodeArea().addCaretListener(e -> {
|
|
||||||
if (syncInProgress.get()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
syncInProgress.set(true);
|
|
||||||
syncToMethod(smaliPanel, javaPanel);
|
|
||||||
syncInProgress.set(false);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupSplitPane() {
|
|
||||||
JTabbedPane leftTabbedPane = new JTabbedPane(JTabbedPane.BOTTOM);
|
|
||||||
JTabbedPane rightTabbedPane = new JTabbedPane(JTabbedPane.BOTTOM);
|
|
||||||
|
|
||||||
CodePanel[] leftPanels = {
|
|
||||||
new CodePanel(new CodeArea(this, (JClass) node)), // Java
|
|
||||||
new CodePanel(new SmaliArea(this, (JClass) node, false)), // Smali
|
|
||||||
new CodePanel(new SmaliArea(this, (JClass) node, true)), // Smali with Dalvik
|
|
||||||
new CodePanel(new CodeArea(this, new JCodeMode((JClass) node, DecompilationMode.SIMPLE))), // Simple
|
|
||||||
new CodePanel(new CodeArea(this, new JCodeMode((JClass) node, DecompilationMode.FALLBACK))) // Fallback
|
|
||||||
};
|
|
||||||
|
|
||||||
CodePanel[] rightPanels = {
|
|
||||||
new CodePanel(new SmaliArea(this, (JClass) node, false)), // Smali
|
|
||||||
new CodePanel(new SmaliArea(this, (JClass) node, true)), // Smali with Dalvik
|
|
||||||
new CodePanel(new CodeArea(this, (JClass) node)), // Java
|
|
||||||
new CodePanel(new CodeArea(this, new JCodeMode((JClass) node, DecompilationMode.SIMPLE))), // Simple
|
|
||||||
new CodePanel(new CodeArea(this, new JCodeMode((JClass) node, DecompilationMode.FALLBACK))) // Fallback
|
|
||||||
};
|
|
||||||
|
|
||||||
leftTabbedPane.add(leftPanels[0], NLS.str("tabs.code"));
|
|
||||||
leftTabbedPane.add(leftPanels[1], NLS.str("tabs.smali"));
|
|
||||||
leftTabbedPane.add(leftPanels[2], NLS.str("tabs.smali_bytecode"));
|
|
||||||
leftTabbedPane.add(leftPanels[3], "Simple");
|
|
||||||
leftTabbedPane.add(leftPanels[4], "Fallback");
|
|
||||||
|
|
||||||
rightTabbedPane.add(rightPanels[0], NLS.str("tabs.smali"));
|
|
||||||
rightTabbedPane.add(rightPanels[1], NLS.str("tabs.smali_bytecode"));
|
|
||||||
rightTabbedPane.add(rightPanels[2], NLS.str("tabs.code"));
|
|
||||||
rightTabbedPane.add(rightPanels[3], "Simple");
|
|
||||||
rightTabbedPane.add(rightPanels[4], "Fallback");
|
|
||||||
|
|
||||||
for (CodePanel p : leftPanels) {
|
|
||||||
p.load();
|
|
||||||
}
|
|
||||||
for (CodePanel p : rightPanels) {
|
|
||||||
p.load();
|
|
||||||
}
|
|
||||||
|
|
||||||
leftTabbedPane.addChangeListener(e -> ((CodePanel) leftTabbedPane.getSelectedComponent()).load());
|
|
||||||
rightTabbedPane.addChangeListener(e -> ((CodePanel) rightTabbedPane.getSelectedComponent()).load());
|
|
||||||
|
|
||||||
// Attach caret sync between all combinations
|
|
||||||
for (CodePanel leftPanel : leftPanels) {
|
|
||||||
for (CodePanel rightPanel : rightPanels) {
|
|
||||||
attachSyncListeners(leftPanel, rightPanel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create and configure split pane
|
|
||||||
JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leftTabbedPane, rightTabbedPane);
|
|
||||||
splitPane.setResizeWeight(0.5);
|
|
||||||
leftTabbedPane.setMinimumSize(new Dimension(200, 200));
|
|
||||||
rightTabbedPane.setMinimumSize(new Dimension(200, 200));
|
|
||||||
add(splitPane);
|
|
||||||
|
|
||||||
// Set divider location after layout
|
|
||||||
SwingUtilities.invokeLater(() -> splitPane.setDividerLocation(0.5));
|
|
||||||
|
|
||||||
rightTabbedPane.setSelectedIndex(0);
|
|
||||||
addCustomControls(leftTabbedPane, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private JTabbedPane buildTabbedPane(JClass jCls) {
|
|
||||||
JTabbedPane areaTabbedPane = new JTabbedPane(JTabbedPane.BOTTOM);
|
JTabbedPane areaTabbedPane = new JTabbedPane(JTabbedPane.BOTTOM);
|
||||||
areaTabbedPane.setBorder(new EmptyBorder(0, 0, 0, 0));
|
areaTabbedPane.setBorder(new EmptyBorder(0, 0, 0, 0));
|
||||||
areaTabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
|
areaTabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
|
||||||
areaTabbedPane.add(javaCodePanel, NLS.str("tabs.code"));
|
CodePanel javaPanel = new CodePanel(new CodeArea(this, jCls));
|
||||||
areaTabbedPane.add(smaliCodePanel, NLS.str("tabs.smali"));
|
CodePanel smaliPanel = new CodePanel(new SmaliArea(this, jCls, false));
|
||||||
|
if (leftPanel) {
|
||||||
|
this.javaCodePanel = javaPanel;
|
||||||
|
this.smaliCodePanel = smaliPanel;
|
||||||
|
}
|
||||||
|
areaTabbedPane.add(javaPanel, NLS.str("tabs.code"));
|
||||||
|
areaTabbedPane.add(smaliPanel, NLS.str("tabs.smali"));
|
||||||
areaTabbedPane.add(new CodePanel(new SmaliArea(this, jCls, true)), NLS.str("tabs.smali_bytecode"));
|
areaTabbedPane.add(new CodePanel(new SmaliArea(this, jCls, true)), NLS.str("tabs.smali_bytecode"));
|
||||||
areaTabbedPane.add(new CodePanel(new CodeArea(this, new JCodeMode(jCls, DecompilationMode.SIMPLE))), "Simple");
|
areaTabbedPane.add(new CodePanel(new CodeArea(this, new JCodeMode(jCls, DecompilationMode.SIMPLE))), "Simple");
|
||||||
areaTabbedPane.add(new CodePanel(new CodeArea(this, new JCodeMode(jCls, DecompilationMode.FALLBACK))), "Fallback");
|
areaTabbedPane.add(new CodePanel(new CodeArea(this, new JCodeMode(jCls, DecompilationMode.FALLBACK))), "Fallback");
|
||||||
areaTabbedPane.addChangeListener(e -> {
|
areaTabbedPane.setMinimumSize(new Dimension(200, 200));
|
||||||
CodePanel selectedPanel = (CodePanel) areaTabbedPane.getSelectedComponent();
|
areaTabbedPane.addChangeListener(e -> onCodePanelActivation((CodePanel) areaTabbedPane.getSelectedComponent()));
|
||||||
// TODO: to run background load extract ui update to other method
|
|
||||||
selectedPanel.load();
|
|
||||||
// execInBackground(selectedPanel::load);
|
|
||||||
});
|
|
||||||
return areaTabbedPane;
|
return areaTabbedPane;
|
||||||
}
|
}
|
||||||
|
|
||||||
private JCheckBox addCustomControls(JTabbedPane tabbedPane, boolean splitCheckboxInitialState) {
|
private void onCodePanelActivation(CodePanel selectedPanel) {
|
||||||
JCheckBox splitCheckBox = new JCheckBox("Split view", splitCheckboxInitialState);
|
selectedPanel.load();
|
||||||
|
updateSync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void activateCodePanel(CodePanel javaCodePanel) {
|
||||||
|
if (leftTabbedPane.getSelectedComponent() == javaCodePanel) {
|
||||||
|
// already selected, change listener will not be called, run update manually
|
||||||
|
onCodePanelActivation(javaCodePanel);
|
||||||
|
} else {
|
||||||
|
leftTabbedPane.setSelectedComponent(javaCodePanel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addCustomControls(JTabbedPane tabbedPane) {
|
||||||
|
JCheckBox splitCheckBox = new JCheckBox("Split view", false);
|
||||||
splitCheckBox.addItemListener(e -> {
|
splitCheckBox.addItemListener(e -> {
|
||||||
boolean newSplitView = splitCheckBox.isSelected();
|
boolean newSplitView = splitCheckBox.isSelected();
|
||||||
if (splitView != newSplitView) {
|
if (isSplitViewActivated != newSplitView) {
|
||||||
this.initView(newSplitView);
|
isSplitViewActivated = newSplitView;
|
||||||
|
initView();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -197,18 +144,77 @@ public final class ClassCodeContentPanel extends AbstractCodeContentPanel implem
|
|||||||
trailing.addSeparator(new Dimension(50, 1));
|
trailing.addSeparator(new Dimension(50, 1));
|
||||||
trailing.add(splitCheckBox);
|
trailing.add(splitCheckBox);
|
||||||
tabbedPane.putClientProperty(TABBED_PANE_TRAILING_COMPONENT, trailing);
|
tabbedPane.putClientProperty(TABBED_PANE_TRAILING_COMPONENT, trailing);
|
||||||
return splitCheckBox;
|
}
|
||||||
|
|
||||||
|
private void updateSync() {
|
||||||
|
caretListeners.removeAll();
|
||||||
|
if (!isSplitViewActivated) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
AbstractCodeArea leftArea = getCodePanel(leftTabbedPane).getCodeArea();
|
||||||
|
AbstractCodeArea rightArea = getCodePanel(rightTabbedPane).getCodeArea();
|
||||||
|
if (leftArea instanceof CodeAreaSyncee && rightArea instanceof CodeAreaSyncee) {
|
||||||
|
CodeAreaSyncer leftSyncer = buildCodeAreaSyncer(leftArea);
|
||||||
|
CodeAreaSyncer rightSyncer = buildCodeAreaSyncer(rightArea);
|
||||||
|
if (leftSyncer != null && rightSyncer != null) {
|
||||||
|
caretListeners.add(leftArea, e -> syncCodeArea(leftArea, rightArea, leftSyncer));
|
||||||
|
caretListeners.add(rightArea, e -> syncCodeArea(rightArea, leftArea, rightSyncer));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void syncCodeArea(AbstractCodeArea fromArea, AbstractCodeArea toArea, CodeAreaSyncer syncer) {
|
||||||
|
if (syncInProgress.get()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
syncInProgress.set(true);
|
||||||
|
boolean synced = ((CodeAreaSyncee) toArea).sync(syncer);
|
||||||
|
if (!synced) {
|
||||||
|
if (!FallbackSyncer.sync(fromArea, toArea)) {
|
||||||
|
LOG.warn("Code pane area sync not possible");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
LOG.warn("Failed to sync method/class across views: {}", ex.getLocalizedMessage());
|
||||||
|
} finally {
|
||||||
|
syncInProgress.set(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static CodePanel getCodePanel(@Nullable JTabbedPane tabbedPane) {
|
||||||
|
if (tabbedPane == null) {
|
||||||
|
throw new IllegalStateException("tabbedPane is null");
|
||||||
|
}
|
||||||
|
return (CodePanel) tabbedPane.getSelectedComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static @Nullable CodeAreaSyncer buildCodeAreaSyncer(AbstractCodeArea codeArea) {
|
||||||
|
if (codeArea instanceof CodeAreaSyncerAbstractFactory) {
|
||||||
|
return ((CodeAreaSyncerAbstractFactory) codeArea).createCodeAreaSyncer();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void loadSettings() {
|
public void loadSettings() {
|
||||||
javaCodePanel.loadSettings();
|
for (Component component : leftTabbedPane.getComponents()) {
|
||||||
smaliCodePanel.loadSettings();
|
if (component instanceof CodePanel) {
|
||||||
|
((CodePanel) component).loadSettings();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (rightTabbedPane != null) {
|
||||||
|
for (Component component : rightTabbedPane.getComponents()) {
|
||||||
|
if (component instanceof CodePanel) {
|
||||||
|
((CodePanel) component).loadSettings();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
updateUI();
|
updateUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AbstractCodeArea getCodeArea() {
|
public @NotNull AbstractCodeArea getCodeArea() {
|
||||||
return javaCodePanel.getCodeArea();
|
return javaCodePanel.getCodeArea();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,12 +228,12 @@ public final class ClassCodeContentPanel extends AbstractCodeContentPanel implem
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void switchPanel() {
|
public void switchPanel() {
|
||||||
boolean toSmali = areaTabbedPane.getSelectedComponent() == javaCodePanel;
|
boolean toSmali = leftTabbedPane.getSelectedComponent() == javaCodePanel;
|
||||||
areaTabbedPane.setSelectedComponent(toSmali ? smaliCodePanel : javaCodePanel);
|
activateCodePanel(toSmali ? smaliCodePanel : javaCodePanel);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AbstractCodeArea getCurrentCodeArea() {
|
public AbstractCodeArea getCurrentCodeArea() {
|
||||||
return ((CodePanel) areaTabbedPane.getSelectedComponent()).getCodeArea();
|
return ((CodePanel) leftTabbedPane.getSelectedComponent()).getCodeArea();
|
||||||
}
|
}
|
||||||
|
|
||||||
public AbstractCodeArea getSmaliCodeArea() {
|
public AbstractCodeArea getSmaliCodeArea() {
|
||||||
@@ -235,25 +241,40 @@ public final class ClassCodeContentPanel extends AbstractCodeContentPanel implem
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void showSmaliPane() {
|
public void showSmaliPane() {
|
||||||
areaTabbedPane.setSelectedComponent(smaliCodePanel);
|
activateCodePanel(smaliCodePanel);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void saveEditorViewState(EditorViewState viewState) {
|
public void saveEditorViewState(EditorViewState viewState) {
|
||||||
CodePanel codePanel = (CodePanel) areaTabbedPane.getSelectedComponent();
|
CodePanel codePanel = (CodePanel) leftTabbedPane.getSelectedComponent();
|
||||||
int caretPos = codePanel.getCodeArea().getCaretPosition();
|
int caretPos = codePanel.getCodeArea().getCaretPosition();
|
||||||
Point viewPoint = codePanel.getCodeScrollPane().getViewport().getViewPosition();
|
Point viewPoint = codePanel.getCodeScrollPane().getViewport().getViewPosition();
|
||||||
String subPath = codePanel == javaCodePanel ? "java" : "smali";
|
viewState.setSubPath(String.valueOf(leftTabbedPane.getSelectedIndex()));
|
||||||
viewState.setSubPath(subPath);
|
|
||||||
viewState.setCaretPos(caretPos);
|
viewState.setCaretPos(caretPos);
|
||||||
viewState.setViewPoint(viewPoint);
|
viewState.setViewPoint(viewPoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void restoreEditorViewState(EditorViewState viewState) {
|
public void restoreEditorViewState(EditorViewState viewState) {
|
||||||
boolean isJava = viewState.getSubPath().equals("java");
|
UiUtils.uiThreadGuard();
|
||||||
CodePanel activePanel = isJava ? javaCodePanel : smaliCodePanel;
|
String subPath = viewState.getSubPath();
|
||||||
areaTabbedPane.setSelectedComponent(activePanel);
|
CodePanel activePanel = null;
|
||||||
|
if (subPath.equals("java")) {
|
||||||
|
activePanel = javaCodePanel;
|
||||||
|
} else if (subPath.equals("smali")) {
|
||||||
|
activePanel = smaliCodePanel;
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
int index = Utils.safeParseInt(subPath, 0);
|
||||||
|
activePanel = (CodePanel) leftTabbedPane.getComponentAt(index);
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.debug("Failed to restore active code panel: {}", subPath, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (activePanel == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
activateCodePanel(activePanel);
|
||||||
try {
|
try {
|
||||||
activePanel.getCodeScrollPane().getViewport().setViewPosition(viewState.getViewPoint());
|
activePanel.getCodeScrollPane().getViewport().setViewPosition(viewState.getViewPoint());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -273,36 +294,19 @@ public final class ClassCodeContentPanel extends AbstractCodeContentPanel implem
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
javaCodePanel.dispose();
|
caretListeners.removeAll();
|
||||||
smaliCodePanel.dispose();
|
disposeTabbedPane(leftTabbedPane);
|
||||||
for (Component component : areaTabbedPane.getComponents()) {
|
disposeTabbedPane(rightTabbedPane);
|
||||||
if (component instanceof CodePanel) {
|
|
||||||
((CodePanel) component).dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void syncToMethod(CodePanel fromPanel, CodePanel toPanel) {
|
private void disposeTabbedPane(@Nullable JTabbedPane tabbedPane) {
|
||||||
if (!fromPanel.isShowing() || !toPanel.isShowing()) {
|
if (tabbedPane != null) {
|
||||||
return;
|
for (Component component : tabbedPane.getComponents()) {
|
||||||
}
|
if (component instanceof CodePanel) {
|
||||||
try {
|
((CodePanel) component).dispose();
|
||||||
AbstractCodeArea from = fromPanel.getCodeArea();
|
|
||||||
AbstractCodeArea to = toPanel.getCodeArea();
|
|
||||||
toPanel.load();
|
|
||||||
|
|
||||||
if (from instanceof CodePanelSyncerAbstractFactory && to instanceof CodePanelSyncee) {
|
|
||||||
CodePanelSyncer syncer = ((CodePanelSyncerAbstractFactory) from).createCodePanelSyncer();
|
|
||||||
if (((CodePanelSyncee) to).sync(syncer)) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!FallbackSyncer.sync(fromPanel, toPanel)) {
|
|
||||||
LOG.warn("Code pane area sync not possible");
|
|
||||||
}
|
|
||||||
} catch (Exception ex) {
|
|
||||||
LOG.warn("Failed to sync method/class across views: {}", ex.getLocalizedMessage());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import java.awt.Point;
|
|||||||
import java.awt.event.InputEvent;
|
import java.awt.event.InputEvent;
|
||||||
import java.awt.event.MouseAdapter;
|
import java.awt.event.MouseAdapter;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
@@ -15,6 +16,7 @@ import javax.swing.event.PopupMenuEvent;
|
|||||||
import org.fife.ui.rsyntaxtextarea.RSyntaxDocument;
|
import org.fife.ui.rsyntaxtextarea.RSyntaxDocument;
|
||||||
import org.fife.ui.rsyntaxtextarea.Token;
|
import org.fife.ui.rsyntaxtextarea.Token;
|
||||||
import org.fife.ui.rsyntaxtextarea.TokenTypes;
|
import org.fife.ui.rsyntaxtextarea.TokenTypes;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -50,9 +52,9 @@ import jadx.gui.ui.action.ViewRawControlFlowGraphAction;
|
|||||||
import jadx.gui.ui.action.ViewRegionControlFlowGraphAction;
|
import jadx.gui.ui.action.ViewRegionControlFlowGraphAction;
|
||||||
import jadx.gui.ui.action.XposedAction;
|
import jadx.gui.ui.action.XposedAction;
|
||||||
import jadx.gui.ui.codearea.mode.JCodeMode;
|
import jadx.gui.ui.codearea.mode.JCodeMode;
|
||||||
import jadx.gui.ui.codearea.sync.CodePanelSyncee;
|
import jadx.gui.ui.codearea.sync.CodeAreaSyncee;
|
||||||
import jadx.gui.ui.codearea.sync.CodePanelSyncer;
|
import jadx.gui.ui.codearea.sync.CodeAreaSyncer;
|
||||||
import jadx.gui.ui.codearea.sync.CodePanelSyncerAbstractFactory;
|
import jadx.gui.ui.codearea.sync.CodeAreaSyncerAbstractFactory;
|
||||||
import jadx.gui.ui.codearea.sync.JavaSyncer;
|
import jadx.gui.ui.codearea.sync.JavaSyncer;
|
||||||
import jadx.gui.ui.panel.ContentPanel;
|
import jadx.gui.ui.panel.ContentPanel;
|
||||||
import jadx.gui.utils.CaretPositionFix;
|
import jadx.gui.utils.CaretPositionFix;
|
||||||
@@ -67,7 +69,7 @@ import jadx.gui.utils.shortcut.ShortcutsController;
|
|||||||
* The {@link AbstractCodeArea} implementation used for displaying Java code and text based
|
* The {@link AbstractCodeArea} implementation used for displaying Java code and text based
|
||||||
* resources (e.g. AndroidManifest.xml)
|
* resources (e.g. AndroidManifest.xml)
|
||||||
*/
|
*/
|
||||||
public final class CodeArea extends AbstractCodeArea implements CodePanelSyncerAbstractFactory, CodePanelSyncee {
|
public final class CodeArea extends AbstractCodeArea implements CodeAreaSyncerAbstractFactory, CodeAreaSyncee {
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(CodeArea.class);
|
private static final Logger LOG = LoggerFactory.getLogger(CodeArea.class);
|
||||||
|
|
||||||
private static final long serialVersionUID = 6312736869579635796L;
|
private static final long serialVersionUID = 6312736869579635796L;
|
||||||
@@ -335,7 +337,7 @@ public final class CodeArea extends AbstractCodeArea implements CodePanelSyncerA
|
|||||||
/**
|
/**
|
||||||
* Search referenced java node by offset in {@code jCls} code
|
* Search referenced java node by offset in {@code jCls} code
|
||||||
*/
|
*/
|
||||||
public JavaNode getJavaNodeAtOffset(int offset) {
|
public @Nullable JavaNode getJavaNodeAtOffset(int offset) {
|
||||||
if (offset == -1) {
|
if (offset == -1) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -347,7 +349,7 @@ public final class CodeArea extends AbstractCodeArea implements CodePanelSyncerA
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public JavaNode getClosestJavaNode(int offset) {
|
public @Nullable JavaNode getClosestJavaNode(int offset) {
|
||||||
if (offset == -1) {
|
if (offset == -1) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -359,7 +361,7 @@ public final class CodeArea extends AbstractCodeArea implements CodePanelSyncerA
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public JavaNode getEnclosingJavaNode(int offset) {
|
public @Nullable JavaNode getEnclosingJavaNode(int offset) {
|
||||||
if (offset == -1) {
|
if (offset == -1) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -459,20 +461,19 @@ public final class CodeArea extends AbstractCodeArea implements CodePanelSyncerA
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CodePanelSyncer createCodePanelSyncer() {
|
public CodeAreaSyncer createCodeAreaSyncer() {
|
||||||
return new JavaSyncer(this);
|
return new JavaSyncer(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean sync(CodePanelSyncer codePanelSyncer) {
|
public boolean sync(CodeAreaSyncer codeAreaSyncer) {
|
||||||
return codePanelSyncer.syncTo(this);
|
return codeAreaSyncer.syncTo(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
public @Nullable ICodeMetadata getCodeMetadata() {
|
||||||
public ICodeMetadata getCodeMetadata() {
|
|
||||||
ICodeInfo codeInfo = getCodeInfo();
|
ICodeInfo codeInfo = getCodeInfo();
|
||||||
if (!codeInfo.hasMetadata()) {
|
if (!codeInfo.hasMetadata()) {
|
||||||
LOG.warn("No code info metadata for {}", codeInfo.toString());
|
LOG.warn("No code info metadata for {}", codeInfo);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return codeInfo.getCodeMetadata();
|
return codeInfo.getCodeMetadata();
|
||||||
@@ -487,34 +488,44 @@ public final class CodeArea extends AbstractCodeArea implements CodePanelSyncerA
|
|||||||
public Map<Integer, Integer> getLineMappings() {
|
public Map<Integer, Integer> getLineMappings() {
|
||||||
ICodeInfo codeInfo = getCodeInfo();
|
ICodeInfo codeInfo = getCodeInfo();
|
||||||
if (!codeInfo.hasMetadata()) {
|
if (!codeInfo.hasMetadata()) {
|
||||||
LOG.debug("No code info metadata for {}", codeInfo.toString());
|
LOG.debug("No code info metadata for {}", codeInfo);
|
||||||
return Map.of();
|
return Collections.emptyMap();
|
||||||
}
|
}
|
||||||
Map<Integer, Integer> lineMapping = codeInfo.getCodeMetadata().getLineMapping();
|
Map<Integer, Integer> lineMapping = codeInfo.getCodeMetadata().getLineMapping();
|
||||||
if (lineMapping.isEmpty()) {
|
if (lineMapping.isEmpty()) {
|
||||||
LOG.debug("Line mappings are empty for {}", codeInfo.toString());
|
LOG.debug("Line mappings are empty for {}", codeInfo);
|
||||||
return Map.of();
|
return Collections.emptyMap();
|
||||||
}
|
}
|
||||||
return lineMapping;
|
return lineMapping;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Map<Integer, Integer> cachedUniqueLineMappings;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the same as {@link #getLineMappings()} but only if each value (dex debug line number)
|
* Returns the same as {@link #getLineMappings()} but only if each value (dex debug line number)
|
||||||
* appears only once.
|
* appears only once.
|
||||||
* If a value appears more than once then it suggests that methods might share dex debug line
|
* If a value appears more than once then it suggests that methods might share dex debug line
|
||||||
* numbers.
|
* numbers.
|
||||||
* If this is the case then the line mapping cannot be used for code sync correlation.
|
* If this is the case then the line mapping cannot be used for code sync correlation.
|
||||||
*
|
|
||||||
* @return the line mapping
|
|
||||||
*/
|
*/
|
||||||
public Map<Integer, Integer> getFunctionUniqueLineMappings() {
|
public Map<Integer, Integer> getFunctionUniqueLineMappings() {
|
||||||
final var lineMappings = getLineMappings();
|
Map<Integer, Integer> mappings = cachedUniqueLineMappings;
|
||||||
final boolean isAnyRepeated =
|
if (mappings == null) {
|
||||||
lineMappings.values().stream().collect(Collectors.groupingBy(Function.identity(), Collectors.counting())).values().stream()
|
mappings = calcUniqueLineMappings();
|
||||||
.filter(v -> v > 1).findAny().isPresent();
|
cachedUniqueLineMappings = mappings;
|
||||||
|
}
|
||||||
|
return mappings;
|
||||||
|
}
|
||||||
|
|
||||||
|
private @NotNull Map<Integer, Integer> calcUniqueLineMappings() {
|
||||||
|
Map<Integer, Integer> lineMappings = getLineMappings();
|
||||||
|
boolean isAnyRepeated = lineMappings.values().stream()
|
||||||
|
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
|
||||||
|
.values().stream()
|
||||||
|
.anyMatch(v -> v > 1);
|
||||||
if (isAnyRepeated) {
|
if (isAnyRepeated) {
|
||||||
LOG.debug("Dex debug line mappings are not unique");
|
LOG.debug("Dex debug line mappings are not unique");
|
||||||
return Map.of();
|
return Collections.emptyMap();
|
||||||
}
|
}
|
||||||
return lineMappings;
|
return lineMappings;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import java.util.Map;
|
|||||||
|
|
||||||
import javax.swing.AbstractAction;
|
import javax.swing.AbstractAction;
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
import javax.swing.JCheckBoxMenuItem;
|
|
||||||
import javax.swing.KeyStroke;
|
import javax.swing.KeyStroke;
|
||||||
import javax.swing.text.BadLocationException;
|
import javax.swing.text.BadLocationException;
|
||||||
import javax.swing.text.EditorKit;
|
import javax.swing.text.EditorKit;
|
||||||
@@ -39,19 +38,17 @@ import jadx.gui.device.debugger.BreakpointManager;
|
|||||||
import jadx.gui.device.debugger.DbgUtils;
|
import jadx.gui.device.debugger.DbgUtils;
|
||||||
import jadx.gui.jobs.IBackgroundTask;
|
import jadx.gui.jobs.IBackgroundTask;
|
||||||
import jadx.gui.jobs.LoadTask;
|
import jadx.gui.jobs.LoadTask;
|
||||||
import jadx.gui.settings.JadxSettings;
|
|
||||||
import jadx.gui.treemodel.JClass;
|
import jadx.gui.treemodel.JClass;
|
||||||
import jadx.gui.treemodel.JNode;
|
import jadx.gui.treemodel.JNode;
|
||||||
import jadx.gui.treemodel.TextNode;
|
import jadx.gui.treemodel.TextNode;
|
||||||
import jadx.gui.ui.codearea.sync.CodePanelSyncee;
|
import jadx.gui.ui.codearea.sync.CodeAreaSyncee;
|
||||||
import jadx.gui.ui.codearea.sync.CodePanelSyncer;
|
import jadx.gui.ui.codearea.sync.CodeAreaSyncer;
|
||||||
import jadx.gui.ui.codearea.sync.CodePanelSyncerAbstractFactory;
|
import jadx.gui.ui.codearea.sync.CodeAreaSyncerAbstractFactory;
|
||||||
import jadx.gui.ui.codearea.sync.SmaliSyncer;
|
import jadx.gui.ui.codearea.sync.SmaliSyncer;
|
||||||
import jadx.gui.ui.panel.ContentPanel;
|
import jadx.gui.ui.panel.ContentPanel;
|
||||||
import jadx.gui.utils.NLS;
|
|
||||||
import jadx.gui.utils.UiUtils;
|
import jadx.gui.utils.UiUtils;
|
||||||
|
|
||||||
public final class SmaliArea extends AbstractCodeArea implements CodePanelSyncerAbstractFactory, CodePanelSyncee {
|
public final class SmaliArea extends AbstractCodeArea implements CodeAreaSyncerAbstractFactory, CodeAreaSyncee {
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(SmaliArea.class);
|
private static final Logger LOG = LoggerFactory.getLogger(SmaliArea.class);
|
||||||
|
|
||||||
private static final long serialVersionUID = 1334485631870306494L;
|
private static final long serialVersionUID = 1334485631870306494L;
|
||||||
@@ -62,49 +59,22 @@ public final class SmaliArea extends AbstractCodeArea implements CodePanelSyncer
|
|||||||
private static final Color DEBUG_LINE_COLOR = Color.decode("#9c1138");
|
private static final Color DEBUG_LINE_COLOR = Color.decode("#9c1138");
|
||||||
|
|
||||||
private final JNode textNode;
|
private final JNode textNode;
|
||||||
private final JCheckBoxMenuItem cbUseSmaliV2;
|
private final SmaliModel model;
|
||||||
private final boolean allowToggleV2 = false; // add to constructor args to change back
|
|
||||||
private final boolean initialDisplayV2;
|
|
||||||
|
|
||||||
private boolean curVersion = false;
|
SmaliArea(ContentPanel contentPanel, JClass node, boolean showBytecode) {
|
||||||
private SmaliModel model;
|
|
||||||
|
|
||||||
SmaliArea(ContentPanel contentPanel, JClass node, boolean initialDisplayV2) {
|
|
||||||
super(contentPanel, node);
|
super(contentPanel, node);
|
||||||
this.textNode = new TextNode(node.getName());
|
|
||||||
this.initialDisplayV2 = initialDisplayV2;
|
|
||||||
|
|
||||||
setCodeFoldingEnabled(true);
|
setCodeFoldingEnabled(true);
|
||||||
|
this.textNode = new TextNode(node.getName());
|
||||||
cbUseSmaliV2 = new JCheckBoxMenuItem(NLS.str("popup.bytecode_col"), shouldUseSmaliPrinterV2());
|
this.model = showBytecode ? new DebugModel() : new NormalModel(this);
|
||||||
cbUseSmaliV2.setAction(new AbstractAction(NLS.str("popup.bytecode_col")) {
|
setUnLoaded();
|
||||||
private static final long serialVersionUID = -1111111202103170737L;
|
load();
|
||||||
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e) {
|
|
||||||
JadxSettings settings = getContentPanel().getMainWindow().getSettings();
|
|
||||||
settings.setSmaliAreaShowBytecode(!settings.isSmaliAreaShowBytecode());
|
|
||||||
contentPanel.getTabbedPane().getTabs().forEach(v -> {
|
|
||||||
if (v instanceof ClassCodeContentPanel) {
|
|
||||||
switchModel();
|
|
||||||
((ClassCodeContentPanel) v).getSmaliCodeArea().refresh();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
settings.sync();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (allowToggleV2) {
|
|
||||||
getPopupMenu().add(cbUseSmaliV2);
|
|
||||||
}
|
|
||||||
switchModel();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IBackgroundTask getLoadTask() {
|
public IBackgroundTask getLoadTask() {
|
||||||
return new LoadTask<>(
|
return new LoadTask<>(
|
||||||
() -> model.loadCode(),
|
model::loadCode,
|
||||||
code -> {
|
code -> {
|
||||||
curVersion = shouldUseSmaliPrinterV2();
|
|
||||||
model.loadUI(code);
|
model.loadUI(code);
|
||||||
setCaretPosition(0);
|
setCaretPosition(0);
|
||||||
setLoaded();
|
setLoaded();
|
||||||
@@ -135,24 +105,7 @@ public final class SmaliArea extends AbstractCodeArea implements CodePanelSyncer
|
|||||||
return (JClass) node;
|
return (JClass) node;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void switchModel() {
|
|
||||||
if (model != null) {
|
|
||||||
model.unload();
|
|
||||||
}
|
|
||||||
curVersion = shouldUseSmaliPrinterV2();
|
|
||||||
model = curVersion ? new DebugModel() : new NormalModel(this);
|
|
||||||
setUnLoaded();
|
|
||||||
load();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void scrollToDebugPos(int pos) {
|
public void scrollToDebugPos(int pos) {
|
||||||
// don't sync when it's set programmatically.
|
|
||||||
getContentPanel().getMainWindow().getSettings().setSmaliAreaShowBytecode(true);
|
|
||||||
cbUseSmaliV2.setState(shouldUseSmaliPrinterV2());
|
|
||||||
if (!(model instanceof DebugModel)) {
|
|
||||||
switchModel();
|
|
||||||
refresh();
|
|
||||||
}
|
|
||||||
model.togglePosHighlight(pos);
|
model.togglePosHighlight(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,10 +122,6 @@ public final class SmaliArea extends AbstractCodeArea implements CodePanelSyncer
|
|||||||
return getFont();
|
return getFont();
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean shouldUseSmaliPrinterV2() {
|
|
||||||
return getContentPanel().getMainWindow().getSettings().isSmaliAreaShowBytecode();
|
|
||||||
}
|
|
||||||
|
|
||||||
private abstract class SmaliModel {
|
private abstract class SmaliModel {
|
||||||
abstract String loadCode();
|
abstract String loadCode();
|
||||||
|
|
||||||
@@ -196,7 +145,7 @@ public final class SmaliArea extends AbstractCodeArea implements CodePanelSyncer
|
|||||||
}
|
}
|
||||||
|
|
||||||
private class NormalModel extends SmaliModel {
|
private class NormalModel extends SmaliModel {
|
||||||
public NormalModel(SmaliArea smaliArea) {
|
private NormalModel(SmaliArea smaliArea) {
|
||||||
getContentPanel().getMainWindow().getEditorThemeManager().apply(smaliArea);
|
getContentPanel().getMainWindow().getEditorThemeManager().apply(smaliArea);
|
||||||
setSyntaxEditingStyle(SYNTAX_STYLE_SMALI);
|
setSyntaxEditingStyle(SYNTAX_STYLE_SMALI);
|
||||||
}
|
}
|
||||||
@@ -228,7 +177,7 @@ public final class SmaliArea extends AbstractCodeArea implements CodePanelSyncer
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public DebugModel() {
|
private DebugModel() {
|
||||||
loadV2Style();
|
loadV2Style();
|
||||||
setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_ASSEMBLER_6502);
|
setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_ASSEMBLER_6502);
|
||||||
addPropertyChangeListener(SYNTAX_SCHEME_PROPERTY, schemeListener);
|
addPropertyChangeListener(SYNTAX_SCHEME_PROPERTY, schemeListener);
|
||||||
@@ -470,12 +419,12 @@ public final class SmaliArea extends AbstractCodeArea implements CodePanelSyncer
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CodePanelSyncer createCodePanelSyncer() {
|
public CodeAreaSyncer createCodeAreaSyncer() {
|
||||||
return new SmaliSyncer(this);
|
return new SmaliSyncer(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean sync(CodePanelSyncer codePanelSyncer) {
|
public boolean sync(CodeAreaSyncer codeAreaSyncer) {
|
||||||
return codePanelSyncer.syncTo(this);
|
return codeAreaSyncer.syncTo(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package jadx.gui.ui.codearea.sync;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accepts syncer for syncing code areas
|
||||||
|
*/
|
||||||
|
public interface CodeAreaSyncee {
|
||||||
|
boolean sync(CodeAreaSyncer syncer);
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
package jadx.gui.ui.codearea.sync;
|
||||||
|
|
||||||
|
public interface CodeAreaSyncer extends IToJavaSyncStrategy, IToSmaliSyncStrategy {
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package jadx.gui.ui.codearea.sync;
|
||||||
|
|
||||||
|
public interface CodeAreaSyncerAbstractFactory {
|
||||||
|
CodeAreaSyncer createCodeAreaSyncer();
|
||||||
|
}
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
package jadx.gui.ui.codearea.sync;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Accepts a code panel syncer for syncing code areas
|
|
||||||
*/
|
|
||||||
public interface CodePanelSyncee {
|
|
||||||
boolean sync(CodePanelSyncer syncer);
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
package jadx.gui.ui.codearea.sync;
|
|
||||||
|
|
||||||
public interface CodePanelSyncer extends IToJavaSyncStrategy, IToSmaliSyncStrategy {
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
package jadx.gui.ui.codearea.sync;
|
|
||||||
|
|
||||||
public interface CodePanelSyncerAbstractFactory {
|
|
||||||
CodePanelSyncer createCodePanelSyncer();
|
|
||||||
}
|
|
||||||
@@ -25,16 +25,19 @@ public class DebugLineJavaSyncer implements IToSmaliSyncStrategy, IToJavaSyncStr
|
|||||||
public boolean syncTo(CodeArea to) {
|
public boolean syncTo(CodeArea to) {
|
||||||
// This might be any combination between java/simple/fallback
|
// This might be any combination between java/simple/fallback
|
||||||
// We cannot just rely on the current line.
|
// We cannot just rely on the current line.
|
||||||
// Instead try to correlate with line mappings.
|
// Instead, try to correlate with line mappings.
|
||||||
try {
|
try {
|
||||||
int lineIndex = from.getCaretLineNumber();
|
|
||||||
Map<Integer, Integer> toLineMapping = to.getFunctionUniqueLineMappings();
|
Map<Integer, Integer> toLineMapping = to.getFunctionUniqueLineMappings();
|
||||||
|
if (toLineMapping.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int lineIndex = from.getCaretLineNumber();
|
||||||
// lineIndex is 0-indexed whereas the line mappings are based off a 1-index.
|
// lineIndex is 0-indexed whereas the line mappings are based off a 1-index.
|
||||||
Integer sourceLine = getClosestSourceLine(lineIndex + 1);
|
Integer sourceLine = getClosestSourceLine(lineIndex + 1);
|
||||||
if (sourceLine == null) {
|
if (sourceLine == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// find the equivalent linenumber in the 'to' by a reverse lookup from the source line
|
// find the equivalent line number in the 'to' by a reverse lookup from the source line
|
||||||
for (Map.Entry<Integer, Integer> entry : toLineMapping.entrySet()) {
|
for (Map.Entry<Integer, Integer> entry : toLineMapping.entrySet()) {
|
||||||
int toLine = entry.getKey();
|
int toLine = entry.getKey();
|
||||||
int candidateSourceLine = entry.getValue();
|
int candidateSourceLine = entry.getValue();
|
||||||
@@ -85,7 +88,7 @@ public class DebugLineJavaSyncer implements IToSmaliSyncStrategy, IToJavaSyncStr
|
|||||||
private @Nullable Integer getClosestSourceLine(int lineNum) {
|
private @Nullable Integer getClosestSourceLine(int lineNum) {
|
||||||
// get the line mappings of the Java/Simple/Fallback code
|
// get the line mappings of the Java/Simple/Fallback code
|
||||||
Map<Integer, Integer> lineMapping = from.getFunctionUniqueLineMappings();
|
Map<Integer, Integer> lineMapping = from.getFunctionUniqueLineMappings();
|
||||||
if (lineMapping == null || lineMapping.isEmpty()) {
|
if (lineMapping.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
// get the source line from the decomp line
|
// get the source line from the decomp line
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ public class DebugLineSmaliSyncer implements IToJavaSyncStrategy {
|
|||||||
@Override
|
@Override
|
||||||
public boolean syncTo(CodeArea to) {
|
public boolean syncTo(CodeArea to) {
|
||||||
try {
|
try {
|
||||||
// Get the from lines and currentline index
|
// Get the from lines and current line index
|
||||||
int lineIndex = from.getCaretLineNumber();
|
int lineIndex = from.getCaretLineNumber();
|
||||||
String[] fromLines = from.getText().split("\\R");
|
String[] fromLines = from.getText().split("\\R");
|
||||||
if (lineIndex >= fromLines.length) {
|
if (lineIndex >= fromLines.length) {
|
||||||
@@ -62,8 +62,7 @@ public class DebugLineSmaliSyncer implements IToJavaSyncStrategy {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
private @Nullable Anchor findNearestAnchor(int smaliLineNumber, String[] lines) {
|
||||||
private Anchor findNearestAnchor(int smaliLineNumber, String[] lines) {
|
|
||||||
for (int i = smaliLineNumber; i >= 0; i--) {
|
for (int i = smaliLineNumber; i >= 0; i--) {
|
||||||
String trimmedLine = lines[i].trim();
|
String trimmedLine = lines[i].trim();
|
||||||
if (trimmedLine.startsWith(".line")) {
|
if (trimmedLine.startsWith(".line")) {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import org.slf4j.Logger;
|
|||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import jadx.api.metadata.ICodeAnnotation;
|
import jadx.api.metadata.ICodeAnnotation;
|
||||||
|
import jadx.api.metadata.ICodeMetadata;
|
||||||
import jadx.api.metadata.ICodeNodeRef;
|
import jadx.api.metadata.ICodeNodeRef;
|
||||||
import jadx.api.metadata.annotations.InsnCodeOffset;
|
import jadx.api.metadata.annotations.InsnCodeOffset;
|
||||||
import jadx.api.metadata.annotations.NodeDeclareRef;
|
import jadx.api.metadata.annotations.NodeDeclareRef;
|
||||||
@@ -95,27 +96,20 @@ public class InsnOffsetJavaSyncer implements IToJavaSyncStrategy, IToSmaliSyncSt
|
|||||||
if (fromMthRange == null) {
|
if (fromMthRange == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Integer mthDefPos = fromMthRange.getStart().getKey();
|
Integer mthDefPos = fromMthRange.getStart().getKey();
|
||||||
Integer mthEndPos = fromMthRange.getEnd().getKey();
|
Integer mthEndPos = fromMthRange.getEnd().getKey();
|
||||||
|
LOG.debug("InsnOffsetJavaSyncer caretPos = {}, mthDefPos = {}, mthEndPos = {}", caretPos, mthDefPos, mthEndPos);
|
||||||
LOG.debug("InsnOffsetJavaSyncer caretPos = {}", caretPos);
|
|
||||||
LOG.debug("InsnOffsetJavaSyncer mthDefPos = {}", mthDefPos);
|
|
||||||
LOG.debug("InsnOffsetJavaSyncer mthEndPos = {}", mthEndPos);
|
|
||||||
|
|
||||||
CodeMetadataRange fromInsnOffsetRange = findOffsetRange(caretPos, mthDefPos, mthEndPos);
|
CodeMetadataRange fromInsnOffsetRange = findOffsetRange(caretPos, mthDefPos, mthEndPos);
|
||||||
if (fromInsnOffsetRange == null) {
|
if (fromInsnOffsetRange == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
String mthID = getMthRawFullID(mthDefPos);
|
String mthID = getMthRawFullID(mthDefPos);
|
||||||
|
|
||||||
// now search for this range within the target area
|
// now search for this range within the target area
|
||||||
CodeMetadataRange toMthRange = findMethodRange(mthID, to);
|
CodeMetadataRange toMthRange = findMethodRange(mthID, to);
|
||||||
if (toMthRange == null) {
|
if (toMthRange == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// search for the first insn offset
|
// search for the first insn offset
|
||||||
int firstInsnOffset = ((InsnCodeOffset) fromInsnOffsetRange.getStart().getValue()).getOffset();
|
int firstInsnOffset = ((InsnCodeOffset) fromInsnOffsetRange.getStart().getValue()).getOffset();
|
||||||
Integer highlightPosStart = to.getCodeMetadata().searchDown(toMthRange.getStart().getKey(), (offset, ann) -> {
|
Integer highlightPosStart = to.getCodeMetadata().searchDown(toMthRange.getStart().getKey(), (offset, ann) -> {
|
||||||
@@ -149,7 +143,6 @@ public class InsnOffsetJavaSyncer implements IToJavaSyncStrategy, IToSmaliSyncSt
|
|||||||
if (highlightPosEnd == null) {
|
if (highlightPosEnd == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
to.scrollToPos(highlightPosStart);
|
to.scrollToPos(highlightPosStart);
|
||||||
try {
|
try {
|
||||||
CodeSyncHighlighter.defaultHighlighter().highlightRange(to, highlightPosStart, highlightPosEnd);
|
CodeSyncHighlighter.defaultHighlighter().highlightRange(to, highlightPosStart, highlightPosEnd);
|
||||||
@@ -162,9 +155,12 @@ public class InsnOffsetJavaSyncer implements IToJavaSyncStrategy, IToSmaliSyncSt
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
private static @Nullable CodeMetadataRange findMethodRange(String mthFullRawID, CodeArea area) {
|
||||||
private static CodeMetadataRange findMethodRange(String mthFullRawID, CodeArea area) {
|
ICodeMetadata codeMetadata = area.getCodeMetadata();
|
||||||
Map.Entry<Integer, ICodeAnnotation> toMthDecl = area.getCodeMetadata().searchDown(0, (offset, ann) -> {
|
if (codeMetadata == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Map.Entry<Integer, ICodeAnnotation> toMthDecl = codeMetadata.searchDown(0, (offset, ann) -> {
|
||||||
if (ann.getAnnType() != ICodeAnnotation.AnnType.DECLARATION) {
|
if (ann.getAnnType() != ICodeAnnotation.AnnType.DECLARATION) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -179,28 +175,27 @@ public class InsnOffsetJavaSyncer implements IToJavaSyncStrategy, IToSmaliSyncSt
|
|||||||
}
|
}
|
||||||
return new SimpleEntry<>(offset, ann);
|
return new SimpleEntry<>(offset, ann);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (toMthDecl == null) {
|
if (toMthDecl == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
Map.Entry<Integer, ICodeAnnotation> toMthEnd = codeMetadata.searchDown(toMthDecl.getKey(), (offset, ann) -> {
|
||||||
Map.Entry<Integer, ICodeAnnotation> toMthEnd = area.getCodeMetadata().searchDown(toMthDecl.getKey(), (offset, ann) -> {
|
|
||||||
if (ann.getAnnType() != ICodeAnnotation.AnnType.END) {
|
if (ann.getAnnType() != ICodeAnnotation.AnnType.END) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return new SimpleEntry<>(offset, ann);
|
return new SimpleEntry<>(offset, ann);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (toMthEnd == null) {
|
if (toMthEnd == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new CodeMetadataRange(toMthDecl, toMthEnd);
|
return new CodeMetadataRange(toMthDecl, toMthEnd);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
private @Nullable CodeMetadataRange findEnclosingMethodRange(Integer startPos) {
|
||||||
private CodeMetadataRange findEnclosingMethodRange(Integer startPos) {
|
ICodeMetadata codeMetadata = from.getCodeMetadata();
|
||||||
Map.Entry<Integer, ICodeAnnotation> mthDef = from.getCodeMetadata().searchUp(startPos, (offset, ann) -> {
|
if (codeMetadata == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Map.Entry<Integer, ICodeAnnotation> mthDef = codeMetadata.searchUp(startPos, (offset, ann) -> {
|
||||||
if (ann.getAnnType() != ICodeAnnotation.AnnType.DECLARATION) {
|
if (ann.getAnnType() != ICodeAnnotation.AnnType.DECLARATION) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -211,22 +206,18 @@ public class InsnOffsetJavaSyncer implements IToJavaSyncStrategy, IToSmaliSyncSt
|
|||||||
}
|
}
|
||||||
return new SimpleEntry<>(offset, ann);
|
return new SimpleEntry<>(offset, ann);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (mthDef == null) {
|
if (mthDef == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
Map.Entry<Integer, ICodeAnnotation> mthEnd = codeMetadata.searchDown(startPos, (offset, ann) -> {
|
||||||
Map.Entry<Integer, ICodeAnnotation> mthEnd = from.getCodeMetadata().searchDown(startPos, (offset, ann) -> {
|
|
||||||
if (ann.getAnnType() != ICodeAnnotation.AnnType.END) {
|
if (ann.getAnnType() != ICodeAnnotation.AnnType.END) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return new SimpleEntry<>(offset, ann);
|
return new SimpleEntry<>(offset, ann);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (mthEnd == null) {
|
if (mthEnd == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new CodeMetadataRange(mthDef, mthEnd);
|
return new CodeMetadataRange(mthDef, mthEnd);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -234,12 +225,11 @@ public class InsnOffsetJavaSyncer implements IToJavaSyncStrategy, IToSmaliSyncSt
|
|||||||
* Gets a CodeMetadataRange for the from CodeArea where start and end
|
* Gets a CodeMetadataRange for the from CodeArea where start and end
|
||||||
* are InsnCodeOffsets whose offsets are monotonically increasing.
|
* are InsnCodeOffsets whose offsets are monotonically increasing.
|
||||||
*
|
*
|
||||||
* @param - startPos the starting position to start searching from
|
* @param startPos the starting position to start searching from
|
||||||
* @param - mthDefPos the method node decl position enclosing the range
|
* @param mthDefPos the method node decl position enclosing the range
|
||||||
* @param - mthEndPos the method end position enclosing the range
|
* @param mthEndPos the method end position enclosing the range
|
||||||
*/
|
*/
|
||||||
@Nullable
|
private @Nullable CodeMetadataRange findOffsetRange(Integer startPos, Integer mthDefPos, Integer mthEndPos) {
|
||||||
private CodeMetadataRange findOffsetRange(Integer startPos, Integer mthDefPos, Integer mthEndPos) {
|
|
||||||
Map.Entry<Integer, ICodeAnnotation> first = findInsnOffsetBeforePos(startPos, mthDefPos);
|
Map.Entry<Integer, ICodeAnnotation> first = findInsnOffsetBeforePos(startPos, mthDefPos);
|
||||||
Map.Entry<Integer, ICodeAnnotation> second = findInsnOffsetAfterPos(startPos, mthEndPos);
|
Map.Entry<Integer, ICodeAnnotation> second = findInsnOffsetAfterPos(startPos, mthEndPos);
|
||||||
if (first == null || second == null) {
|
if (first == null || second == null) {
|
||||||
@@ -256,29 +246,35 @@ public class InsnOffsetJavaSyncer implements IToJavaSyncStrategy, IToSmaliSyncSt
|
|||||||
return new CodeMetadataRange(first, second);
|
return new CodeMetadataRange(first, second);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
private @Nullable Map.Entry<Integer, ICodeAnnotation> findInsnOffsetBeforePos(Integer startPos, Integer limit) {
|
||||||
private Map.Entry<Integer, ICodeAnnotation> findInsnOffsetBeforePos(Integer startPos, Integer limit) {
|
ICodeMetadata codeMetadata = from.getCodeMetadata();
|
||||||
return from.getCodeMetadata().searchUp(startPos, (offset, ann) -> {
|
if (codeMetadata == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return codeMetadata.searchUp(startPos, (offset, ann) -> {
|
||||||
if (offset <= limit) {
|
if (offset <= limit) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (ann.getAnnType() != ICodeAnnotation.AnnType.OFFSET) {
|
if (ann.getAnnType() != ICodeAnnotation.AnnType.OFFSET) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return new SimpleEntry<Integer, ICodeAnnotation>(offset, ann);
|
return new SimpleEntry<>(offset, ann);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
private @Nullable Map.Entry<Integer, ICodeAnnotation> findInsnOffsetAfterPos(Integer startPos, Integer limit) {
|
||||||
private Map.Entry<Integer, ICodeAnnotation> findInsnOffsetAfterPos(Integer startPos, Integer limit) {
|
ICodeMetadata codeMetadata = from.getCodeMetadata();
|
||||||
return from.getCodeMetadata().searchDown(startPos, (offset, ann) -> {
|
if (codeMetadata == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return codeMetadata.searchDown(startPos, (offset, ann) -> {
|
||||||
if (offset >= limit) {
|
if (offset >= limit) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (ann.getAnnType() != ICodeAnnotation.AnnType.OFFSET) {
|
if (ann.getAnnType() != ICodeAnnotation.AnnType.OFFSET) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return new SimpleEntry<Integer, ICodeAnnotation>(offset, ann);
|
return new SimpleEntry<>(offset, ann);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -298,7 +294,6 @@ public class InsnOffsetJavaSyncer implements IToJavaSyncStrategy, IToSmaliSyncSt
|
|||||||
*
|
*
|
||||||
* @param smaliMethodNode - method of interest
|
* @param smaliMethodNode - method of interest
|
||||||
* @param insnCodeOffsetRange - code offset range from the caret pos
|
* @param insnCodeOffsetRange - code offset range from the caret pos
|
||||||
* @return
|
|
||||||
*/
|
*/
|
||||||
private static List<Integer> getMappedSmaliLines(
|
private static List<Integer> getMappedSmaliLines(
|
||||||
SmaliMethodNode smaliMethodNode,
|
SmaliMethodNode smaliMethodNode,
|
||||||
|
|||||||
@@ -1,17 +1,12 @@
|
|||||||
package jadx.gui.ui.codearea.sync;
|
package jadx.gui.ui.codearea.sync;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import jadx.gui.ui.codearea.CodeArea;
|
import jadx.gui.ui.codearea.CodeArea;
|
||||||
import jadx.gui.ui.codearea.SmaliArea;
|
import jadx.gui.ui.codearea.SmaliArea;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Syncs a Java code panel area (Java/Simple/Fallback) to another area
|
* Syncs a Java code panel area (Java/Simple/Fallback) to another area
|
||||||
*/
|
*/
|
||||||
public class JavaSyncer implements CodePanelSyncer {
|
public class JavaSyncer implements CodeAreaSyncer {
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(JavaSyncer.class);
|
|
||||||
|
|
||||||
private final DebugLineJavaSyncer debugLineSyncer;
|
private final DebugLineJavaSyncer debugLineSyncer;
|
||||||
private final InsnOffsetJavaSyncer insnOffsetSyncer;
|
private final InsnOffsetJavaSyncer insnOffsetSyncer;
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import jadx.gui.ui.codearea.SmaliArea;
|
|||||||
/**
|
/**
|
||||||
* Syncs a Smali code panel area to another area
|
* Syncs a Smali code panel area to another area
|
||||||
*/
|
*/
|
||||||
public class SmaliSyncer implements CodePanelSyncer {
|
public class SmaliSyncer implements CodeAreaSyncer {
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(SmaliSyncer.class);
|
private static final Logger LOG = LoggerFactory.getLogger(SmaliSyncer.class);
|
||||||
|
|
||||||
private final SmaliArea from;
|
private final SmaliArea from;
|
||||||
|
|||||||
@@ -10,17 +10,16 @@ import org.slf4j.LoggerFactory;
|
|||||||
|
|
||||||
import jadx.gui.ui.codearea.AbstractCodeArea;
|
import jadx.gui.ui.codearea.AbstractCodeArea;
|
||||||
import jadx.gui.ui.codearea.CodeArea;
|
import jadx.gui.ui.codearea.CodeArea;
|
||||||
import jadx.gui.ui.codearea.CodePanel;
|
|
||||||
import jadx.gui.ui.codearea.SmaliArea;
|
import jadx.gui.ui.codearea.SmaliArea;
|
||||||
import jadx.gui.ui.codearea.sync.CodeSyncHighlighter;
|
import jadx.gui.ui.codearea.sync.CodeSyncHighlighter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Regex/String based sync strategy of toPanel when clicking in fromPanel
|
* Regex/String based sync strategy of toArea when clicking in fromArea
|
||||||
* Summary of syncing strategy:
|
* Summary of syncing strategy:
|
||||||
* 1) Look for an identifying class member token under the caret position.
|
* 1) Look for an identifying class member token under the caret position.
|
||||||
* 2) If found look for the enclosing method or class declaration.
|
* 2) If found look for the enclosing method or class declaration.
|
||||||
* 3) If the line is a declaration line, find the equivalent line in the other code panel.
|
* 3) If the line is a declaration line, find the equivalent line in the other code panel.
|
||||||
* 4) Otherwise find the nth occurence of the token in the enclosing method/class in the other code
|
* 4) Otherwise find the nth occurrence of the token in the enclosing method/class in the other code
|
||||||
* panel.
|
* panel.
|
||||||
* The following are not yet supported:
|
* The following are not yet supported:
|
||||||
* - generic classes/methods
|
* - generic classes/methods
|
||||||
@@ -31,15 +30,12 @@ import jadx.gui.ui.codearea.sync.CodeSyncHighlighter;
|
|||||||
public class FallbackSyncer {
|
public class FallbackSyncer {
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(FallbackSyncer.class);
|
private static final Logger LOG = LoggerFactory.getLogger(FallbackSyncer.class);
|
||||||
|
|
||||||
public static boolean sync(CodePanel fromPanel, CodePanel toPanel) throws BadLocationException, Exception {
|
public static boolean sync(AbstractCodeArea fromArea, AbstractCodeArea toArea) throws Exception {
|
||||||
LOG.debug("FALLBACK SYNC START");
|
LOG.debug("FALLBACK SYNC START");
|
||||||
try {
|
try {
|
||||||
AbstractCodeArea from = fromPanel.getCodeArea();
|
int caretPos = fromArea.getCaretPosition();
|
||||||
AbstractCodeArea to = toPanel.getCodeArea();
|
int lineIndex = fromArea.getLineOfOffset(caretPos);
|
||||||
|
String[] fromLines = fromArea.getText().split("\\R");
|
||||||
int caretPos = from.getCaretPosition();
|
|
||||||
int lineIndex = from.getLineOfOffset(caretPos);
|
|
||||||
String[] fromLines = from.getText().split("\\R");
|
|
||||||
if (lineIndex >= fromLines.length) {
|
if (lineIndex >= fromLines.length) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -48,7 +44,7 @@ public class FallbackSyncer {
|
|||||||
LOG.debug("Caret line [{}]: {}", caretPos, caretLine);
|
LOG.debug("Caret line [{}]: {}", caretPos, caretLine);
|
||||||
|
|
||||||
// Extract token under caret (string literal or identifier)
|
// Extract token under caret (string literal or identifier)
|
||||||
AbstractCodeAreaToken areaToken = FallbackSyncer.getToken(from, caretPos);
|
AbstractCodeAreaToken areaToken = FallbackSyncer.getToken(fromArea, caretPos);
|
||||||
String token = areaToken.getStr();
|
String token = areaToken.getStr();
|
||||||
LOG.debug("Token at caret: '{}'", token);
|
LOG.debug("Token at caret: '{}'", token);
|
||||||
if (token == null || token.isEmpty()) {
|
if (token == null || token.isEmpty()) {
|
||||||
@@ -60,14 +56,14 @@ public class FallbackSyncer {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return syncToIdentifyingNthOccurence(areaToken, to);
|
return syncToIdentifyingNthOccurence(areaToken, toArea);
|
||||||
} finally {
|
} finally {
|
||||||
LOG.debug("FALLBACK SYNC END");
|
LOG.debug("FALLBACK SYNC END");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function just serves as a way to create the correct Token type
|
// This function just serves as a way to create the correct Token type
|
||||||
// FallbackSyncer should be refactored to use CodePanelSyncer
|
// FallbackSyncer should be refactored to use CodeAreaSyncer
|
||||||
private static AbstractCodeAreaToken getToken(AbstractCodeArea from, int caretPos) throws BadLocationException, FallbackSyncException {
|
private static AbstractCodeAreaToken getToken(AbstractCodeArea from, int caretPos) throws BadLocationException, FallbackSyncException {
|
||||||
if (from instanceof SmaliArea) {
|
if (from instanceof SmaliArea) {
|
||||||
return new SmaliAreaToken((SmaliArea) from, caretPos);
|
return new SmaliAreaToken((SmaliArea) from, caretPos);
|
||||||
@@ -196,7 +192,7 @@ public class FallbackSyncer {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Similar with the function above if refactored to use the CodePanelSyncer Abstraction we can
|
// Similar with the function above if refactored to use the CodeAreaSyncer Abstraction we can
|
||||||
// remove this.
|
// remove this.
|
||||||
private static AbstractCodeAreaLine getLine(AbstractCodeArea area, int lineIndex) throws BadLocationException, FallbackSyncException {
|
private static AbstractCodeAreaLine getLine(AbstractCodeArea area, int lineIndex) throws BadLocationException, FallbackSyncException {
|
||||||
if (area instanceof SmaliArea) {
|
if (area instanceof SmaliArea) {
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package jadx.gui.utils.ui;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.IdentityHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
|
||||||
|
import javax.swing.event.CaretListener;
|
||||||
|
import javax.swing.text.JTextComponent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Track attached UI listeners and remove on request
|
||||||
|
*/
|
||||||
|
public class ListenersHelper<C, L> {
|
||||||
|
|
||||||
|
public static ListenersHelper<JTextComponent, CaretListener> buildForCaretListener() {
|
||||||
|
return new ListenersHelper<>(JTextComponent::addCaretListener, JTextComponent::removeCaretListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Map<C, List<L>> listenerMap = new IdentityHashMap<>();
|
||||||
|
|
||||||
|
private final BiConsumer<C, L> addMth;
|
||||||
|
private final BiConsumer<C, L> removeMth;
|
||||||
|
|
||||||
|
private ListenersHelper(BiConsumer<C, L> add, BiConsumer<C, L> remove) {
|
||||||
|
this.addMth = add;
|
||||||
|
this.removeMth = remove;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void add(C component, L listener) {
|
||||||
|
addMth.accept(component, listener);
|
||||||
|
listenerMap.computeIfAbsent(component, c -> new ArrayList<>()).add(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void removeAll() {
|
||||||
|
listenerMap.forEach((comp, list) -> {
|
||||||
|
for (L l : list) {
|
||||||
|
remove(comp, l);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
listenerMap.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void removeFor(C component) {
|
||||||
|
List<L> list = listenerMap.get(component);
|
||||||
|
if (list != null) {
|
||||||
|
list.forEach(l -> remove(component, l));
|
||||||
|
listenerMap.remove(component);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void remove(C component, L listener) {
|
||||||
|
removeMth.accept(component, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -112,7 +112,7 @@ tabs.closeAllRight=Alles rechts schließen
|
|||||||
#tabs.closeAllLeft=Close All Left
|
#tabs.closeAllLeft=Close All Left
|
||||||
tabs.code=Code
|
tabs.code=Code
|
||||||
tabs.smali=Smali
|
tabs.smali=Smali
|
||||||
tabs.smali_bytecode=Smali+Bytecode
|
#tabs.smali_bytecode=Bytecode
|
||||||
|
|
||||||
nav.back=Zurück
|
nav.back=Zurück
|
||||||
nav.forward=Vorwärts
|
nav.forward=Vorwärts
|
||||||
@@ -360,7 +360,6 @@ msg.cant_add_comment=Kann hier keinen Kommentar hinzufügen
|
|||||||
|
|
||||||
#methods_dialog.title=Select methods
|
#methods_dialog.title=Select methods
|
||||||
|
|
||||||
popup.bytecode_col=Dalvik-Bytecode anzeigen
|
|
||||||
popup.line_wrap=Zeilenumbruch
|
popup.line_wrap=Zeilenumbruch
|
||||||
popup.undo=Rückgängig
|
popup.undo=Rückgängig
|
||||||
popup.redo=Wiederholen
|
popup.redo=Wiederholen
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ tabs.closeAllRight=Close All Right
|
|||||||
tabs.closeAllLeft=Close All Left
|
tabs.closeAllLeft=Close All Left
|
||||||
tabs.code=Code
|
tabs.code=Code
|
||||||
tabs.smali=Smali
|
tabs.smali=Smali
|
||||||
tabs.smali_bytecode=Smali+Bytecode
|
tabs.smali_bytecode=Bytecode
|
||||||
|
|
||||||
nav.back=Back
|
nav.back=Back
|
||||||
nav.forward=Forward
|
nav.forward=Forward
|
||||||
@@ -360,7 +360,6 @@ msg.non_displayable_chars.title=Undisplayed Strings
|
|||||||
|
|
||||||
methods_dialog.title=Select methods
|
methods_dialog.title=Select methods
|
||||||
|
|
||||||
popup.bytecode_col=Show Dalvik Bytecode
|
|
||||||
popup.line_wrap=Line Wrap
|
popup.line_wrap=Line Wrap
|
||||||
popup.undo=Undo
|
popup.undo=Undo
|
||||||
popup.redo=Redo
|
popup.redo=Redo
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ tabs.closeAllRight=Cierra todo a la derecha
|
|||||||
#tabs.closeAllLeft=Close All Left
|
#tabs.closeAllLeft=Close All Left
|
||||||
#tabs.code=Code
|
#tabs.code=Code
|
||||||
#tabs.smali=Smali
|
#tabs.smali=Smali
|
||||||
#tabs.smali_bytecode=Smali+Bytecode
|
#tabs.smali_bytecode=Bytecode
|
||||||
|
|
||||||
nav.back=Atrás
|
nav.back=Atrás
|
||||||
nav.forward=Adelante
|
nav.forward=Adelante
|
||||||
@@ -360,7 +360,6 @@ msg.language_changed=El nuevo idioma se mostrará la próxima vez que la aplicac
|
|||||||
|
|
||||||
#methods_dialog.title=Select methods
|
#methods_dialog.title=Select methods
|
||||||
|
|
||||||
#popup.bytecode_col=Show Dalvik Bytecode
|
|
||||||
#popup.line_wrap=Line Wrap
|
#popup.line_wrap=Line Wrap
|
||||||
popup.undo=Deshacer
|
popup.undo=Deshacer
|
||||||
popup.redo=Rehacer
|
popup.redo=Rehacer
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ tabs.closeAllRight=Tutup Semua yang Kanan
|
|||||||
#tabs.closeAllLeft=Close All Left
|
#tabs.closeAllLeft=Close All Left
|
||||||
tabs.code=Kode
|
tabs.code=Kode
|
||||||
tabs.smali=Smali
|
tabs.smali=Smali
|
||||||
tabs.smali_bytecode=Smali+Bytecode
|
#tabs.smali_bytecode=Bytecode
|
||||||
|
|
||||||
nav.back=Kembali
|
nav.back=Kembali
|
||||||
nav.forward=Maju
|
nav.forward=Maju
|
||||||
@@ -360,7 +360,6 @@ msg.cant_add_comment=Tidak dapat menambahkan komentar di sini
|
|||||||
|
|
||||||
#methods_dialog.title=Select methods
|
#methods_dialog.title=Select methods
|
||||||
|
|
||||||
popup.bytecode_col=Tampilkan Bytecode Dalvik
|
|
||||||
popup.line_wrap=Baris Wrap
|
popup.line_wrap=Baris Wrap
|
||||||
popup.undo=Kembalikan
|
popup.undo=Kembalikan
|
||||||
popup.redo=Ulang
|
popup.redo=Ulang
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ tabs.closeAllRight=오른쪽의 모든 것을 닫으십시오
|
|||||||
#tabs.closeAllLeft=Close All Left
|
#tabs.closeAllLeft=Close All Left
|
||||||
tabs.code=코드
|
tabs.code=코드
|
||||||
tabs.smali=Smali
|
tabs.smali=Smali
|
||||||
tabs.smali_bytecode=Smali+Bytecode
|
#tabs.smali_bytecode=Bytecode
|
||||||
|
|
||||||
nav.back=뒤로
|
nav.back=뒤로
|
||||||
nav.forward=앞으로
|
nav.forward=앞으로
|
||||||
@@ -360,7 +360,6 @@ msg.cant_add_comment=여기에 주석을 추가할수 없음
|
|||||||
|
|
||||||
#methods_dialog.title=Select methods
|
#methods_dialog.title=Select methods
|
||||||
|
|
||||||
popup.bytecode_col=Dalvik Bytecode 보이기
|
|
||||||
popup.line_wrap=줄 바꿈
|
popup.line_wrap=줄 바꿈
|
||||||
popup.undo=실행 취소
|
popup.undo=실행 취소
|
||||||
popup.redo=다시 실행
|
popup.redo=다시 실행
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ tabs.closeAllRight=Feche tudo à direita
|
|||||||
#tabs.closeAllLeft=Close All Left
|
#tabs.closeAllLeft=Close All Left
|
||||||
tabs.code=Código
|
tabs.code=Código
|
||||||
tabs.smali=Smali
|
tabs.smali=Smali
|
||||||
tabs.smali_bytecode=Smali+Bytecode
|
#tabs.smali_bytecode=Bytecode
|
||||||
|
|
||||||
nav.back=Voltar
|
nav.back=Voltar
|
||||||
nav.forward=Avançar
|
nav.forward=Avançar
|
||||||
@@ -360,7 +360,6 @@ msg.cant_add_comment=Não é possível adicionar comentários aqui
|
|||||||
|
|
||||||
#methods_dialog.title=Select methods
|
#methods_dialog.title=Select methods
|
||||||
|
|
||||||
popup.bytecode_col=Mostrar Dalvik Bytecode
|
|
||||||
popup.line_wrap=Quebra de linha
|
popup.line_wrap=Quebra de linha
|
||||||
popup.undo=Desfazer
|
popup.undo=Desfazer
|
||||||
popup.redo=Refazer
|
popup.redo=Refazer
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ tabs.closeAllRight=Закройте все справа
|
|||||||
#tabs.closeAllLeft=Close All Left
|
#tabs.closeAllLeft=Close All Left
|
||||||
tabs.code=Код
|
tabs.code=Код
|
||||||
tabs.smali=Smali
|
tabs.smali=Smali
|
||||||
tabs.smali_bytecode=Smali+Bytecode
|
#tabs.smali_bytecode=Bytecode
|
||||||
|
|
||||||
nav.back=Назад
|
nav.back=Назад
|
||||||
nav.forward=Вперед
|
nav.forward=Вперед
|
||||||
@@ -360,7 +360,6 @@ msg.cant_add_comment=Невозможно добавить комментари
|
|||||||
|
|
||||||
#methods_dialog.title=Select methods
|
#methods_dialog.title=Select methods
|
||||||
|
|
||||||
popup.bytecode_col=Показать Dalvik байткод
|
|
||||||
popup.line_wrap=Перенос строк
|
popup.line_wrap=Перенос строк
|
||||||
popup.undo=Отменить
|
popup.undo=Отменить
|
||||||
popup.redo=Вернуть
|
popup.redo=Вернуть
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ tabs.closeAllRight=关闭右边的所有
|
|||||||
tabs.closeAllLeft=关闭左边的所有
|
tabs.closeAllLeft=关闭左边的所有
|
||||||
tabs.code=代码
|
tabs.code=代码
|
||||||
tabs.smali=Smali
|
tabs.smali=Smali
|
||||||
tabs.smali_bytecode=Smali+Bytecode
|
#tabs.smali_bytecode=Bytecode
|
||||||
|
|
||||||
nav.back=后退
|
nav.back=后退
|
||||||
nav.forward=前进
|
nav.forward=前进
|
||||||
@@ -360,7 +360,6 @@ msg.non_displayable_chars.title=未显示的字符串
|
|||||||
|
|
||||||
methods_dialog.title=选择方法
|
methods_dialog.title=选择方法
|
||||||
|
|
||||||
popup.bytecode_col=显示Dalvik字节码
|
|
||||||
popup.line_wrap=自动换行
|
popup.line_wrap=自动换行
|
||||||
popup.undo=撤销
|
popup.undo=撤销
|
||||||
popup.redo=重做
|
popup.redo=重做
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ tabs.closeAllRight=關閉右邊的所有
|
|||||||
#tabs.closeAllLeft=Close All Left
|
#tabs.closeAllLeft=Close All Left
|
||||||
tabs.code=程式碼
|
tabs.code=程式碼
|
||||||
tabs.smali=Smali
|
tabs.smali=Smali
|
||||||
tabs.smali_bytecode=Smali+Bytecode
|
#tabs.smali_bytecode=Bytecode
|
||||||
|
|
||||||
nav.back=返回
|
nav.back=返回
|
||||||
nav.forward=向前
|
nav.forward=向前
|
||||||
@@ -360,7 +360,6 @@ msg.cant_add_comment=無法在此新增註解
|
|||||||
|
|
||||||
methods_dialog.title=選擇方法
|
methods_dialog.title=選擇方法
|
||||||
|
|
||||||
popup.bytecode_col=顯示 Dalvik 位元組碼
|
|
||||||
popup.line_wrap=自動換行
|
popup.line_wrap=自動換行
|
||||||
popup.undo=復原
|
popup.undo=復原
|
||||||
popup.redo=重做
|
popup.redo=重做
|
||||||
|
|||||||
Reference in New Issue
Block a user