refactor: move mappings feature to separate plugin module

This commit is contained in:
Skylot
2022-08-17 20:53:51 +01:00
parent cb91c8c41c
commit eae9bac938
38 changed files with 346 additions and 540 deletions
@@ -133,17 +133,16 @@ public class JadxCLIArgs {
@Parameter(names = { "--deobf-max" }, description = "max length of name, renamed if longer")
protected int deobfuscationMaxLength = 64;
@Deprecated
@Parameter(
names = { "--deobf-cfg-file" },
description = "deobfuscation mappings file used for JADX auto-generated names (in the JOBF file format), default: same dir and name as input file with '.jobf' extension (deprecated)"
description = "deobfuscation mappings file used for JADX auto-generated names (in the JOBF file format),"
+ " default: same dir and name as input file with '.jobf' extension"
)
protected String generatedRenamesMappingFile;
@Deprecated
@Parameter(
names = { "--deobf-cfg-file-mode" },
description = "set mode for handling the JADX auto-generated names' deobfuscation map file (deprecated):"
description = "set mode for handling the JADX auto-generated names' deobfuscation map file:"
+ "\n 'read' - read if found, don't save (default)"
+ "\n 'read-or-save' - read if found, save otherwise (don't overwrite)"
+ "\n 'overwrite' - don't read, always save"
@@ -424,12 +423,10 @@ public class JadxCLIArgs {
return deobfuscationMaxLength;
}
@Deprecated
public String getGeneratedRenamesMappingFile() {
return generatedRenamesMappingFile;
}
@Deprecated
public GeneratedRenamesMappingFileMode getGeneratedRenamesMappingFileMode() {
return generatedRenamesMappingFileMode;
}
+1 -7
View File
@@ -5,13 +5,6 @@ plugins {
dependencies {
api(project(':jadx-plugins:jadx-plugins-api'))
// TODO: Switch back to upstream once this PR gets merged:
// https://github.com/FabricMC/mapping-io/pull/19
// api 'net.fabricmc:mapping-io:0.3.0'
api files('libs/mapping-io-0.4.0-SNAPSHOT.jar')
// mapping-io's dependencies
runtimeOnly 'org.ow2.asm:asm:9.3'
implementation 'com.google.code.gson:gson:2.10.1'
// TODO: move resources decoding to separate plugin module
@@ -25,6 +18,7 @@ dependencies {
testRuntimeOnly(project(':jadx-plugins:jadx-java-convert'))
testRuntimeOnly(project(':jadx-plugins:jadx-java-input'))
testRuntimeOnly(project(':jadx-plugins:jadx-raung-input'))
testRuntimeOnly(project(':jadx-plugins:jadx-rename-mappings'))
testImplementation 'org.eclipse.jdt:ecj:3.32.0'
testImplementation 'tools.profiler:async-profiler:2.9'
@@ -355,24 +355,20 @@ public class JadxArgs {
this.deobfuscationOn = deobfuscationOn;
}
@Deprecated
public boolean isDeobfuscationForceSave() {
return generatedRenamesMappingFileMode == GeneratedRenamesMappingFileMode.OVERWRITE;
}
@Deprecated
public void setDeobfuscationForceSave(boolean deobfuscationForceSave) {
if (deobfuscationForceSave) {
this.generatedRenamesMappingFileMode = GeneratedRenamesMappingFileMode.OVERWRITE;
}
}
@Deprecated
public GeneratedRenamesMappingFileMode getGeneratedRenamesMappingFileMode() {
return generatedRenamesMappingFileMode;
}
@Deprecated
public void setGeneratedRenamesMappingFileMode(GeneratedRenamesMappingFileMode mode) {
this.generatedRenamesMappingFileMode = mode;
}
@@ -409,12 +405,10 @@ public class JadxArgs {
this.deobfuscationMaxLength = deobfuscationMaxLength;
}
@Deprecated
public File getGeneratedRenamesMappingFile() {
return generatedRenamesMappingFile;
}
@Deprecated
public void setGeneratedRenamesMappingFile(File file) {
this.generatedRenamesMappingFile = file;
}
@@ -673,10 +673,6 @@ public final class JadxDecompiler implements IJadxDecompiler, Closeable {
root.notifyCodeDataListeners();
}
public void reloadMappings() {
root.notifyMappingsListeners();
}
public JadxArgs getArgs() {
return args;
}
@@ -1,6 +1,5 @@
package jadx.api.args;
@Deprecated
public enum GeneratedRenamesMappingFileMode {
/**
@@ -60,9 +60,7 @@ import jadx.core.dex.visitors.regions.LoopRegionVisitor;
import jadx.core.dex.visitors.regions.RegionMakerVisitor;
import jadx.core.dex.visitors.regions.ReturnVisitor;
import jadx.core.dex.visitors.regions.variables.ProcessVariables;
import jadx.core.dex.visitors.rename.CodeMappingsVisitor;
import jadx.core.dex.visitors.rename.CodeRenameVisitor;
import jadx.core.dex.visitors.rename.MappingsVisitor;
import jadx.core.dex.visitors.rename.RenameVisitor;
import jadx.core.dex.visitors.shrink.CodeShrinkVisitor;
import jadx.core.dex.visitors.ssa.SSATransform;
@@ -99,7 +97,6 @@ public class Jadx {
// rename and deobfuscation
passes.add(new DeobfuscatorVisitor());
passes.add(new RenameVisitor());
passes.add(new MappingsVisitor());
passes.add(new SaveDeobfMapping());
passes.add(new UsageInfoVisitor());
@@ -146,7 +143,6 @@ public class Jadx {
passes.add(new ProcessKotlinInternals());
}
passes.add(new CodeRenameVisitor());
passes.add(new CodeMappingsVisitor());
if (args.isInlineMethods()) {
passes.add(new InlineMethods());
}
@@ -218,7 +214,6 @@ public class Jadx {
}
passes.add(new FinishTypeInference());
passes.add(new CodeRenameVisitor());
passes.add(new CodeMappingsVisitor());
passes.add(new DeboxingVisitor());
passes.add(new ModVisitor());
passes.add(new CodeShrinkVisitor());
@@ -24,6 +24,10 @@ public abstract class NotificationAttrNode extends LineAttrNode implements ICode
this.add(AFlag.INCONSISTENT_CODE);
}
public void addCodeComment(String comment) {
addAttr(AType.CODE_COMMENTS, comment);
}
public void addWarnComment(String warn) {
initCommentsAttr().add(CommentsLevel.WARN, warn);
}
@@ -1,8 +0,0 @@
package jadx.core.dex.nodes;
import net.fabricmc.mappingio.tree.MemoryMappingTree;
public interface IMappingsUpdateListener {
void updated(MemoryMappingTree mappingTree);
}
@@ -1,7 +1,6 @@
package jadx.core.dex.nodes;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
@@ -14,10 +13,6 @@ import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.fabricmc.mappingio.MappingReader;
import net.fabricmc.mappingio.MappingUtil;
import net.fabricmc.mappingio.tree.MemoryMappingTree;
import jadx.api.ICodeCache;
import jadx.api.ICodeWriter;
import jadx.api.JadxArgs;
@@ -25,7 +20,6 @@ import jadx.api.JadxDecompiler;
import jadx.api.ResourceFile;
import jadx.api.ResourceType;
import jadx.api.ResourcesLoader;
import jadx.api.args.UserRenamesMappingsMode;
import jadx.api.core.nodes.IRootNode;
import jadx.api.data.ICodeData;
import jadx.api.impl.passes.DecompilePassWrapper;
@@ -72,13 +66,11 @@ public class RootNode implements IRootNode {
private final JadxArgs args;
private final List<IDexTreeVisitor> preDecompilePasses;
private final List<ICodeDataUpdateListener> codeDataUpdateListeners = new ArrayList<>();
private final List<IMappingsUpdateListener> mappingsUpdateListeners = new ArrayList<>();
private final ProcessClass processClasses;
private final ErrorsCounter errorsCounter = new ErrorsCounter();
private final StringUtils stringUtils;
private final ConstStorage constValues;
private MemoryMappingTree mappingTree;
private final InfoStorage infoStorage = new InfoStorage();
private final CacheStorage cacheStorage = new CacheStorage();
private final TypeUpdate typeUpdate;
@@ -219,26 +211,6 @@ public class RootNode implements IRootNode {
} catch (Exception e) {
LOG.error("Failed to parse '.arsc' file", e);
}
if (args.getUserRenamesMappingsMode() != UserRenamesMappingsMode.IGNORE
&& args.getUserRenamesMappingsPath() != null) {
try {
mappingTree = new MemoryMappingTree();
MappingReader.read(args.getUserRenamesMappingsPath(), mappingTree);
if (mappingTree.getSrcNamespace() == null) {
mappingTree.setSrcNamespace(MappingUtil.NS_SOURCE_FALLBACK);
}
if (mappingTree.getDstNamespaces() == null || mappingTree.getDstNamespaces().isEmpty()) {
mappingTree.setDstNamespaces(Arrays.asList(MappingUtil.NS_TARGET_FALLBACK));
} else if (mappingTree.getDstNamespaces().size() > 1) {
throw new JadxRuntimeException(
String.format("JADX only supports mappings with just one destination namespace! The provided ones have %s.",
mappingTree.getDstNamespaces().size()));
}
} catch (Exception e) {
mappingTree = null;
throw new JadxRuntimeException("Failed to load mappings", e);
}
}
}
private void updateManifestAttribMap(IResParser parser) {
@@ -593,19 +565,11 @@ public class RootNode implements IRootNode {
this.codeDataUpdateListeners.add(listener);
}
public void registerMappingsUpdateListener(IMappingsUpdateListener listener) {
this.mappingsUpdateListeners.add(listener);
}
public void notifyCodeDataListeners() {
ICodeData codeData = args.getCodeData();
codeDataUpdateListeners.forEach(l -> l.updated(codeData));
}
public void notifyMappingsListeners() {
mappingsUpdateListeners.forEach(l -> l.updated(mappingTree));
}
public ClspGraph getClsp() {
return clsp;
}
@@ -632,14 +596,6 @@ public class RootNode implements IRootNode {
return constValues;
}
public MemoryMappingTree getMappingTree() {
return mappingTree;
}
public void setMappingTree(MemoryMappingTree mappingTree) {
this.mappingTree = mappingTree;
}
public InfoStorage getInfoStorage() {
return infoStorage;
}
@@ -113,7 +113,6 @@ public class AttachCommentsVisitor extends AbstractVisitor {
if (node == null) {
return;
}
node.remove(AType.CODE_COMMENTS);
node.addAttr(AType.CODE_COMMENTS, comment);
}
@@ -1,109 +0,0 @@
package jadx.core.dex.visitors.rename;
import java.io.File;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.fabricmc.mappingio.tree.MappingTree;
import net.fabricmc.mappingio.tree.MappingTree.ClassMapping;
import net.fabricmc.mappingio.tree.MappingTree.FieldMapping;
import net.fabricmc.mappingio.tree.MappingTree.MethodMapping;
import jadx.core.codegen.TypeGen;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.attributes.nodes.MethodOverrideAttr;
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.core.dex.visitors.AbstractVisitor;
import jadx.core.dex.visitors.JadxVisitor;
@JadxVisitor(
name = "MappingsVisitor",
desc = "Apply mappings to classes, fields and methods",
runAfter = {
RenameVisitor.class
}
)
public class MappingsVisitor extends AbstractVisitor {
private static final Logger LOG = LoggerFactory.getLogger(MappingsVisitor.class);
@Override
public void init(RootNode root) {
List<File> inputFiles = root.getArgs().getInputFiles();
if (inputFiles.isEmpty()) {
return;
}
MappingTree tree = root.getMappingTree();
if (tree == null) {
return;
}
for (ClassNode cls : root.getClasses(true)) {
ClassMapping mapping = tree.getClass(cls.getClassInfo().makeRawFullName().replace('.', '/'));
if (mapping == null) {
continue;
}
processClass(cls, mapping);
}
}
private static void processClass(ClassNode cls, ClassMapping classMapping) {
if (classMapping.getDstName(0) != null) {
cls.getClassInfo().changeShortName(classMapping.getDstName(0));
}
if (classMapping.getComment() != null) {
cls.addInfoComment(classMapping.getComment());
}
// Fields
for (FieldNode field : cls.getFields()) {
FieldMapping fieldMapping =
classMapping.getField(field.getFieldInfo().getName(), TypeGen.signature(field.getFieldInfo().getType()));
if (fieldMapping == null) {
continue;
}
if (fieldMapping.getDstName(0) != null) {
field.getFieldInfo().setAlias(fieldMapping.getDstName(0));
}
if (fieldMapping.getComment() != null) {
field.addInfoComment(fieldMapping.getComment());
}
}
// Methods
String methodName;
String methodDesc;
for (MethodNode method : cls.getMethods()) {
methodName = method.getMethodInfo().getName();
methodDesc = method.getMethodInfo().getShortId().substring(methodName.length());
MethodMapping methodMapping = classMapping.getMethod(methodName, methodDesc);
if (methodMapping == null) {
continue;
}
processMethod(method, methodMapping);
}
}
private static void processMethod(MethodNode method, MethodMapping methodMapping) {
MethodOverrideAttr overrideAttr = method.get(AType.METHOD_OVERRIDE);
if (methodMapping.getDstName(0) != null) {
if (overrideAttr == null) {
method.getMethodInfo().setAlias(methodMapping.getDstName(0));
} else {
for (MethodNode relatedMth : overrideAttr.getRelatedMthNodes()) {
method.getMethodInfo().setAlias(methodMapping.getDstName(0));
}
}
}
if (methodMapping.getComment() != null) {
method.addInfoComment(methodMapping.getComment());
}
// Method args & vars are handled in CodeMappingsVisitor
}
}
@@ -36,6 +36,7 @@ public class RenameVisitor extends AbstractVisitor {
return;
}
process(root);
root.registerCodeDataUpdateListener(codeData -> process(root));
}
private void process(RootNode root) {
+3
View File
@@ -9,6 +9,9 @@ dependencies {
implementation(project(':jadx-core'))
implementation(project(":jadx-cli"))
// import mappings
implementation project(':jadx-plugins:jadx-rename-mappings')
// jadx-script autocomplete support
implementation(project(":jadx-plugins::jadx-script:jadx-script-ide"))
implementation("org.jetbrains.kotlin:kotlin-scripting-common:1.7.20")
+4 -1
View File
@@ -34,7 +34,10 @@ public class JadxGUI {
LafManager.init(settings);
NLS.setLocale(settings.getLangLocale());
ExceptionDialog.registerUncaughtExceptionHandler();
SwingUtilities.invokeLater(new MainWindow(settings)::init);
SwingUtilities.invokeLater(() -> {
MainWindow mw = new MainWindow(settings);
mw.init();
});
} catch (Exception e) {
LOG.error("Error: {}", e.getMessage(), e);
System.exit(1);
@@ -25,9 +25,7 @@ import jadx.api.plugins.JadxPluginManager;
import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.ProcessState;
import jadx.core.dex.nodes.RootNode;
import jadx.core.dex.visitors.rename.RenameVisitor;
import jadx.core.utils.exceptions.JadxRuntimeException;
import jadx.core.utils.files.FileUtils;
import jadx.gui.plugins.context.PluginsContext;
import jadx.gui.settings.JadxProject;
import jadx.gui.settings.JadxSettings;
@@ -50,7 +48,6 @@ public class JadxWrapper {
private final MainWindow mainWindow;
private volatile @Nullable JadxDecompiler decompiler;
private PluginsContext pluginsContext;
private boolean resetDiskCacheOnNextReload = false;
public JadxWrapper(MainWindow mainWindow) {
this.mainWindow = mainWindow;
@@ -62,8 +59,7 @@ public class JadxWrapper {
synchronized (DECOMPILER_UPDATE_SYNC) {
JadxProject project = getProject();
JadxArgs jadxArgs = getSettings().toJadxArgs();
jadxArgs.setInputFiles(FileUtils.toFiles(project.getFilePaths()));
jadxArgs.setCodeData(project.getCodeData());
project.fillJadxArgs(jadxArgs);
this.decompiler = new JadxDecompiler(jadxArgs);
this.pluginsContext = new PluginsContext(mainWindow);
@@ -72,8 +68,8 @@ public class JadxWrapper {
initCodeCache();
}
} catch (Exception e) {
LOG.error("Jadx decompiler wrapper init error", e);
close();
throw new JadxRuntimeException("Jadx decompiler wrapper init error", e);
}
}
@@ -119,15 +115,8 @@ public class JadxWrapper {
}
}
public void resetDiskCacheOnNextReload() {
resetDiskCacheOnNextReload = true;
}
private BufferCodeCache buildBufferedDiskCache() {
DiskCodeCache diskCache = new DiskCodeCache(getDecompiler().getRoot(), getProject(), getSettings());
if (resetDiskCacheOnNextReload) {
diskCache.reset();
}
DiskCodeCache diskCache = new DiskCodeCache(getDecompiler().getRoot(), getProject().getCacheDir());
return new BufferCodeCache(diskCache);
}
@@ -233,18 +222,8 @@ public class JadxWrapper {
return getDecompiler().getRoot();
}
public void reInitRenameVisitor() {
new RenameVisitor().init(getRootNode());
}
public void reloadCodeData() {
getDecompiler().reloadCodeData();
mainWindow.renamesChanged();
}
public void reloadMappings() {
getDecompiler().reloadMappings();
mainWindow.renamesChanged();
}
public JavaNode getJavaNodeByRef(ICodeNodeRef nodeRef) {
@@ -19,6 +19,7 @@ import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import jadx.api.JadxArgs;
import jadx.api.data.ICodeComment;
import jadx.api.data.ICodeRename;
import jadx.api.data.IJavaCodeRef;
@@ -31,6 +32,7 @@ import jadx.api.data.impl.JadxNodeRef;
import jadx.api.plugins.utils.CommonFileUtils;
import jadx.core.utils.GsonUtils;
import jadx.core.utils.exceptions.JadxRuntimeException;
import jadx.core.utils.files.FileUtils;
import jadx.gui.settings.data.ProjectData;
import jadx.gui.settings.data.TabViewState;
import jadx.gui.ui.MainWindow;
@@ -59,6 +61,12 @@ public class JadxProject {
this.mainWindow = mainWindow;
}
public void fillJadxArgs(JadxArgs jadxArgs) {
jadxArgs.setInputFiles(FileUtils.toFiles(getFilePaths()));
jadxArgs.setUserRenamesMappingsPath(getMappingsPath());
jadxArgs.setCodeData(getCodeData());
}
public @Nullable Path getWorkingDir() {
if (projectPath != null) {
return projectPath.toAbsolutePath().getParent();
@@ -164,14 +172,8 @@ public class JadxProject {
}
public void setMappingsPath(Path mappingsPath) {
if (mappingsPath == null) {
data.setMappingsPath(mappingsPath);
changed();
} else if (mappingsPath != getMappingsPath()
&& mappingsPath.toFile().exists()) {
data.setMappingsPath(mappingsPath);
changed();
}
data.setMappingsPath(mappingsPath);
changed();
}
public @NotNull Path getCacheDir() {
@@ -338,10 +338,6 @@ public class JadxSettings extends JadxCLIArgs {
this.debugInfo = useDebugInfo;
}
public void setUserRenamesMappingsPath(Path path) {
this.userRenamesMappingsPath = path;
}
public void setUserRenamesMappingsMode(UserRenamesMappingsMode mode) {
this.userRenamesMappingsMode = mode;
}
@@ -36,6 +36,7 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
@@ -84,9 +85,7 @@ import org.slf4j.LoggerFactory;
import ch.qos.logback.classic.Level;
import net.fabricmc.mappingio.MappingReader;
import net.fabricmc.mappingio.MappingUtil;
import net.fabricmc.mappingio.format.MappingFormat;
import net.fabricmc.mappingio.tree.MemoryMappingTree;
import jadx.api.JadxArgs;
import jadx.api.JavaNode;
@@ -97,6 +96,7 @@ 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;
@@ -105,7 +105,6 @@ import jadx.gui.jobs.BackgroundExecutor;
import jadx.gui.jobs.DecompileTask;
import jadx.gui.jobs.ExportTask;
import jadx.gui.jobs.TaskStatus;
import jadx.gui.plugins.mappings.MappingExporter;
import jadx.gui.plugins.quark.QuarkDialog;
import jadx.gui.settings.JadxProject;
import jadx.gui.settings.JadxSettings;
@@ -149,6 +148,7 @@ import jadx.gui.utils.fileswatcher.LiveReloadWorker;
import jadx.gui.utils.logs.LogCollector;
import jadx.gui.utils.ui.ActionHandler;
import jadx.gui.utils.ui.NodeLabel;
import jadx.plugins.mappings.save.MappingExporter;
import static io.reactivex.internal.functions.Functions.EMPTY_RUNNABLE;
import static javax.swing.KeyStroke.getKeyStroke;
@@ -188,7 +188,6 @@ public class MainWindow extends JFrame {
private final transient BackgroundExecutor backgroundExecutor;
private transient @NotNull JadxProject project;
private boolean projectOpen = false;
private transient Action newProjectAction;
private transient Action saveProjectAction;
@@ -346,7 +345,7 @@ public class MainWindow extends JFrame {
if (!ensureProjectIsSaved()) {
return;
}
closeAll(false);
closeAll();
updateProject(new JadxProject(this));
}
@@ -406,48 +405,19 @@ public class MainWindow extends JFrame {
settings.setLastOpenFilePath(fileDialog.getCurrentDir());
Path filePath = selectedPaths.get(0);
LOG.info("Loading mappings from: {}", filePath.toAbsolutePath());
MemoryMappingTree mappingTree = new MemoryMappingTree();
try {
MappingReader.read(filePath, mappingTree);
} catch (IOException e) {
throw new JadxRuntimeException("Failed to load mappings file", e);
}
if (mappingTree.getSrcNamespace() == null) {
mappingTree.setSrcNamespace(MappingUtil.NS_SOURCE_FALLBACK);
}
if (mappingTree.getDstNamespaces() == null || mappingTree.getDstNamespaces().isEmpty()) {
mappingTree.setDstNamespaces(Arrays.asList(MappingUtil.NS_TARGET_FALLBACK));
} else if (mappingTree.getDstNamespaces().size() > 1) {
JOptionPane.showMessageDialog(
this,
NLS.str("msg.mapping_namespace_count_error", mappingTree.getDstNamespaces().size()),
NLS.str("msg.mapping_namespace_count_error_title"),
JOptionPane.ERROR_MESSAGE);
return;
}
closeMappings(true);
project.setMappingsPath(filePath);
currentMappingFormat = mappingFormat;
reopen();
}
private void closeMappings(boolean resetMappingsMode) {
if (projectOpen) {
wrapper.getRootNode().setMappingTree(null);
}
if (resetMappingsMode) {
wrapper.getSettings().setUserRenamesMappingsPath(null);
wrapper.getSettings().setUserRenamesMappingsMode(UserRenamesMappingsMode.getDefault());
}
}
private void closeMappingsAndRemoveFromProject() {
closeMappings(true);
project.setMappingsPath(null);
currentMappingFormat = null;
}
private void saveMappings() {
Path savePath = project.getMappingsPath();
Objects.requireNonNull(savePath, "expect mapping path to be set");
if (currentMappingFormat == null) {
try {
currentMappingFormat = MappingReader.detectFormat(savePath);
@@ -457,11 +427,8 @@ public class MainWindow extends JFrame {
}
renamesChanged = false;
backgroundExecutor.execute(NLS.str("progress.save_mappings"),
() -> {
new MappingExporter(wrapper.getDecompiler().getRoot())
.exportMappings(savePath, project.getCodeData(), currentMappingFormat);
project.setMappingsPath(savePath);
},
() -> new MappingExporter(wrapper.getDecompiler().getRoot())
.exportMappings(savePath, project.getCodeData(), currentMappingFormat),
s -> update());
}
@@ -469,7 +436,8 @@ public class MainWindow extends JFrame {
FileDialogWrapper fileDialog = new FileDialogWrapper(this, FileOpenMode.CUSTOM_SAVE);
fileDialog.setTitle(NLS.str("file.save_mappings_as"));
if (mappingFormat.hasSingleFile()) {
fileDialog.setSelectedFile(fileDialog.getCurrentDir().resolve("mappings." + mappingFormat.fileExt));
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 {
@@ -554,14 +522,14 @@ public class MainWindow extends JFrame {
private void open(List<Path> paths, Runnable onFinish) {
saveAll();
closeAll(false);
closeAll();
if (paths.size() == 1 && openSingleFile(paths.get(0), onFinish)) {
return;
}
// start new project
project = new JadxProject(this);
project.setFilePaths(paths);
loadFiles(false, onFinish);
loadFiles(onFinish);
}
private boolean openSingleFile(Path singleFile, Runnable onFinish) {
@@ -587,8 +555,8 @@ public class MainWindow extends JFrame {
public synchronized void reopen() {
saveAll();
closeAll(true);
loadFiles(true, EMPTY_RUNNABLE);
closeAll();
loadFiles(EMPTY_RUNNABLE);
}
private void openProject(Path path, Runnable onFinish) {
@@ -603,42 +571,13 @@ public class MainWindow extends JFrame {
}
settings.addRecentProject(path);
project = jadxProject;
loadFiles(false, onFinish);
loadFiles(onFinish);
}
private void loadFiles(boolean reopening, Runnable onFinish) {
private void loadFiles(Runnable onFinish) {
if (project.getFilePaths().isEmpty()) {
return;
}
JadxSettings settings = wrapper.getSettings();
if (settings.getUserRenamesMappingsMode() != UserRenamesMappingsMode.IGNORE) {
// Use CLI specified mappings path if present
if (settings.getUserRenamesMappingsPath() != null && settings.getUserRenamesMappingsPath().toFile().exists()) {
project.setMappingsPath(settings.getUserRenamesMappingsPath());
} else {
if (settings.getUserRenamesMappingsPath() != null) {
LOG.error("The specified mappings path doesn't exist, falling back to the project's previously loaded ones");
}
MappingFormat mappingFormat = null;
try {
mappingFormat = MappingReader.detectFormat(project.getMappingsPath());
} catch (Exception ignored) {
}
// Use the project's last opened mappings, if present
if (mappingFormat != null) {
settings.setUserRenamesMappingsPath(project.getMappingsPath());
currentMappingFormat = mappingFormat;
} else {
if (project.getMappingsPath() != null
|| (project.getMappingsPath() == null && settings.getUserRenamesMappingsPath() != null)) {
LOG.error("The project's last opened mappings path is corrupted, resetting");
}
// None of the mapping paths exist, so remove them from the settings
settings.setUserRenamesMappingsPath(null);
project.setMappingsPath(null);
}
}
}
AtomicReference<Exception> wrapperException = new AtomicReference<>();
backgroundExecutor.execute(NLS.str("progress.load"),
() -> {
@@ -650,7 +589,7 @@ public class MainWindow extends JFrame {
},
status -> {
if (wrapperException.get() != null) {
closeAll(reopening);
closeAll();
Exception e = wrapperException.get();
if (e instanceof RuntimeException) {
throw (RuntimeException) e;
@@ -678,21 +617,17 @@ public class MainWindow extends JFrame {
BreakpointManager.saveAndExit();
}
private void closeAll(boolean reopening) {
private void closeAll() {
notifyLoadListeners(false);
renamesChanged = false;
cancelBackgroundJobs();
clearTree();
if (projectOpen) {
closeMappings(!reopening);
}
resetCache();
LogCollector.getInstance().reset();
wrapper.close();
tabbedPane.closeAllTabs();
UiUtils.resetClipboardOwner();
System.gc();
projectOpen = false;
renamesChanged = false;
update();
}
@@ -716,10 +651,7 @@ public class MainWindow extends JFrame {
}
private void onOpen() {
deobfToggleBtn.setSelected(settings.isDeobfuscationOn());
initTree();
projectOpen = true;
update();
updateLiveReload(project.isEnableLiveReload());
BreakpointManager.init(project.getFilePaths().get(0).toAbsolutePath().getParent());
@@ -730,6 +662,7 @@ public class MainWindow extends JFrame {
restoreOpenTabs(openTabs);
runInitialBackgroundJobs();
notifyLoadListeners(true);
update();
});
}
@@ -755,8 +688,8 @@ public class MainWindow extends JFrame {
private boolean ensureProjectIsSaved() {
if (!project.isSaved() && !project.isInitial()) {
if (wrapper.getRootNode().getMappingTree() != null
&& wrapper.getSettings().getUserRenamesMappingsMode() == UserRenamesMappingsMode.READ_AND_AUTOSAVE_BEFORE_CLOSING) {
if (project.getMappingsPath() != null
&& settings.getUserRenamesMappingsMode() == UserRenamesMappingsMode.READ_AND_AUTOSAVE_BEFORE_CLOSING) {
saveMappings();
}
int res = JOptionPane.showConfirmDialog(
@@ -780,13 +713,15 @@ public class MainWindow extends JFrame {
}
private void update() {
UiUtils.uiThreadGuard();
newProjectAction.setEnabled(!project.isInitial());
saveProjectAction.setEnabled(projectOpen && !project.isSaved());
openMappingsMenu.setEnabled(projectOpen);
saveMappingsAction.setEnabled(projectOpen && renamesChanged == true);
saveMappingsAsMenu.setEnabled(projectOpen && (!project.getCodeData().getRenames().isEmpty()
|| !project.getCodeData().getComments().isEmpty() || wrapper.getRootNode().getMappingTree() != null));
closeMappingsAction.setEnabled(projectOpen && wrapper.getRootNode().getMappingTree() != null);
saveProjectAction.setEnabled(loaded && !project.isSaved());
openMappingsMenu.setEnabled(loaded);
saveMappingsAction.setEnabled(loaded && renamesChanged && project.getMappingsPath() != null);
saveMappingsAsMenu.setEnabled(loaded
&& (!project.getCodeData().getRenames().isEmpty() || !project.getCodeData().getComments().isEmpty()));
closeMappingsAction.setEnabled(loaded && project.getMappingsPath() != null);
deobfToggleBtn.setSelected(settings.isDeobfuscationOn());
Path projectPath = project.getProjectPath();
String pathString;
@@ -800,8 +735,8 @@ public class MainWindow extends JFrame {
}
public void renamesChanged() {
UserRenamesMappingsMode mode = wrapper.getSettings().getUserRenamesMappingsMode();
if (mode == UserRenamesMappingsMode.READ_AND_AUTOSAVE_EVERY_CHANGE) {
if (project.getMappingsPath() != null
&& settings.getUserRenamesMappingsMode() == UserRenamesMappingsMode.READ_AND_AUTOSAVE_EVERY_CHANGE) {
saveMappings();
} else {
renamesChanged = true;
@@ -1723,7 +1658,7 @@ public class MainWindow extends JFrame {
saveSplittersInfo();
}
heapUsageBar.reset();
closeAll(false);
closeAll();
FileUtils.deleteTempRootDir();
dispose();
@@ -31,29 +31,15 @@ import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.fabricmc.mappingio.MappedElementKind;
import net.fabricmc.mappingio.tree.MappingTree.ClassMapping;
import net.fabricmc.mappingio.tree.MappingTree.FieldMapping;
import net.fabricmc.mappingio.tree.MappingTree.MethodMapping;
import net.fabricmc.mappingio.tree.MemoryMappingTree;
import jadx.api.JavaClass;
import jadx.api.JavaField;
import jadx.api.JavaMethod;
import jadx.api.JavaNode;
import jadx.api.data.ICodeRename;
import jadx.api.data.impl.JadxCodeData;
import jadx.core.codegen.TypeGen;
import jadx.core.utils.Utils;
import jadx.gui.jobs.TaskStatus;
import jadx.gui.settings.JadxProject;
import jadx.gui.treemodel.JClass;
import jadx.gui.treemodel.JField;
import jadx.gui.treemodel.JMethod;
import jadx.gui.treemodel.JNode;
import jadx.gui.treemodel.JPackage;
import jadx.gui.treemodel.JRenameNode;
import jadx.gui.treemodel.JVariable;
import jadx.gui.ui.MainWindow;
import jadx.gui.ui.TabbedPane;
import jadx.gui.ui.codearea.ClassCodeContentPanel;
@@ -143,97 +129,6 @@ public class RenameDialog extends JDialog {
if (!newName.isEmpty()) {
renames.add(rename);
}
MemoryMappingTree mappingTree = mainWindow.getWrapper().getRootNode().getMappingTree();
if (mappingTree == null) {
return;
}
if (newName.isEmpty() || (javaNode != null && newName.equals(javaNode.getName()))) {
newName = null;
}
if (node instanceof JMethod) {
JavaMethod javaMethod = ((JMethod) node).getJavaMethod();
String classPath = javaMethod.getDeclaringClass().getClassNode().getClassInfo().makeRawFullName().replace('.', '/');
String methodName = javaMethod.getMethodNode().getMethodInfo().getName();
String methodDesc = javaMethod.getMethodNode().getMethodInfo().getShortId().substring(methodName.length());
if (newName == null) {
MethodMapping mapping = mappingTree.getMethod(classPath, methodName, methodDesc);
if (mapping == null || deleteMappingIfEmpty(mapping, methodName, methodDesc)) {
return;
}
}
mappingTree.visitClass(classPath);
mappingTree.visitMethod(methodName, methodDesc);
mappingTree.visitDstName(MappedElementKind.METHOD, 0, newName);
mappingTree.visitEnd();
} else if (node instanceof JField) {
JavaField javaField = ((JField) node).getJavaField();
String classPath = javaField.getDeclaringClass().getClassNode().getClassInfo().makeRawFullName().replace('.', '/');
String fieldName = javaField.getFieldNode().getFieldInfo().getName();
String fieldDesc = TypeGen.signature(javaField.getFieldNode().getFieldInfo().getType());
if (newName == null) {
FieldMapping mapping = mappingTree.getField(classPath, fieldName, fieldDesc);
if (mapping == null || deleteMappingIfEmpty(mapping, fieldName, fieldDesc)) {
return;
}
}
mappingTree.visitClass(classPath);
mappingTree.visitField(fieldName, fieldDesc);
mappingTree.visitDstName(MappedElementKind.FIELD, 0, newName);
mappingTree.visitEnd();
} else if (node instanceof JClass) {
JavaClass javaClass = ((JClass) node).getCls();
String classPath = javaClass.getClassNode().getClassInfo().makeRawFullName().replace('.', '/');
if (newName == null) {
ClassMapping mapping = mappingTree.getClass(classPath);
if (mapping == null || deleteMappingIfEmpty(mapping)) {
return;
}
}
mappingTree.visitClass(classPath);
mappingTree.visitDstName(MappedElementKind.CLASS, 0, newName);
mappingTree.visitEnd();
} else if (node instanceof JPackage) {
JPackage jPackage = (JPackage) node;
String origPackageName = jPackage.getFullName().replace('.', '/');
for (ClassMapping cls : mappingTree.getClasses()) {
if (!cls.getSrcName().startsWith(origPackageName)) {
continue;
}
if (newName == null) {
newName = "";
}
String newDstName = newName.replace('.', '/') + cls.getDstName(0).substring(newName.length() + 1);
cls.setDstName(newDstName, 0);
}
} else if (node instanceof JVariable) {
// TODO
}
}
private boolean deleteMappingIfEmpty(ClassMapping mapping) {
if (mapping.getFields().isEmpty() && mapping.getMethods().isEmpty()) {
mapping.getTree().removeClass(mapping.getSrcName());
return true;
}
return false;
}
private boolean deleteMappingIfEmpty(MethodMapping mapping, String methodName, String methodDesc) {
if (mapping.getArgs().isEmpty() && mapping.getVars().isEmpty()) {
mapping.getOwner().removeMethod(methodName, methodDesc);
deleteMappingIfEmpty(mapping.getOwner());
return true;
}
return false;
}
private boolean deleteMappingIfEmpty(FieldMapping mapping, String fieldName, String fieldDesc) {
mapping.getOwner().removeMethod(fieldName, fieldDesc);
if (mapping.getOwner().getFields().isEmpty() && mapping.getOwner().getMethods().isEmpty()) {
mapping.getTree().removeClass(mapping.getOwner().getSrcName());
return true;
}
return false;
}
private void updateCodeRenames(Consumer<Set<ICodeRename>> updater) {
@@ -248,12 +143,9 @@ public class RenameDialog extends JDialog {
Collections.sort(list);
codeData.setRenames(list);
project.setCodeData(codeData);
mainWindow.getWrapper().reloadCodeData();
}
private void refreshState() {
mainWindow.getWrapper().reInitRenameVisitor();
List<JavaNode> toUpdate = new ArrayList<>();
if (source != null && source != node) {
toUpdate.add(source.getJavaNode());
@@ -269,20 +161,23 @@ public class RenameDialog extends JDialog {
.collect(Collectors.toSet());
LOG.debug("Classes to update: {}", updatedTopClasses);
refreshTabs(mainWindow.getTabbedPane(), updatedTopClasses);
if (!updatedTopClasses.isEmpty()) {
mainWindow.getBackgroundExecutor().execute("Refreshing",
() -> refreshClasses(updatedTopClasses),
(status) -> {
if (status == TaskStatus.CANCEL_BY_MEMORY) {
mainWindow.showHeapUsageBar();
UiUtils.errorMessage(this, NLS.str("message.memoryLow"));
}
node.reload(mainWindow);
});
if (updatedTopClasses.isEmpty()) {
return;
}
mainWindow.getBackgroundExecutor().execute("Refreshing",
() -> {
mainWindow.getWrapper().reloadCodeData();
UiUtils.uiRunAndWait(() -> refreshTabs(mainWindow.getTabbedPane(), updatedTopClasses));
refreshClasses(updatedTopClasses);
},
(status) -> {
if (status == TaskStatus.CANCEL_BY_MEMORY) {
mainWindow.showHeapUsageBar();
UiUtils.errorMessage(this, NLS.str("message.memoryLow"));
}
node.reload(mainWindow);
mainWindow.renamesChanged();
});
}
private void refreshClasses(Set<JClass> updatedTopClasses) {
@@ -14,6 +14,7 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.FileTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@@ -37,8 +38,6 @@ import jadx.core.dex.nodes.RootNode;
import jadx.core.utils.Utils;
import jadx.core.utils.exceptions.JadxRuntimeException;
import jadx.core.utils.files.FileUtils;
import jadx.gui.settings.JadxProject;
import jadx.gui.settings.JadxSettings;
import static java.nio.file.StandardOpenOption.CREATE;
import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING;
@@ -62,14 +61,13 @@ public class DiskCodeCache implements ICodeCache {
private final Map<String, Integer> namesMap = new ConcurrentHashMap<>();
private final Map<String, Integer> allClsIds;
public DiskCodeCache(RootNode root, JadxProject project, JadxSettings settings) {
Path baseDir = project.getCacheDir();
public DiskCodeCache(RootNode root, Path baseDir) {
srcDir = baseDir.resolve("sources");
metaDir = baseDir.resolve("metadata");
codeVersionFile = baseDir.resolve("code-version");
namesMapFile = baseDir.resolve("names-map");
JadxArgs args = root.getArgs();
codeVersion = buildCodeVersion(args, project, settings);
codeVersion = buildCodeVersion(args);
writePool = Executors.newFixedThreadPool(args.getThreadsCount());
codeMetadataAdapter = new CodeMetadataAdapter(root);
allClsIds = buildClassIdsMap(root.getClasses());
@@ -93,7 +91,7 @@ public class DiskCodeCache implements ICodeCache {
}
}
public void reset() {
private void reset() {
try {
long start = System.currentTimeMillis();
LOG.info("Resetting disk code cache, base dir: {}", srcDir.getParent().toAbsolutePath());
@@ -198,19 +196,24 @@ public class DiskCodeCache implements ICodeCache {
}
}
private String buildCodeVersion(JadxArgs args, JadxProject project, JadxSettings settings) {
long mappingsLastModified = -1;
if (settings.getUserRenamesMappingsMode() != UserRenamesMappingsMode.IGNORE
&& project.getMappingsPath() != null
&& project.getMappingsPath().toFile().exists()) {
mappingsLastModified = project.getMappingsPath().toFile().lastModified();
private String buildCodeVersion(JadxArgs args) {
List<File> inputFiles = new ArrayList<>(args.getInputFiles());
Path userMappingPath = args.getUserRenamesMappingsPath();
if (args.getUserRenamesMappingsMode() != UserRenamesMappingsMode.IGNORE
&& userMappingPath != null
&& Files.exists(userMappingPath)) {
inputFiles.add(userMappingPath.toFile());
}
File generatedMappingFile = args.getGeneratedRenamesMappingFile();
if (args.getGeneratedRenamesMappingFileMode().shouldRead()
&& generatedMappingFile != null
&& generatedMappingFile.exists()) {
inputFiles.add(generatedMappingFile);
}
return DATA_FORMAT_VERSION
+ ":" + Jadx.getVersion()
+ ":" + args.makeCodeArgsHash()
+ ":" + buildInputsHash(args.getInputFiles())
+ ":" + mappingsLastModified;
+ ":" + buildInputsHash(inputFiles);
}
/**
@@ -219,8 +219,6 @@ msg.language_changed_title=Sprache speichern
msg.language_changed=Die neue Sprache wird beim nächsten Start der Anwendung angezeigt.
msg.project_error_title=Fehler
msg.project_error=Projekt konnte nicht geladen werden
#msg.mapping_namespace_count_error_title=
#msg.mapping_namespace_count_error=
msg.cmd_select_class_error=Klasse\n%s auswählen nicht möglich\nSie existiert nicht.
msg.cant_add_comment=Kann hier keinen Kommentar hinzufügen
@@ -219,8 +219,6 @@ msg.language_changed_title=Language changed
msg.language_changed=New language will be displayed the next time application starts.
msg.project_error_title=Error
msg.project_error=Project could not be loaded
msg.mapping_namespace_count_error_title=Error
msg.mapping_namespace_count_error=JADX only supports mappings with just one destination namespace! The provided ones have %s.
msg.cmd_select_class_error=Failed to select the class\n%s\nThe class does not exist.
msg.cant_add_comment=Can't add comment here
@@ -219,8 +219,6 @@ msg.language_changed_title=Idioma cambiado
msg.language_changed=El nuevo idioma se mostrará la próxima vez que la aplicación se inicie.
#msg.project_error_title=
#msg.project_error=
#msg.mapping_namespace_count_error_title=
#msg.mapping_namespace_count_error=
#msg.cmd_select_class_error=
#msg.cant_add_comment=Can't add comment here
@@ -219,8 +219,6 @@ msg.language_changed_title=언어 변경됨
msg.language_changed=다음에 응용 프로그램이 시작되면 새 언어가 표시됩니다.
msg.project_error_title=오류
msg.project_error=프로젝트를 로드 할 수 없습니다.
#msg.mapping_namespace_count_error_title=
#msg.mapping_namespace_count_error=
msg.cmd_select_class_error=클래스를 선택하지 못했습니다.\n%s\n클래스가 없습니다.
msg.cant_add_comment=여기에 주석을 추가할수 없음
@@ -219,8 +219,6 @@ msg.language_changed_title=Idioma alterado
msg.language_changed=Novo idioma será mostrado na próxima inicialização.
msg.project_error_title=Erro
msg.project_error=Projeto não pôde ser carregado
#msg.mapping_namespace_count_error_title=Error
#msg.mapping_namespace_count_error=JADX only supports mappings with just one destination namespace! The provided ones have %s.
msg.cmd_select_class_error=Falha ao selecionar classe\n%s\nA classe não existe.
msg.cant_add_comment=Não é possível adicionar comentários aqui
@@ -219,8 +219,6 @@ msg.language_changed_title=语言已更改
msg.language_changed=新的语言将在下次应用程序启动时显示。
msg.project_error_title=错误
msg.project_error=项目无法加载
#msg.mapping_namespace_count_error_title=
#msg.mapping_namespace_count_error=
msg.cmd_select_class_error=无法选择类\n%s\n该类不存在。
msg.cant_add_comment=无法在此添加注释
@@ -219,8 +219,6 @@ msg.language_changed_title=已更改語言
msg.language_changed=新語言將於下次應用程式啟動時套用。
msg.project_error_title=錯誤
msg.project_error=無法載入專案
#msg.mapping_namespace_count_error_title=
#msg.mapping_namespace_count_error=
msg.cmd_select_class_error=無法選擇類別\n%s\n類別不存在。
msg.cant_add_comment=無法在此新增註解
@@ -11,9 +11,6 @@ import org.slf4j.LoggerFactory;
import jadx.api.ICodeInfo;
import jadx.api.impl.NoOpCodeCache;
import jadx.core.dex.nodes.ClassNode;
import jadx.gui.settings.JadxProject;
import jadx.gui.settings.JadxSettings;
import jadx.gui.ui.MainWindow;
import jadx.gui.utils.codecache.disk.DiskCodeCache;
import jadx.tests.api.IntegrationTest;
@@ -32,10 +29,7 @@ class DiskCodeCacheTest extends IntegrationTest {
ClassNode clsNode = getClassNode(DiskCodeCacheTest.class);
ICodeInfo codeInfo = clsNode.getCode();
JadxSettings settings = new JadxSettings();
JadxProject project = new JadxProject(new MainWindow(settings));
project.setCacheDir(tempDir);
DiskCodeCache cache = new DiskCodeCache(clsNode.root(), project, settings);
DiskCodeCache cache = new DiskCodeCache(clsNode.root(), tempDir);
String clsKey = clsNode.getFullName();
cache.add(clsKey, codeInfo);
@@ -12,10 +12,6 @@ public class OrderedJadxPassInfo implements JadxPassInfo {
private final List<String> runAfter;
private final List<String> runBefore;
public OrderedJadxPassInfo(String name) {
this(name, name);
}
public OrderedJadxPassInfo(String name, String desc) {
this(name, desc, new ArrayList<>(), new ArrayList<>());
}
@@ -27,6 +23,16 @@ public class OrderedJadxPassInfo implements JadxPassInfo {
this.runBefore = runBefore;
}
public OrderedJadxPassInfo after(String pass) {
runAfter.add(pass);
return this;
}
public OrderedJadxPassInfo before(String pass) {
runBefore.add(pass);
return this;
}
@Override
public String getName() {
return name;
@@ -0,0 +1,26 @@
plugins {
id 'jadx-library'
id 'com.github.johnrengelman.shadow' version '7.1.2'
}
dependencies {
api(project(":jadx-core"))
// TODO: Switch back to upstream once this PR gets merged:
// https://github.com/FabricMC/mapping-io/pull/19
// implementation 'net.fabricmc:mapping-io:0.3.0'
api(files('libs/mapping-io-0.4.0-SNAPSHOT.jar'))
constraints {
runtimeOnly 'org.ow2.asm:asm:9.3'
}
}
publishing {
publications {
shadow(MavenPublication) { publication ->
project.shadow.component(publication)
}
}
}
@@ -0,0 +1,62 @@
package jadx.plugins.mappings;
import java.util.Collections;
import net.fabricmc.mappingio.MappingReader;
import net.fabricmc.mappingio.MappingUtil;
import net.fabricmc.mappingio.tree.MappingTree;
import net.fabricmc.mappingio.tree.MemoryMappingTree;
import jadx.api.JadxArgs;
import jadx.api.JadxDecompiler;
import jadx.api.args.UserRenamesMappingsMode;
import jadx.api.plugins.JadxPlugin;
import jadx.api.plugins.JadxPluginContext;
import jadx.api.plugins.JadxPluginInfo;
import jadx.api.plugins.pass.JadxPassContext;
import jadx.core.utils.exceptions.JadxRuntimeException;
import jadx.plugins.mappings.load.CodeMappingsVisitor;
import jadx.plugins.mappings.load.MappingsVisitor;
public class RenameMappingsPlugin implements JadxPlugin {
@Override
public JadxPluginInfo getPluginInfo() {
return new JadxPluginInfo("jadx-rename-mappings", "Rename Mappings", "various mappings support");
}
@Override
public void init(JadxPluginContext context) {
JadxArgs args = ((JadxDecompiler) context.getDecompiler()).getArgs();
MappingTree mappingTree = openMapping(args);
if (mappingTree != null) {
JadxPassContext passContext = context.getPassContext();
passContext.addPass(new MappingsVisitor(mappingTree));
passContext.addPass(new CodeMappingsVisitor(mappingTree));
}
}
public MappingTree openMapping(JadxArgs args) {
if (args.getUserRenamesMappingsMode() != UserRenamesMappingsMode.IGNORE
&& args.getUserRenamesMappingsPath() != null) {
try {
MemoryMappingTree mappingTree = new MemoryMappingTree();
MappingReader.read(args.getUserRenamesMappingsPath(), mappingTree);
if (mappingTree.getSrcNamespace() == null) {
mappingTree.setSrcNamespace(MappingUtil.NS_SOURCE_FALLBACK);
}
if (mappingTree.getDstNamespaces() == null || mappingTree.getDstNamespaces().isEmpty()) {
mappingTree.setDstNamespaces(Collections.singletonList(MappingUtil.NS_TARGET_FALLBACK));
} else if (mappingTree.getDstNamespaces().size() > 1) {
throw new JadxRuntimeException(
String.format("JADX only supports mappings with just one destination namespace! The provided ones have %s.",
mappingTree.getDstNamespaces().size()));
}
return mappingTree;
} catch (Exception e) {
throw new JadxRuntimeException("Failed to load mappings", e);
}
}
return null;
}
}
@@ -1,51 +1,52 @@
package jadx.core.dex.visitors.rename;
package jadx.plugins.mappings.load;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.fabricmc.mappingio.tree.MappingTree;
import net.fabricmc.mappingio.tree.MappingTree.ClassMapping;
import net.fabricmc.mappingio.tree.MappingTree.MethodArgMapping;
import net.fabricmc.mappingio.tree.MappingTree.MethodMapping;
import net.fabricmc.mappingio.tree.MemoryMappingTree;
import jadx.api.core.nodes.IClassNode;
import jadx.api.core.nodes.IMethodNode;
import jadx.api.core.nodes.IRootNode;
import jadx.api.plugins.pass.JadxPassInfo;
import jadx.api.plugins.pass.impl.OrderedJadxPassInfo;
import jadx.api.plugins.pass.types.JadxDecompilePass;
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.core.dex.visitors.AbstractVisitor;
import jadx.core.dex.visitors.InitCodeVariables;
import jadx.core.dex.visitors.JadxVisitor;
import jadx.core.dex.visitors.debuginfo.DebugInfoApplyVisitor;
import jadx.core.utils.exceptions.JadxException;
import jadx.core.utils.mappings.DalvikToJavaBytecodeUtils;
@JadxVisitor(
name = "ApplyCodeMappings",
desc = "Apply mappings to method args and vars",
runAfter = {
InitCodeVariables.class,
DebugInfoApplyVisitor.class
}
)
public class CodeMappingsVisitor extends AbstractVisitor {
private static final Logger LOG = LoggerFactory.getLogger(CodeMappingsVisitor.class);
import jadx.plugins.mappings.utils.DalvikToJavaBytecodeUtils;
public class CodeMappingsVisitor implements JadxDecompilePass {
private final MappingTree mappingTree;
private Map<String, ClassMapping> clsRenamesMap;
@Override
public void init(RootNode root) throws JadxException {
updateMappingsMap(root.getMappingTree());
root.registerMappingsUpdateListener(this::updateMappingsMap);
public CodeMappingsVisitor(MappingTree mappingTree) {
this.mappingTree = mappingTree;
}
@Override
public boolean visit(ClassNode cls) {
public JadxPassInfo getInfo() {
return new OrderedJadxPassInfo(
"ApplyCodeMappings",
"Apply mappings to method args and vars")
.before("CodeRenameVisitor");
}
@Override
public void init(IRootNode iroot) {
RootNode root = (RootNode) iroot;
updateMappingsMap();
root.registerCodeDataUpdateListener(codeData -> updateMappingsMap());
}
@Override
public boolean visit(IClassNode icls) {
ClassNode cls = (ClassNode) icls;
ClassMapping classMapping = getMapping(cls);
if (classMapping != null) {
applyRenames(cls, classMapping);
@@ -54,6 +55,10 @@ public class CodeMappingsVisitor extends AbstractVisitor {
return false;
}
@Override
public void visit(IMethodNode mth) {
}
private static void applyRenames(ClassNode cls, ClassMapping classMapping) {
for (MethodNode mth : cls.getMethods()) {
String methodName = mth.getMethodInfo().getName();
@@ -86,15 +91,11 @@ public class CodeMappingsVisitor extends AbstractVisitor {
return null;
}
String classPath = cls.getClassInfo().makeRawFullName().replace('.', '/');
ClassMapping clsMapping = clsRenamesMap.get(classPath);
return clsMapping;
return clsRenamesMap.get(classPath);
}
private void updateMappingsMap(@Nullable MemoryMappingTree mappingTree) {
private void updateMappingsMap() {
clsRenamesMap = new HashMap<>();
if (mappingTree == null) {
return;
}
for (ClassMapping cls : mappingTree.getClasses()) {
for (MethodMapping mth : cls.getMethods()) {
if (!mth.getArgs().isEmpty() || !mth.getVars().isEmpty()) {
@@ -0,0 +1,102 @@
package jadx.plugins.mappings.load;
import net.fabricmc.mappingio.tree.MappingTree;
import net.fabricmc.mappingio.tree.MappingTree.ClassMapping;
import net.fabricmc.mappingio.tree.MappingTree.FieldMapping;
import net.fabricmc.mappingio.tree.MappingTree.MethodMapping;
import jadx.api.core.nodes.IRootNode;
import jadx.api.plugins.pass.JadxPassInfo;
import jadx.api.plugins.pass.impl.OrderedJadxPassInfo;
import jadx.api.plugins.pass.types.JadxPreparePass;
import jadx.core.codegen.TypeGen;
import jadx.core.dex.info.FieldInfo;
import jadx.core.dex.info.MethodInfo;
import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.FieldNode;
import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.nodes.RootNode;
public class MappingsVisitor implements JadxPreparePass {
private final MappingTree mappingTree;
public MappingsVisitor(MappingTree mappingTree) {
this.mappingTree = mappingTree;
}
@Override
public JadxPassInfo getInfo() {
return new OrderedJadxPassInfo(
"MappingVisitor",
"Apply mappings to classes, fields and methods")
.before("RenameVisitor");
}
@Override
public void init(IRootNode iroot) {
RootNode root = (RootNode) iroot;
process(root);
root.registerCodeDataUpdateListener(codeData -> process(root));
}
private void process(RootNode root) {
for (ClassNode cls : root.getClasses()) {
ClassMapping mapping = mappingTree.getClass(cls.getClassInfo().makeRawFullName().replace('.', '/'));
if (mapping == null) {
continue;
}
processClass(cls, mapping);
}
}
private static void processClass(ClassNode cls, ClassMapping classMapping) {
String alias = classMapping.getDstName(0);
if (alias != null) {
cls.rename(alias.replace('/', '.'));
}
if (classMapping.getComment() != null) {
cls.addCodeComment(classMapping.getComment());
}
for (FieldNode field : cls.getFields()) {
FieldInfo fieldInfo = field.getFieldInfo();
String signature = TypeGen.signature(fieldInfo.getType());
FieldMapping fieldMapping = classMapping.getField(fieldInfo.getName(), signature);
if (fieldMapping != null) {
processField(field, fieldMapping);
}
}
for (MethodNode method : cls.getMethods()) {
MethodInfo methodInfo = method.getMethodInfo();
String methodName = methodInfo.getName();
String methodDesc = methodInfo.getShortId().substring(methodName.length());
MethodMapping methodMapping = classMapping.getMethod(methodName, methodDesc);
if (methodMapping != null) {
processMethod(method, methodMapping);
}
}
}
private static void processField(FieldNode field, FieldMapping fieldMapping) {
String alias = fieldMapping.getDstName(0);
if (alias != null) {
field.rename(alias);
}
String comment = fieldMapping.getComment();
if (comment != null) {
field.addCodeComment(comment);
}
}
private static void processMethod(MethodNode method, MethodMapping methodMapping) {
String alias = methodMapping.getDstName(0);
if (alias != null) {
method.rename(alias);
}
String comment = methodMapping.getComment();
if (comment != null) {
method.addCodeComment(comment);
}
// Method args & vars are handled in CodeMappingsVisitor
}
}
@@ -1,10 +1,11 @@
package jadx.gui.plugins.mappings;
package jadx.plugins.mappings.save;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -42,7 +43,7 @@ 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.core.utils.mappings.DalvikToJavaBytecodeUtils;
import jadx.plugins.mappings.utils.DalvikToJavaBytecodeUtils;
public class MappingExporter {
private static final Logger LOG = LoggerFactory.getLogger(MappingExporter.class);
@@ -128,22 +129,18 @@ public class MappingExporter {
try {
if (mappingFormat.hasSingleFile()) {
if (path.toFile().exists()) {
path.toFile().delete();
}
path.toFile().createNewFile();
FileUtils.deleteFileIfExists(path);
FileUtils.makeDirsForFile(path);
Files.createFile(path);
} else {
FileUtils.makeDirs(path);
}
String srcNamespace = MappingUtil.NS_SOURCE_FALLBACK;
String dstNamespace = MappingUtil.NS_TARGET_FALLBACK;
if (root.getMappingTree() != null && root.getMappingTree().getDstNamespaces() != null) {
srcNamespace = root.getMappingTree().getSrcNamespace();
dstNamespace = root.getMappingTree().getDstNamespaces().get(0);
}
mappingTree.visitHeader();
mappingTree.visitNamespaces(srcNamespace, Arrays.asList(dstNamespace));
mappingTree.visitNamespaces(srcNamespace, Collections.singletonList(dstNamespace));
mappingTree.visitContent();
for (ClassNode cls : root.getClasses()) {
@@ -237,10 +234,6 @@ public class MappingExporter {
}
}
}
// Copy mappings from potentially imported mappings file
if (root.getMappingTree() != null && root.getMappingTree().getDstNamespaces() != null) {
root.getMappingTree().accept(mappingTree);
}
// Write file
MappingWriter writer = MappingWriter.create(path, mappingFormat);
mappingTree.accept(writer);
@@ -1,4 +1,4 @@
package jadx.core.utils.mappings;
package jadx.plugins.mappings.utils;
import java.util.ArrayList;
import java.util.List;
@@ -0,0 +1 @@
jadx.plugins.mappings.RenameMappingsPlugin
+1
View File
@@ -11,6 +11,7 @@ include("jadx-plugins:jadx-java-input")
include("jadx-plugins:jadx-raung-input")
include("jadx-plugins:jadx-smali-input")
include("jadx-plugins:jadx-java-convert")
include("jadx-plugins:jadx-rename-mappings")
include("jadx-plugins:jadx-script:jadx-script-plugin")
include("jadx-plugins:jadx-script:jadx-script-runtime")