fix(plugins): use loaded mapping tree on export (#1732)

This commit is contained in:
Skylot
2023-04-14 21:05:18 +01:00
parent 1d6ebed7e5
commit 2e652b4219
14 changed files with 336 additions and 189 deletions
@@ -2,8 +2,20 @@ package jadx.api.plugins.input.data.attributes;
/**
* Marker interface for attribute type.
* Similar to enumeration but extensible.
* <p>
* Used for attach attribute instance class information (T).
* T - class of attribute instance
* <p>
* To create new one define static field like this:
* {@code
* static final IJadxAttrType<AttrTypeClass> ATTR_TYPE = IJadxAttrType.create();
* }
*/
public interface IJadxAttrType<T extends IJadxAttribute> {
static <A extends IJadxAttribute> IJadxAttrType<A> create() {
return new IJadxAttrType<>() {
};
}
}
@@ -32,6 +32,7 @@ import jadx.api.plugins.pass.types.JadxPreparePass;
import jadx.core.Jadx;
import jadx.core.ProcessClass;
import jadx.core.clsp.ClspGraph;
import jadx.core.dex.attributes.AttributeStorage;
import jadx.core.dex.info.ClassInfo;
import jadx.core.dex.info.ConstStorage;
import jadx.core.dex.info.FieldInfo;
@@ -75,6 +76,7 @@ public class RootNode {
private final TypeUpdate typeUpdate;
private final MethodUtils methodUtils;
private final TypeUtils typeUtils;
private final AttributeStorage attributes = new AttributeStorage();
private final Map<ClassInfo, ClassNode> clsMap = new HashMap<>();
private final Map<String, ClassNode> rawClsMap = new HashMap<>();
@@ -701,6 +703,10 @@ public class RootNode {
return typeUtils;
}
public AttributeStorage getAttributes() {
return attributes;
}
public boolean isProto() {
return isProto;
}
@@ -1,4 +1,4 @@
package jadx.gui.treemodel;
package jadx.gui.plugins.mappings;
import java.nio.file.Path;
@@ -15,6 +15,8 @@ import jadx.api.ICodeInfo;
import jadx.api.impl.SimpleCodeInfo;
import jadx.core.utils.exceptions.JadxRuntimeException;
import jadx.core.utils.files.FileUtils;
import jadx.gui.treemodel.JClass;
import jadx.gui.treemodel.JEditableNode;
import jadx.gui.ui.MainWindow;
import jadx.gui.ui.TabbedPane;
import jadx.gui.ui.codearea.CodeContentPanel;
@@ -63,7 +65,8 @@ public class JInputMapping extends JEditableNode {
@Override
public JPopupMenu onTreePopupMenu(MainWindow mainWindow) {
JPopupMenu menu = new JPopupMenu();
menu.add(new SimpleMenuItem(NLS.str("popup.remove"), mainWindow::closeMappingsAndRemoveFromProject));
menu.add(new SimpleMenuItem(NLS.str("popup.remove"),
() -> mainWindow.getRenameMappings().closeMappingsAndRemoveFromProject()));
return menu;
}
@@ -0,0 +1,231 @@
package jadx.gui.plugins.mappings;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.function.Consumer;
import java.util.stream.Stream;
import javax.swing.Action;
import javax.swing.JFileChooser;
import javax.swing.JMenu;
import javax.swing.JOptionPane;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.fabricmc.mappingio.MappingReader;
import net.fabricmc.mappingio.format.MappingFormat;
import jadx.api.args.UserRenamesMappingsMode;
import jadx.api.plugins.utils.CommonFileUtils;
import jadx.core.dex.nodes.RootNode;
import jadx.core.utils.Utils;
import jadx.gui.jobs.TaskStatus;
import jadx.gui.settings.JadxProject;
import jadx.gui.settings.JadxSettings;
import jadx.gui.ui.MainWindow;
import jadx.gui.ui.filedialog.FileDialogWrapper;
import jadx.gui.ui.filedialog.FileOpenMode;
import jadx.gui.utils.NLS;
import jadx.gui.utils.UiUtils;
import jadx.gui.utils.ui.ActionHandler;
import jadx.plugins.mappings.RenameMappingsOptions;
import jadx.plugins.mappings.save.MappingExporter;
public class RenameMappingsGui {
private static final Logger LOG = LoggerFactory.getLogger(RenameMappingsGui.class);
private final MainWindow mainWindow;
// private MappingFormat currentMappingFormat;
private boolean renamesChanged = false;
private transient JMenu openMappingsMenu;
private transient Action saveMappingsAction;
private transient JMenu saveMappingsAsMenu;
private transient Action closeMappingsAction;
public RenameMappingsGui(MainWindow mainWindow) {
this.mainWindow = mainWindow;
mainWindow.addLoadListener(this::onLoad);
}
public void addMenuActions(JMenu menu) {
openMappingsMenu = new JMenu(NLS.str("file.open_mappings"));
openMappingsMenu.add(new ActionHandler(ev -> openMappings(MappingFormat.PROGUARD, true)).withNameAndDesc("Proguard (inverted)"));
openMappingsMenu.add(new ActionHandler(ev -> openMappings(MappingFormat.PROGUARD, false)).withNameAndDesc("Proguard"));
saveMappingsAction = new ActionHandler(this::saveMappings).withNameAndDesc(NLS.str("file.save_mappings"));
saveMappingsAsMenu = new JMenu(NLS.str("file.save_mappings_as"));
for (MappingFormat mappingFormat : MappingFormat.values()) {
if (mappingFormat != MappingFormat.PROGUARD) {
openMappingsMenu.add(new ActionHandler(ev -> openMappings(mappingFormat, false))
.withNameAndDesc(mappingFormat.name));
}
saveMappingsAsMenu.add(new ActionHandler(ev -> saveMappingsAs(mappingFormat))
.withNameAndDesc(mappingFormat.name));
}
closeMappingsAction = new ActionHandler(ev -> closeMappingsAndRemoveFromProject())
.withNameAndDesc(NLS.str("file.close_mappings"));
menu.addSeparator();
menu.add(openMappingsMenu);
menu.add(saveMappingsAction);
menu.add(saveMappingsAsMenu);
menu.add(closeMappingsAction);
}
private boolean onLoad(boolean loaded) {
renamesChanged = false;
if (loaded) {
RootNode rootNode = mainWindow.getWrapper().getRootNode();
rootNode.registerCodeDataUpdateListener(codeData -> onRename());
} else {
// project or window close
JadxProject project = mainWindow.getProject();
JadxSettings settings = mainWindow.getSettings();
if (project.getMappingsPath() != null
&& settings.getUserRenamesMappingsMode() == UserRenamesMappingsMode.READ_AND_AUTOSAVE_BEFORE_CLOSING) {
saveMappings();
}
}
return false;
}
private void onRename() {
JadxProject project = mainWindow.getProject();
JadxSettings settings = mainWindow.getSettings();
if (project.getMappingsPath() != null
&& settings.getUserRenamesMappingsMode() == UserRenamesMappingsMode.READ_AND_AUTOSAVE_EVERY_CHANGE) {
saveMappings();
} else {
renamesChanged = true;
UiUtils.uiRun(mainWindow::update);
}
}
public void onUpdate(boolean loaded) {
JadxProject project = mainWindow.getProject();
openMappingsMenu.setEnabled(loaded);
saveMappingsAction.setEnabled(loaded && renamesChanged && project.getMappingsPath() != null);
saveMappingsAsMenu.setEnabled(loaded);
closeMappingsAction.setEnabled(project.getMappingsPath() != null);
}
private void openMappings(MappingFormat mappingFormat, boolean inverted) {
FileDialogWrapper fileDialog = new FileDialogWrapper(mainWindow, FileOpenMode.CUSTOM_OPEN);
fileDialog.setTitle(NLS.str("file.open_mappings"));
if (mappingFormat.hasSingleFile()) {
fileDialog.setFileExtList(Collections.singletonList(mappingFormat.fileExt));
fileDialog.setSelectionMode(JFileChooser.FILES_ONLY);
} else {
fileDialog.setSelectionMode(JFileChooser.DIRECTORIES_ONLY);
}
List<Path> selectedPaths = fileDialog.show();
if (selectedPaths.size() != 1) {
return;
}
Path filePath = selectedPaths.get(0);
LOG.info("Loading mappings from: {}", filePath.toAbsolutePath());
JadxProject project = mainWindow.getProject();
project.setMappingsPath(filePath);
project.updatePluginOptions(options -> {
options.put(RenameMappingsOptions.FORMAT_OPT, mappingFormat.name());
options.put(RenameMappingsOptions.INVERT_OPT, inverted ? "yes" : "no");
});
mainWindow.reopen();
}
public void closeMappingsAndRemoveFromProject() {
mainWindow.getProject().setMappingsPath(null);
mainWindow.reopen();
}
private void saveMappings() {
renamesChanged = false;
saveInBackground(getCurrentMappingFormat(),
mainWindow.getProject().getMappingsPath(),
s -> mainWindow.update());
}
private void saveMappingsAs(MappingFormat mappingFormat) {
FileDialogWrapper fileDialog = new FileDialogWrapper(mainWindow, FileOpenMode.CUSTOM_SAVE);
fileDialog.setTitle(NLS.str("file.save_mappings_as"));
if (mappingFormat.hasSingleFile()) {
Path currentDir = Utils.getOrElse(fileDialog.getCurrentDir(), CommonFileUtils.CWD_PATH);
fileDialog.setSelectedFile(currentDir.resolve("mappings." + mappingFormat.fileExt));
fileDialog.setFileExtList(Collections.singletonList(mappingFormat.fileExt));
fileDialog.setSelectionMode(JFileChooser.FILES_ONLY);
} else {
fileDialog.setSelectionMode(JFileChooser.DIRECTORIES_ONLY);
}
List<Path> selectedPaths = fileDialog.show();
if (selectedPaths.size() != 1) {
return;
}
Path selectedPath = selectedPaths.get(0);
Path savePath;
// Append file extension if missing
if (mappingFormat.hasSingleFile()
&& !selectedPath.getFileName().toString().toLowerCase(Locale.ROOT).endsWith(mappingFormat.fileExt)) {
savePath = selectedPath.resolveSibling(selectedPath.getFileName() + "." + mappingFormat.fileExt);
} else {
savePath = selectedPath;
}
// If the target file already exists (and it's not an empty directory), show an overwrite
// confirmation
if (Files.exists(savePath)) {
boolean emptyDir = false;
try (Stream<Path> entries = Files.list(savePath)) {
emptyDir = entries.findFirst().isEmpty();
} catch (IOException ignored) {
}
if (!emptyDir) {
int res = JOptionPane.showConfirmDialog(
mainWindow,
NLS.str("confirm.save_as_message", savePath.getFileName()),
NLS.str("confirm.save_as_title"),
JOptionPane.YES_NO_OPTION);
if (res == JOptionPane.NO_OPTION) {
return;
}
}
}
LOG.info("Saving mappings to: {}", savePath.toAbsolutePath());
JadxProject project = mainWindow.getProject();
project.setMappingsPath(savePath);
project.updatePluginOptions(options -> {
options.put(RenameMappingsOptions.FORMAT_OPT, mappingFormat.name());
options.put(RenameMappingsOptions.INVERT_OPT, "no");
});
saveInBackground(mappingFormat, savePath, s -> mainWindow.reopen());
}
private void saveInBackground(MappingFormat mappingFormat, Path savePath, Consumer<TaskStatus> onFinishUiRunnable) {
mainWindow.getBackgroundExecutor().execute(NLS.str("progress.save_mappings"),
() -> new MappingExporter(mainWindow.getWrapper().getRootNode())
.exportMappings(savePath, mainWindow.getProject().getCodeData(), mappingFormat),
onFinishUiRunnable);
}
private MappingFormat getCurrentMappingFormat() {
JadxProject project = mainWindow.getProject();
String fmtStr = project.getPluginOption(RenameMappingsOptions.FORMAT_OPT);
if (fmtStr != null) {
return MappingFormat.valueOf(fmtStr);
}
Path mappingsPath = project.getMappingsPath();
try {
return MappingReader.detectFormat(mappingsPath);
} catch (IOException e) {
throw new RuntimeException("Failed to detect mapping format for: " + mappingsPath);
}
}
}
@@ -5,10 +5,10 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jadx.api.JavaClass;
import jadx.gui.plugins.mappings.JInputMapping;
import jadx.gui.settings.data.TabViewState;
import jadx.gui.settings.data.ViewPoint;
import jadx.gui.treemodel.JClass;
import jadx.gui.treemodel.JInputMapping;
import jadx.gui.treemodel.JInputScript;
import jadx.gui.treemodel.JNode;
import jadx.gui.treemodel.JResource;
@@ -11,6 +11,7 @@ import javax.swing.ImageIcon;
import jadx.core.utils.files.FileUtils;
import jadx.gui.JadxWrapper;
import jadx.gui.plugins.mappings.JInputMapping;
import jadx.gui.settings.JadxProject;
import jadx.gui.utils.NLS;
import jadx.gui.utils.UiUtils;
@@ -24,7 +24,6 @@ import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.AffineTransform;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.file.Files;
@@ -35,11 +34,9 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Stream;
import javax.swing.AbstractAction;
import javax.swing.Action;
@@ -78,19 +75,15 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ch.qos.logback.classic.Level;
import net.fabricmc.mappingio.MappingReader;
import net.fabricmc.mappingio.format.MappingFormat;
import jadx.api.JadxArgs;
import jadx.api.JavaNode;
import jadx.api.ResourceFile;
import jadx.api.args.UserRenamesMappingsMode;
import jadx.api.plugins.utils.CommonFileUtils;
import jadx.core.Jadx;
import jadx.core.export.TemplateFile;
import jadx.core.utils.ListUtils;
import jadx.core.utils.StringUtils;
import jadx.core.utils.Utils;
import jadx.core.utils.exceptions.JadxRuntimeException;
import jadx.core.utils.files.FileUtils;
import jadx.gui.JadxWrapper;
@@ -102,6 +95,7 @@ import jadx.gui.jobs.TaskStatus;
import jadx.gui.logs.LogCollector;
import jadx.gui.logs.LogOptions;
import jadx.gui.logs.LogPanel;
import jadx.gui.plugins.mappings.RenameMappingsGui;
import jadx.gui.plugins.quark.QuarkDialog;
import jadx.gui.settings.JadxProject;
import jadx.gui.settings.JadxSettings;
@@ -145,8 +139,6 @@ import jadx.gui.utils.UiUtils;
import jadx.gui.utils.fileswatcher.LiveReloadWorker;
import jadx.gui.utils.ui.ActionHandler;
import jadx.gui.utils.ui.NodeLabel;
import jadx.plugins.mappings.RenameMappingsOptions;
import jadx.plugins.mappings.save.MappingExporter;
import static io.reactivex.internal.functions.Functions.EMPTY_RUNNABLE;
import static javax.swing.KeyStroke.getKeyStroke;
@@ -188,12 +180,6 @@ public class MainWindow extends JFrame {
private transient Action newProjectAction;
private transient Action saveProjectAction;
private transient JMenu openMappingsMenu;
private transient Action saveMappingsAction;
private transient JMenu saveMappingsAsMenu;
private transient Action closeMappingsAction;
private MappingFormat currentMappingFormat;
private boolean renamesChanged = false;
private transient JPanel mainPanel;
private transient JSplitPane treeSplitPane;
@@ -230,12 +216,15 @@ public class MainWindow extends JFrame {
private JMenu pluginsMenu;
private final transient RenameMappingsGui renameMappings;
public MainWindow(JadxSettings settings) {
this.settings = settings;
this.cacheObject = new CacheObject();
this.project = new JadxProject(this);
this.wrapper = new JadxWrapper(this);
this.liveReloadWorker = new LiveReloadWorker(this);
this.renameMappings = new RenameMappingsGui(this);
resetCache();
FontUtils.registerBundledFonts();
@@ -390,100 +379,6 @@ public class MainWindow extends JFrame {
update();
}
private void openMappings(MappingFormat mappingFormat, boolean inverted) {
FileDialogWrapper fileDialog = new FileDialogWrapper(this, FileOpenMode.CUSTOM_OPEN);
fileDialog.setTitle(NLS.str("file.open_mappings"));
if (mappingFormat.hasSingleFile()) {
fileDialog.setFileExtList(Collections.singletonList(mappingFormat.fileExt));
fileDialog.setSelectionMode(JFileChooser.FILES_ONLY);
} else {
fileDialog.setSelectionMode(JFileChooser.DIRECTORIES_ONLY);
}
List<Path> selectedPaths = fileDialog.show();
if (selectedPaths.size() != 1) {
return;
}
settings.setLastOpenFilePath(fileDialog.getCurrentDir());
Path filePath = selectedPaths.get(0);
LOG.info("Loading mappings from: {}", filePath.toAbsolutePath());
project.setMappingsPath(filePath);
currentMappingFormat = mappingFormat;
project.updatePluginOptions(options -> {
options.put(RenameMappingsOptions.FORMAT_OPT, mappingFormat.name());
options.put(RenameMappingsOptions.INVERT_OPT, inverted ? "yes" : "no");
});
reopen();
}
public void closeMappingsAndRemoveFromProject() {
project.setMappingsPath(null);
currentMappingFormat = null;
reopen();
}
private void saveMappings() {
Path savePath = project.getMappingsPath();
Objects.requireNonNull(savePath, "expect mapping path to be set");
if (currentMappingFormat == null) {
try {
currentMappingFormat = MappingReader.detectFormat(savePath);
} catch (IOException e) {
throw new JadxRuntimeException("Failed to save mappings", e);
}
}
renamesChanged = false;
backgroundExecutor.execute(NLS.str("progress.save_mappings"),
() -> new MappingExporter(wrapper.getDecompiler().getRoot())
.exportMappings(savePath, project.getCodeData(), currentMappingFormat),
s -> update());
}
private void saveMappingsAs(MappingFormat mappingFormat) {
FileDialogWrapper fileDialog = new FileDialogWrapper(this, FileOpenMode.CUSTOM_SAVE);
fileDialog.setTitle(NLS.str("file.save_mappings_as"));
if (mappingFormat.hasSingleFile()) {
Path currentDir = Utils.getOrElse(fileDialog.getCurrentDir(), CommonFileUtils.CWD_PATH);
fileDialog.setSelectedFile(currentDir.resolve("mappings." + mappingFormat.fileExt));
fileDialog.setFileExtList(Collections.singletonList(mappingFormat.fileExt));
fileDialog.setSelectionMode(JFileChooser.FILES_ONLY);
} else {
fileDialog.setSelectionMode(JFileChooser.DIRECTORIES_ONLY);
}
List<Path> selectedPaths = fileDialog.show();
if (selectedPaths.size() != 1) {
return;
}
settings.setLastSaveFilePath(fileDialog.getCurrentDir());
Path savePath = selectedPaths.get(0);
// Append file extension if missing
if (mappingFormat.hasSingleFile() && !savePath.getFileName().toString().toLowerCase(Locale.ROOT).endsWith(mappingFormat.fileExt)) {
savePath = savePath.resolveSibling(savePath.getFileName() + "." + mappingFormat.fileExt);
}
// If the target file already exists (and it's not an empty directory), show an overwrite
// confirmation
if (Files.exists(savePath)) {
boolean emptyDir = false;
try (Stream<Path> entries = Files.list(savePath)) {
emptyDir = !entries.findFirst().isPresent();
} catch (IOException ignored) {
}
if (!emptyDir) {
int res = JOptionPane.showConfirmDialog(
this,
NLS.str("confirm.save_as_message", savePath.getFileName()),
NLS.str("confirm.save_as_title"),
JOptionPane.YES_NO_OPTION);
if (res == JOptionPane.NO_OPTION) {
return;
}
}
}
LOG.info("Saving mappings to: {}", savePath.toAbsolutePath());
project.setMappingsPath(savePath);
currentMappingFormat = mappingFormat;
saveMappings();
}
public void addNewScript() {
FileDialogWrapper fileDialog = new FileDialogWrapper(this, FileOpenMode.CUSTOM_SAVE);
fileDialog.setTitle(NLS.str("file.save"));
@@ -625,7 +520,6 @@ public class MainWindow extends JFrame {
private void closeAll() {
notifyLoadListeners(false);
renamesChanged = false;
cancelBackgroundJobs();
clearTree();
resetCache();
@@ -694,10 +588,6 @@ public class MainWindow extends JFrame {
private boolean ensureProjectIsSaved() {
if (!project.isSaved() && !project.isInitial()) {
if (project.getMappingsPath() != null
&& settings.getUserRenamesMappingsMode() == UserRenamesMappingsMode.READ_AND_AUTOSAVE_BEFORE_CLOSING) {
saveMappings();
}
int res = JOptionPane.showConfirmDialog(
this,
NLS.str("confirm.not_saved_message"),
@@ -718,15 +608,12 @@ public class MainWindow extends JFrame {
update();
}
private void update() {
public void update() {
UiUtils.uiThreadGuard();
newProjectAction.setEnabled(!project.isInitial());
saveProjectAction.setEnabled(loaded && !project.isSaved());
openMappingsMenu.setEnabled(loaded);
saveMappingsAction.setEnabled(loaded && renamesChanged && project.getMappingsPath() != null);
saveMappingsAsMenu.setEnabled(loaded && !project.getCodeData().isEmpty());
closeMappingsAction.setEnabled(project.getMappingsPath() != null);
deobfToggleBtn.setSelected(settings.isDeobfuscationOn());
renameMappings.onUpdate(loaded);
Path projectPath = project.getProjectPath();
String pathString;
@@ -739,16 +626,6 @@ public class MainWindow extends JFrame {
+ project.getName() + pathString + " - " + DEFAULT_TITLE);
}
public void renamesChanged() {
if (project.getMappingsPath() != null
&& settings.getUserRenamesMappingsMode() == UserRenamesMappingsMode.READ_AND_AUTOSAVE_EVERY_CHANGE) {
saveMappings();
} else {
renamesChanged = true;
update();
}
}
protected void resetCache() {
cacheObject.reset();
}
@@ -1018,26 +895,6 @@ public class MainWindow extends JFrame {
liveReloadMenuItem = new JCheckBoxMenuItem(liveReload);
liveReloadMenuItem.setState(project.isEnableLiveReload());
openMappingsMenu = new JMenu(NLS.str("file.open_mappings"));
openMappingsMenu.add(new ActionHandler(ev -> openMappings(MappingFormat.PROGUARD, true)).withNameAndDesc("Proguard (inverted)"));
openMappingsMenu.add(new ActionHandler(ev -> openMappings(MappingFormat.PROGUARD, false)).withNameAndDesc("Proguard"));
saveMappingsAction = new ActionHandler(this::saveMappings).withNameAndDesc(NLS.str("file.save_mappings"));
saveMappingsAsMenu = new JMenu(NLS.str("file.save_mappings_as"));
for (MappingFormat mappingFormat : MappingFormat.values()) {
if (mappingFormat != MappingFormat.PROGUARD) {
openMappingsMenu.add(new ActionHandler(ev -> openMappings(mappingFormat, false))
.withNameAndDesc(mappingFormat.name));
}
saveMappingsAsMenu.add(new ActionHandler(ev -> saveMappingsAs(mappingFormat))
.withNameAndDesc(mappingFormat.name));
}
closeMappingsAction = new ActionHandler(ev -> closeMappingsAndRemoveFromProject())
.withNameAndDesc(NLS.str("file.close_mappings"));
Action saveAllAction = new AbstractAction(NLS.str("file.save_all"), Icons.SAVE_ALL) {
@Override
public void actionPerformed(ActionEvent e) {
@@ -1230,11 +1087,7 @@ public class MainWindow extends JFrame {
file.addSeparator();
file.add(reload);
file.add(liveReloadMenuItem);
file.addSeparator();
file.add(openMappingsMenu);
file.add(saveMappingsAction);
file.add(saveMappingsAsMenu);
file.add(closeMappingsAction);
renameMappings.addMenuActions(file);
file.addSeparator();
file.add(saveAllAction);
file.add(exportAction);
@@ -1770,4 +1623,8 @@ public class MainWindow extends JFrame {
public JMenu getPluginsMenu() {
return pluginsMenu;
}
public RenameMappingsGui getRenameMappings() {
return renameMappings;
}
}
@@ -176,7 +176,6 @@ public class RenameDialog extends JDialog {
UiUtils.errorMessage(this, NLS.str("message.memoryLow"));
}
node.reload(mainWindow);
mainWindow.renamesChanged();
});
}
@@ -0,0 +1,38 @@
package jadx.plugins.mappings;
import org.jetbrains.annotations.Nullable;
import net.fabricmc.mappingio.tree.MappingTree;
import jadx.api.plugins.input.data.attributes.IJadxAttrType;
import jadx.api.plugins.input.data.attributes.IJadxAttribute;
import jadx.core.dex.nodes.RootNode;
public class RenameMappingsData implements IJadxAttribute {
private static final IJadxAttrType<RenameMappingsData> DATA = IJadxAttrType.create();
public static @Nullable RenameMappingsData getData(RootNode root) {
return root.getAttributes().get(DATA);
}
public static @Nullable MappingTree getTree(RootNode root) {
RenameMappingsData data = getData(root);
return data == null ? null : data.getMappings();
}
private final MappingTree mappings;
public RenameMappingsData(MappingTree mappings) {
this.mappings = mappings;
}
public MappingTree getMappings() {
return mappings;
}
@Override
public IJadxAttrType<RenameMappingsData> getAttrType() {
return DATA;
}
}
@@ -34,10 +34,9 @@ public class RenameMappingsPlugin implements JadxPlugin {
if (mappingsPath == null || !Files.isReadable(mappingsPath)) {
return;
}
LoadMappingsPass loadPass = new LoadMappingsPass(options);
context.addPass(loadPass);
context.addPass(new ApplyMappingsPass(loadPass));
context.addPass(new CodeMappingsPass(loadPass));
context.addPass(new LoadMappingsPass(options));
context.addPass(new ApplyMappingsPass());
context.addPass(new CodeMappingsPass());
// use mapping file time modification to check for changes
context.registerInputsHashSupplier(() -> FileUtils.md5Sum(getInputsHashString(mappingsPath)));
@@ -15,15 +15,10 @@ import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.FieldNode;
import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.nodes.RootNode;
import jadx.plugins.mappings.RenameMappingsData;
public class ApplyMappingsPass implements JadxPreparePass {
private final LoadMappingsPass loadPass;
public ApplyMappingsPass(LoadMappingsPass loadPass) {
this.loadPass = loadPass;
}
@Override
public JadxPassInfo getInfo() {
return new OrderedJadxPassInfo(
@@ -35,10 +30,11 @@ public class ApplyMappingsPass implements JadxPreparePass {
@Override
public void init(RootNode root) {
MappingTree mappingTree = loadPass.getMappings();
if (mappingTree == null) {
RenameMappingsData data = RenameMappingsData.getData(root);
if (data == null) {
return;
}
MappingTree mappingTree = data.getMappings();
process(root, mappingTree);
root.registerCodeDataUpdateListener(codeData -> process(root, mappingTree));
}
@@ -16,16 +16,12 @@ import jadx.core.dex.instructions.args.SSAVar;
import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.nodes.RootNode;
import jadx.plugins.mappings.RenameMappingsData;
import jadx.plugins.mappings.utils.DalvikToJavaBytecodeUtils;
public class CodeMappingsPass implements JadxDecompilePass {
private final LoadMappingsPass loadPass;
private Map<String, ClassMapping> clsRenamesMap;
public CodeMappingsPass(LoadMappingsPass loadPass) {
this.loadPass = loadPass;
}
@Override
public JadxPassInfo getInfo() {
return new OrderedJadxPassInfo(
@@ -36,10 +32,11 @@ public class CodeMappingsPass implements JadxDecompilePass {
@Override
public void init(RootNode root) {
MappingTree mappingTree = loadPass.getMappings();
if (mappingTree == null) {
RenameMappingsData data = RenameMappingsData.getData(root);
if (data == null) {
return;
}
MappingTree mappingTree = data.getMappings();
updateMappingsMap(mappingTree);
root.registerCodeDataUpdateListener(codeData -> updateMappingsMap(mappingTree));
}
@@ -3,8 +3,6 @@ package jadx.plugins.mappings.load;
import java.nio.file.Path;
import java.util.Collections;
import org.jetbrains.annotations.Nullable;
import net.fabricmc.mappingio.MappingReader;
import net.fabricmc.mappingio.MappingUtil;
import net.fabricmc.mappingio.adapter.MappingSourceNsSwitch;
@@ -17,12 +15,12 @@ import jadx.api.plugins.pass.impl.SimpleJadxPassInfo;
import jadx.api.plugins.pass.types.JadxPreparePass;
import jadx.core.dex.nodes.RootNode;
import jadx.core.utils.exceptions.JadxRuntimeException;
import jadx.plugins.mappings.RenameMappingsData;
import jadx.plugins.mappings.RenameMappingsOptions;
public class LoadMappingsPass implements JadxPreparePass {
private final RenameMappingsOptions options;
private MappingTree mappings;
public LoadMappingsPass(RenameMappingsOptions options) {
this.options = options;
@@ -35,11 +33,8 @@ public class LoadMappingsPass implements JadxPreparePass {
@Override
public void init(RootNode root) {
mappings = loadMapping(root.getArgs());
}
public @Nullable MappingTree getMappings() {
return mappings;
MappingTree mappings = loadMapping(root.getArgs());
root.getAttributes().add(new RenameMappingsData(mappings));
}
private MappingTree loadMapping(JadxArgs args) {
@@ -13,6 +13,7 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -20,6 +21,7 @@ import net.fabricmc.mappingio.MappedElementKind;
import net.fabricmc.mappingio.MappingUtil;
import net.fabricmc.mappingio.MappingWriter;
import net.fabricmc.mappingio.format.MappingFormat;
import net.fabricmc.mappingio.tree.MappingTree;
import net.fabricmc.mappingio.tree.MemoryMappingTree;
import jadx.api.ICodeInfo;
@@ -43,14 +45,18 @@ import jadx.core.dex.nodes.FieldNode;
import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.nodes.RootNode;
import jadx.core.utils.files.FileUtils;
import jadx.plugins.mappings.RenameMappingsData;
import jadx.plugins.mappings.utils.DalvikToJavaBytecodeUtils;
public class MappingExporter {
private static final Logger LOG = LoggerFactory.getLogger(MappingExporter.class);
private final RootNode root;
public MappingExporter(RootNode rootNode) {
this.root = rootNode;
private final RootNode root;
private final @Nullable MappingTree loadedMappingTree;
public MappingExporter(RootNode root) {
this.root = root;
this.loadedMappingTree = RenameMappingsData.getTree(this.root);
}
private List<SimpleEntry<VarNode, Integer>> collectMethodVars(MethodNode methodNode) {
@@ -138,7 +144,10 @@ public class MappingExporter {
String srcNamespace = MappingUtil.NS_SOURCE_FALLBACK;
String dstNamespace = MappingUtil.NS_TARGET_FALLBACK;
if (loadedMappingTree != null && loadedMappingTree.getDstNamespaces() != null) {
srcNamespace = loadedMappingTree.getSrcNamespace();
dstNamespace = loadedMappingTree.getDstNamespaces().get(0);
}
mappingTree.visitHeader();
mappingTree.visitNamespaces(srcNamespace, Collections.singletonList(dstNamespace));
mappingTree.visitContent();
@@ -234,6 +243,10 @@ public class MappingExporter {
}
}
}
// Copy mappings from potentially imported mappings file
if (loadedMappingTree != null && loadedMappingTree.getDstNamespaces() != null) {
loadedMappingTree.accept(mappingTree);
}
// Write file
MappingWriter writer = MappingWriter.create(path, mappingFormat);
mappingTree.accept(writer);